updates setup script to open subshell
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Tue, 4 Aug 2026 07:45:27 +0000 (07:45 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Tue, 4 Aug 2026 07:45:27 +0000 (07:45 +0000)
developer/authored/Manuscript.copy/Element/endnote.js
developer/authored/Manuscript.copy/Element/grid.js
setup [changed mode: 0644->0755]

index 3151a34..53e84c7 100644 (file)
 /*
-  Element/endnote.js
-  Processes <RT·endnote> tags inline and dumps them when <RT·endnotes> is encountered.
+  Element/grid.js
+  Compiles semantic tabular structures into a Cartesian GridState.
 */
 
-(function(){
+(function() {
+  if (!window.RT) return;
 
-  if(!window.RT) return;
-
-  const apply_style = function(link, config) {
-    link.style.cursor = 'pointer';
-    link.style.color = config.brand_link || '#0056b3';
-    link.style.textDecoration = 'none';
-  };
-
-  const apply_list_style = function(list_container, config) {
-    list_container.style.marginTop = '1rem';
-    list_container.style.borderTop = '1px solid ' + (config.surface_3 || '#ccc');
-    list_container.style.paddingTop = '1rem';
-    list_container.style.listStyle = 'none';
-    list_container.style.paddingLeft = '0';
-    list_container.style.margin = '0';
-  };
-
-  function process_endnotes(){
-    const config = window.RT.layout_config || {};
-    const article = document.querySelector('RT·article, rt·article, RT·memo, rt·memo');
-    
-    if(!article) return;
-
-    const initial_make = document.createElement('RT·counter·make');
-    initial_make.setAttribute('counter', 'EndNoteCounter');
-    initial_make.setAttribute('style', 'CountingNumber');
-    initial_make.setAttribute('on-first-step', '0');
-    article.insertBefore(initial_make, article.firstChild);
-
-    const nodes = document.querySelectorAll('RT·endnote, rt·endnote, RT-endnote, rt-endnote, RT·endnotes, rt·endnotes, RT-endnotes, rt-endnotes');
-    
-    let endnote_buffer = [];
-    let anchor_id_counter = 1;
-
-    for(let i = 0; i < nodes.length; i++){
-      const node = nodes[i];
-      const tag = node.tagName.toLowerCase();
-
-      if(tag === 'rt·endnote' || tag === 'rt-endnote'){
-        const snap_name = 'endnote_cite_' + anchor_id_counter;
-        const ref_text = node.innerHTML;
-
-        const step = document.createElement('RT·counter·step');
-        step.setAttribute('counter', 'EndNoteCounter');
+  class GridState {
+    constructor() {
+      this.cells = [];
+      this.max_x = 0;
+      this.max_y = 0;
+    }
 
-        const snapshot = document.createElement('RT·counter·snapshot');
-        snapshot.setAttribute('counter', 'EndNoteCounter');
-        snapshot.setAttribute('snapshot', snap_name);
+    insert(cell_data) {
+      this.cells.push(cell_data);
+      if (cell_data.x_extent > this.max_x) this.max_x = cell_data.x_extent;
+      if (cell_data.y_extent > this.max_y) this.max_y = cell_data.y_extent;
+    }
+  }
 
-        const link = document.createElement('a');
-        link.href = '#note_' + anchor_id_counter;
-        link.id = 'cite_' + anchor_id_counter;
-        link.innerHTML = '[<RT·counter·read snapshot="' + snap_name + '"></RT·counter·read>]';
-        
-        apply_style(link, config);
+  function pad_headers(grid_state) {
+    const max_x = grid_state.max_x;
+    const max_y = grid_state.max_y;
+
+    // Pad horizontal headers (x-label)
+    const x_labels = grid_state.cells.filter(c => c.type === 'x-label');
+    if (x_labels.length > 0) {
+      const header_y = x_labels[0].y;
+      const existing_xs = new Set(x_labels.map(c => c.x));
+      for (let i = 0; i <= max_x; i++) {
+        if (!existing_xs.has(i)) {
+          const empty_el = document.createElement('RT·e');
+          empty_el.textContent = '';
+          grid_state.insert({
+            element: empty_el, type: 'x-label',
+            x: i, y: header_y, x_extent: i, y_extent: header_y
+          });
+        }
+      }
+    }
 
-        step.appendChild(snapshot);
-        step.appendChild(link);
+    // Pad vertical headers (y-label)
+    const y_labels = grid_state.cells.filter(c => c.type === 'y-label');
+    if (y_labels.length > 0) {
+      const header_x = y_labels[0].x;
+      const existing_ys = new Set(y_labels.map(c => c.y));
+      for (let i = 0; i <= max_y; i++) {
+        if (!existing_ys.has(i)) {
+          const empty_el = document.createElement('RT·e');
+          empty_el.textContent = '';
+          grid_state.insert({
+            element: empty_el, type: 'y-label',
+            x: header_x, y: i, x_extent: header_x, y_extent: i
+          });
+        }
+      }
+    }
 
-        node.parentNode.replaceChild(step, node);
+    // Pad tuple names (name)
+    const names = grid_state.cells.filter(c => c.type === 'name');
+    if (names.length > 0) {
+      const name_x = names[0].x;
+      const existing_ys = new Set(names.map(c => c.y));
+      for (let i = 0; i <= max_y; i++) {
+        if (!existing_ys.has(i)) {
+          const empty_el = document.createElement('RT·e');
+          empty_el.textContent = '';
+          grid_state.insert({
+            element: empty_el, type: 'name',
+            x: name_x, y: i, x_extent: name_x, y_extent: i
+          });
+        }
+      }
+    }
+  }
 
-        endnote_buffer.push({ id: anchor_id_counter, text: ref_text, snap: snap_name });
-        anchor_id_counter++;
+  const apply_style = function(el, cell, is_transposed, options, config) {
+    el.style.padding = '0.25rem 0.5rem';
+    el.style.margin = '0';
+    el.style.lineHeight = '1.3';
 
-      } else if(tag === 'rt·endnotes' || tag === 'rt-endnotes'){
-        if(endnote_buffer.length === 0){
-          node.parentNode.removeChild(node);
-          continue;
-        }
+    if (cell.type === 'x-label' || cell.type === 'y-label' || cell.type === 'name') {
+      el.style.fontWeight = '600';
+    }
 
-        const frag = document.createDocumentFragment();
+    // Direct cell boundary mapping handles continuous lines if cells are padded
+    if (cell.type === 'x-label') {
+      if (is_transposed) el.style.borderRight = '2px solid ' + (config.border_strong || '#000');
+      else el.style.borderBottom = '2px solid ' + (config.border_strong || '#000');
+    }
+    if (cell.type === 'y-label') {
+      if (is_transposed) el.style.borderBottom = '2px solid ' + (config.border_strong || '#000');
+      else el.style.borderRight = '2px solid ' + (config.border_strong || '#000');
+    }
+    if (cell.type === 'name') {
+      if (is_transposed) el.style.borderBottom = '1px solid ' + (config.border_faint || '#ccc');
+      else el.style.borderRight = '1px solid ' + (config.border_faint || '#ccc');
+    }
 
-        const pb = document.createElement('RT·page-break');
-        frag.appendChild(pb);
+    if (cell.type === 'data') el.style.textAlign = 'center';
+    if (cell.type === 'x-label') el.style.textAlign = 'center';
+    
+    if (cell.type === (is_transposed ? 'x-label' : 'y-label') || cell.type === 'name') {
+      el.style.textAlign = 'right';
+      el.style.paddingRight = '0.5rem'; 
+    }
 
-        const header = document.createElement('h1');
-        header.innerText = 'Endnotes';
-        frag.appendChild(header);
+    if (options && options.no_wrap) {
+      el.style.whiteSpace = 'nowrap';
+      el.style.padding = '0.15rem 0.5rem';
+    }
+  };
 
-        const list_container = document.createElement('ul');
-        list_container.className = 'RT_endnote_list';
-        apply_list_style(list_container, config);
+  function project_grid(container_node, grid_state, model, options = {}) {
+    const config = window.RT.layout_config || {};
+    switch (model) {
+      case 'html-grid-direct':
+        return render_model_html_standard(container_node, grid_state, options, false, config);
+      case 'html-grid-transpose':
+        return render_model_html_standard(container_node, grid_state, options, true, config);
+      case 'html-grid-dictionary':
+        return render_model_html_dictionary(container_node, grid_state, options, config);
+    }
+  }
 
-        for(let j = 0; j < endnote_buffer.length; j++){
-          const item = endnote_buffer[j];
+  function render_model_html_standard(container_node, grid_state, options, is_transposed, config) {
+    const wrapper = document.createElement('div');
+    wrapper.style.display = 'grid';
+    wrapper.style.justifyContent = 'start';
+    wrapper.className = `RT_grid_container ${options.css_class || ''}`;
+
+    if (options.delimiters) {
+      wrapper.style.borderLeft = '2px solid ' + (config.content_main || '#000');
+      wrapper.style.borderRight = '2px solid ' + (config.content_main || '#000');
+      wrapper.style.borderRadius = '5px'; 
+      wrapper.style.padding = '0.2rem';
+      wrapper.style.margin = '1rem 0';
+    } else {
+      wrapper.style.margin = '1.5rem 0';
+    }
 
-          const li = document.createElement('li');
-          li.id = 'note_' + item.id;
-          li.style.display = 'flex';
-          li.style.marginBottom = '0.5rem';
+    grid_state.cells.forEach(cell => {
+      let render_x = is_transposed ? cell.y : cell.x;
+      let render_y = is_transposed ? cell.x : cell.y;
+      let render_x_extent = is_transposed ? cell.y_extent : cell.x_extent;
+      let render_y_extent = is_transposed ? cell.x_extent : cell.y_extent;
+
+      const el = cell.element;
+      el.style.gridColumn = `${render_x + 1} / ${render_x_extent + 2}`;
+      el.style.gridRow = `${render_y + 1} / ${render_y_extent + 2}`;
+      el.className = `RT_grid_${cell.type}`;
+      
+      apply_style(el, cell, is_transposed, options, config);
+      wrapper.appendChild(el);
+    });
+
+    container_node.replaceWith(wrapper);
+    execute_two_pass_measurement(wrapper, options);
+  }
 
-          const left_div = document.createElement('div');
-          left_div.style.marginRight = '0.5rem';
-          left_div.innerHTML = '[<RT·counter·read snapshot="' + item.snap + '"></RT·counter·read>]';
+  function render_model_html_dictionary(container_node, grid_state, options, config) {
+    const wrapper = document.createElement('div');
+    wrapper.style.display = 'grid';
+    wrapper.style.gridAutoColumns = 'max-content';
+    wrapper.style.justifyContent = 'start';
+    wrapper.className = `RT_grid_container ${options.css_class || ''}`;
+    wrapper.style.margin = '1.5rem 0';
+
+    grid_state.cells.forEach(cell => {
+      const el = cell.element;
+      el.style.gridColumn = `${cell.x + 1} / ${cell.x_extent + 2}`;
+      el.style.gridRow = `${cell.y + 1} / ${cell.y_extent + 2}`;
+      el.className = `RT_grid_${cell.type}`;
+      
+      apply_style(el, cell, false, options, config);
+      if (cell.type === 'data') el.style.textAlign = 'left';
+
+      wrapper.appendChild(el);
+    });
+
+    container_node.replaceWith(wrapper);
+    execute_two_pass_measurement(wrapper, options);
+  }
 
-          const right_div = document.createElement('div');
-          const return_link = document.createElement('a');
-          return_link.href = '#cite_' + item.id;
-          return_link.style.textDecoration = 'none';
-          return_link.innerHTML = '&#8617;';
-          return_link.style.marginLeft = '0.5em';
+  function execute_two_pass_measurement(wrapper, options) {
+    requestAnimationFrame(() => {
+      if (options.wrap_check) {
+        const data_cells = wrapper.querySelectorAll('.RT_grid_data, .RT_grid_name');
+        data_cells.forEach(cell => {
+          const computed = window.getComputedStyle(cell);
+          const line_height = parseFloat(computed.lineHeight) || (parseFloat(computed.fontSize) * 1.2);
+          const pTop = parseFloat(computed.paddingTop) || 0;
+          const pBot = parseFloat(computed.paddingBottom) || 0;
+          const content_height = cell.scrollHeight - pTop - pBot;
+          
+          if (content_height > line_height * 1.5) {
+            cell.style.paddingBottom = '1.25rem'; 
+          }
+        });
+      }
+    });
+  }
 
-          right_div.innerHTML = item.text + ' ';
-          right_div.appendChild(return_link);
+  function parse_coordinate(attr_value, current_val) {
+    if (!attr_value) return { start: current_val, extent: current_val };
+    const parts = attr_value.split('-');
+    const start = parseInt(parts[0], 10);
+    const extent = parts.length > 1 ? parseInt(parts[1], 10) : start;
+    return { start, extent };
+  }
 
-          li.appendChild(left_div);
-          li.appendChild(right_div);
-          list_container.appendChild(li);
+  RT.Element.add(function process_grids() {
+    document.querySelectorAll('RT·grid, rt·grid').forEach(node => {
+      const state = new GridState();
+      const model = node.getAttribute('model') || 'html-grid-direct';
+      const major_axis = node.getAttribute('major') || 'x';
+      
+      let cursor_x = 0;
+      let cursor_y = 0;
+
+      node.querySelectorAll('RT·e, rt·e').forEach(e => {
+        const attr_x = e.getAttribute('x');
+        const attr_y = e.getAttribute('y');
+        
+        if (major_axis === 'x') {
+          if (attr_y && !attr_x && attr_y !== String(cursor_y)) cursor_x = 0;
+        } else {
+          if (attr_x && !attr_y && attr_x !== String(cursor_x)) cursor_y = 0;
         }
 
-        frag.appendChild(list_container);
-
-        const counter_reset = document.createElement('RT·counter·make');
-        counter_reset.setAttribute('counter', 'EndNoteCounter');
-        counter_reset.setAttribute('style', 'CountingNumber');
-        counter_reset.setAttribute('on-first-step', '0');
-        frag.appendChild(counter_reset);
+        const parsed_x = parse_coordinate(attr_x, cursor_x);
+        const parsed_y = parse_coordinate(attr_y, cursor_y);
+
+        cursor_x = parsed_x.start;
+        cursor_y = parsed_y.start;
+
+        const type = e.getAttribute('type') || 'data';
+        state.insert({ 
+          element: e.cloneNode(true), type: type,
+          x: cursor_x, y: cursor_y, 
+          x_extent: parsed_x.extent, y_extent: parsed_y.extent 
+        });
+
+        if (major_axis === 'x') cursor_x = parsed_x.extent + 1;
+        else cursor_y = parsed_y.extent + 1;
+      });
+
+      pad_headers(state);
+      project_grid(node, state, model, { wrap_check: true });
+    });
+
+    document.querySelectorAll('RT·dictionary, rt·dictionary').forEach(node => {
+      const state = new GridState();
+      const key_label = node.getAttribute('key');
+      const def_label = node.getAttribute('definition');
+      
+      let y = 0;
+
+      if (key_label || def_label) {
+        const h1 = document.createElement('RT·e'); h1.textContent = key_label || '';
+        const h2 = document.createElement('RT·e'); h2.textContent = def_label || '';
+        state.insert({ element: h1, type: 'x-label', x: 0, y: y, x_extent: 0, y_extent: y });
+        state.insert({ element: h2, type: 'x-label', x: 1, y: y, x_extent: 1, y_extent: y });
+        y++;
+      }
 
-        // Unpack fragment directly into the article context to enable native UL splitting
-        node.parentNode.replaceChild(frag, node);
+      node.querySelectorAll('RT·entry, rt·entry').forEach(entry => {
+        const k = document.createElement('RT·e');
+        k.textContent = entry.getAttribute('key') || '';
+        
+        const v = document.createElement('RT·e');
+        v.innerHTML = entry.innerHTML;
+        
+        state.insert({ element: k, type: 'name', x: 0, y: y, x_extent: 0, y_extent: y });
+        state.insert({ element: v, type: 'data', x: 1, y: y, x_extent: 1, y_extent: y });
+        y++;
+      });
+
+      pad_headers(state);
+      project_grid(node, state, 'html-grid-dictionary', { wrap_check: true });
+    });
+
+    document.querySelectorAll('RT·relation, rt·relation').forEach(node => {
+      const state = new GridState();
+      const layout_intent = node.getAttribute('layout-intention') || 'row-tuple';
+      const model = layout_intent === 'column-tuple' ? 'html-grid-transpose' : 'html-grid-direct';
+
+      let offset_x = 0;
+      let offset_y = 0;
+
+      const col_head = node.querySelector('RT·tuple-meta, rt·tuple-meta');
+      if (col_head) {
+        offset_y = 1;
+        let cx = node.querySelector('RT·name, rt·name') ? 1 : 0;
+        col_head.querySelectorAll('RT·e, rt·e').forEach(e => {
+          state.insert({ element: e.cloneNode(true), type: 'x-label', x: cx, y: 0, x_extent: cx, y_extent: 0 });
+          cx++;
+        });
+      }
 
-        endnote_buffer = [];
+      let y = offset_y;
+      node.querySelectorAll('RT·tuple, rt·tuple').forEach(tuple => {
+        let x = 0;
+        const name = tuple.querySelector('RT·name, rt·name');
+        if (name) {
+          offset_x = 1;
+          state.insert({ element: name.cloneNode(true), type: 'name', x: 0, y: y, x_extent: 0, y_extent: y });
+        }
+        x = offset_x;
+        tuple.querySelectorAll('RT·e, rt·e').forEach(e => {
+          state.insert({ element: e.cloneNode(true), type: 'data', x: x, y: y, x_extent: x, y_extent: y });
+          x++;
+        });
+        y++;
+      });
+
+      pad_headers(state);
+      project_grid(node, state, model, { wrap_check: true });
+    });
+
+    document.querySelectorAll('RT·matrix, rt·matrix').forEach(node => {
+      const state = new GridState();
+      const layout_intent = node.getAttribute('layout-intention') || 'row-vector';
+      const model = layout_intent === 'column-vector' ? 'html-grid-transpose' : 'html-grid-direct';
+
+      let offset_j = 0;
+      let offset_i = 0;
+
+      const vector_meta = node.querySelector('RT·vector-meta, rt·vector-meta');
+      if (vector_meta) {
+        offset_i = 1;
+        let cj = node.querySelector('RT·name, rt·name') ? 1 : 0;
+        vector_meta.querySelectorAll('RT·label, rt·label').forEach(e => {
+          state.insert({ element: e.cloneNode(true), type: 'x-label', x: cj, y: 0, x_extent: cj, y_extent: 0 });
+          cj++;
+        });
       }
-    }
-  }
 
-  window.RT.Element.add(process_endnotes);
+      let i = offset_i;
+      node.querySelectorAll('RT·vector, rt·vector').forEach(vec => {
+        let j = 0;
+        const name = vec.querySelector('RT·name, rt·name');
+        if (name) {
+          offset_j = 1;
+          state.insert({ element: name.cloneNode(true), type: 'name', x: 0, y: i, x_extent: 0, y_extent: i });
+        }
+        j = offset_j;
+        vec.querySelectorAll('RT·e, rt·e').forEach(e => {
+          state.insert({ element: e.cloneNode(true), type: 'data', x: j, y: i, x_extent: j, y_extent: i });
+          j++;
+        });
+        i++;
+      });
+
+      pad_headers(state);
+      project_grid(node, state, model, { wrap_check: false, no_wrap: true, delimiters: true });
+    });
+  });
 
 })();
index 6859bac..7b16278 100644 (file)
@@ -1,11 +1,14 @@
 /*
   Element/grid.js
   Compiles semantic tabular structures into a Cartesian GridState.
+  [INSTRUMENTED DIAGNOSTIC BUILD]
 */
 
 (function() {
   if (!window.RT) return;
 
+  console.warn(">>> [RT GRID DIAGNOSTIC] grid.js parsed by browser at: " + new Date().toISOString() + " <<<");
+
   class GridState {
     constructor() {
       this.cells = [];
     }
   }
 
-  function pad_headers(grid_state) {
-    const headers = grid_state.cells.filter(c => c.type === 'x-label');
-    if (headers.length === 0) return;
-
-    const max_x = Math.max(...grid_state.cells.map(c => c.x_extent));
-    const min_header_x = Math.min(...headers.map(c => c.x));
-    const existing_xs = new Set(headers.map(c => c.x));
-
-    for (let i = min_header_x; i <= max_x; i++) {
-      if (!existing_xs.has(i)) {
-        const empty_el = document.createElement('RT·e');
-        empty_el.innerHTML = '&nbsp;';
-        grid_state.insert({
-          element: empty_el,
-          type: 'x-label',
-          x: i,
-          y: 0,
-          x_extent: i,
-          y_extent: 0
-        });
-      }
-    }
-  }
-
   const apply_style = function(el, cell, is_transposed, options, config) {
     el.style.padding = '0.25rem 0.5rem';
     el.style.margin = '0';
     el.style.lineHeight = '1.3';
 
-    if (cell.type === 'x-label' || cell.type === 'y-label' || cell.type === 'name') {
+    const type = cell.type;
+    const border_strong = '2px solid ' + (config.border_strong || '#000');
+    const border_faint = '1px solid ' + (config.border_faint || '#ccc');
+
+    if (type === 'x-label' || type === 'y-label' || type === 'name' || type === 'corner') {
       el.style.fontWeight = '600';
     }
 
-    if (cell.type === 'x-label') {
+    if (type === 'x-label') {
+      if (is_transposed) el.style.borderRight = border_strong;
+      else el.style.borderBottom = border_strong;
+    }
+    
+    if (type === 'y-label') {
+      if (is_transposed) el.style.borderBottom = border_strong;
+      else el.style.borderRight = border_strong;
+    }
+    
+    if (type === 'name') {
+      if (is_transposed) el.style.borderBottom = border_faint;
+      else el.style.borderRight = border_faint;
+    }
+    
+    if (type === 'corner') {
       if (is_transposed) {
-        el.style.borderRight = '2px solid ' + (config.border_strong || '#000');
+        el.style.borderRight = border_strong;
+        el.style.borderBottom = border_faint;
       } else {
-        el.style.borderBottom = '2px solid ' + (config.border_strong || '#000');
+        el.style.borderBottom = border_strong;
+        el.style.borderRight = border_faint;
       }
     }
-    
-    if (cell.type === 'name') {
-      el.style.borderRight = '1px solid ' + (config.border_faint || '#ccc');
-    }
 
-    if (cell.type === 'data') el.style.textAlign = 'center';
-    if (cell.type === 'x-label') el.style.textAlign = 'center';
+    if (type === 'data' || type === 'x-label') {
+      el.style.textAlign = 'center';
+    }
     
-    if (cell.type === (is_transposed ? 'x-label' : 'y-label') || cell.type === 'name') {
+    if ((is_transposed ? type === 'x-label' : type === 'y-label') || type === 'name' || type === 'corner') {
       el.style.textAlign = 'right';
       el.style.paddingRight = '0.5rem'; 
     }
   }
 
   RT.Element.add(function process_grids() {
-    document.querySelectorAll('RT·grid, rt·grid').forEach(node => {
+    console.error(">>> [RT GRID DIAGNOSTIC] process_grids() triggered by stage_manager <<<");
+
+    // 1. Native Grid
+    document.querySelectorAll('RT·grid, rt·grid, RT-grid, rt-grid').forEach(node => {
       const state = new GridState();
       const model = node.getAttribute('model') || 'html-grid-direct';
       const major_axis = node.getAttribute('major') || 'x';
       let cursor_x = 0;
       let cursor_y = 0;
 
-      node.querySelectorAll('RT·e, rt·e').forEach(e => {
+      node.querySelectorAll('RT·e, rt·e, RT-e, rt-e').forEach(e => {
         const attr_x = e.getAttribute('x');
         const attr_y = e.getAttribute('y');
         
         else cursor_y = parsed_y.extent + 1;
       });
 
-      pad_headers(state);
       project_grid(node, state, model, { wrap_check: true });
     });
 
-    document.querySelectorAll('RT·dictionary, rt·dictionary').forEach(node => {
+    // 2. Dictionary
+    document.querySelectorAll('RT·dictionary, rt·dictionary, RT-dictionary, rt-dictionary').forEach(node => {
       const state = new GridState();
       const key_label = node.getAttribute('key');
       const def_label = node.getAttribute('definition');
       let y = 0;
 
       if (key_label || def_label) {
-        const h1 = document.createElement('RT·e'); h1.textContent = key_label || '';
-        const h2 = document.createElement('RT·e'); h2.textContent = def_label || '';
+        const h1 = document.createElement('RT·e'); h1.textContent = key_label || 'X';
+        const h2 = document.createElement('RT·e'); h2.textContent = def_label || 'X';
         state.insert({ element: h1, type: 'x-label', x: 0, y: y, x_extent: 0, y_extent: y });
         state.insert({ element: h2, type: 'x-label', x: 1, y: y, x_extent: 1, y_extent: y });
         y++;
       }
 
-      node.querySelectorAll('RT·entry, rt·entry').forEach(entry => {
+      node.querySelectorAll('RT·entry, rt·entry, RT-entry, rt-entry').forEach(entry => {
         const k = document.createElement('RT·e');
-        k.textContent = entry.getAttribute('key') || '';
+        k.textContent = entry.getAttribute('key') || 'X';
         
         const v = document.createElement('RT·e');
         v.innerHTML = entry.innerHTML;
         y++;
       });
 
-      pad_headers(state);
       project_grid(node, state, 'html-grid-dictionary', { wrap_check: true });
     });
 
-    document.querySelectorAll('RT·relation, rt·relation').forEach(node => {
+    // 3. Relation
+    document.querySelectorAll('RT·relation, rt·relation, RT-relation, rt-relation').forEach(node => {
+      console.warn(">>> [RT GRID DIAGNOSTIC] <RT·relation> node found <<<");
+      
       const state = new GridState();
       const layout_intent = node.getAttribute('layout-intention') || 'row-tuple';
       const model = layout_intent === 'column-tuple' ? 'html-grid-transpose' : 'html-grid-direct';
+      const tuples = node.querySelectorAll('RT·tuple, rt·tuple, RT-tuple, rt-tuple');
+
+      let row_labels_debug = [];
+      let has_any_name = false;
+
+      tuples.forEach(tuple => {
+        const names = tuple.querySelectorAll('RT·name, rt·name, RT-name, rt-name');
+        if (names.length > 0) {
+          has_any_name = true;
+          let sublist = [];
+          names.forEach(n => sublist.push(n.textContent.trim()));
+          row_labels_debug.push(sublist);
+        } else {
+          row_labels_debug.push(['X']);
+        }
+      });
 
-      let offset_x = 0;
+      console.error(`>>> [RT GRID DIAGNOSTIC] Relation row labels: ${JSON.stringify(row_labels_debug)} <<<`);
+
+      let offset_x = has_any_name ? 1 : 0;
       let offset_y = 0;
 
-      const col_head = node.querySelector('RT·tuple-meta, rt·tuple-meta');
+      const col_head = node.querySelector('RT·tuple-meta, rt·tuple-meta, RT-tuple-meta, rt-tuple-meta');
       if (col_head) {
         offset_y = 1;
-        let cx = node.querySelector('RT·name, rt·name') ? 1 : 0;
-        col_head.querySelectorAll('RT·e, rt·e').forEach(e => {
+        let cx = offset_x;
+        col_head.querySelectorAll('RT·e, rt·e, RT-e, rt-e').forEach(e => {
           state.insert({ element: e.cloneNode(true), type: 'x-label', x: cx, y: 0, x_extent: cx, y_extent: 0 });
           cx++;
         });
+        
+        if (has_any_name) {
+          const corner_el = document.createElement('RT·e');
+          corner_el.textContent = 'X';
+          state.insert({ element: corner_el, type: 'corner', x: 0, y: 0, x_extent: 0, y_extent: 0 });
+        }
       }
 
       let y = offset_y;
-      node.querySelectorAll('RT·tuple, rt·tuple').forEach(tuple => {
-        let x = 0;
-        const name = tuple.querySelector('RT·name, rt·name');
-        if (name) {
-          offset_x = 1;
-          state.insert({ element: name.cloneNode(true), type: 'name', x: 0, y: y, x_extent: 0, y_extent: y });
+      tuples.forEach(tuple => {
+        if (has_any_name) {
+          const names = tuple.querySelectorAll('RT·name, rt·name, RT-name, rt-name');
+          const name_container = document.createElement('RT·e');
+          if (names.length > 0) {
+            names.forEach(n => name_container.appendChild(n.cloneNode(true)));
+          } else {
+            name_container.textContent = 'X';
+          }
+          state.insert({ element: name_container, type: 'name', x: 0, y: y, x_extent: 0, y_extent: y });
         }
-        x = offset_x;
-        tuple.querySelectorAll('RT·e, rt·e').forEach(e => {
+        
+        let x = offset_x;
+        tuple.querySelectorAll('RT·e, rt·e, RT-e, rt-e').forEach(e => {
           state.insert({ element: e.cloneNode(true), type: 'data', x: x, y: y, x_extent: x, y_extent: y });
           x++;
         });
         y++;
       });
 
-      pad_headers(state);
       project_grid(node, state, model, { wrap_check: true });
     });
 
-    document.querySelectorAll('RT·matrix, rt·matrix').forEach(node => {
+    // 4. Matrix
+    document.querySelectorAll('RT·matrix, rt·matrix, RT-matrix, rt-matrix').forEach(node => {
       const state = new GridState();
       const layout_intent = node.getAttribute('layout-intention') || 'row-vector';
       const model = layout_intent === 'column-vector' ? 'html-grid-transpose' : 'html-grid-direct';
+      const vectors = node.querySelectorAll('RT·vector, rt·vector, RT-vector, rt-vector');
+
+      let row_labels_debug = [];
+      let has_any_name = false;
+
+      vectors.forEach(vec => {
+        const names = vec.querySelectorAll('RT·name, rt·name, RT-name, rt-name');
+        if (names.length > 0) {
+          has_any_name = true;
+          let sublist = [];
+          names.forEach(n => sublist.push(n.textContent.trim()));
+          row_labels_debug.push(sublist);
+        } else {
+          row_labels_debug.push(['X']);
+        }
+      });
 
-      let offset_j = 0;
-      let offset_i = 0;
+      let offset_x = has_any_name ? 1 : 0;
+      let offset_y = 0;
 
-      const vector_meta = node.querySelector('RT·vector-meta, rt·vector-meta');
+      const vector_meta = node.querySelector('RT·vector-meta, rt·vector-meta, RT-vector-meta, rt-vector-meta');
       if (vector_meta) {
-        offset_i = 1;
-        let cj = node.querySelector('RT·name, rt·name') ? 1 : 0;
-        vector_meta.querySelectorAll('RT·label, rt·label').forEach(e => {
-          state.insert({ element: e.cloneNode(true), type: 'x-label', x: cj, y: 0, x_extent: cj, y_extent: 0 });
-          cj++;
+        offset_y = 1;
+        let cx = offset_x;
+        vector_meta.querySelectorAll('RT·label, rt·label, RT-label, rt-label').forEach(e => {
+          state.insert({ element: e.cloneNode(true), type: 'x-label', x: cx, y: 0, x_extent: cx, y_extent: 0 });
+          cx++;
         });
+        
+        if (has_any_name) {
+          const corner_el = document.createElement('RT·e');
+          corner_el.textContent = 'X';
+          state.insert({ element: corner_el, type: 'corner', x: 0, y: 0, x_extent: 0, y_extent: 0 });
+        }
       }
 
-      let i = offset_i;
-      node.querySelectorAll('RT·vector, rt·vector').forEach(vec => {
-        let j = 0;
-        const name = vec.querySelector('RT·name, rt·name');
-        if (name) {
-          offset_j = 1;
-          state.insert({ element: name.cloneNode(true), type: 'name', x: 0, y: i, x_extent: 0, y_extent: i });
+      let y = offset_y;
+      vectors.forEach(vec => {
+        if (has_any_name) {
+          const names = vec.querySelectorAll('RT·name, rt·name, RT-name, rt-name');
+          const name_container = document.createElement('RT·e');
+          if (names.length > 0) {
+            names.forEach(n => name_container.appendChild(n.cloneNode(true)));
+          } else {
+            name_container.textContent = 'X';
+          }
+          state.insert({ element: name_container, type: 'name', x: 0, y: y, x_extent: 0, y_extent: y });
         }
-        j = offset_j;
-        vec.querySelectorAll('RT·e, rt·e').forEach(e => {
-          state.insert({ element: e.cloneNode(true), type: 'data', x: j, y: i, x_extent: j, y_extent: i });
-          j++;
+        
+        let x = offset_x;
+        vec.querySelectorAll('RT·e, rt·e, RT-e, rt-e').forEach(e => {
+          state.insert({ element: e.cloneNode(true), type: 'data', x: x, y: y, x_extent: x, y_extent: y });
+          x++;
         });
-        i++;
+        y++;
       });
 
-      pad_headers(state);
       project_grid(node, state, model, { wrap_check: false, no_wrap: true, delimiters: true });
     });
   });
diff --git a/setup b/setup
old mode 100644 (file)
new mode 100755 (executable)
index a249e2e..f750f1d
--- a/setup
+++ b/setup
@@ -1,63 +1,70 @@
-#!/usr/bin/env bash
+#!/usr/bin/env python3
 # setup - enter a project role environment
-# (must be sourced)
-
-script_afp=$(realpath "${BASH_SOURCE[0]}")
-if [ "${BASH_SOURCE[0]}" == "${0}" ]; then
-  echo "${script_afp}:: This script must be sourced, not executed."
-  exit 1
-fi
-
-project_roles="administrator consumer developer tester"
-
-print_usage(){
-  echo "usage: . setup <role>"
-  echo "known roles: ${project_roles}"
-}
-
-if [ -z "${1:-}" ] || [ "${1}" == "-h" ] || [ "${1}" == "--help" ]; then
-  print_usage
-  return 0
-fi
-
-role_is_valid=false
-for r in ${project_roles}; do
-  if [ "${1}" == "${r}" ]; then
-    role_is_valid=true
-    break
-  fi
-done
-
-if [ "${role_is_valid}" == "false" ]; then
-  echo "setup: unrecognized role or option '${1}'"
-  print_usage
-  return 1
-fi
-
-# setup the project
-#
-  source shared/tool/setup
-  if [[ -f "shared/authored/setup" ]]; then
-    source shared/authored/setup
-  fi
-
-# setup the role
-#
-  export ROLE="${1}"
-  export ROLE_HOME="$REPO_HOME/$ROLE"
-  echo ROLE_HOME "$ROLE_HOME"
-
-  tool="${ROLE_HOME}/tool"
-  if [[ ":${PATH}:" != *":${tool}:"* ]]; then
-    export PATH="${tool}:${PATH}"
-  fi
-
-  export SETUP="${ROLE}/tool/setup"
-
-  cd "${ROLE}" || return 1
-  if [ -f "tool/setup" ]; then
-    source "tool/setup"
-    echo "in environment: ${SETUP}"
-  else
-    echo "not found: ${SETUP}"
-  fi
+
+import os
+import sys
+import argparse
+import tempfile
+
+PROJECT_ROLES = ["administrator", "consumer", "developer", "tester"]
+
+def main():
+    parser = argparse.ArgumentParser(
+        description="Enter a project role environment by spawning an isolated subshell."
+    )
+    parser.add_argument("role", choices=PROJECT_ROLES, help="The project role to assume.")
+    args = parser.parse_args()
+
+    env = os.environ.copy()
+
+    # Python-domain logic: Variables and Paths
+    repo_home = env.get("REPO_HOME", os.getcwd())
+    role = args.role
+    role_home = os.path.join(repo_home, role)
+    tool_dir = os.path.join(role_home, "tool")
+    setup_file = os.path.join(role, "tool", "setup")
+
+    env["ROLE"] = role
+    env["ROLE_HOME"] = role_home
+    env["SETUP"] = setup_file
+
+    print(f"ROLE_HOME {role_home}")
+
+    # Python-domain logic: PATH injection
+    path_parts = env.get("PATH", "").split(os.pathsep)
+    if tool_dir not in path_parts:
+        env["PATH"] = f"{tool_dir}{os.pathsep}{env.get('PATH', '')}"
+
+    # Python-domain logic: Directory validation
+    if not os.path.isdir(role):
+        print(f"setup: failed to locate directory for role '{role}'")
+        sys.exit(1)
+
+    # Bash-domain logic: The rcfile only handles operations Python cannot inherit 
+    # (sourcing bash functions) and performs the directory change internally to 
+    # guarantee the shared scripts are sourced from the project root.
+    bash_logic = [
+        'if [[ -f ~/.bashrc ]]; then source ~/.bashrc; fi',
+        'if [[ -f "shared/tool/setup" ]]; then source "shared/tool/setup"; fi',
+        'if [[ -f "shared/authored/setup" ]]; then source "shared/authored/setup"; fi',
+        f'cd "{role}"',
+        'if [[ -f "tool/setup" ]]; then',
+        '    source "tool/setup"',
+        f'    echo "in environment: {setup_file}"',
+        'else',
+        f'    echo "not found: {setup_file}"',
+        'fi',
+        'if [[ -n "${INSIDE_EMACS}" ]] && command -v env_to_emacs >/dev/null 2>&1; then',
+        '    env_to_emacs',
+        'fi',
+        'rm -f "${BASH_SOURCE[0]}"'
+    ]
+
+    fd, rcfile_path = tempfile.mkstemp(suffix="-rt-setup.sh")
+    with os.fdopen(fd, "w") as f:
+        f.write("\n".join(bash_logic) + "\n")
+
+    os.execvpe("bash", ["bash", "--rcfile", rcfile_path], env)
+
+if __name__ == "__main__":
+    main()