From 2e8a239614c6b02d16db2283b20909c85515299c Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Thu, 30 Apr 2026 03:43:21 +0000 Subject: [PATCH] fix: allow Cmd/Ctrl+K new chat while a conversation is busy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Cmd/Ctrl+K shortcut was gated on !S.busy, which meant users had to wait for an in-flight generation to finish before they could start a fresh conversation — exactly the moment they want to switch context. Removes the !S.busy guard. The in-flight stream keeps running on its own session; the user just gets a fresh blank one in the foreground. The existing "no messages → focus composer instead of stacking empty sessions" behavior is preserved. Tests: tests/test_mobile_layout.py — new test_new_conversation_shortcut_works_while_busy guard assertion. Existing closeMobileSidebar window check widened to 12 lines to accommodate the comment block explaining the change. Full suite: 3253 passed. Salvaged from contributor work in PR #1084. Co-authored-by: Hermes Agent --- static/boot.js | 7 ++++++- tests/test_mobile_layout.py | 26 +++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/static/boot.js b/static/boot.js index e6a1d267..78c982c0 100644 --- a/static/boot.js +++ b/static/boot.js @@ -531,7 +531,12 @@ document.addEventListener('keydown',async e=>{ // If the current session has no messages, just focus the composer rather than // creating another empty session that will clutter the sidebar list (#1171). if(S.session&&(S.session.message_count||0)===0){$('msg').focus();return;} - if(!S.busy){await newSession();await renderSessionList();closeMobileSidebar();$('msg').focus();} + // Cmd/Ctrl+K should always create a new conversation, even while the current + // one is still streaming. The old !S.busy guard meant users had to wait for + // a long generation to finish before they could start something new — exactly + // the moment they want to switch context. newSession() leaves the in-flight + // stream running on its own session; the user just gets a fresh blank one. + await newSession();await renderSessionList();closeMobileSidebar();$('msg').focus(); } if(e.key==='Escape'){ // Close onboarding overlay if open (skip/dismiss the wizard) diff --git a/tests/test_mobile_layout.py b/tests/test_mobile_layout.py index 6d687a7c..b46c6f14 100644 --- a/tests/test_mobile_layout.py +++ b/tests/test_mobile_layout.py @@ -157,11 +157,35 @@ def test_new_conversation_closes_mobile_sidebar(): shortcut_line = next((ln for ln in boot_js.splitlines() if "e.key==='k'" in ln or "e.key === 'k'" in ln), "") assert shortcut_line, "Cmd/Ctrl+K new chat shortcut missing from static/boot.js" - shortcut_block = "\n".join(boot_js.splitlines()[boot_js.splitlines().index(shortcut_line):boot_js.splitlines().index(shortcut_line)+6]) + shortcut_block = "\n".join(boot_js.splitlines()[boot_js.splitlines().index(shortcut_line):boot_js.splitlines().index(shortcut_line)+12]) assert "closeMobileSidebar" in shortcut_block, \ "Cmd/Ctrl+K new chat shortcut must closeMobileSidebar() after creating the new session" +def test_new_conversation_shortcut_works_while_busy(): + """Cmd/Ctrl+K should still create a new conversation while the current one is busy. + + The previous behavior gated the shortcut on !S.busy, which meant users had + to wait for a long generation to finish before they could start something + new — the exact moment they want to switch context. + """ + boot_js = (REPO / "static" / "boot.js").read_text(encoding="utf-8") + shortcut_line = next((ln for ln in boot_js.splitlines() if "e.key==='k'" in ln or "e.key === 'k'" in ln), "") + assert shortcut_line, "Cmd/Ctrl+K new chat shortcut missing from static/boot.js" + # Inspect the next 10 lines after the keybinding match — the gating block + # would live there if it had been kept. + idx = boot_js.splitlines().index(shortcut_line) + shortcut_block = "\n".join(boot_js.splitlines()[idx:idx + 10]) + # Strip the existing message-count guard (which is unrelated and stays) so + # we only check for an S.busy gate on the newSession() call itself. + assert "if(!S.busy)" not in shortcut_block, ( + "Cmd/Ctrl+K must not be blocked by the current session's busy state" + ) + assert "if (!S.busy)" not in shortcut_block, ( + "Cmd/Ctrl+K must not be blocked by the current session's busy state" + ) + + # ── Viewport and scroll safety ──────────────────────────────────────────────── def test_body_overflow_hidden():