Skip to content

Add keyboard shortcuts for jumping to sidebar threads#1456

Merged
juliusmarminge merged 6 commits intomainfrom
t3code/thread-number-shortcuts
Mar 29, 2026
Merged

Add keyboard shortcuts for jumping to sidebar threads#1456
juliusmarminge merged 6 commits intomainfrom
t3code/thread-number-shortcuts

Conversation

@t3dotgg
Copy link
Copy Markdown
Member

@t3dotgg t3dotgg commented Mar 27, 2026

Summary

  • Added platform-aware thread jump shortcuts for the first nine visible sidebar threads (Cmd+1-Cmd+9 on macOS, Ctrl+1-Ctrl+9 elsewhere).
  • Mod+Shift+[ andMod+Shift+] for back/forward
  • Displayed contextual shortcut hints next to thread rows while the modifier key is held.
  • Extracted jump-key logic into shared sidebar helpers and covered it with unit tests.
  • Reused the existing thread navigation path so shortcut selection behaves the same as clicking a thread.

Testing

  • Not run

Note

Medium Risk
Adds new global window keyboard listeners and expands the supported keybinding command set, which could introduce shortcut conflicts or unexpected navigation if matching logic is wrong.

Overview
Adds default keybindings and contract support for thread.jump.1 through thread.jump.9, enabling Cmd/Ctrl+number shortcuts.

Implements sidebar thread-jump navigation: computes the first 9 visible thread targets, intercepts matching shortcut events to navigate (reusing the existing selection/anchor behavior), and shows inline shortcut hint badges while the modifier key is held. Includes unit tests for jump target selection and command index parsing.

Written by Cursor Bugbot for commit 6aadae6. This will update automatically on new commits. Configure here.

Note

Add keyboard shortcuts for jumping to sidebar threads

  • Adds thread.previous (mod+shift+[), thread.next (mod+shift+]), and numeric jump shortcuts (mod+1mod+9) for navigating between sidebar threads.
  • The sidebar shows jump hint labels on thread rows while the relevant modifier keys are held, then navigates on key release.
  • Shortcut matching now uses event.code (physical key) in addition to event.key, improving reliability across keyboard layouts for bracket and digit shortcuts.
  • shortcutLabelForCommand now accepts when-context and platform options, resolving the effective label accounting for conflicts and conditional bindings.
  • New helpers (threadJumpCommandForIndex, resolveAdjacentThreadId, getVisibleSidebarThreadIds) are added to support index-based and previous/next thread traversal logic.

Macroscope summarized 954546f.

Summary by CodeRabbit

  • New Features
    • Added keyboard shortcuts to quickly jump between threads using keys 1–9 with a platform-specific modifier (Cmd on macOS, Ctrl on other platforms).
    • Visual hints now display on thread rows indicating which key to press for each thread when the jump modifier is held.

- Map the first nine visible threads to number keys
- Show platform-specific shortcut hints while modifier is held
- Add tests for key mapping and modifier detection
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning PR description is incomplete—missing UI screenshots/videos, and the checklist is not properly filled out. While it covers what changed and why, required template elements are absent. Add before/after screenshots showing the thread jump hints UI, include a video demonstrating the keyboard shortcut interaction, and complete the checklist items.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and concisely summarizes the main change: adding keyboard shortcuts for jumping to sidebar threads, which aligns with the primary focus of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/thread-number-shortcuts

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Mar 27, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/web/src/components/Sidebar.logic.test.ts (1)

110-147: Good platform-aware modifier detection tests.

Tests correctly cover:

  • macOS with metaKey
  • Windows with ctrlKey
  • Rejection when shiftKey is also pressed

Consider adding a test case for altKey: true to verify it's also rejected, matching the implementation requirement.

