feat: add opt-in vim-style chat navigation shortcuts - #28967
Draft
jscottmiller wants to merge 4 commits into
Draft
feat: add opt-in vim-style chat navigation shortcuts#28967jscottmiller wants to merge 4 commits into
jscottmiller wants to merge 4 commits into
Conversation
… shortcuts Adds a localStorage-backed "Vim-style chat navigation" toggle under Keyboard shortcuts on the Agents settings page. When enabled: - Cmd/Ctrl+J and Cmd/Ctrl+K select the next and previous chat in the sidebar's visual order, anchoring to the nearest visible neighbor when the active chat is inside a collapsed section or parent. - Cmd/Ctrl+Shift+J and Cmd/Ctrl+Shift+K jump to the last and first loaded chat. - Cmd/Ctrl+Shift+O starts a new chat and Cmd/Ctrl+Shift+E opens the rename dialog for the active chat. - Search moves from Cmd/Ctrl+K to Cmd/Ctrl+/. - Escape on a focused sidebar row returns focus to the composer. Keys are ignored while focus is inside a dialog, and navigation is disabled on settings routes and while the list is loading or errored. With the toggle off, keybindings are unchanged.
…rd layouts Adds isLetterKey, which matches on event.key when it is an ASCII letter and otherwise on the physical event.code, so Cmd/Ctrl+J/K/O/E/N work on Cyrillic, Greek, and Hebrew layouts while remapped Latin layouts such as Dvorak keep following the printed letter.
Adds a Ctrl, Alt, or Super (Meta) modifier setting for the vim-style chat navigation shortcuts, stored in localStorage next to the toggle and defaulting to Cmd on macOS and Ctrl elsewhere. Every chord and every hint derives from the selected modifier, with Super rendered as Cmd and Alt as Option on macOS.
Adds the chat-vim-navigation deployment experiment and hides the vim-style chat navigation setting, its shortcuts, and its hints unless the experiment is enabled. A stored setting has no effect while the experiment is off.
jscottmiller
force-pushed
the
scott/agents-vim-nav
branch
from
September 11, 2026 19:03
bb9b907 to
518b49c
Compare
Contributor
Docs previewCheck off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds an opt-in "Vim-style chat navigation" toggle to the Agents settings page (Keyboard shortcuts section), with a configurable modifier key, gated behind the
chat-vim-navigationdeployment experiment. The setting is stored in localStorage and is off by default; with it off, or with the experiment disabled, keybindings are unchanged.When enabled, with
Modbeing the selected modifier (Ctrl, Alt/Option, or Super/Win/Cmd; default Cmd on macOS and Ctrl elsewhere):Modis the platform modifier this replaces Cmd/Ctrl+K; otherwise Cmd/Ctrl+K keeps working tooNavigation is immediate and works while typing in the composer. The visible order is derived by a pure helper (
getVisibleChatOrder) that mirrors the sidebar render: pinned, shared with you, then time/status sections, skipping collapsed sections and including children of expanded roots. When the active chat is hidden inside a collapsed section or parent, J/K anchor to its nearest visible neighbor. Keys are ignored while focus is inside a dialog, and navigation is disabled on settings routes and while the list is loading or errored.Chords match exactly one modifier (so AltGr, which reports Ctrl+Alt, never matches). Letters and
/match byevent.keywhen it is ASCII and fall back to the physicalevent.codeotherwise, which keeps non-Latin layouts and macOS Option chords (Option+J reports "∆") working. Every hint and the settings description derive from the selected modifier; on macOS Super is labelled Cmd and Alt is labelled Option, and the sidebarKbdhint uses the same⌘glyph as the existing search hint.Experiment
codersdk.ExperimentChatVimNavigation(chat-vim-navigation). The settings block, both key handlers, and the hint are gated onuseDashboard().experiments; when off, nokeydownlistener is registered for navigation and a stored setting is inert.Known limitations
/on Windows and Super+letter on most Linux desktops (withmeta), Alt+Shift input-language switching (withalt). These are documented onisModifierPressed.alton macOS, Option always produces a non-ASCII glyph, so letters resolve by QWERTY physical position on remapped layouts such as Dvorak.Implementation plan
Findings
site/src/pages/AgentsPage/hooks/useAgentsPageKeybindings.ts. Listens ondocumentkeydown, modifier wasisMac() ? metaKey : ctrlKey. Bindings before this change: Cmd/Ctrl+N (new agent), Cmd/Ctrl+K (toggle search). Already fires while an input is focused.preventDefaulton the document listener suppresses that.ChatsPanel.tsx: pinned -> shared with you -> time/status sections, with collapsible sections and expandable children. No exported "visible order" helper existed./agents/:agentId; rows areNavLinks that preservelocation.search./agents/settings/generalalready has a "Keyboard shortcuts" heading. Two storage patterns exist: server-side user preferences and localStorage viauseSyncExternalStore(useChatFullWidth.ts).g/G/Enter cannot be global.site/srcviauseDashboard().experiments.includes(...); stories passparameters.experimentsthroughwithDashboardProvider.Decisions
altKeyrejection. Meta stays selectable on Windows (labelled Win) with the OS conflicts documented.getOSKeyglyph for Cmd on macOS and text labels otherwise; prose and the Select use text labels (Cmd, Option).Changes
hooks/useVimNavigation.ts: localStorage toggleagents.vim-navigation(useVimNavigationSetting), modifieragents.vim-navigation-modifier(useVimNavigationModifier), anduseVimNavigationActive= experiment && toggle.utils/keyboardShortcuts.ts:isLetterKey,isSlashKey,isModifierPressed,getDefaultVimModifier,getModifierLabel,getModifierKeycap.tree/visibleChatOrder.ts: pure helper returning{ visible, all }chat ID orders.hooks/useChatVimNavigation.ts: J/K, Shift+J/K, Escape handling; dialog guard; hidden-row anchoring; takes the modifier.useAgentsPageKeybindings.ts: Mod+/ search, Mod+Shift+O new, Mod+Shift+E rename; Cmd/Ctrl+K search unless the vim modifier takes it.ChatsPanel.tsx: computes order, mounts the nav hook, navigates preservinglocation.search, swaps the SearchKbdhint.AgentsPageLayout.tsx: wires rename to the sharedRenameChatDialog, falling back to the per-chat query cache when the active chat is not in the paginated list.ChatVimNavigationSettings.tsx(toggle + modifierSelect) under a hoisted "Keyboard shortcuts" heading; rendered byAgentSettingsGeneralPageViewonly when the experiment is on.codersdk/deployment.go:ExperimentChatVimNavigation;make genoutput for swagger, docs, andtypesGenerated.ts.visibleChatOrdertests. Stories: one per modifier for the settings block and for the sidebar search hint.Generated by Coder Agents on behalf of @jscottmiller.