From 30fd052efa80d5ac9030b42db2e2ffefd8720598 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Mon, 29 Jun 2026 14:29:47 -0400 Subject: [PATCH] fix(boot): preserve native Ctrl+K text editing during URL prefill rework (#4961) --- static/boot.js | 3 + .../test_issue5099_ctrl_k_text_input_guard.py | 55 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 tests/test_issue5099_ctrl_k_text_input_guard.py diff --git a/static/boot.js b/static/boot.js index 5931b4642..9c3aaa5d7 100644 --- a/static/boot.js +++ b/static/boot.js @@ -1753,6 +1753,9 @@ document.addEventListener('keydown',async e=>{ } } if((e.metaKey||e.ctrlKey)&&e.key==='k'){ + const t=e.target; + const isText=t&&(t.tagName==='INPUT'||t.tagName==='TEXTAREA'||t.isContentEditable); + if(isText) return; e.preventDefault(); // If the current session has no messages AND nothing is in flight, just focus // the composer rather than creating another empty session that will clutter diff --git a/tests/test_issue5099_ctrl_k_text_input_guard.py b/tests/test_issue5099_ctrl_k_text_input_guard.py new file mode 100644 index 000000000..fd31dced7 --- /dev/null +++ b/tests/test_issue5099_ctrl_k_text_input_guard.py @@ -0,0 +1,55 @@ +"""Static-analysis tests for #5099 (Ctrl+K must not steal kill-line in text fields). + +Emacs-adjacent users expect Ctrl+K to kill to end-of-line while the composer +or other editable fields are focused. Cmd/Ctrl+K should still create a new chat +when focus is outside text inputs, matching the existing Ctrl+B guard pattern. +""" +from pathlib import Path + +BOOT_JS = (Path(__file__).parent.parent / "static" / "boot.js").read_text(encoding="utf-8") + + +def _ctrl_k_branch_window() -> str: + idx = BOOT_JS.find("(e.metaKey||e.ctrlKey)&&e.key==='k'") + assert idx >= 0, "Cmd/Ctrl+K handler not found in boot.js" + return BOOT_JS[idx:idx + 1500] + + +class TestIssue5099CtrlKTextInputGuard: + def test_ctrl_k_skips_text_inputs(self): + branch = _ctrl_k_branch_window() + assert "tagName==='INPUT'" in branch + assert "tagName==='TEXTAREA'" in branch + assert "isContentEditable" in branch + assert "if(isText) return" in branch + + def test_ctrl_k_prevent_default_after_text_guard(self): + branch = _ctrl_k_branch_window() + guard_idx = branch.find("if(isText) return") + prevent_idx = branch.find("e.preventDefault()") + assert guard_idx >= 0 and prevent_idx >= 0, ( + "Ctrl+K must guard text inputs before calling preventDefault()" + ) + assert guard_idx < prevent_idx, ( + "preventDefault() must not run before the text-input early return" + ) + + def test_ctrl_k_guard_matches_ctrl_b_idiom(self): + ctrl_b_idx = BOOT_JS.find("(e.key==='b'||e.key==='B')") + assert ctrl_b_idx >= 0, "Ctrl+B handler not found in boot.js" + ctrl_b_block = BOOT_JS[max(0, ctrl_b_idx - 250):ctrl_b_idx + 300] + ctrl_k_block = _ctrl_k_branch_window() + for needle in ( + "const t=e.target", + "const isText=t&&", + "tagName==='INPUT'", + "tagName==='TEXTAREA'", + "isContentEditable", + ): + assert needle in ctrl_b_block, f"Ctrl+B guard missing {needle!r}" + assert needle in ctrl_k_block, f"Ctrl+K guard missing {needle!r}" + + def test_ctrl_k_still_creates_new_session_outside_inputs(self): + branch = _ctrl_k_branch_window() + assert "newSession()" in branch + assert "closeMobileSidebar()" in branch