feat(app): make the New tab launcher keyboard-first

Cmd/Ctrl+T now opens the focused pane's launcher, while explicit target shortcuts bypass the menu. Dispatcher ownership is scoped to the app tree so retained workspaces cannot intercept commands.
This commit is contained in:
Mohamed Boudra
2026-08-17 21:15:16 +02:00
parent 8655722296
commit 60bafad3ee
38 changed files with 1047 additions and 168 deletions
@@ -44,7 +44,7 @@ import {
type CommandCenterScope,
} from "@/stores/keyboard-shortcuts-store";
import { navigateToWorkspace } from "@/stores/navigation-active-workspace-store";
import { keyboardActionDispatcher } from "@/keyboard/keyboard-action-dispatcher";
import { useKeyboardActionDispatcher } from "@/keyboard/keyboard-action-dispatcher-context";
import {
clearCommandCenterFocusRestoreElement,
takeCommandCenterFocusRestoreElement,
@@ -220,6 +220,7 @@ interface CommandCenterState {
}
function useCommandCenterState(): CommandCenterState {
const keyboardActionDispatcher = useKeyboardActionDispatcher();
const { t } = useTranslation();
const open = useKeyboardShortcutsStore((state) => state.commandCenterOpen);
const scope = useKeyboardShortcutsStore((state) => state.commandCenterScope);
@@ -321,7 +322,7 @@ function useCommandCenterState(): CommandCenterState {
keyboardActionDispatcher.dispatch({ id: "message-input.focus", scope: "message-input" }),
});
return cancel;
}, [open]);
}, [keyboardActionDispatcher, open]);
return {
open,
@@ -16,7 +16,7 @@ import { withUnistyles } from "react-native-unistyles";
import { getIsElectronRuntime } from "@/constants/layout";
import { useKeyboardShortcutOverrides } from "@/hooks/use-keyboard-shortcut-overrides";
import { useOpenAddProject } from "@/hooks/use-open-add-project";
import { keyboardActionDispatcher } from "@/keyboard/keyboard-action-dispatcher";
import { useKeyboardActionDispatcher } from "@/keyboard/keyboard-action-dispatcher-context";
import { useKeyboardShortcutsAvailable } from "@/keyboard/availability";
import { resolveShortcutKeysForAction } from "@/keyboard/keyboard-shortcuts";
import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
@@ -92,6 +92,7 @@ function CircleDashedIcon({ size }: CommandCenterIconProps) {
}
export function CommandCenterRootActions() {
const keyboardActionDispatcher = useKeyboardActionDispatcher();
const { t } = useTranslation();
const { overrides } = useKeyboardShortcutOverrides();
const shortcutsAvailable = useKeyboardShortcutsAvailable();
@@ -266,6 +267,7 @@ export function CommandCenterRootActions() {
}, [
groupMode,
homeRoute,
keyboardActionDispatcher,
openAddProject,
overrides,
schedulesRoute,
@@ -114,7 +114,7 @@ describe("workspace command center contributions", () => {
for (const contribution of contributions) contribution.run();
expect(fixture.dispatched).toEqual([
{ id: "workspace.tab.new", scope: "workspace" },
{ id: "workspace.agent.new", scope: "workspace" },
{ id: "workspace.terminal.new", scope: "workspace" },
{ id: "workspace.browser.new", scope: "workspace" },
{ id: "workspace.pane.split.right", scope: "workspace" },
@@ -108,7 +108,7 @@ export function buildWorkspaceCommandCenterContributions(
keywords: ["tab", "new", "agent", "chat"],
icon: source.icons.newAgent,
shortcutKeys: source.shortcuts.newAgent,
action: { id: "workspace.tab.new", scope: "workspace" },
action: { id: "workspace.agent.new", scope: "workspace" },
visibility: "always",
}),
];
@@ -10,7 +10,7 @@ import {
resolveShortcutKeysForAction,
type ShortcutOverrides,
} from "@/keyboard/keyboard-shortcuts";
import { keyboardActionDispatcher } from "@/keyboard/keyboard-action-dispatcher";
import { useKeyboardActionDispatcher } from "@/keyboard/keyboard-action-dispatcher-context";
import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
import { useWorkspaceDirectory } from "@/stores/session-store-hooks";
import { clearCommandCenterFocusRestoreElement } from "@/utils/command-center-focus-restore";
@@ -55,6 +55,7 @@ function resolveWorkspaceShortcuts(overrides: ShortcutOverrides): WorkspaceComma
}
export function useWorkspaceCommandCenterActions(): void {
const keyboardActionDispatcher = useKeyboardActionDispatcher();
const { t } = useTranslation();
const selection = useActiveWorkspaceSelection();
const serverId = selection?.serverId ?? null;
@@ -96,7 +97,7 @@ export function useWorkspaceCommandCenterActions(): void {
},
runGitAction,
}),
[gitActions, isCompact, overrides, runGitAction, t],
[gitActions, isCompact, keyboardActionDispatcher, overrides, runGitAction, t],
);
useCommandCenterActions({