Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: add opt-in vim-style chat navigation shortcuts - #28967

Draft
jscottmiller wants to merge 4 commits into
mainfrom
scott/agents-vim-nav
Draft

feat: add opt-in vim-style chat navigation shortcuts#28967
jscottmiller wants to merge 4 commits into
mainfrom
scott/agents-vim-nav

Conversation

@jscottmiller

@jscottmiller jscottmiller commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

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-navigation deployment 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 Mod being the selected modifier (Ctrl, Alt/Option, or Super/Win/Cmd; default Cmd on macOS and Ctrl elsewhere):

Shortcut Action
Mod+J / Mod+K Next / previous chat in the sidebar's visual order
Mod+Shift+J / Mod+Shift+K Last / first loaded chat
Mod+Shift+O New chat
Mod+Shift+E Rename the active chat
Mod+/ Search. When Mod is the platform modifier this replaces Cmd/Ctrl+K; otherwise Cmd/Ctrl+K keeps working too
Escape (on a focused sidebar row) Return focus to the composer

Navigation 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 by event.key when it is ASCII and fall back to the physical event.code otherwise, 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 sidebar Kbd hint 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 on useDashboard().experiments; when off, no keydown listener is registered for navigation and a stored setting is inert.

Known limitations

  • "Last chat" means the last loaded page.
  • Some OS chords cannot be intercepted by the page: Win+J/K/E// on Windows and Super+letter on most Linux desktops (with meta), Alt+Shift input-language switching (with alt). These are documented on isModifierPressed.
  • With alt on macOS, Option always produces a non-ASCII glyph, so letters resolve by QWERTY physical position on remapped layouts such as Dvorak.
  • The Rename menu item does not yet show a shortcut hint.
Implementation plan

Findings

  • Shortcut hook: site/src/pages/AgentsPage/hooks/useAgentsPageKeybindings.ts. Listens on document keydown, modifier was isMac() ? metaKey : ctrlKey. Bindings before this change: Cmd/Ctrl+N (new agent), Cmd/Ctrl+K (toggle search). Already fires while an input is focused.
  • Cmd/Ctrl+J is unused. Ctrl+J opens Downloads in Chrome/Edge on Windows/Linux; preventDefault on the document listener suppresses that.
  • Sidebar display order is computed inline in ChatsPanel.tsx: pinned -> shared with you -> time/status sections, with collapsible sections and expandable children. No exported "visible order" helper existed.
  • Active chat comes from the route /agents/:agentId; rows are NavLinks that preserve location.search.
  • Settings: /agents/settings/general already has a "Keyboard shortcuts" heading. Two storage patterns exist: server-side user preferences and localStorage via useSyncExternalStore (useChatFullWidth.ts).
  • The Lexical composer consumes Enter and Escape, so unmodified g/G/Enter cannot be global.
  • Experiments are read in site/src via useDashboard().experiments.includes(...); stories pass parameters.experiments through withDashboardProvider.

Decisions

  1. Cmd+K conflict: vim mode moves search to Mod+/; Cmd/Ctrl+K stays available when the vim modifier is not the platform modifier.
  2. Selection model: immediate navigation on J/K (no separate cursor + Enter).
  3. Storage: localStorage for both the toggle and the modifier.
  4. Pagination: "last chat" means last loaded chat.
  5. Rename chord: Mod+Shift+E. New chat chord: Mod+Shift+O (Cmd/Ctrl+N is reserved by browsers outside the PWA).
  6. All new keys are gated behind the toggle and the experiment; nothing is always on.
  7. Modifier choices are Ctrl, Alt, Meta; exact-modifier matching replaces the previous altKey rejection. Meta stays selectable on Windows (labelled Win) with the OS conflicts documented.
  8. Kbd hint uses the existing getOSKey glyph for Cmd on macOS and text labels otherwise; prose and the Select use text labels (Cmd, Option).

Changes

  1. hooks/useVimNavigation.ts: localStorage toggle agents.vim-navigation (useVimNavigationSetting), modifier agents.vim-navigation-modifier (useVimNavigationModifier), and useVimNavigationActive = experiment && toggle.
  2. utils/keyboardShortcuts.ts: isLetterKey, isSlashKey, isModifierPressed, getDefaultVimModifier, getModifierLabel, getModifierKeycap.
  3. tree/visibleChatOrder.ts: pure helper returning { visible, all } chat ID orders.
  4. hooks/useChatVimNavigation.ts: J/K, Shift+J/K, Escape handling; dialog guard; hidden-row anchoring; takes the modifier.
  5. useAgentsPageKeybindings.ts: Mod+/ search, Mod+Shift+O new, Mod+Shift+E rename; Cmd/Ctrl+K search unless the vim modifier takes it.
  6. ChatsPanel.tsx: computes order, mounts the nav hook, navigates preserving location.search, swaps the Search Kbd hint.
  7. AgentsPageLayout.tsx: wires rename to the shared RenameChatDialog, falling back to the per-chat query cache when the active chat is not in the paginated list.
  8. ChatVimNavigationSettings.tsx (toggle + modifier Select) under a hoisted "Keyboard shortcuts" heading; rendered by AgentSettingsGeneralPageView only when the experiment is on.
  9. codersdk/deployment.go: ExperimentChatVimNavigation; make gen output for swagger, docs, and typesGenerated.ts.
  10. Tests: matcher and label unit tests per modifier, hook tests for each modifier, settings component test (stored values), sidebar test for experiment off vs on (route assertion), visibleChatOrder tests. Stories: one per modifier for the settings block and for the sidebar search hint.

Generated by Coder Agents on behalf of @jscottmiller.

@jscottmiller jscottmiller added the experimental Changes that might not necessarily be merged, until its approved to proceed with. label Sep 4, 2026
… 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.
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Docs preview

Check 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.

@jscottmiller jscottmiller changed the title feat(site/src/pages/AgentsPage): add opt-in vim-style chat navigation shortcuts feat: add opt-in vim-style chat navigation shortcuts Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

experimental Changes that might not necessarily be merged, until its approved to proceed with.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant