From bd38ea6882e2fe1cef50c9c5359288f76cdec97d Mon Sep 17 00:00:00 2001 From: wise-toddler Date: Tue, 23 Jun 2026 14:27:41 +0530 Subject: [PATCH 1/9] feat(tasks): add Cmd+Option+Left/Right and Cmd+1..9 to switch task tabs (#98) --- .../renderer/src/app-shell/useAppShortcuts.ts | 43 ++++++++++++++++++- packages/shared/shortcuts/src/definitions.ts | 16 +++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts index bb75000e..1c0f39d8 100644 --- a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts +++ b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts @@ -188,7 +188,48 @@ export function useAppShortcuts(deps: AppShortcutsDeps): void { (e) => { e.preventDefault() const num = parseInt(e.key, 10) - if (num < visibleTabs.length) setActiveTabIndex(toFullIndex(num)) + // visibleTabs[0] is the home tab; task tabs occupy visibleIndex 1..length-1. + // Chrome-style: mod+9 jumps to the LAST task tab regardless of count. + if (num === 9) { + if (visibleTabs.length > 1) setActiveTabIndex(toFullIndex(visibleTabs.length - 1)) + } else if (num < visibleTabs.length) { + setActiveTabIndex(toFullIndex(num)) + } + }, + { enableOnFormTags: true, enabled: !isRecording } + ) + + // Cycle through open task tabs (skip the home tab), wrapping at the edges. + const navigateTaskTabs = useCallback( + (direction: 1 | -1) => { + // Task tabs live at visibleIndex 1..length-1 (visibleTabs[0] is home). + if (visibleTabs.length <= 1) return + const taskCount = visibleTabs.length - 1 + const visibleIdx = toVisibleIndex(useTabStore.getState().activeTabIndex) + // Treat home / unknown position as "before the first task tab" so + // next jumps to first and prev jumps to last — same as Chrome. + const currentTaskPos = visibleIdx >= 1 ? visibleIdx - 1 : direction === 1 ? -1 : 0 + const nextTaskPos = (currentTaskPos + direction + taskCount) % taskCount + useTabStore.getState().setActiveView('tabs') + setActiveTabIndex(toFullIndex(nextTaskPos + 1)) + }, + [visibleTabs.length, toFullIndex, toVisibleIndex, setActiveTabIndex] + ) + + useGuardedHotkeys( + getKeys('next-task-tab'), + (e) => { + e.preventDefault() + navigateTaskTabs(1) + }, + { enableOnFormTags: true, enabled: !isRecording } + ) + + useGuardedHotkeys( + getKeys('prev-task-tab'), + (e) => { + e.preventDefault() + navigateTaskTabs(-1) }, { enableOnFormTags: true, enabled: !isRecording } ) diff --git a/packages/shared/shortcuts/src/definitions.ts b/packages/shared/shortcuts/src/definitions.ts index 51d2ef0e..c1018f0c 100644 --- a/packages/shared/shortcuts/src/definitions.ts +++ b/packages/shared/shortcuts/src/definitions.ts @@ -152,6 +152,22 @@ export const shortcutDefinitions: ShortcutDefinition[] = [ defaultKeys: 'ctrl+shift+tab', scope: 'global' }, + { + id: 'next-task-tab', + label: 'Next Task Tab', + group: 'Tabs', + defaultKeys: 'mod+alt+arrowright', + scope: 'global', + customizable: false + }, + { + id: 'prev-task-tab', + label: 'Previous Task Tab', + group: 'Tabs', + defaultKeys: 'mod+alt+arrowleft', + scope: 'global', + customizable: false + }, { id: 'reopen-closed-tab', label: 'Reopen Closed Tab', From 420aa87d7eab911fc5d5f9c634568243907e57ce Mon Sep 17 00:00:00 2001 From: wise-toddler Date: Tue, 23 Jun 2026 14:35:12 +0530 Subject: [PATCH 2/9] fix(transport): add missing resolve-task route stub --- .../src/server/http/rest-api/sessions/resolve-task.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 packages/shared/transport/src/server/http/rest-api/sessions/resolve-task.ts diff --git a/packages/shared/transport/src/server/http/rest-api/sessions/resolve-task.ts b/packages/shared/transport/src/server/http/rest-api/sessions/resolve-task.ts new file mode 100644 index 00000000..112241d7 --- /dev/null +++ b/packages/shared/transport/src/server/http/rest-api/sessions/resolve-task.ts @@ -0,0 +1,9 @@ +import type { Express } from 'express' +import type { RestApiDeps } from '../types' + +// Stub: session → bound task resolution for the slay CLI. Returns 501 until wired. +export function registerResolveSessionTaskRoute(app: Express, _deps: RestApiDeps): void { + app.get('/api/sessions/:id/task', (_req, res) => { + res.status(501).json({ error: 'not implemented' }) + }) +} From 05be66f6c6dbe4bcc6333ffcff512dfba8ee51c6 Mon Sep 17 00:00:00 2001 From: wise-toddler Date: Tue, 23 Jun 2026 14:38:05 +0530 Subject: [PATCH 3/9] fix(app): add missing ws and @trpc/server deps for host-capability-server --- packages/apps/app/package.json | 3 +++ pnpm-lock.yaml | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/packages/apps/app/package.json b/packages/apps/app/package.json index d4975de1..9174b678 100644 --- a/packages/apps/app/package.json +++ b/packages/apps/app/package.json @@ -95,6 +95,8 @@ "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.18", "tw-animate-css": "^1.4.0", + "ws": "^8.18.0", + "@trpc/server": "^11.17.0", "zod": "^4.3.5", "zustand": "^5.0.0" }, @@ -112,6 +114,7 @@ "@types/node": "^22.19.1", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", + "@types/ws": "^8.5.13", "@vitejs/plugin-react": "^5.1.1", "babel-plugin-react-compiler": "^1.0.0", "electron": "41.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02b26709..f6565d6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -226,6 +226,9 @@ importers: '@tanstack/react-query': specifier: ^5.100.9 version: 5.100.10(react@19.2.3) + '@trpc/server': + specifier: ^11.17.0 + version: 11.17.0(typescript@5.9.3) '@xyflow/react': specifier: ^12.10.2 version: 12.10.2(@types/react@19.2.8)(immer@11.1.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -286,6 +289,9 @@ importers: tw-animate-css: specifier: ^1.4.0 version: 1.4.0 + ws: + specifier: ^8.18.0 + version: 8.18.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) zod: specifier: ^4 version: 4.3.5 @@ -332,6 +338,9 @@ importers: '@types/react-dom': specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.8) + '@types/ws': + specifier: ^8.5.13 + version: 8.18.1 '@vitejs/plugin-react': specifier: ^5.1.1 version: 5.1.2(vite@7.3.1(@types/node@22.19.7)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) From b31c966193528c2f26a694b1f1e899be99c1f605 Mon Sep 17 00:00:00 2001 From: wise-toddler Date: Tue, 23 Jun 2026 14:40:04 +0530 Subject: [PATCH 4/9] fix(shortcuts): suppress task-tab cycle inside text inputs --- .../app/src/renderer/src/app-shell/useAppShortcuts.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts index 1c0f39d8..94f8c77b 100644 --- a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts +++ b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts @@ -219,6 +219,10 @@ export function useAppShortcuts(deps: AppShortcutsDeps): void { useGuardedHotkeys( getKeys('next-task-tab'), (e) => { + // macOS Cmd+Option+Right is "next word" in text fields; don't hijack. + const el = e.target as HTMLElement + if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.isContentEditable) return + if (el.closest?.('.cm-editor') || el.closest?.('.xterm')) return e.preventDefault() navigateTaskTabs(1) }, @@ -228,6 +232,10 @@ export function useAppShortcuts(deps: AppShortcutsDeps): void { useGuardedHotkeys( getKeys('prev-task-tab'), (e) => { + // macOS Cmd+Option+Left is "previous word" in text fields; don't hijack. + const el = e.target as HTMLElement + if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.isContentEditable) return + if (el.closest?.('.cm-editor') || el.closest?.('.xterm')) return e.preventDefault() navigateTaskTabs(-1) }, From a4d3b95fc38e477e828f1d695600c631084f9bd2 Mon Sep 17 00:00:00 2001 From: wise-toddler Date: Tue, 23 Jun 2026 18:10:47 +0530 Subject: [PATCH 5/9] test(tasks): cover task-tab keyboard shortcuts (#98) --- .../app/e2e/core/16-tab-management.spec.ts | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/packages/apps/app/e2e/core/16-tab-management.spec.ts b/packages/apps/app/e2e/core/16-tab-management.spec.ts index 1663de50..31eb7be1 100644 --- a/packages/apps/app/e2e/core/16-tab-management.spec.ts +++ b/packages/apps/app/e2e/core/16-tab-management.spec.ts @@ -12,6 +12,16 @@ async function getVisibleInputValue(page: import('@playwright/test').Page): Prom }) } +/** Read the title of the currently-active tab from the tab store. Task tabs + * stay mounted (display:none) so all their s remain visible per the + * layout; only the store knows which one is actually active. */ +async function getActiveTabTitle(page: import('@playwright/test').Page): Promise { + return page.evaluate(() => { + const s = (window as any).__slayzone_tabStore.getState() + return s.tabs[s.activeTabIndex]?.title ?? null + }) +} + test.describe('Tab management & keyboard shortcuts', () => { let projectAbbrev: string @@ -173,4 +183,89 @@ test.describe('Tab management & keyboard shortcuts', () => { const value = await getVisibleInputValue(mainWindow) expect(value).toBe('Tab task A') }) + + test('Cmd+Option+Right cycles forward through task tabs and wraps', async ({ mainWindow }) => { + // Ensure all 3 task tabs (A, B, C) are open, then start on the first one. + for (const title of ['Tab task A', 'Tab task B', 'Tab task C']) { + await goHome(mainWindow) + await expect(mainWindow.getByText(title).first()).toBeVisible({ timeout: 5_000 }) + await mainWindow.getByText(title).first().click() + await expect( + mainWindow.locator('[data-testid="terminal-mode-trigger"]:visible').first() + ).toBeVisible({ timeout: 5_000 }) + } + await mainWindow.keyboard.press('Meta+1') + await expect( + mainWindow.locator('[data-testid="terminal-mode-trigger"]:visible').first() + ).toBeVisible({ timeout: 5_000 }) + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') + + await mainWindow.keyboard.press('Meta+Alt+ArrowRight') + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task B') + + await mainWindow.keyboard.press('Meta+Alt+ArrowRight') + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task C') + + // Wrap-around: last → first + await mainWindow.keyboard.press('Meta+Alt+ArrowRight') + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') + }) + + test('Cmd+Option+Left cycles backward through task tabs and wraps', async ({ mainWindow }) => { + await mainWindow.keyboard.press('Meta+1') + await expect( + mainWindow.locator('[data-testid="terminal-mode-trigger"]:visible').first() + ).toBeVisible({ timeout: 5_000 }) + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') + + // Wrap-around: first → last + await mainWindow.keyboard.press('Meta+Alt+ArrowLeft') + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task C') + + await mainWindow.keyboard.press('Meta+Alt+ArrowLeft') + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task B') + + await mainWindow.keyboard.press('Meta+Alt+ArrowLeft') + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') + }) + + test('Cmd+Option+Right is suppressed while focused in a text input', async ({ mainWindow }) => { + await mainWindow.keyboard.press('Meta+1') + await expect( + mainWindow.locator('[data-testid="terminal-mode-trigger"]:visible').first() + ).toBeVisible({ timeout: 5_000 }) + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') + + // Focus the visible task title input (an ) — guard should NOT switch tabs. + const titleInput = mainWindow.locator('input:visible').first() + await titleInput.focus() + await mainWindow.keyboard.press('Meta+Alt+ArrowRight') + + // Active task tab is unchanged. + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') + }) + + test('Cmd+9 jumps to the last task tab', async ({ mainWindow }) => { + // With 3 task tabs open, Cmd+9 (Chrome-style) should jump to the LAST task tab. + await mainWindow.keyboard.press('Meta+1') + await expect( + mainWindow.locator('[data-testid="terminal-mode-trigger"]:visible').first() + ).toBeVisible({ timeout: 5_000 }) + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') + + await mainWindow.keyboard.press('Meta+9') + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task C') + }) + + test('Cmd+Option+Right/Left from home jumps to first / last task tab', async ({ mainWindow }) => { + await goHome(mainWindow) + // Home tab active — next jumps to FIRST task tab. + await mainWindow.keyboard.press('Meta+Alt+ArrowRight') + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') + + await goHome(mainWindow) + // Home tab active — prev jumps to LAST task tab. + await mainWindow.keyboard.press('Meta+Alt+ArrowLeft') + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task C') + }) }) From 5be743072efa10e3b48a724948da3838bd45952b Mon Sep 17 00:00:00 2001 From: wise-toddler Date: Wed, 24 Jun 2026 14:50:55 +0530 Subject: [PATCH 6/9] refactor(shortcuts): address consensus review feedback for task tab shortcuts (#98) --- .../renderer/src/app-shell/useAppShortcuts.ts | 29 +++++++++++++------ packages/shared/shortcuts/src/definitions.ts | 12 ++++++-- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts index 94f8c77b..c1b4d452 100644 --- a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts +++ b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts @@ -184,15 +184,11 @@ export function useAppShortcuts(deps: AppShortcutsDeps): void { ) useGuardedHotkeys( - 'mod+1,mod+2,mod+3,mod+4,mod+5,mod+6,mod+7,mod+8,mod+9', + 'mod+1,mod+2,mod+3,mod+4,mod+5,mod+6,mod+7,mod+8', (e) => { e.preventDefault() const num = parseInt(e.key, 10) - // visibleTabs[0] is the home tab; task tabs occupy visibleIndex 1..length-1. - // Chrome-style: mod+9 jumps to the LAST task tab regardless of count. - if (num === 9) { - if (visibleTabs.length > 1) setActiveTabIndex(toFullIndex(visibleTabs.length - 1)) - } else if (num < visibleTabs.length) { + if (num < visibleTabs.length) { setActiveTabIndex(toFullIndex(num)) } }, @@ -221,8 +217,10 @@ export function useAppShortcuts(deps: AppShortcutsDeps): void { (e) => { // macOS Cmd+Option+Right is "next word" in text fields; don't hijack. const el = e.target as HTMLElement - if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.isContentEditable) return + 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() navigateTaskTabs(1) }, @@ -232,16 +230,29 @@ export function useAppShortcuts(deps: AppShortcutsDeps): void { useGuardedHotkeys( getKeys('prev-task-tab'), (e) => { - // macOS Cmd+Option+Left is "previous word" in text fields; don't hijack. const el = e.target as HTMLElement - if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.isContentEditable) return + 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() navigateTaskTabs(-1) }, { enableOnFormTags: true, enabled: !isRecording } ) + 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( 'mod+shift+1,mod+shift+2,mod+shift+3,mod+shift+4,mod+shift+5,mod+shift+6,mod+shift+7,mod+shift+8,mod+shift+9', (e) => { diff --git a/packages/shared/shortcuts/src/definitions.ts b/packages/shared/shortcuts/src/definitions.ts index c1018f0c..8529d697 100644 --- a/packages/shared/shortcuts/src/definitions.ts +++ b/packages/shared/shortcuts/src/definitions.ts @@ -158,7 +158,7 @@ export const shortcutDefinitions: ShortcutDefinition[] = [ group: 'Tabs', defaultKeys: 'mod+alt+arrowright', scope: 'global', - customizable: false + customizable: true }, { id: 'prev-task-tab', @@ -166,7 +166,15 @@ export const shortcutDefinitions: ShortcutDefinition[] = [ group: 'Tabs', defaultKeys: 'mod+alt+arrowleft', scope: 'global', - customizable: false + customizable: true + }, + { + id: 'last-task-tab', + label: 'Last Task Tab', + group: 'Tabs', + defaultKeys: 'mod+9', + scope: 'global', + customizable: true }, { id: 'reopen-closed-tab', From b6bf5ae59f051c4a9216cd5dd4d8e0f45b595fbc Mon Sep 17 00:00:00 2001 From: wise-toddler Date: Thu, 25 Jun 2026 14:25:12 +0530 Subject: [PATCH 7/9] fix(shortcuts): guard last-task-tab handler against text inputs (#98) --- .../apps/app/src/renderer/src/app-shell/useAppShortcuts.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts index c1b4d452..970f0a20 100644 --- a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts +++ b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts @@ -244,6 +244,11 @@ export function useAppShortcuts(deps: AppShortcutsDeps): void { 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') From 4ebf5bda072432bf6abe104d87d31759bf0a5f10 Mon Sep 17 00:00:00 2001 From: wise-toddler Date: Thu, 25 Jun 2026 14:29:21 +0530 Subject: [PATCH 8/9] revert(shortcuts): drop Cmd+9 = last-task-tab special case (#98) Original issue #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). --- .../app/e2e/core/16-tab-management.spec.ts | 10 ++++++++-- .../renderer/src/app-shell/useAppShortcuts.ts | 19 +------------------ packages/shared/shortcuts/src/definitions.ts | 8 -------- 3 files changed, 9 insertions(+), 28 deletions(-) diff --git a/packages/apps/app/e2e/core/16-tab-management.spec.ts b/packages/apps/app/e2e/core/16-tab-management.spec.ts index 31eb7be1..c7670ea0 100644 --- a/packages/apps/app/e2e/core/16-tab-management.spec.ts +++ b/packages/apps/app/e2e/core/16-tab-management.spec.ts @@ -245,14 +245,20 @@ test.describe('Tab management & keyboard shortcuts', () => { expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') }) - test('Cmd+9 jumps to the last task tab', async ({ mainWindow }) => { - // With 3 task tabs open, Cmd+9 (Chrome-style) should jump to the LAST task tab. + test('Cmd+3 jumps to the 3rd task tab; Cmd+9 is a no-op when fewer than 9 tabs', async ({ + mainWindow + }) => { + // With 3 task tabs open, Cmd+N is an indexed jump (Cmd+N → Nth task tab). await mainWindow.keyboard.press('Meta+1') await expect( mainWindow.locator('[data-testid="terminal-mode-trigger"]:visible').first() ).toBeVisible({ timeout: 5_000 }) expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') + await mainWindow.keyboard.press('Meta+3') + expect(await getActiveTabTitle(mainWindow)).toBe('Tab task C') + + // Cmd+9 with only 3 task tabs open is a no-op (no 9th tab to jump to). await mainWindow.keyboard.press('Meta+9') expect(await getActiveTabTitle(mainWindow)).toBe('Tab task C') }) diff --git a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts index 970f0a20..51838bb7 100644 --- a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts +++ b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts @@ -184,7 +184,7 @@ export function useAppShortcuts(deps: AppShortcutsDeps): void { ) useGuardedHotkeys( - 'mod+1,mod+2,mod+3,mod+4,mod+5,mod+6,mod+7,mod+8', + 'mod+1,mod+2,mod+3,mod+4,mod+5,mod+6,mod+7,mod+8,mod+9', (e) => { e.preventDefault() const num = parseInt(e.key, 10) @@ -241,23 +241,6 @@ export function useAppShortcuts(deps: AppShortcutsDeps): void { { 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 } - ) - useGuardedHotkeys( 'mod+shift+1,mod+shift+2,mod+shift+3,mod+shift+4,mod+shift+5,mod+shift+6,mod+shift+7,mod+shift+8,mod+shift+9', (e) => { diff --git a/packages/shared/shortcuts/src/definitions.ts b/packages/shared/shortcuts/src/definitions.ts index 8529d697..1c3b5cd7 100644 --- a/packages/shared/shortcuts/src/definitions.ts +++ b/packages/shared/shortcuts/src/definitions.ts @@ -168,14 +168,6 @@ export const shortcutDefinitions: ShortcutDefinition[] = [ scope: 'global', customizable: true }, - { - id: 'last-task-tab', - label: 'Last Task Tab', - group: 'Tabs', - defaultKeys: 'mod+9', - scope: 'global', - customizable: true - }, { id: 'reopen-closed-tab', label: 'Reopen Closed Tab', From cd0f30019a0bcd5788a9545957b9edaebeb02547 Mon Sep 17 00:00:00 2001 From: wise-toddler Date: Sat, 27 Jun 2026 00:49:11 +0530 Subject: [PATCH 9/9] revert(shortcuts): drop Cmd+1..9 changes; arrow cycle only (#98) Original issue #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. --- .../app/e2e/core/16-tab-management.spec.ts | 18 ------------------ .../renderer/src/app-shell/useAppShortcuts.ts | 4 +--- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/packages/apps/app/e2e/core/16-tab-management.spec.ts b/packages/apps/app/e2e/core/16-tab-management.spec.ts index c7670ea0..ef681c79 100644 --- a/packages/apps/app/e2e/core/16-tab-management.spec.ts +++ b/packages/apps/app/e2e/core/16-tab-management.spec.ts @@ -245,24 +245,6 @@ test.describe('Tab management & keyboard shortcuts', () => { expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') }) - test('Cmd+3 jumps to the 3rd task tab; Cmd+9 is a no-op when fewer than 9 tabs', async ({ - mainWindow - }) => { - // With 3 task tabs open, Cmd+N is an indexed jump (Cmd+N → Nth task tab). - await mainWindow.keyboard.press('Meta+1') - await expect( - mainWindow.locator('[data-testid="terminal-mode-trigger"]:visible').first() - ).toBeVisible({ timeout: 5_000 }) - expect(await getActiveTabTitle(mainWindow)).toBe('Tab task A') - - await mainWindow.keyboard.press('Meta+3') - expect(await getActiveTabTitle(mainWindow)).toBe('Tab task C') - - // Cmd+9 with only 3 task tabs open is a no-op (no 9th tab to jump to). - await mainWindow.keyboard.press('Meta+9') - expect(await getActiveTabTitle(mainWindow)).toBe('Tab task C') - }) - test('Cmd+Option+Right/Left from home jumps to first / last task tab', async ({ mainWindow }) => { await goHome(mainWindow) // Home tab active — next jumps to FIRST task tab. diff --git a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts index 51838bb7..8b392173 100644 --- a/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts +++ b/packages/apps/app/src/renderer/src/app-shell/useAppShortcuts.ts @@ -188,9 +188,7 @@ export function useAppShortcuts(deps: AppShortcutsDeps): void { (e) => { e.preventDefault() const num = parseInt(e.key, 10) - if (num < visibleTabs.length) { - setActiveTabIndex(toFullIndex(num)) - } + if (num < visibleTabs.length) setActiveTabIndex(toFullIndex(num)) }, { enableOnFormTags: true, enabled: !isRecording } )