diff --git a/wasm/dist-repl/repl.js b/wasm/dist-repl/repl.js index 51141e7..ab00233 100644 --- a/wasm/dist-repl/repl.js +++ b/wasm/dist-repl/repl.js @@ -146,13 +146,15 @@ function relock() { } // ─── Tabs ─────────────────────────────────────────────────────────── -function newTab(name) { +async function newTab(name) { const id = state.nextTabId++; name = name || `session ${id}`; state.tabs.push({ id, name, tier: "c", transcript: [], inputDraft: "" }); - state.activeTabId = id; - renderAll(); - saveSoon(); + // Route through setActiveTab so the outgoing tab's auto-pause + // fires and the toolbar buttons sync to the freshly-created + // (idle) tab. Without this, opening a new tab during an eval + // leaves the send button disabled and the eval orphaned. + await setActiveTab(id); } function closeTab(id) { @@ -206,8 +208,19 @@ async function setActiveTab(id) { } state.activeTabId = id; renderAll(); - saveSoon(); + // Sync the global toolbar button states to the incoming tab — + // send/cancel/pause are not tab-scoped DOM elements, so a + // switch-away during one tab's eval would otherwise leave the + // newly-active (idle) tab with send disabled. The active tab + // is idle iff nothing in state.pending matches its id. const incoming = activeTab(); + if (incoming) { + const evalRunning = isEvalInFlight(incoming); + sendBtn.disabled = evalRunning; + cancelBtn.disabled = !evalRunning; + pauseBtn.disabled = !evalRunning || !pauseFlagView; + } + saveSoon(); if (incoming && incoming.autoPause) { await autoResumeTab(incoming); } diff --git a/wasm/repl/repl.js b/wasm/repl/repl.js index 51141e7..ab00233 100644 --- a/wasm/repl/repl.js +++ b/wasm/repl/repl.js @@ -146,13 +146,15 @@ function relock() { } // ─── Tabs ─────────────────────────────────────────────────────────── -function newTab(name) { +async function newTab(name) { const id = state.nextTabId++; name = name || `session ${id}`; state.tabs.push({ id, name, tier: "c", transcript: [], inputDraft: "" }); - state.activeTabId = id; - renderAll(); - saveSoon(); + // Route through setActiveTab so the outgoing tab's auto-pause + // fires and the toolbar buttons sync to the freshly-created + // (idle) tab. Without this, opening a new tab during an eval + // leaves the send button disabled and the eval orphaned. + await setActiveTab(id); } function closeTab(id) { @@ -206,8 +208,19 @@ async function setActiveTab(id) { } state.activeTabId = id; renderAll(); - saveSoon(); + // Sync the global toolbar button states to the incoming tab — + // send/cancel/pause are not tab-scoped DOM elements, so a + // switch-away during one tab's eval would otherwise leave the + // newly-active (idle) tab with send disabled. The active tab + // is idle iff nothing in state.pending matches its id. const incoming = activeTab(); + if (incoming) { + const evalRunning = isEvalInFlight(incoming); + sendBtn.disabled = evalRunning; + cancelBtn.disabled = !evalRunning; + pauseBtn.disabled = !evalRunning || !pauseFlagView; + } + saveSoon(); if (incoming && incoming.autoPause) { await autoResumeTab(incoming); } diff --git a/www/repl/repl.js b/www/repl/repl.js index 51141e7..ab00233 100644 --- a/www/repl/repl.js +++ b/www/repl/repl.js @@ -146,13 +146,15 @@ function relock() { } // ─── Tabs ─────────────────────────────────────────────────────────── -function newTab(name) { +async function newTab(name) { const id = state.nextTabId++; name = name || `session ${id}`; state.tabs.push({ id, name, tier: "c", transcript: [], inputDraft: "" }); - state.activeTabId = id; - renderAll(); - saveSoon(); + // Route through setActiveTab so the outgoing tab's auto-pause + // fires and the toolbar buttons sync to the freshly-created + // (idle) tab. Without this, opening a new tab during an eval + // leaves the send button disabled and the eval orphaned. + await setActiveTab(id); } function closeTab(id) { @@ -206,8 +208,19 @@ async function setActiveTab(id) { } state.activeTabId = id; renderAll(); - saveSoon(); + // Sync the global toolbar button states to the incoming tab — + // send/cancel/pause are not tab-scoped DOM elements, so a + // switch-away during one tab's eval would otherwise leave the + // newly-active (idle) tab with send disabled. The active tab + // is idle iff nothing in state.pending matches its id. const incoming = activeTab(); + if (incoming) { + const evalRunning = isEvalInFlight(incoming); + sendBtn.disabled = evalRunning; + cancelBtn.disabled = !evalRunning; + pauseBtn.disabled = !evalRunning || !pauseFlagView; + } + saveSoon(); if (incoming && incoming.autoPause) { await autoResumeTab(incoming); }