feat(tasks): Cmd+Option+Left/Right to cycle task tabs#99
Conversation
| useGuardedHotkeys( | ||
| getKeys('last-task-tab'), | ||
| (e) => { | ||
| e.preventDefault() | ||
| if (visibleTabs.length > 1) { | ||
| useTabStore.getState().setActiveView('tabs') | ||
| setActiveTabIndex(toFullIndex(visibleTabs.length - 1)) | ||
| } | ||
| }, | ||
| { enableOnFormTags: true, enabled: !isRecording } | ||
| ) |
There was a problem hiding this comment.
Last-Tab Shortcut Hijacks Editors
When focus is inside an input, textarea, contentEditable editor, CodeMirror, xterm, Milkdown, ProseMirror, or select, Cmd+9 still runs because this handler is enabled on form tags and has no editing guard. It calls preventDefault() and can switch tabs instead of letting the focused editor handle the key.
| useGuardedHotkeys( | |
| getKeys('last-task-tab'), | |
| (e) => { | |
| e.preventDefault() | |
| if (visibleTabs.length > 1) { | |
| useTabStore.getState().setActiveView('tabs') | |
| setActiveTabIndex(toFullIndex(visibleTabs.length - 1)) | |
| } | |
| }, | |
| { enableOnFormTags: true, enabled: !isRecording } | |
| ) | |
| useGuardedHotkeys( | |
| getKeys('last-task-tab'), | |
| (e) => { | |
| const el = e.target as HTMLElement | |
| if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') return | |
| if (el.isContentEditable || el.getAttribute('role') === 'textbox') return | |
| if (el.closest?.('.cm-editor') || el.closest?.('.xterm')) return | |
| if (el.closest?.('.milkdown') || el.closest?.('.ProseMirror')) return | |
| e.preventDefault() | |
| if (visibleTabs.length > 1) { | |
| useTabStore.getState().setActiveView('tabs') | |
| setActiveTabIndex(toFullIndex(visibleTabs.length - 1)) | |
| } | |
| }, | |
| { enableOnFormTags: true, enabled: !isRecording } | |
| ) |
Context Used: CLAUDE.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts
Line: 244-254
Comment:
**Last-Tab Shortcut Hijacks Editors**
When focus is inside an input, textarea, contentEditable editor, CodeMirror, xterm, Milkdown, ProseMirror, or select, `Cmd+9` still runs because this handler is enabled on form tags and has no editing guard. It calls `preventDefault()` and can switch tabs instead of letting the focused editor handle the key.
```suggestion
useGuardedHotkeys(
getKeys('last-task-tab'),
(e) => {
const el = e.target as HTMLElement
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') return
if (el.isContentEditable || el.getAttribute('role') === 'textbox') return
if (el.closest?.('.cm-editor') || el.closest?.('.xterm')) return
if (el.closest?.('.milkdown') || el.closest?.('.ProseMirror')) return
e.preventDefault()
if (visibleTabs.length > 1) {
useTabStore.getState().setActiveView('tabs')
setActiveTabIndex(toFullIndex(visibleTabs.length - 1))
}
},
{ enableOnFormTags: true, enabled: !isRecording }
)
```
**Context Used:** CLAUDE.md ([source](https://app.greptile.com/slayzone/github/debuglebowski/slayzone/-/custom-context?memory=4d0a5da1-ee49-442f-ba49-d4a3dc97238d))
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| id: 'last-task-tab', | ||
| label: 'Last Task Tab', | ||
| group: 'Tabs', | ||
| defaultKeys: 'mod+9', | ||
| scope: 'global', | ||
| customizable: true | ||
| }, |
There was a problem hiding this comment.
Rebinding Leaves Cmd+9 Unbound
last-task-tab is customizable, but the numbered tab handler now only binds Cmd+1 through Cmd+8. If a user rebinds this shortcut, Cmd+9 no longer has any handler even though the existing switch-tab-1-9 shortcut definition still presents mod+1-9 as the numbered tab range.
Context Used: CLAUDE.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/shared/shortcuts/src/definitions.ts
Line: 172-178
Comment:
**Rebinding Leaves Cmd+9 Unbound**
`last-task-tab` is customizable, but the numbered tab handler now only binds `Cmd+1` through `Cmd+8`. If a user rebinds this shortcut, `Cmd+9` no longer has any handler even though the existing `switch-tab-1-9` shortcut definition still presents `mod+1-9` as the numbered tab range.
**Context Used:** CLAUDE.md ([source](https://app.greptile.com/slayzone/github/debuglebowski/slayzone/-/custom-context?memory=4d0a5da1-ee49-442f-ba49-d4a3dc97238d))
How can I resolve this? If you propose a fix, please make it concise.…wski#98) Original issue debuglebowski#98 asked for Cmd+1..9 as indexed jumps to the Nth task tab. The Chrome-style "Cmd+9 = last tab" branch was an over-interpretation of the spec. Restore Cmd+9 to indexed behavior: jump to the 9th task tab if it exists, otherwise no-op. - Delete the `last-task-tab` shortcut definition. - Drop the dedicated `last-task-tab` handler; extend the indexed handler from `mod+1..8` to `mod+1..9`. - Replace the "Cmd+9 = last" e2e test with an indexed-jump test (Cmd+3 with 3 tabs, Cmd+9 is a no-op).
…wski#98) Original issue debuglebowski#98 listed numeric jumps as a 'bonus'. User confirmed only Cmd+Option+Left/Right is wanted. Restored main's switch-tab-1-9 behavior verbatim.
Summary
Cmd+Option+Left/Cmd+Option+Rightto cycle through open task tabs in the top tab bar (wrap-around at edges, skips the home tab).Closes
Closes #98
Notes on the diff
This branch also includes two small unrelated fixes that the typecheck on
maincurrently fails without:fix(transport): add missing resolve-task route stub— registers a 501 stub for the missing module imported byrest-api/index.ts.fix(app): add missing ws and @trpc/server deps for host-capability-server— adds runtime deps already used bypackages/apps/app/src/main/host-capability-server.ts.Happy to split these out into a separate PR if preferred.
Test plan
pnpm typecheckpassespnpm --filter @slayzone/app test:e2e e2e/core/16-tab-management.spec.ts— 11/11 pass, including 4 new cases for the shortcuts above.Cmd+Option+LeftandCmd+Option+Rightcycle through them and wrap at the edges.<select>.