From 8c68bc6bf7b2b9c75159bab05fcfc3931c73c1f1 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Thu, 18 Jun 2026 15:46:53 -0400 Subject: [PATCH 1/5] feat(#4391): add Ctrl/Cmd+, shortcut for settings toggle --- static/boot.js | 7 ++ tests/test_issue4391_settings_shortcut.py | 126 ++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 tests/test_issue4391_settings_shortcut.py diff --git a/static/boot.js b/static/boot.js index d6d54accb..4ea740ea3 100644 --- a/static/boot.js +++ b/static/boot.js @@ -1538,6 +1538,13 @@ document.addEventListener('keydown',async e=>{ // stream running on its own session; the user just gets a fresh blank one. await newSession();await renderSessionList();closeMobileSidebar();$('msg').focus(); } + // Cmd/Ctrl+, opens/closes Settings (VS Code convention). + // Fire globally — like VS Code, don't skip text inputs. + if((e.metaKey||e.ctrlKey)&&!e.shiftKey&&!e.altKey&&e.key===','){ + e.preventDefault(); + if(typeof toggleSettings==='function') toggleSettings(); + return; + } if(e.key==='Escape'){ // Close onboarding overlay if open (skip/dismiss the wizard) const onboardingOverlay=$('onboardingOverlay'); diff --git a/tests/test_issue4391_settings_shortcut.py b/tests/test_issue4391_settings_shortcut.py new file mode 100644 index 000000000..c3fafea57 --- /dev/null +++ b/tests/test_issue4391_settings_shortcut.py @@ -0,0 +1,126 @@ +"""Static-analysis tests for Ctrl/Cmd+, settings shortcut. + +The global keydown handler in boot.js must have a branch that: +- Detects Ctrl/Cmd+, (comma key) +- Fires globally without a text-input skip guard (VS Code convention) +- Calls toggleSettings() if it exists +- Uses e.key (not e.code) for keyboard layout independence + +Issue: #4391 (Ctrl+, opens Settings) +""" +import re +from pathlib import Path + +BOOT_JS = (Path(__file__).parent.parent / "static" / "boot.js").read_text(encoding="utf-8") +PANELS_JS = (Path(__file__).parent.parent / "static" / "panels.js").read_text(encoding="utf-8") + + +class TestSettingsShortcutPresence: + """The Ctrl+, handler must be present and correctly positioned.""" + + def test_e_key_comma_check_present(self): + """Shortcut must use e.key===',' to detect the comma key.""" + assert "e.key===','" in BOOT_JS, ( + "boot.js must contain e.key===',' to detect the comma key; " + "this is layout-independent (e.code varies by physical layout)" + ) + + def test_no_e_code_comma_check(self): + """Must NOT use e.code===',' since that varies by keyboard layout.""" + assert "e.code===','" not in BOOT_JS, ( + "boot.js must NOT use e.code===','; use e.key instead for layout independence" + ) + + def test_prevent_default_called(self): + """e.preventDefault() must be called in the Ctrl+, branch.""" + # Find the e.key===',' block and verify e.preventDefault() is nearby. + idx = BOOT_JS.find("e.key===',' ") + if idx == -1: + idx = BOOT_JS.find("e.key===','") + assert idx >= 0, "Shortcut e.key===',' branch not found" + # Extract a reasonable window around the match to verify preventDefault is called. + branch = BOOT_JS[max(0, idx - 200):idx + 500] + assert "e.preventDefault()" in branch, ( + "e.preventDefault() must be called before toggleSettings() " + "in the Ctrl+, branch" + ) + + def test_toggleSettings_called(self): + """toggleSettings() must be called in the Ctrl+, branch.""" + idx = BOOT_JS.find("e.key===',' ") + if idx == -1: + idx = BOOT_JS.find("e.key===','") + assert idx >= 0, "Shortcut e.key===',' branch not found" + branch = BOOT_JS[max(0, idx - 200):idx + 500] + assert "toggleSettings()" in branch, ( + "toggleSettings() must be called in the Ctrl+, branch" + ) + + def test_typeof_guard_on_toggleSettings(self): + """toggleSettings() must be guarded with typeof toggleSettings==='function'.""" + idx = BOOT_JS.find("e.key===',' ") + if idx == -1: + idx = BOOT_JS.find("e.key===','") + assert idx >= 0, "Shortcut e.key===',' branch not found" + branch = BOOT_JS[max(0, idx - 200):idx + 500] + assert "typeof toggleSettings===" in branch or "typeof toggleSettings =" in branch, ( + "toggleSettings must be guarded with typeof toggleSettings==='function' " + "to avoid runtime errors" + ) + + def test_no_text_input_skip_guard(self): + """Shortcut must fire globally — no isText or isTextInput guard (VS Code behavior).""" + idx = BOOT_JS.find("e.key===',' ") + if idx == -1: + idx = BOOT_JS.find("e.key===','") + assert idx >= 0, "Shortcut e.key===',' branch not found" + # Find the next 'if(e.key===' after this one (end of the Ctrl+, branch). + branch_end = BOOT_JS.find("if(e.key===", idx + 50) + if branch_end == -1: + branch_end = len(BOOT_JS) + branch = BOOT_JS[idx:branch_end] + assert "isText" not in branch and "isTextInput" not in branch, ( + "Shortcut must NOT have a text-input skip guard; fire globally like VS Code" + ) + + def test_modifier_idiom_matches_ctrl_b(self): + """Modifier check must use the same pattern as Ctrl+B: (e.metaKey||e.ctrlKey)&&!e.shiftKey&&!e.altKey.""" + # Verify the Ctrl+B idiom exists. + assert "(e.metaKey||e.ctrlKey)&&!e.shiftKey&&!e.altKey&&(e.key==='b'||e.key==='B')" in BOOT_JS, ( + "Ctrl+B idiom not found in expected form" + ) + # Verify the Ctrl+, block uses the same modifier idiom. + idx = BOOT_JS.find("e.key===',' ") + if idx == -1: + idx = BOOT_JS.find("e.key===','") + assert idx >= 0, "Shortcut e.key===',' branch not found" + branch = BOOT_JS[max(0, idx - 200):idx + 100] + assert "(e.metaKey||e.ctrlKey)" in branch, ( + "Ctrl+, must use (e.metaKey||e.ctrlKey) to detect Cmd or Ctrl" + ) + assert "!e.shiftKey" in branch, ( + "Ctrl+, must use !e.shiftKey to ensure Shift is not pressed" + ) + assert "!e.altKey" in branch, ( + "Ctrl+, must use !e.altKey to ensure Alt is not pressed" + ) + + +class TestTargetFunctionExists: + """The toggleSettings() function must exist in panels.js and be callable.""" + + def test_toggleSettings_defined(self): + """toggleSettings() function must be defined in panels.js.""" + assert "function toggleSettings(" in PANELS_JS, ( + "panels.js must define toggleSettings() function" + ) + + def test_toggleSettings_not_just_declared(self): + """toggleSettings() must be a real function, not just referenced.""" + idx = PANELS_JS.find("function toggleSettings(") + assert idx >= 0, "toggleSettings() function not found" + # Verify the next few lines contain a body (very basic check). + body = PANELS_JS[idx:idx + 300] + assert "{" in body, ( + "toggleSettings() must have a function body with braces" + ) From 6b967d5d5dab3f60099afb9416f2d13db21679a1 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Thu, 18 Jun 2026 16:15:33 -0400 Subject: [PATCH 2/5] Remove unused re import flagged by ruff --- tests/test_issue4391_settings_shortcut.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_issue4391_settings_shortcut.py b/tests/test_issue4391_settings_shortcut.py index c3fafea57..9e82f16ea 100644 --- a/tests/test_issue4391_settings_shortcut.py +++ b/tests/test_issue4391_settings_shortcut.py @@ -8,7 +8,6 @@ The global keydown handler in boot.js must have a branch that: Issue: #4391 (Ctrl+, opens Settings) """ -import re from pathlib import Path BOOT_JS = (Path(__file__).parent.parent / "static" / "boot.js").read_text(encoding="utf-8") From 1e33f24ac6434c2b3c38054bd0eae2f9e31408f6 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Thu, 18 Jun 2026 17:13:37 -0400 Subject: [PATCH 3/5] Retrigger CI for flaky test_gateway_sync isolation failure From ce443af95e6dddeb61885372a37aae49163ab55a Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Thu, 18 Jun 2026 17:25:21 -0400 Subject: [PATCH 4/5] Retrigger browser-smoke after CI install timeout From c5cb0baedeff3058b5b6a275e7feb6c32e05626b Mon Sep 17 00:00:00 2001 From: nesquena-hermes Date: Thu, 18 Jun 2026 21:36:17 +0000 Subject: [PATCH 5/5] =?UTF-8?q?release:=20v0.51.501=20=E2=80=94=20Ctrl/Cmd?= =?UTF-8?q?+,=20opens=20Settings=20(#4438,=20#4391)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: rodboev Closes #4438 Closes #4391 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16c1e37cd..8feaf0a8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ ## [Unreleased] +## [v0.51.501] — 2026-06-18 — Release RK (Ctrl/Cmd+, opens Settings) + +### Added + +- **`Ctrl+,` (or `Cmd+,` on macOS) now opens and closes Settings (#4391).** Following the VS Code convention, the shortcut fires globally — including from text inputs — and toggles the Settings panel. Thanks @rodboev. + ## [v0.51.500] — 2026-06-18 — Release RJ (fix stuck-scroll on long sessions with dense tool blocks) ### Fixed