tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

commit fccaf54e5f351d9969753f0c91c2e3e61ca3796a
parent 98f06f8e75491578458723d6812d6c4134f46c6c
Author: James Eversole <james@eversole.co>
Date:   Mon, 19 Oct 2026 21:51:51 -0500

Make inline inspection results the popup's primary focus

Place larger result text before concise interpretation metadata and subdued footer controls. Replace the prominent Close label with an accessible compact close button, preserving keyboard dismissal.

Validation: nix flake check, generated JS parity, site build, and desktop/mobile Chromium inspection probes on Haskell and Zig. Add reading-order, geometry, typography and accessible-close regression checks.

Diffstat:
Mext/demo-site/dist/definition-inspector.js | 10++++++----
Mext/demo-site/src/definition-inspector.ts | 9+++++----
Mext/demo-site/style.css | 10+++++++---
Mext/demo-site/test/import-picker-probe.mjs | 6++++++
4 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/ext/demo-site/dist/definition-inspector.js b/ext/demo-site/dist/definition-inspector.js @@ -28,10 +28,12 @@ export function definitionInspector(input, request) { for (const [value, label] of [['auto', 'Auto'], ['tricu', 'tricu · t source'], ['triangle', '△ source'], ['dag', 'DAG · shared △'], ['ternary', 'Ternary'], ['string', 'Text'], ['number', 'Natural number'], ['bool', 'Boolean'], ['bytes', 'Bytes'], ['values', 'List · text / numbers']]) format.add(new Option(label, value)); const close = document.createElement('button'); - close.textContent = 'Close'; + close.textContent = '×'; close.type = 'button'; + close.setAttribute('aria-label', 'Close definition inspection'); + close.title = 'Close (Escape)'; const heading = document.createElement('div'); - heading.className = 'definition-card-heading'; + heading.className = 'definition-card-controls'; heading.append(title, format, close); const status = document.createElement('p'); status.className = 'hint'; @@ -39,7 +41,7 @@ export function definitionInspector(input, request) { const result = document.createElement('pre'); result.tabIndex = 0; result.setAttribute('aria-label', 'Compiled definition result'); - card.append(heading, status, result); + card.append(result, status, heading); wrapper.append(card); let selected, source = '', ready = false, busy = false, serial = 0; let pending, last = '', generation = 0, wasReady = false, scheduled = false; @@ -189,7 +191,7 @@ export function definitionInspector(input, request) { pending = undefined; if (!selected || !ready) return true; - status.textContent = message.type === 'inspection-error' ? `Cannot interpret this tree: ${message.message}` : `Compiled definition · ${format.value === 'auto' ? 'Auto → ' + (interpretationLabels[message.result.type] ?? message.result.type) : format.selectedOptions[0].textContent} · No arguments applied; IO is not executed.`; + status.textContent = message.type === 'inspection-error' ? `Cannot interpret this tree: ${message.message}` : `${format.value === 'auto' ? 'Auto → ' + (interpretationLabels[message.result.type] ?? message.result.type) : format.selectedOptions[0].textContent}`; const value = message.type === 'inspected' ? valueText(message.result) : ''; result.textContent = value.slice(0, 16_384); if (value.length > 16_384) diff --git a/ext/demo-site/src/definition-inspector.ts b/ext/demo-site/src/definition-inspector.ts @@ -21,11 +21,12 @@ export function definitionInspector(input: HTMLTextAreaElement, request: (id: nu const title = document.createElement('strong'), format = document.createElement('select'); format.setAttribute('aria-label', 'Definition interpretation'); for (const [value, label] of [['auto', 'Auto'], ['tricu', 'tricu · t source'], ['triangle', '△ source'], ['dag', 'DAG · shared △'], ['ternary', 'Ternary'], ['string', 'Text'], ['number', 'Natural number'], ['bool', 'Boolean'], ['bytes', 'Bytes'], ['values', 'List · text / numbers']]) format.add(new Option(label, value)); - const close = document.createElement('button'); close.textContent = 'Close'; close.type = 'button'; - const heading = document.createElement('div'); heading.className = 'definition-card-heading'; heading.append(title, format, close); + const close = document.createElement('button'); close.textContent = '×'; close.type = 'button'; + close.setAttribute('aria-label', 'Close definition inspection'); close.title = 'Close (Escape)'; + const heading = document.createElement('div'); heading.className = 'definition-card-controls'; heading.append(title, format, close); const status = document.createElement('p'); status.className = 'hint'; status.setAttribute('role', 'status'); const result = document.createElement('pre'); result.tabIndex = 0; result.setAttribute('aria-label', 'Compiled definition result'); - card.append(heading, status, result); wrapper.append(card); + card.append(result, status, heading); wrapper.append(card); let selected: string | undefined, source = '', ready = false, busy = false, serial = 0; let pending: number | undefined, last = '', generation = 0, wasReady = false, scheduled = false; const buttons = new Map<string, HTMLButtonElement>(); @@ -118,7 +119,7 @@ export function definitionInspector(input: HTMLTextAreaElement, request: (id: nu if (message.id !== pending) return false; pending = undefined; if (!selected || !ready) return true; - status.textContent = message.type === 'inspection-error' ? `Cannot interpret this tree: ${message.message}` : `Compiled definition · ${format.value === 'auto' ? 'Auto → ' + (interpretationLabels[message.result.type] ?? message.result.type) : format.selectedOptions[0]!.textContent} · No arguments applied; IO is not executed.`; + status.textContent = message.type === 'inspection-error' ? `Cannot interpret this tree: ${message.message}` : `${format.value === 'auto' ? 'Auto → ' + (interpretationLabels[message.result.type] ?? message.result.type) : format.selectedOptions[0]!.textContent}`; const value = message.type === 'inspected' ? valueText(message.result) : ''; result.textContent = value.slice(0, 16_384); if (value.length > 16_384) status.textContent += ' · Preview: first 16,384 characters.'; diff --git a/ext/demo-site/style.css b/ext/demo-site/style.css @@ -66,9 +66,13 @@ header nav{align-items:center;gap:12px}#theme-separator{color:var(--muted)} .definition-gutter button{position:absolute;left:7px;width:24px;height:20px;min-height:0;display:grid;place-items:center;line-height:1;padding:0;border:0;background:transparent;color:#bfe2ba;font-size:13px;pointer-events:auto} .definition-gutter button:hover,.definition-gutter button[aria-expanded="true"]{background:#385045;color:#fff} .definition-card{position:absolute;left:40px;right:12px;z-index:3;max-height:260px;overflow:auto;margin:0;padding:12px;border:1px solid #587362;border-radius:8px;background:#21352b;color:#e3edde;min-width:0} -.definition-card-heading{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.definition-card-heading strong{flex:1;overflow-wrap:anywhere} -.definition-card select,.definition-card button{background:#21352b;color:#e3edde;border-color:#587362;max-width:100%;font-size:13px} -.definition-card .hint{color:#b1c1b7}.definition-card pre{max-height:220px;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;margin:8px 0 0} +.definition-card-controls{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:10px;padding-top:8px;border-top:1px solid #385045} +.definition-card-controls strong{flex:1;min-width:0;overflow-wrap:anywhere;font-size:12px;font-weight:500;color:#b1c1b7} +.definition-card select,.definition-card button{background:transparent;color:#b1c1b7;border-color:#385045;max-width:100%;font-size:12px;min-height:32px;padding:4px 6px} +.definition-card select{width:auto;flex:0 1 auto}.definition-card button{width:32px;height:32px;display:grid;place-items:center;padding:0;font-size:20px;border-color:transparent} +.definition-card button:hover{color:#e3edde;background:#385045} +.definition-card .hint{color:#b1c1b7;font-size:11px;margin:6px 0 0} +.definition-card pre{max-height:160px;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;margin:0;padding:2px 0 6px;font-size:18px;line-height:1.5} #playground-intro{margin:0;padding:8px 20px 14px;color:#d2e0ce;font-size:13px}#playground-intro a{color:inherit;text-decoration:underline} #import-exports{display:flex;gap:6px;flex-wrap:wrap}#import-exports button{font:inherit;font-size:13px;padding:5px 8px} #import-results h3{margin-bottom:0}#try-library,#copy-qualified{margin:8px 8px 8px 0} diff --git a/ext/demo-site/test/import-picker-probe.mjs b/ext/demo-site/test/import-picker-probe.mjs @@ -49,6 +49,12 @@ export async function run(control,backend='haskell-wasm'){ const interpretation=value=>{const select=document.querySelector('.definition-card select');select.value=value;select.dispatchEvent(new Event('change'));}; await wait(()=>document.querySelector('.definition-card pre').textContent==='Hello, world!','automatic published text interpretation'); assert(document.querySelector('.definition-card select').value==='auto'&&document.querySelector('.definition-card [role=status]').textContent.includes('Auto → Text'),'Auto identifies its chosen interpretation'); + const card=document.querySelector('.definition-card'), displayed=card.querySelector('pre'), controls=card.querySelector('.definition-card-controls'); + assert(card.firstElementChild===displayed,'Result precedes status and controls in reading order'); + assert(displayed.getBoundingClientRect().bottom<=controls.getBoundingClientRect().top,'Result is above the secondary controls'); + assert(parseFloat(getComputedStyle(displayed).fontSize)>parseFloat(getComputedStyle(controls.querySelector('select')).fontSize),'Result typography is more prominent than controls'); + assert(card.querySelector('[role=status]').textContent==='Auto → Text','Successful inspection uses concise metadata'); + assert(card.querySelector('button').getAttribute('aria-label')==='Close definition inspection','Compact close remains accessible'); inspectButton('greeting').click();assert(document.querySelector('.definition-card').hidden,'Second glyph click closes, not cycles'); inspectButton('greeting').click();await wait(()=>document.querySelector('.definition-card pre').textContent==='Hello, world!','reopen Auto'); assert(el('result').textContent===''&&el('terminal').textContent==='','Inspection does not alter evaluator output or IO');