📝 Optional: Add altKey rejection test
expect(
  isThreadJumpModifierPressed(
    {
      key: "Control",
      metaKey: false,
      ctrlKey: true,
      shiftKey: false,
      altKey: true,
    },
    "Win32",
  ),
).toBe(false);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/Sidebar.logic.test.ts` around lines 110 - 147, Add a
test asserting that isThreadJumpModifierPressed rejects when altKey is true: in
the test "detects the active jump modifier by platform" add a case calling
isThreadJumpModifierPressed with a Windows platform ("Win32") and an event
object where ctrlKey: true, altKey: true (and shiftKey: false, metaKey: false)
and expect the result to be false; this mirrors the existing shiftKey rejection
case and verifies altKey is also treated as an invalid modifier.
apps/web/src/components/Sidebar.logic.ts (1)

81-111: Default navigator.platform may throw in non-browser contexts.

While Sidebar.tsx always passes platform explicitly, the default parameter navigator.platform would throw a ReferenceError if these helpers are ever called without an argument in SSR or test environments where navigator is undefined.

Consider using a safer default or documenting that the argument is required outside browser contexts.

🛡️ Suggested defensive default
 export function isThreadJumpModifierPressed(
   event: ThreadJumpEvent,
-  platform = navigator.platform,
+  platform = typeof navigator !== "undefined" ? navigator.platform : "",
 ): boolean {

Apply the same pattern to resolveThreadJumpIndex and formatThreadJumpHintLabel.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/Sidebar.logic.ts` around lines 81 - 111, Defaulting
parameters to navigator.platform can throw in non-browser environments; update
isThreadJumpModifierPressed, resolveThreadJumpIndex, and
formatThreadJumpHintLabel to use a safe platform default (e.g. const
safePlatform = typeof navigator !== "undefined" ? navigator.platform : "" or
undefined) instead of directly using navigator.platform in the parameter list,
then use safePlatform in calls to isMacPlatform and other logic so these helpers
won’t ReferenceError in SSR/tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/src/components/Sidebar.logic.test.ts`:
- Around line 110-147: Add a test asserting that isThreadJumpModifierPressed
rejects when altKey is true: in the test "detects the active jump modifier by
platform" add a case calling isThreadJumpModifierPressed with a Windows platform
("Win32") and an event object where ctrlKey: true, altKey: true (and shiftKey:
false, metaKey: false) and expect the result to be false; this mirrors the
existing shiftKey rejection case and verifies altKey is also treated as an
invalid modifier.

In `@apps/web/src/components/Sidebar.logic.ts`:
- Around line 81-111: Defaulting parameters to navigator.platform can throw in
non-browser environments; update isThreadJumpModifierPressed,
resolveThreadJumpIndex, and formatThreadJumpHintLabel to use a safe platform
default (e.g. const safePlatform = typeof navigator !== "undefined" ?
navigator.platform : "" or undefined) instead of directly using
navigator.platform in the parameter list, then use safePlatform in calls to
isMacPlatform and other logic so these helpers won’t ReferenceError in
SSR/tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f3c49538-55ac-433c-86b5-46df99b9e76a

📥 Commits

Reviewing files that changed from the base of the PR and between add5f34 and aa91042.

📒 Files selected for processing (3)
  • apps/web/src/components/Sidebar.logic.test.ts
  • apps/web/src/components/Sidebar.logic.ts
  • apps/web/src/components/Sidebar.tsx

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

});
},
[clearSelection, navigate, selectedThreadIds.size, setSelectionAnchor],
);
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.

Duplicated navigation logic in handleThreadClick and navigateToThread

Low Severity

The new navigateToThread callback duplicates the plain-click branch of the existing handleThreadClick callback (clear selection, set anchor, navigate). The plain-click path in handleThreadClick (lines 964–972) performs the exact same sequence — check selectedThreadIds.size, call clearSelection, setSelectionAnchor, and navigate — but was not updated to call navigateToThread. This duplication means a future change to the navigation logic risks being applied in one place but missed in the other.

Additional Locations (1)
Fix in Cursor Fix in Web

- add archive/unarchive commands, projections, and persistence
- surface archived-thread state in the UI and settings
- improve bootstrap, editor launch, and shell env handling
- Add thread previous/next and jump-by-number bindings
- Update sidebar navigation and hint rendering to use them
- Expand keybinding and contract tests for thread shortcuts
@juliusmarminge juliusmarminge force-pushed the t3code/thread-number-shortcuts branch from 6aadae6 to 9c8e8c6 Compare March 29, 2026 00:14
…shortcuts

# Conflicts:
#	apps/web/src/components/Sidebar.logic.test.ts
#	apps/web/src/components/Sidebar.logic.ts
#	apps/web/src/components/Sidebar.tsx
- Treat shadowed keybindings as unavailable when rendering labels
- Pass terminal/thread context into chat and sidebar shortcut lookups
- Update tests for effective binding resolution and when-clause matching
@github-actions github-actions bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Mar 29, 2026
- Bind fork/promise helpers to the current services in Claude adapter and tests
- Switch drainable worker to tx primitives and simplify enqueue
- Classify sqlite errors with the built-in helper
- Update tests to use the new Effect mocking and runtime helpers
@github-actions github-actions bot added size:L 100-499 changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Mar 29, 2026
@juliusmarminge juliusmarminge marked this pull request as ready for review March 29, 2026 01:20
@juliusmarminge juliusmarminge merged commit 58ba4f1 into main Mar 29, 2026
15 checks passed
@juliusmarminge juliusmarminge deleted the t3code/thread-number-shortcuts branch March 29, 2026 01:23
sfncore pushed a commit to sfncore/t3code that referenced this pull request Mar 29, 2026
Upstream changes (v0.0.15):
- Refactor projection pipeline side effects (pingdotgg#1512)
- Inline sqlite error classification (pingdotgg#1515)
- DrainableWorker drain off outstanding count (pingdotgg#1514)
- Thread archiving with archivedAt (pingdotgg#1505-related migrations)
- Auto-generate first-turn thread titles (pingdotgg#1375)
- Keyboard shortcuts for sidebar threads (pingdotgg#1456)
- Various UI fixes (sidebar, settings, markdown links)

Conflict resolution:
- ProjectionPipeline.ts: took upstream (our REINDEX workaround
  no longer needed with doltlite canDefer=0 fix)
- Migrations.ts: renumbered our 016-018 to 019-021, keeping
  upstream's 016-018 (CanonicalizeModelSelections, ArchivedAt)
- ProjectionThreads.ts, orchestration.ts: took upstream (archiving)
- Sidebar files: took upstream (new features)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants