diff --git a/wasm/repl/index.html b/wasm/repl/index.html index c6adfa1..d396ec9 100644 --- a/wasm/repl/index.html +++ b/wasm/repl/index.html @@ -101,7 +101,7 @@ λ> + placeholder="(+ 1 2)"> python (pyodide) c (emcc) diff --git a/wasm/repl/repl.css b/wasm/repl/repl.css index ed7b792..aed8d95 100644 --- a/wasm/repl/repl.css +++ b/wasm/repl/repl.css @@ -168,81 +168,85 @@ body.repl { /* ─── Transcript ───────────────────────────────────────────────── */ +/* Transcript reads as one continuous terminal stream — no card chrome. + * Prompt + output stack with minimal vertical space, tier+time annotate + * as a subtle comment-style suffix. */ .transcript-wrap { overflow: auto; - padding: 1rem; + padding: 0.5rem 1rem 0; background: var(--code-bg); } .transcript { - display: grid; - gap: 0.6rem; font-family: var(--mono); - font-size: 0.9em; - line-height: 1.5; + font-size: 0.92em; + line-height: 1.45; max-width: 90rem; margin: 0 auto; + padding-bottom: 0.4rem; } .transcript .entry { - display: grid; - gap: 0.25rem; - padding: 0.4rem 0.6rem; - border-left: 2px solid var(--rule); + margin: 0; + padding: 0; } .transcript .entry .prompt-line { color: var(--green); white-space: pre-wrap; + word-break: break-word; +} +.transcript .entry .prompt-line .sigil { + display: inline-block; + width: 3ch; + opacity: 0.7; } .transcript .entry .tier-output { - display: grid; - grid-template-columns: 7rem 1fr; - gap: 0.6rem; - padding: 0.25rem 0; - border-top: 1px dashed var(--rule); -} -.transcript .entry .tier-output:first-of-type { border-top: none; } -.transcript .entry .tier-label { - color: var(--muted); - font-size: 0.7em; - text-transform: uppercase; - letter-spacing: 0.08em; - padding-top: 0.15rem; + display: block; + padding: 0; + color: var(--fg); } .transcript .entry .tier-result { white-space: pre-wrap; + word-break: break-word; + padding-left: 3ch; /* line up under "λ> " */ } -.transcript .entry .tier-time { +.transcript .entry .tier-meta { color: var(--muted); - font-size: 0.8em; - margin-left: 0.4rem; + font-size: 0.78em; + margin-left: 0.6em; + user-select: none; +} +.transcript .entry .tier-meta::before { + content: "; "; /* render as a lisp comment */ + color: var(--muted); + opacity: 0.6; } .transcript .entry.error .tier-result { color: var(--err); } +.transcript .entry + .entry { margin-top: 0.4rem; } /* ─── Prompt bar ───────────────────────────────────────────────── */ .prompt-bar { display: grid; grid-template-columns: auto 1fr auto auto; - gap: 0.5rem; - align-items: end; - padding: 0.5rem 1rem 0.8rem; + gap: 0.4rem; + align-items: center; + padding: 0.3rem 1rem; border-top: 1px solid var(--rule); - background: var(--bg); + background: var(--code-bg); } .prompt-bar .prompt-sigil { color: var(--green); font-weight: 600; - padding: 0.4rem 0 0; + font-size: 0.95em; } .prompt-bar textarea { resize: none; background: var(--code-bg); - border: 1px solid var(--rule); + border: none; color: var(--fg); - padding: 0.45rem 0.7rem; + padding: 0.25rem 0; font-family: var(--mono); - font-size: 0.9em; - border-radius: 3px; - min-height: 1.6em; + font-size: 0.92em; + min-height: 1.5em; max-height: 30vh; min-width: 0; } diff --git a/wasm/repl/repl.js b/wasm/repl/repl.js index 6eb4385..70fb66c 100644 --- a/wasm/repl/repl.js +++ b/wasm/repl/repl.js @@ -253,7 +253,8 @@ function renderAll() { tabsEl.appendChild(el); } - // Transcript + // Transcript — terminal-style: prompt line, then output lines underneath + // indented under the prompt with a tier-tag suffix as a Lisp comment. transcriptEl.innerHTML = ""; const tab = activeTab(); if (!tab) return; @@ -261,25 +262,33 @@ function renderAll() { for (const entry of tab.transcript) { const block = document.createElement("div"); block.className = "entry" + (entry.kind === "error" ? " error" : ""); - const prompt = document.createElement("div"); - prompt.className = "prompt-line"; - prompt.textContent = "λ> " + entry.input; - block.appendChild(prompt); + + // Render the input across one or more lines (preserve newlines). + const inputLines = entry.input.split(/\n/); + for (let i = 0; i < inputLines.length; i++) { + const line = document.createElement("div"); + line.className = "prompt-line"; + const sigil = document.createElement("span"); + sigil.className = "sigil"; + sigil.textContent = i === 0 ? "λ>" : ".."; + line.appendChild(sigil); + line.appendChild(document.createTextNode(inputLines[i])); + block.appendChild(line); + } + for (const r of (entry.results || [])) { const row = document.createElement("div"); row.className = "tier-output"; - const lab = document.createElement("div"); - lab.className = "tier-label"; - lab.textContent = TIER_LABEL[r.tier] || r.tier; - const tm = document.createElement("span"); - tm.className = "tier-time"; - tm.textContent = ` ${(r.elapsed | 0)}ms`; - lab.appendChild(tm); - const out = document.createElement("div"); - out.className = "tier-result"; - out.textContent = r.output || (r.error ? "error: " + r.error : ""); - row.appendChild(lab); - row.appendChild(out); + const result = document.createElement("span"); + result.className = "tier-result"; + const text = (r.output != null ? r.output : ""); + const errText = r.error ? "error: " + r.error : ""; + result.textContent = (text + (errText && text ? "\n" : "") + errText).replace(/\n$/, ""); + const meta = document.createElement("span"); + meta.className = "tier-meta"; + meta.textContent = `${TIER_LABEL[r.tier] || r.tier} · ${(r.elapsed | 0)}ms`; + row.appendChild(result); + row.appendChild(meta); block.appendChild(row); } transcriptEl.appendChild(block); diff --git a/www/repl/index.html b/www/repl/index.html index c6adfa1..d396ec9 100644 --- a/www/repl/index.html +++ b/www/repl/index.html @@ -101,7 +101,7 @@ λ> + placeholder="(+ 1 2)"> python (pyodide) c (emcc) diff --git a/www/repl/repl.css b/www/repl/repl.css index ed7b792..aed8d95 100644 --- a/www/repl/repl.css +++ b/www/repl/repl.css @@ -168,81 +168,85 @@ body.repl { /* ─── Transcript ───────────────────────────────────────────────── */ +/* Transcript reads as one continuous terminal stream — no card chrome. + * Prompt + output stack with minimal vertical space, tier+time annotate + * as a subtle comment-style suffix. */ .transcript-wrap { overflow: auto; - padding: 1rem; + padding: 0.5rem 1rem 0; background: var(--code-bg); } .transcript { - display: grid; - gap: 0.6rem; font-family: var(--mono); - font-size: 0.9em; - line-height: 1.5; + font-size: 0.92em; + line-height: 1.45; max-width: 90rem; margin: 0 auto; + padding-bottom: 0.4rem; } .transcript .entry { - display: grid; - gap: 0.25rem; - padding: 0.4rem 0.6rem; - border-left: 2px solid var(--rule); + margin: 0; + padding: 0; } .transcript .entry .prompt-line { color: var(--green); white-space: pre-wrap; + word-break: break-word; +} +.transcript .entry .prompt-line .sigil { + display: inline-block; + width: 3ch; + opacity: 0.7; } .transcript .entry .tier-output { - display: grid; - grid-template-columns: 7rem 1fr; - gap: 0.6rem; - padding: 0.25rem 0; - border-top: 1px dashed var(--rule); -} -.transcript .entry .tier-output:first-of-type { border-top: none; } -.transcript .entry .tier-label { - color: var(--muted); - font-size: 0.7em; - text-transform: uppercase; - letter-spacing: 0.08em; - padding-top: 0.15rem; + display: block; + padding: 0; + color: var(--fg); } .transcript .entry .tier-result { white-space: pre-wrap; + word-break: break-word; + padding-left: 3ch; /* line up under "λ> " */ } -.transcript .entry .tier-time { +.transcript .entry .tier-meta { color: var(--muted); - font-size: 0.8em; - margin-left: 0.4rem; + font-size: 0.78em; + margin-left: 0.6em; + user-select: none; +} +.transcript .entry .tier-meta::before { + content: "; "; /* render as a lisp comment */ + color: var(--muted); + opacity: 0.6; } .transcript .entry.error .tier-result { color: var(--err); } +.transcript .entry + .entry { margin-top: 0.4rem; } /* ─── Prompt bar ───────────────────────────────────────────────── */ .prompt-bar { display: grid; grid-template-columns: auto 1fr auto auto; - gap: 0.5rem; - align-items: end; - padding: 0.5rem 1rem 0.8rem; + gap: 0.4rem; + align-items: center; + padding: 0.3rem 1rem; border-top: 1px solid var(--rule); - background: var(--bg); + background: var(--code-bg); } .prompt-bar .prompt-sigil { color: var(--green); font-weight: 600; - padding: 0.4rem 0 0; + font-size: 0.95em; } .prompt-bar textarea { resize: none; background: var(--code-bg); - border: 1px solid var(--rule); + border: none; color: var(--fg); - padding: 0.45rem 0.7rem; + padding: 0.25rem 0; font-family: var(--mono); - font-size: 0.9em; - border-radius: 3px; - min-height: 1.6em; + font-size: 0.92em; + min-height: 1.5em; max-height: 30vh; min-width: 0; } diff --git a/www/repl/repl.js b/www/repl/repl.js index 6eb4385..70fb66c 100644 --- a/www/repl/repl.js +++ b/www/repl/repl.js @@ -253,7 +253,8 @@ function renderAll() { tabsEl.appendChild(el); } - // Transcript + // Transcript — terminal-style: prompt line, then output lines underneath + // indented under the prompt with a tier-tag suffix as a Lisp comment. transcriptEl.innerHTML = ""; const tab = activeTab(); if (!tab) return; @@ -261,25 +262,33 @@ function renderAll() { for (const entry of tab.transcript) { const block = document.createElement("div"); block.className = "entry" + (entry.kind === "error" ? " error" : ""); - const prompt = document.createElement("div"); - prompt.className = "prompt-line"; - prompt.textContent = "λ> " + entry.input; - block.appendChild(prompt); + + // Render the input across one or more lines (preserve newlines). + const inputLines = entry.input.split(/\n/); + for (let i = 0; i < inputLines.length; i++) { + const line = document.createElement("div"); + line.className = "prompt-line"; + const sigil = document.createElement("span"); + sigil.className = "sigil"; + sigil.textContent = i === 0 ? "λ>" : ".."; + line.appendChild(sigil); + line.appendChild(document.createTextNode(inputLines[i])); + block.appendChild(line); + } + for (const r of (entry.results || [])) { const row = document.createElement("div"); row.className = "tier-output"; - const lab = document.createElement("div"); - lab.className = "tier-label"; - lab.textContent = TIER_LABEL[r.tier] || r.tier; - const tm = document.createElement("span"); - tm.className = "tier-time"; - tm.textContent = ` ${(r.elapsed | 0)}ms`; - lab.appendChild(tm); - const out = document.createElement("div"); - out.className = "tier-result"; - out.textContent = r.output || (r.error ? "error: " + r.error : ""); - row.appendChild(lab); - row.appendChild(out); + const result = document.createElement("span"); + result.className = "tier-result"; + const text = (r.output != null ? r.output : ""); + const errText = r.error ? "error: " + r.error : ""; + result.textContent = (text + (errText && text ? "\n" : "") + errText).replace(/\n$/, ""); + const meta = document.createElement("span"); + meta.className = "tier-meta"; + meta.textContent = `${TIER_LABEL[r.tier] || r.tier} · ${(r.elapsed | 0)}ms`; + row.appendChild(result); + row.appendChild(meta); block.appendChild(row); } transcriptEl.appendChild(block);