Skip to content

feat: keyboard shortcuts for navigation, views, and task actions#293

Open
pikann wants to merge 1 commit into
masterfrom
feature/implement-keyboard-shortcut-system
Open

feat: keyboard shortcuts for navigation, views, and task actions#293
pikann wants to merge 1 commit into
masterfrom
feature/implement-keyboard-shortcut-system

Conversation

@pikann

@pikann pikann commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

What

A discoverable keyboard shortcut system for the web app:

  • Navigation — jump to Home, Timeline, Backlog, Active Sprint, Agents, Conversations, Automation, Team, Docs, and Settings from anywhere (Mod+Shift+H, Mod+I, Mod+G, etc.)
  • Interaction page — cycle views with Mod+[ / Mod+], focus search with Mod+F, toggle view settings with Mod+Shift+F, create a task with Mod+Enter
  • Task under the pointer — hover a card/row and press Mod+O (open), Mod+E (epic), Mod+←/Mod+→ (move to the previous/next column of whatever the view is grouped by — status, sprint, assignee, or type), or Mod+⌫ (delete)
  • Right-click context menu on every task card/row — Open, Move to (submenu of the active view's columns), Status, Assignee, Type, Priority, Epic, Copy ID/link, Delete — with shortcut hints next to items that have one
  • Mod+/ help dialog listing every binding, generated from a single keymap registry so it can never drift from the real bindings

Full design rationale (including which Mod+key combos are hard-blocked by the browser/OS and had to be worked around) is in docs/product/keyboard-shortcuts.md.

Localized in all 9 supported locales (en, es, fr, ja, ko, pt-BR, ru, vi, zh-CN).

Why

Closes #230. The issue's discussion converged on exactly this shape — hovering a task and pressing a key, plus a right-click menu that shows the matching shortcut next to each action — so this PR builds that as general infrastructure (any task field, any view's columns) rather than a single sprint/backlog toggle.

This replaces #231 with a broader solution: instead of one double-click gesture for Backlog ↔ Sprint, moving a task to any column (including Sprint or Backlog when a view groups by sprint) is available via Mod+←/Mod+→ while hovering, or via the right-click "Move to" menu — plus the rest of the shortcut system above. Big thanks to @RSnew for the original report and for pushing the discussion toward the hover-shortcut + context-menu design in the comments — credited as co-author on the commit.

Test plan

  • tsc -b --noEmit — clean
  • biome check . — clean
  • vitest run — 306 tests pass, including new lib/shortcuts/keymap.test.ts coverage of the key matcher
  • Manual pass in the app: navigation shortcuts, view cycling, task hover shortcuts, right-click menu, help dialog, in at least one non-English locale

…tions

Lets users navigate between sidebar pages, switch views on an interaction
page, and act on the task under the pointer (open, epic, move column,
delete) via Mod+key shortcuts, plus a right-click context menu on every
task card/row with matching shortcut hints. Discoverable through a
Mod+/ help dialog and localized across all 9 supported locales.

Builds on the keyboard-shortcut direction discussed in #230.

Co-Authored-By: RSnew <64447978+RSnew@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Several user-facing shortcuts in the new keymap collide with browser/OS defaults or are not actually wired, and goto.settings is layout-broken on common US keyboards. Type check, lint, and tests all pass, so these are UX-correctness issues rather than crashes.

Reviewed changes — added a global keyboard shortcut system, task right-click context menu, platform-aware keycap components, and help dialog across the authenticated web app.

  • Add lib/shortcuts/* keymap, provider, hover/page zustand stores, and keymap.test.ts.
  • Add TaskContextMenu around TaskCard/TaskRow with move-to, status, assignee, type, priority, epic, copy, and delete actions.
  • Add components/ui/context-menu.tsx and components/ui/kbd.tsx.
  • Add ShortcutHelpDialog reachable from Mod+/ and the user menu.
  • Wire page shortcuts and a delete confirmation dialog in InteractionLayout.

ℹ️ Task shortcuts are mouse-only by design

Task-scope shortcuts are registered from onMouseEnter/onMouseLeave, and the docs correctly call this an accessibility gap. Open a follow-up issue for keyboard focus support so the same shortcuts are available to keyboard-only users.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

// Roughly half the keyboard is off-limits because the browser or the OS
// swallows the keydown before page JS ever sees it, or because overriding it
// would break a near-universal user expectation:
// - Hard-blocked, no override possible on any browser: Mod+T/N/W (tab/window

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several chosen shortcuts overlap with browser defaults that the file’s hard-blocked list misses. Even with preventDefault(), tab/window/history/bookmark bindings are often non-cancelable, so users may get browser behavior instead of the app action.

Technical details
# Affected sites
- `apps/web/src/lib/shortcuts/keymap.ts:10-16` — comment lists hard-blocked combos but omits several used below.
- `apps/web/src/lib/shortcuts/keymap.ts:53-79` — PLAIN_ACTIONS / SHIFT_ACTIONS contain the colliding bindings.

## Collisions to verify
Based on Chrome keyboard-shortcut docs:
- `Mod+Shift+W` (automation) = close current window on macOS/Windows/Linux.
- `Mod+Shift+H` (home) = open home page on macOS/Windows.
- `Mod+Shift+A` (active sprint) = tab search in Chrome.
- `Mod+Shift+,` (settings) = Chrome Settings on macOS (`⌘,`, and with Shift it still conflicts).
- `Mod+G` (agents) = find next match.
- `Mod+J` (team) = open downloads (Windows/Linux).
- `Mod+D` (docs) = bookmark current page.
- `Mod+O` (open task) = open file dialog.
- `Mod+[` / `Mod+]` (prev/next view) = history back/forward on macOS.

## Required outcome
Audit every binding against browser/OS defaults and replace contested combos. Prefer keys that are truly free, even if less mnemonic.

a: "goto.activeSprint",
x: "goto.conversations",
w: "goto.automation",
",": "goto.settings",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On US English keyboards, Shift+Comma produces <, not ,, so goto.settings will not match here. Consider matching on e.code === "Comma" or accepting both , and < so the shortcut works across layouts.

chords: [{ mod: true, key: "B" }],
},
{
id: "general.close",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esc is shown in the help dialog as general.close, but ShortcutProvider never matches or dispatches it. Either implement a global close stack or remove general.close from SHORTCUT_DISPLAY and the docs to avoid a false promise.

* Action callbacks the currently-hovered/focused task card or row exposes to
* the global shortcut provider. Views register a task's actions on
* mouseenter/focus and clear them on mouseleave/blur — see
* `use-task-shortcut-target.ts`. Only one task can be "active" at a time, so

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring references use-task-shortcut-target.ts, which doesn't exist. Update it to point to TaskCard and TaskRow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Double-click to move tasks between Backlog and Sprint

1 participant