Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width,initial-scale=1" /> | |
| <title>poolside · laguna-s-2.1 — reasoning terminal</title> | |
| <link rel="icon" href="https://cdn-avatars.huggingface.co/v1/production/uploads/699484cbe85a4b61cbc5ee0f/GpYWuz-CovEFgbPOW21dZ.png" /> | |
| <style> | |
| :root { | |
| /* poolside-aligned palette: indigo + magenta on pure white */ | |
| --indigo: #4f46e5; | |
| --indigo-700: #4338ca; | |
| --magenta: #d946ef; | |
| --ink: #0f1020; | |
| --ink-soft: #1c1d2e; | |
| --paper: #ffffff; | |
| --paper-2: #ffffff; | |
| --paper-line: #ececf3; | |
| --paper-line-2: #d9d9e6; | |
| --muted: #6b6c80; | |
| --dim: #9f9fb3; | |
| --bg: #fafafa; | |
| /* terminal ANSI palette mapped onto paper background */ | |
| --term-bg: #ffffff; | |
| --term-divider: #efeff6; | |
| --term-fg: #0f1020; | |
| --term-muted: #6b6c80; | |
| --term-cyan: #0e7490; | |
| --term-yellow: #b45309; | |
| --term-green: #047857; | |
| --term-bold-green: #059669; | |
| --term-grey: #9ca3af; | |
| --term-red: #b91c1c; | |
| } | |
| * { box-sizing: border-box; } | |
| html, body { margin: 0; padding: 0; height: 100%; } | |
| body { | |
| background: | |
| radial-gradient(circle at 1px 1px, rgba(15,16,32,0.06) 1px, transparent 0) | |
| 0 0 / 18px 18px, | |
| #fafafa; | |
| color: var(--ink); | |
| font-family: 'Inter','SF Pro Display','Helvetica Neue',Arial,sans-serif; | |
| -webkit-font-smoothing: antialiased; | |
| display: flex; flex-direction: column; align-items: center; | |
| padding: 28px 16px 56px; | |
| min-height: 100%; | |
| } | |
| /* --------- Top brand row --------- */ | |
| .brandbar { width: 100%; max-width: 980px; display: flex; align-items: center; | |
| gap: 14px; padding: 0 4px 18px; } | |
| .brandbar .logo { width: 36px; height: 36px; border-radius: 8px; | |
| background: #fff; padding: 4px; box-shadow: 0 1px 2px rgba(15,16,32,0.04); } | |
| .brandbar .wordmark { | |
| font-family: 'Inter','SF Pro Display',sans-serif; | |
| letter-spacing: 0.08em; | |
| text-transform: uppercase; | |
| font-weight: 800; | |
| font-size: 14px; | |
| background: linear-gradient(90deg, var(--indigo) 0%, var(--magenta) 100%); | |
| -webkit-background-clip: text; background-clip: text; color: transparent; | |
| } | |
| .brandbar .sep { color: var(--paper-line-2); } | |
| .brandbar .crumbs { display: flex; gap: 6px; align-items: center; color: var(--muted); font-size: 13px; } | |
| .brandbar .crumbs b { color: var(--ink); font-weight: 600; } | |
| .brandbar .crumbs .grad { | |
| background: linear-gradient(90deg, var(--indigo) 0%, var(--magenta) 100%); | |
| -webkit-background-clip: text; background-clip: text; color: transparent; | |
| font-weight: 700; | |
| } | |
| .frame { width: 100%; max-width: 980px; } | |
| .card { | |
| background: var(--paper-2); | |
| border: 1px solid var(--paper-line); | |
| border-radius: 14px; | |
| box-shadow: 0 1px 2px rgba(15,16,32,0.04), 0 8px 24px -16px rgba(79,70,229,0.18); | |
| overflow: hidden; | |
| } | |
| /* --------- Header strips --------- */ | |
| .sysstrip { | |
| display: flex; align-items: center; gap: 8px; | |
| padding: 10px 14px; | |
| border: 1px solid var(--paper-line); | |
| border-radius: 14px 14px 0 0; | |
| background: | |
| radial-gradient(120% 120% at 0% 0%, rgba(79,70,229,0.06) 0%, rgba(255,255,255,0) 60%), | |
| var(--paper-2); | |
| } | |
| .sysstrip::before { | |
| content: ""; width: 6px; height: 24px; border-radius: 4px; | |
| background: linear-gradient(180deg, var(--indigo) 0%, var(--magenta) 100%); | |
| flex: 0 0 auto; | |
| } | |
| .sysstrip .label { font-weight: 600; color: var(--ink); font-size: 13px; } | |
| .sysstrip .path { color: var(--muted); font-size: 13px; } | |
| .sysstrip .right { margin-left: auto; display: flex; align-items: center; gap: 8px; } | |
| .badge { | |
| display: inline-flex; align-items: center; gap: 6px; | |
| background: rgba(4,120,87,0.10); color: var(--term-green); | |
| padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; | |
| border: 1px solid rgba(4,120,87,0.18); | |
| } | |
| .badge .pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--term-green); box-shadow: 0 0 0 0 rgba(4,120,87,0.4); animation: pulse 1.6s infinite; } | |
| @keyframes pulse { | |
| 0% { box-shadow: 0 0 0 0 rgba(4,120,87,0.45); } | |
| 80% { box-shadow: 0 0 0 8px rgba(4,120,87,0); } | |
| 100% { box-shadow: 0 0 0 0 rgba(4,120,87,0); } | |
| } | |
| /* --------- Terminal chrome (macOS dots + title) --------- */ | |
| .macbar { | |
| display: flex; align-items: center; gap: 8px; | |
| background: linear-gradient(180deg, #f1f1f7 0%, #e9e9f1 100%); | |
| border-top: 1px solid var(--paper-line); | |
| border-bottom: 1px solid var(--paper-line); | |
| padding: 8px 14px; | |
| } | |
| .dots { display: inline-flex; gap: 8px; } | |
| .dot { width: 12px; height: 12px; border-radius: 50%; box-shadow: inset 0 0 0 0.5px rgba(0,0,0,0.10); } | |
| .dot.r { background: #ff5f57; } | |
| .dot.y { background: #febc2e; } | |
| .dot.g { background: #28c840; } | |
| .mactitle { | |
| flex: 1; text-align: center; font-size: 12px; color: var(--muted); | |
| font-family: 'JetBrains Mono','Fira Code','SF Mono','Menlo',monospace; | |
| } | |
| .mactitle b { color: var(--ink); font-weight: 600; } | |
| /* --------- Terminal body (white) --------- */ | |
| .term { | |
| background: var(--term-bg); | |
| padding: 18px 20px 14px; | |
| height: 580px; | |
| overflow: auto; | |
| font-family: 'JetBrains Mono','Fira Code','SF Mono','Menlo',monospace; | |
| font-size: 13px; | |
| line-height: 1.6; | |
| white-space: pre-wrap; | |
| word-break: break-word; | |
| color: var(--term-fg); | |
| scrollbar-width: thin; | |
| scrollbar-color: var(--paper-line-2) transparent; | |
| } | |
| .term::-webkit-scrollbar { width: 8px; } | |
| .term::-webkit-scrollbar-thumb { background: var(--paper-line-2); border-radius: 4px; } | |
| /* --------- Prompt bar --------- */ | |
| .promptbar { | |
| display: flex; gap: 0; align-items: stretch; | |
| margin-top: 14px; | |
| border: 1px solid var(--paper-line); border-radius: 12px; overflow: hidden; | |
| background: var(--paper-2); | |
| } | |
| .promptbar .seal { | |
| background: var(--paper-2); border: none; | |
| padding: 0 16px; | |
| display: flex; align-items: center; font-weight: 600; color: var(--ink); | |
| font-family: 'JetBrains Mono','Fira Code','SF Mono','Menlo',monospace; font-size: 13px; | |
| } | |
| .promptbar .seal .accent { | |
| background: linear-gradient(90deg, var(--indigo) 0%, var(--magenta) 100%); | |
| -webkit-background-clip: text; background-clip: text; color: transparent; | |
| } | |
| .promptbar input[type="text"] { | |
| flex: 1; min-width: 0; | |
| background: var(--paper-2); color: var(--ink); | |
| border: none; | |
| padding: 13px 16px; font: inherit; outline: none; | |
| } | |
| .promptbar input[type="text"]:focus { border-color: var(--indigo); } | |
| .promptbar input[type="text"]::placeholder { color: var(--dim); } | |
| .promptbar .toggle { | |
| display: flex; align-items: center; gap: 8px; | |
| background: var(--paper-2); border: none; border-left: 1px solid var(--paper-line); | |
| padding: 0 14px; font-size: 13px; color: var(--muted); | |
| cursor: pointer; user-select: none; | |
| } | |
| .promptbar .toggle input { accent-color: var(--indigo); } | |
| .promptbar .toggle .label-grad { | |
| background: linear-gradient(90deg, var(--indigo) 0%, var(--magenta) 100%); | |
| -webkit-background-clip: text; background-clip: text; color: transparent; | |
| font-weight: 600; | |
| } | |
| .promptbar button.send { | |
| background: var(--indigo); color: #fff; border: none; | |
| padding: 0 22px; font: inherit; font-weight: 600; cursor: pointer; | |
| transition: background 120ms ease, transform 120ms ease; | |
| border-radius: 0 12px 12px 0; | |
| } | |
| .promptbar button.send:hover { background: var(--indigo-700); } | |
| .promptbar button.send:disabled { opacity: 0.55; cursor: not-allowed; } | |
| .promptbar button.send:hover { background: var(--indigo-700); } | |
| .promptbar button.send:disabled { opacity: 0.55; cursor: not-allowed; } | |
| .promptbar button.clear { | |
| background: var(--paper-2); color: var(--muted); border: none; | |
| border-left: 1px solid var(--paper-line); | |
| padding: 0 16px; font: inherit; cursor: pointer; | |
| } | |
| .promptbar button.clear:hover { color: var(--term-red); } | |
| /* --------- Footer line --------- */ | |
| .pillrow { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; } | |
| .pill { | |
| display: inline-flex; align-items: center; gap: 6px; | |
| background: var(--paper-2); border: 1px solid var(--paper-line); | |
| border-radius: 999px; padding: 6px 12px; font-size: 12px; color: var(--muted); | |
| } | |
| .pill.dot::before { | |
| content: ""; width: 7px; height: 7px; border-radius: 50%; | |
| background: var(--indigo); | |
| } | |
| .pill .grad { | |
| background: linear-gradient(90deg, var(--indigo) 0%, var(--magenta) 100%); | |
| -webkit-background-clip: text; background-clip: text; color: transparent; | |
| font-weight: 600; | |
| } | |
| .pill .try { | |
| display: inline-flex; align-items: center; gap: 6px; | |
| white-space: nowrap; | |
| } | |
| .pill .try .dot-mini { | |
| width: 7px; height: 7px; border-radius: 50%; background: var(--indigo); | |
| } | |
| .footline { | |
| color: var(--muted); font-size: 12px; margin-top: 18px; | |
| padding-top: 14px; border-top: 1px solid var(--paper-line); | |
| display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; | |
| } | |
| .caret::after { | |
| content: "▍"; color: var(--indigo); animation: blink 1s steps(2) infinite; | |
| margin-left: 2px; | |
| } | |
| @keyframes blink { 50% { opacity: 0; } } | |
| /* ---------- responsive ---------- */ | |
| @media (max-width: 720px) { | |
| body { padding: 14px 10px 36px; } | |
| .brandbar { flex-wrap: wrap; gap: 10px; padding: 0 2px 14px; } | |
| .brandbar .crumbs { font-size: 12px; flex-wrap: wrap; } | |
| .brandbar .logo { width: 28px; height: 28px; } | |
| .brandbar .wordmark { font-size: 12px; } | |
| .sysstrip { padding: 8px 12px; gap: 6px; } | |
| .sysstrip .label { font-size: 12px; } | |
| .sysstrip .path { font-size: 11px; flex: 1 1 auto; min-width: 0; | |
| white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } | |
| .badge { font-size: 11px; padding: 3px 8px; } | |
| .macbar { padding: 6px 12px; } | |
| .mactitle { font-size: 11px; } | |
| .card { border-radius: 12px; } | |
| .term { height: 58vh; min-height: 320px; | |
| padding: 14px 14px 12px; font-size: 12.5px; line-height: 1.55; } | |
| /* prompt row stacks: seal + input on row 1, | |
| toggle + clear + send wrap onto row 2 */ | |
| .promptbar { flex-wrap: wrap; gap: 0; } | |
| .promptbar .seal { padding: 10px 12px; } | |
| .promptbar input[type="text"] { | |
| font-size: 16px; /* iOS: prevents zoom on focus */ | |
| padding: 11px 14px; | |
| min-width: 0; flex: 1 1 60%; | |
| border-top: 1px solid var(--paper-line); | |
| } | |
| .promptbar .toggle { | |
| flex: 1 1 50%; justify-content: center; | |
| padding: 10px 8px; font-size: 12px; | |
| border-top: 1px solid var(--paper-line); border-left: none; | |
| } | |
| .promptbar button.clear { | |
| border-top: 1px solid var(--paper-line); border-left: none; | |
| flex: 1 1 30%; padding: 11px 8px; font-size: 13px; | |
| } | |
| .promptbar button.send { | |
| flex: 1 1 70%; padding: 12px 12px; font-size: 14px; | |
| border-top: 1px solid var(--paper-line); border-left: 1px solid var(--indigo-700); | |
| border-radius: 0 0 12px 0; | |
| } | |
| .pillrow { gap: 6px; margin-top: 12px; } | |
| .pill { font-size: 11px; padding: 5px 10px; } | |
| .footline { font-size: 11px; flex-direction: column; gap: 6px; } | |
| } | |
| /* very small phones */ | |
| @media (max-width: 420px) { | |
| .brandbar .wordmark { display: none; } /* keep logo + crumbs */ | |
| .term { font-size: 12px; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="brandbar"> | |
| <img class="logo" alt="poolside" | |
| src="https://cdn-avatars.huggingface.co/v1/production/uploads/699484cbe85a4b61cbc5ee0f/GpYWuz-CovEFgbPOW21dZ.png" /> | |
| <span class="wordmark">POOLSIDE</span> | |
| <span class="sep">/</span> | |
| <span class="crumbs"> | |
| <b>Models</b><span class="sep">›</span> | |
| <span class="grad">Laguna S 2.1</span><span class="sep">›</span> | |
| <b>reasoning terminal</b> | |
| </span> | |
| </div> | |
| <div class="frame"> | |
| <div class="card"> | |
| <div class="sysstrip"> | |
| <span class="label">Laguna S 2.1 — reasoning terminal</span> | |
| <span class="path">/ pool · website / chat</span> | |
| <span class="right"> | |
| <span class="badge"><span class="pulse"></span> online · reasoning preserved</span> | |
| </span> | |
| </div> | |
| <div class="macbar"> | |
| <span class="dots"> | |
| <span class="dot r"></span><span class="dot y"></span><span class="dot g"></span> | |
| </span> | |
| <div class="mactitle"><b>pool</b> — website — <b>~</b></div> | |
| </div> | |
| <div id="term" class="term" aria-live="polite"></div> | |
| <div style="background:var(--paper-2);border-top:1px solid var(--paper-line);padding:14px 14px 16px;"> | |
| <div class="promptbar"> | |
| <div class="seal"><span class="accent">$</span> laguna ></div> | |
| <input id="input" type="text" autocomplete="off" | |
| placeholder="ask anything… e.g. How many r's are in 'strawberry'?" /> | |
| <label class="toggle"><input id="reasoning" type="checkbox" checked /> <span class="label-grad">reasoning</span></label> | |
| <button id="clear" class="clear" type="button">clear</button> | |
| <button id="send" class="send" type="button">Ask Laguna →</button> | |
| </div> | |
| <div class="pillrow"> | |
| <span class="pill"><span class="try"><span class="dot-mini"></span><span class="grad">Try it</span><span class="grad">:</span></span></span> | |
| <span class="pill">How many r's are in <code style="color:var(--ink)">'strawberry'</code>?</span> | |
| <span class="pill">Write a recursive Fibonacci in Python.</span> | |
| <span class="pill">Explain backprop in one paragraph.</span> | |
| </div> | |
| <div class="footline"> | |
| <span>model: <span class="grad" style="font-weight:600">poolside/laguna-s-2.1:free</span> · via openrouter · lossless chain-of-thought across turns via <code>reasoning_details</code></span> | |
| <span>built with gradio <code>Server</code> + @gradio/client</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script type="module"> | |
| import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; | |
| const term = document.getElementById("term"); | |
| const input = document.getElementById("input"); | |
| const sendBtn = document.getElementById("send"); | |
| const clearBtn = document.getElementById("clear"); | |
| const reasoning = document.getElementById("reasoning"); | |
| // messages the model sees — includes reasoning_details on prior assistant turns | |
| let messages = []; | |
| const ANSI = { | |
| reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m", italic: "\x1b[3m", | |
| green: "\x1b[32m", cyan: "\x1b[36m", yellow: "\x1b[33m", | |
| grey: "\x1b[90m", red: "\x1b[31m", | |
| }; | |
| const banner = [ | |
| `${ANSI.green}● ${ANSI.reset}Connected to agent server: ${ANSI.bold}Poolside v2.1${ANSI.reset}`, | |
| `${ANSI.dim} session id: robust-lake-${Math.floor(Math.random()*9000+1000)} · model poolside/laguna-s-2.1:free · openrouter${ANSI.reset}`, | |
| "", | |
| `${ANSI.cyan}>${ANSI.reset} type a question below. the model ${ANSI.bold}reasons before it answers${ANSI.reset}, and remembers its thoughts on the next turn.`, | |
| "", | |
| `${ANSI.dim}─────────────────────────────────────────────────────────────────${ANSI.reset}`, | |
| "", | |
| ].join("\n"); | |
| function esc(s) { return String(s).replace(/[&<>]/g, c => ({ "&": "&", "<": "<", ">": ">" }[c])); } | |
| // Render ANSI escape codes as inline-styled spans mapped onto a light theme. | |
| function render(raw) { | |
| const out = []; | |
| const re = /\x1b\[(\d+)m/g; | |
| let last = 0, m, codes = 0; | |
| const state = (add) => { | |
| codes |= add; | |
| const bold = !!(codes & 1), dim = !!(codes & 2), italic = !!(codes & 4); | |
| const green = !!(codes & 32), cyan = !!(codes & 64), yellow = !!(codes & 128), | |
| grey = !!(codes & 256), red = !!(codes & 512); | |
| const styles = []; | |
| if (bold) styles.push("font-weight:700"); | |
| if (italic) styles.push("font-style:italic"); | |
| // dim → soft marker; subtle so it doesn't blur the text on white | |
| if (dim) styles.push("color:#9f9fb3"); | |
| if (green) styles.push("color:#047857"); | |
| else if (cyan) styles.push("color:#0e7490"); | |
| else if (yellow) styles.push("color:#b45309"); | |
| else if (grey) styles.push("color:#9ca3af"); | |
| else if (red) styles.push("color:#b91c1c"); | |
| if (!styles.length) styles.push("color:#0f1020"); | |
| return styles.join(";"); | |
| }; | |
| while ((m = re.exec(raw)) !== null) { | |
| const txt = raw.slice(last, m.index); | |
| if (txt) out.push(`<span style="${state(0)}">${esc(txt).replace(/\n/g, "<br>")}</span>`); | |
| const code = +m[1]; | |
| if (code === 0) codes = 0; else codes |= (1 << (code - 1)); | |
| last = m.index + m[0].length; | |
| } | |
| if (last < raw.length) { | |
| out.push(`<span style="${state(0)}">${esc(raw.slice(last)).replace(/\n/g, "<br>")}</span>`); | |
| } | |
| return out.join(""); | |
| } | |
| term.innerHTML = render(banner); | |
| term.scrollTop = term.scrollHeight; | |
| function appendText(text) { | |
| const caret = term.querySelector(".caret"); | |
| if (caret) caret.remove(); | |
| term.insertAdjacentHTML("beforeend", render(text)); | |
| const c = document.createElement("span"); | |
| c.className = "caret"; | |
| c.innerHTML = " "; | |
| term.appendChild(c); | |
| term.scrollTop = term.scrollHeight; | |
| } | |
| function freeze() { | |
| const caret = term.querySelector(".caret"); | |
| if (caret) caret.remove(); | |
| } | |
| let busy = false; | |
| async function send() { | |
| const textRaw = input.value; | |
| const text = textRaw.trim(); | |
| if (!text || busy) return; | |
| busy = true; sendBtn.disabled = true; clearBtn.disabled = true; | |
| try { | |
| messages.push({ role: "user", content: text }); | |
| input.value = ""; | |
| const client = await Client.connect(window.location.origin, { | |
| events: ["data", "status"], | |
| }); | |
| const prefix = | |
| `${ANSI.grey}$ ${ANSI.reset}${ANSI.green}user${ANSI.reset} ${ANSI.dim}»${ANSI.reset} ${text.replace(/\n/g, " ")}\n`; | |
| const reasoningMode = reasoning.checked; | |
| appendText(prefix); | |
| appendText( | |
| `${ANSI.cyan}>${ANSI.reset} ${ANSI.grey}connecting to ${ANSI.reset}${ANSI.green}openrouter${ANSI.reset}${ANSI.grey} · reasoning ${ANSI.reset}${ANSI.dim}${reasoningMode ? "on" : "off"}${ANSI.reset}\n` | |
| ); | |
| appendText( | |
| `${ANSI.cyan}>${ANSI.reset} ${ANSI.grey}${reasoningMode ? "thinking… Streaming tokens as they arrive." : "responding…"}${ANSI.reset}\n` | |
| ); | |
| // /chat is a generator. client.predict awaits the final value only, so we | |
| // use client.submit() (returns an async iterator) and read each yielded | |
| // "data" message as it streams. "status" messages report queue progress | |
| // and errors. | |
| const submission = client.submit("/chat", [ | |
| messages, // param 1: full message list (with reasoning_details) | |
| reasoningMode, // param 2: enable reasoning | |
| ]); | |
| let assistantRecord = null; | |
| let streamError = null; | |
| try { | |
| for await (const msg of submission) { | |
| if (msg.type === "data") { | |
| // msg.data is the per-step yield from the endpoint; we yield one | |
| // element at a time so grab the first item. | |
| let payload = msg.data && msg.data[0]; | |
| if (typeof payload === "string") { | |
| try { payload = JSON.parse(payload); } catch { /* leave as-is */ } | |
| } | |
| if (!payload) continue; | |
| if (payload.event === "delta" && typeof payload.text === "string") { | |
| appendText(payload.text); | |
| } else if (payload.event === "done") { | |
| assistantRecord = payload.assistant || null; | |
| } | |
| } else if (msg.type === "status") { | |
| if (msg.stage === "error") { | |
| streamError = msg.message || "generator failed"; | |
| } | |
| } | |
| } | |
| } finally { | |
| freeze(); | |
| } | |
| if (streamError) { | |
| appendText(`\n${ANSI.bold}${ANSI.red}error${ANSI.reset} ${ANSI.dim}»${ANSI.reset} ${streamError}${ANSI.reset}\n`); | |
| } | |
| if (assistantRecord && assistantRecord.content !== undefined) { | |
| messages.push(assistantRecord); | |
| } else { | |
| messages.push({ role: "assistant", content: "" }); | |
| } | |
| appendText(`${ANSI.dim}── done · chain-of-thought persisted ──${ANSI.reset}\n\n`); | |
| } catch (e) { | |
| freeze(); | |
| appendText(`\n${ANSI.bold}${ANSI.red}error${ANSI.reset} ${ANSI.dim}»${ANSI.reset} ${e && e.message || e}\n`); | |
| } finally { | |
| busy = false; sendBtn.disabled = false; clearBtn.disabled = false; | |
| input.focus(); | |
| } | |
| } | |
| sendBtn.addEventListener("click", send); | |
| input.addEventListener("keydown", (e) => { | |
| if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } | |
| }); | |
| clearBtn.addEventListener("click", () => { | |
| messages = []; | |
| term.innerHTML = render(banner); | |
| }); | |
| // pill-driven seeds | |
| document.querySelectorAll('.pill').forEach(p => { | |
| p.addEventListener('click', () => { | |
| // The "Try it:" label pill isn't a seed | |
| if (p.querySelector('.try')) return; | |
| const txt = p.textContent.trim(); | |
| if (txt.startsWith('•') || txt.length < 4) return; | |
| input.value = txt; | |
| input.focus(); | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |