Add keyboard shortcut to create a new workspace
Cmd+N (Ctrl+N off mac) navigates to the new-workspace route through a global handler keyed on the active host. The sidebar header row surfaces the shortcut hint on hover, replacing the redundant "+" button that duplicated the action in the Workspaces section header.
This commit is contained in:
@@ -63,6 +63,7 @@ import { loadDesktopSettings } from "@/desktop/settings/desktop-settings";
|
|||||||
import { RosettaCalloutSource } from "@/desktop/updates/rosetta-callout-source";
|
import { RosettaCalloutSource } from "@/desktop/updates/rosetta-callout-source";
|
||||||
import { UpdateCalloutSource } from "@/desktop/updates/update-callout-source";
|
import { UpdateCalloutSource } from "@/desktop/updates/update-callout-source";
|
||||||
import { useActiveWorktreeNewAction } from "@/hooks/use-active-worktree-new-action";
|
import { useActiveWorktreeNewAction } from "@/hooks/use-active-worktree-new-action";
|
||||||
|
import { useGlobalNewWorkspaceAction } from "@/hooks/use-global-new-workspace-action";
|
||||||
import { useFaviconStatus } from "@/hooks/use-favicon-status";
|
import { useFaviconStatus } from "@/hooks/use-favicon-status";
|
||||||
import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts";
|
import { useKeyboardShortcuts } from "@/hooks/use-keyboard-shortcuts";
|
||||||
import { useCompactWebViewportZoomLock } from "@/hooks/use-compact-web-viewport-zoom-lock";
|
import { useCompactWebViewportZoomLock } from "@/hooks/use-compact-web-viewport-zoom-lock";
|
||||||
@@ -461,6 +462,7 @@ function AppContainer({
|
|||||||
});
|
});
|
||||||
|
|
||||||
useActiveWorktreeNewAction();
|
useActiveWorktreeNewAction();
|
||||||
|
useGlobalNewWorkspaceAction();
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<View style={layoutStyles.surfaceFill}>
|
<View style={layoutStyles.surfaceFill}>
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ import {
|
|||||||
buildSettingsRoute,
|
buildSettingsRoute,
|
||||||
mapPathnameToServer,
|
mapPathnameToServer,
|
||||||
} from "@/utils/host-routes";
|
} from "@/utils/host-routes";
|
||||||
|
import type { ShortcutKey } from "@/utils/format-shortcut";
|
||||||
import { SidebarAgentListSkeleton } from "./sidebar-agent-list-skeleton";
|
import { SidebarAgentListSkeleton } from "./sidebar-agent-list-skeleton";
|
||||||
import { SidebarCalloutSlot } from "./sidebar-callout-slot";
|
import { SidebarCalloutSlot } from "./sidebar-callout-slot";
|
||||||
import { SidebarWorkspaceList } from "./sidebar-workspace-list";
|
import { SidebarWorkspaceList } from "./sidebar-workspace-list";
|
||||||
@@ -112,6 +113,7 @@ interface SidebarSharedProps {
|
|||||||
active: boolean;
|
active: boolean;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
}) => ReactElement;
|
}) => ReactElement;
|
||||||
|
newWorkspaceKeys: ShortcutKey[][] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SidebarLabels {
|
interface SidebarLabels {
|
||||||
@@ -285,6 +287,8 @@ export const LeftSidebar = memo(function LeftSidebar({
|
|||||||
[pathname],
|
[pathname],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const newWorkspaceKeys = useShortcutKeys("new-workspace");
|
||||||
|
|
||||||
const labels = useMemo(
|
const labels = useMemo(
|
||||||
(): SidebarLabels => ({
|
(): SidebarLabels => ({
|
||||||
addProject: t("sidebar.actions.addProject"),
|
addProject: t("sidebar.actions.addProject"),
|
||||||
@@ -320,6 +324,7 @@ export const LeftSidebar = memo(function LeftSidebar({
|
|||||||
handleHostSelect,
|
handleHostSelect,
|
||||||
renderHostOption,
|
renderHostOption,
|
||||||
labels,
|
labels,
|
||||||
|
newWorkspaceKeys,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isCompactLayout) {
|
if (isCompactLayout) {
|
||||||
@@ -598,6 +603,7 @@ function MobileSidebar({
|
|||||||
handleRefresh,
|
handleRefresh,
|
||||||
handleHostSelect,
|
handleHostSelect,
|
||||||
renderHostOption,
|
renderHostOption,
|
||||||
|
newWorkspaceKeys,
|
||||||
handleNewWorkspaceNavigate,
|
handleNewWorkspaceNavigate,
|
||||||
handleOpenProject,
|
handleOpenProject,
|
||||||
handleHome,
|
handleHome,
|
||||||
@@ -809,6 +815,7 @@ function MobileSidebar({
|
|||||||
onPress={handleNewWorkspace}
|
onPress={handleNewWorkspace}
|
||||||
testID="sidebar-global-new-workspace"
|
testID="sidebar-global-new-workspace"
|
||||||
variant="compact"
|
variant="compact"
|
||||||
|
shortcutKeys={newWorkspaceKeys}
|
||||||
/>
|
/>
|
||||||
<SidebarHeaderRow
|
<SidebarHeaderRow
|
||||||
icon={Clock}
|
icon={Clock}
|
||||||
@@ -819,10 +826,7 @@ function MobileSidebar({
|
|||||||
variant="compact"
|
variant="compact"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<WorkspacesSectionHeader
|
<WorkspacesSectionHeader serverId={activeServerId} />
|
||||||
serverId={activeServerId}
|
|
||||||
onNewWorkspacePress={handleNewWorkspace}
|
|
||||||
/>
|
|
||||||
<Pressable
|
<Pressable
|
||||||
style={styles.mobileCloseButton}
|
style={styles.mobileCloseButton}
|
||||||
onPress={closeSidebar}
|
onPress={closeSidebar}
|
||||||
@@ -904,6 +908,7 @@ function DesktopSidebar({
|
|||||||
handleRefresh,
|
handleRefresh,
|
||||||
handleHostSelect,
|
handleHostSelect,
|
||||||
renderHostOption,
|
renderHostOption,
|
||||||
|
newWorkspaceKeys,
|
||||||
handleNewWorkspaceNavigate,
|
handleNewWorkspaceNavigate,
|
||||||
handleOpenProject,
|
handleOpenProject,
|
||||||
handleHome,
|
handleHome,
|
||||||
@@ -990,6 +995,7 @@ function DesktopSidebar({
|
|||||||
onPress={handleNewWorkspaceNavigate}
|
onPress={handleNewWorkspaceNavigate}
|
||||||
testID="sidebar-global-new-workspace"
|
testID="sidebar-global-new-workspace"
|
||||||
variant="compact"
|
variant="compact"
|
||||||
|
shortcutKeys={newWorkspaceKeys}
|
||||||
/>
|
/>
|
||||||
<SidebarHeaderRow
|
<SidebarHeaderRow
|
||||||
icon={Clock}
|
icon={Clock}
|
||||||
@@ -1001,10 +1007,7 @@ function DesktopSidebar({
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<WorkspacesSectionHeader
|
<WorkspacesSectionHeader serverId={activeServerId} />
|
||||||
serverId={activeServerId}
|
|
||||||
onNewWorkspacePress={handleNewWorkspaceNavigate}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{isInitialLoad ? (
|
{isInitialLoad ? (
|
||||||
<SidebarAgentListSkeleton />
|
<SidebarAgentListSkeleton />
|
||||||
@@ -1050,13 +1053,7 @@ function DesktopSidebar({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function WorkspacesSectionHeader({
|
function WorkspacesSectionHeader({ serverId }: { serverId: string | null }) {
|
||||||
serverId,
|
|
||||||
onNewWorkspacePress,
|
|
||||||
}: {
|
|
||||||
serverId: string | null;
|
|
||||||
onNewWorkspacePress: () => void;
|
|
||||||
}) {
|
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
const setCommandCenterOpen = useKeyboardShortcutsStore((state) => state.setCommandCenterOpen);
|
const setCommandCenterOpen = useKeyboardShortcutsStore((state) => state.setCommandCenterOpen);
|
||||||
const commandCenterKeys = useShortcutKeys("toggle-command-center");
|
const commandCenterKeys = useShortcutKeys("toggle-command-center");
|
||||||
@@ -1073,29 +1070,6 @@ function WorkspacesSectionHeader({
|
|||||||
<View style={styles.workspacesSectionHeader}>
|
<View style={styles.workspacesSectionHeader}>
|
||||||
<Text style={styles.workspacesSectionTitle}>Workspaces</Text>
|
<Text style={styles.workspacesSectionTitle}>Workspaces</Text>
|
||||||
<View style={styles.workspacesSectionActions}>
|
<View style={styles.workspacesSectionActions}>
|
||||||
<Tooltip delayDuration={300}>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Pressable
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel="New workspace"
|
|
||||||
testID="sidebar-new-workspace"
|
|
||||||
style={searchButtonStyle}
|
|
||||||
onPress={onNewWorkspacePress}
|
|
||||||
>
|
|
||||||
{({ hovered, pressed }) => (
|
|
||||||
<Plus
|
|
||||||
size={14}
|
|
||||||
color={
|
|
||||||
hovered || pressed ? theme.colors.foreground : theme.colors.foregroundMuted
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Pressable>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" align="center" offset={8}>
|
|
||||||
<HeaderIconTooltipContent label="New workspace" />
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip delayDuration={300}>
|
<Tooltip delayDuration={300}>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Pressable
|
<Pressable
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import type { LucideIcon } from "lucide-react-native";
|
|||||||
import { HEADER_INNER_HEIGHT, HEADER_INNER_HEIGHT_MOBILE } from "@/constants/layout";
|
import { HEADER_INNER_HEIGHT, HEADER_INNER_HEIGHT_MOBILE } from "@/constants/layout";
|
||||||
import { ICON_SIZE } from "@/styles/theme";
|
import { ICON_SIZE } from "@/styles/theme";
|
||||||
import type { Theme } from "@/styles/theme";
|
import type { Theme } from "@/styles/theme";
|
||||||
|
import { Shortcut } from "@/components/ui/shortcut";
|
||||||
|
import type { ShortcutKey } from "@/utils/format-shortcut";
|
||||||
|
|
||||||
const foregroundColorMapping = (theme: Theme) => ({ color: theme.colors.foreground });
|
const foregroundColorMapping = (theme: Theme) => ({ color: theme.colors.foreground });
|
||||||
const foregroundMutedColorMapping = (theme: Theme) => ({ color: theme.colors.foregroundMuted });
|
const foregroundMutedColorMapping = (theme: Theme) => ({ color: theme.colors.foregroundMuted });
|
||||||
@@ -26,6 +28,7 @@ interface SidebarHeaderRowProps {
|
|||||||
* sit in a header group whose wrapper owns the single divider.
|
* sit in a header group whose wrapper owns the single divider.
|
||||||
*/
|
*/
|
||||||
variant?: SidebarHeaderRowVariant;
|
variant?: SidebarHeaderRowVariant;
|
||||||
|
shortcutKeys?: ShortcutKey[][] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SidebarHeaderRow({
|
export function SidebarHeaderRow({
|
||||||
@@ -37,6 +40,7 @@ export function SidebarHeaderRow({
|
|||||||
nativeID,
|
nativeID,
|
||||||
accessibilityLabel,
|
accessibilityLabel,
|
||||||
variant = "header",
|
variant = "header",
|
||||||
|
shortcutKeys = null,
|
||||||
}: SidebarHeaderRowProps) {
|
}: SidebarHeaderRowProps) {
|
||||||
const ThemedIcon = useMemo(() => withUnistyles(Icon), [Icon]);
|
const ThemedIcon = useMemo(() => withUnistyles(Icon), [Icon]);
|
||||||
|
|
||||||
@@ -63,10 +67,13 @@ export function SidebarHeaderRow({
|
|||||||
uniProps={isHighlighted ? foregroundColorMapping : foregroundMutedColorMapping}
|
uniProps={isHighlighted ? foregroundColorMapping : foregroundMutedColorMapping}
|
||||||
/>
|
/>
|
||||||
<SidebarHeaderRowLabel label={label} isHighlighted={isHighlighted} />
|
<SidebarHeaderRowLabel label={label} isHighlighted={isHighlighted} />
|
||||||
|
{shortcutKeys && Boolean(state.hovered) ? (
|
||||||
|
<Shortcut chord={shortcutKeys} style={styles.shortcut} />
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[ThemedIcon, isActive, label],
|
[ThemedIcon, isActive, label, shortcutKeys],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -139,4 +146,7 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
labelHighlighted: {
|
labelHighlighted: {
|
||||||
color: theme.colors.foreground,
|
color: theme.colors.foreground,
|
||||||
},
|
},
|
||||||
|
shortcut: {
|
||||||
|
marginLeft: "auto",
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { useCallback } from "react";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
import { useKeyboardActionHandler } from "@/hooks/use-keyboard-action-handler";
|
||||||
|
import type { KeyboardActionId } from "@/keyboard/keyboard-action-dispatcher";
|
||||||
|
import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
|
||||||
|
import { buildHostNewWorkspaceRoute } from "@/utils/host-routes";
|
||||||
|
|
||||||
|
const WORKSPACE_NEW_ACTIONS: readonly KeyboardActionId[] = ["workspace.new"];
|
||||||
|
|
||||||
|
export function useGlobalNewWorkspaceAction() {
|
||||||
|
const selection = useActiveWorkspaceSelection();
|
||||||
|
const serverId = selection?.serverId ?? null;
|
||||||
|
|
||||||
|
const handle = useCallback(() => {
|
||||||
|
if (!serverId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
router.navigate(buildHostNewWorkspaceRoute(serverId) as never);
|
||||||
|
return true;
|
||||||
|
}, [serverId]);
|
||||||
|
|
||||||
|
useKeyboardActionHandler({
|
||||||
|
handlerId: "workspace-new-global",
|
||||||
|
actions: WORKSPACE_NEW_ACTIONS,
|
||||||
|
enabled: serverId !== null,
|
||||||
|
priority: 0,
|
||||||
|
handle,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -299,6 +299,7 @@ describe("translation resources", () => {
|
|||||||
expect(en.settings.shortcuts.dialogTitle).toBe("Shortcuts");
|
expect(en.settings.shortcuts.dialogTitle).toBe("Shortcuts");
|
||||||
expect(en.settings.shortcuts.sections.tabsPanes).toBe("Tabs & Panes");
|
expect(en.settings.shortcuts.sections.tabsPanes).toBe("Tabs & Panes");
|
||||||
expect(en.settings.shortcuts.help.toggleCommandCenter).toBe("Toggle command center");
|
expect(en.settings.shortcuts.help.toggleCommandCenter).toBe("Toggle command center");
|
||||||
|
expect(en.settings.shortcuts.help.newWorkspace).toBe("New workspace");
|
||||||
expect(en.settings.shortcuts.helpNotes.showKeyboardShortcuts).toBe(
|
expect(en.settings.shortcuts.helpNotes.showKeyboardShortcuts).toBe(
|
||||||
"Available when focus is not in a text field or terminal.",
|
"Available when focus is not in a text field or terminal.",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1528,6 +1528,7 @@ export const ar: TranslationResources = {
|
|||||||
},
|
},
|
||||||
help: {
|
help: {
|
||||||
openProject: "مشروع مفتوح",
|
openProject: "مشروع مفتوح",
|
||||||
|
newWorkspace: "مساحة عمل جديدة",
|
||||||
newWorktree: "شجرة عمل جديدة",
|
newWorktree: "شجرة عمل جديدة",
|
||||||
archiveWorktree: "أرشفة شجرة العمل",
|
archiveWorktree: "أرشفة شجرة العمل",
|
||||||
newTab: "علامة تبويب جديدة",
|
newTab: "علامة تبويب جديدة",
|
||||||
|
|||||||
@@ -1533,6 +1533,7 @@ export const en = {
|
|||||||
},
|
},
|
||||||
help: {
|
help: {
|
||||||
openProject: "Open project",
|
openProject: "Open project",
|
||||||
|
newWorkspace: "New workspace",
|
||||||
newWorktree: "New worktree",
|
newWorktree: "New worktree",
|
||||||
archiveWorktree: "Archive worktree",
|
archiveWorktree: "Archive worktree",
|
||||||
newTab: "New tab",
|
newTab: "New tab",
|
||||||
|
|||||||
@@ -1565,6 +1565,7 @@ export const es: TranslationResources = {
|
|||||||
},
|
},
|
||||||
help: {
|
help: {
|
||||||
openProject: "Abrir proyecto",
|
openProject: "Abrir proyecto",
|
||||||
|
newWorkspace: "Nuevo espacio de trabajo",
|
||||||
newWorktree: "Nuevo árbol de trabajo",
|
newWorktree: "Nuevo árbol de trabajo",
|
||||||
archiveWorktree: "Árbol de trabajo de archivo",
|
archiveWorktree: "Árbol de trabajo de archivo",
|
||||||
newTab: "Nueva pestaña",
|
newTab: "Nueva pestaña",
|
||||||
|
|||||||
@@ -1569,6 +1569,7 @@ export const fr: TranslationResources = {
|
|||||||
},
|
},
|
||||||
help: {
|
help: {
|
||||||
openProject: "Projet ouvert",
|
openProject: "Projet ouvert",
|
||||||
|
newWorkspace: "Nouvel espace de travail",
|
||||||
newWorktree: "Nouvel arbre de travail",
|
newWorktree: "Nouvel arbre de travail",
|
||||||
archiveWorktree: "Arbre de travail d'archivage",
|
archiveWorktree: "Arbre de travail d'archivage",
|
||||||
newTab: "Nouvel onglet",
|
newTab: "Nouvel onglet",
|
||||||
|
|||||||
@@ -1557,6 +1557,7 @@ export const ru: TranslationResources = {
|
|||||||
},
|
},
|
||||||
help: {
|
help: {
|
||||||
openProject: "Открыть проект",
|
openProject: "Открыть проект",
|
||||||
|
newWorkspace: "Новое рабочее пространство",
|
||||||
newWorktree: "Новое рабочее дерево",
|
newWorktree: "Новое рабочее дерево",
|
||||||
archiveWorktree: "Архив рабочего дерева",
|
archiveWorktree: "Архив рабочего дерева",
|
||||||
newTab: "Новая вкладка",
|
newTab: "Новая вкладка",
|
||||||
|
|||||||
@@ -1509,6 +1509,7 @@ export const zhCN: TranslationResources = {
|
|||||||
},
|
},
|
||||||
help: {
|
help: {
|
||||||
openProject: "打开项目",
|
openProject: "打开项目",
|
||||||
|
newWorkspace: "新建 workspace",
|
||||||
newWorktree: "新建 worktree",
|
newWorktree: "新建 worktree",
|
||||||
archiveWorktree: "归档 worktree",
|
archiveWorktree: "归档 worktree",
|
||||||
newTab: "新建标签",
|
newTab: "新建标签",
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export type KeyboardActionId =
|
|||||||
| "command-center.toggle"
|
| "command-center.toggle"
|
||||||
| "shortcuts.dialog.toggle"
|
| "shortcuts.dialog.toggle"
|
||||||
| "workspace.terminal.new"
|
| "workspace.terminal.new"
|
||||||
|
| "workspace.new"
|
||||||
| "worktree.new"
|
| "worktree.new"
|
||||||
| "worktree.archive"
|
| "worktree.archive"
|
||||||
| "view.toggle.focus"
|
| "view.toggle.focus"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export type KeyboardActionId =
|
|||||||
| "workspace.pane.close"
|
| "workspace.pane.close"
|
||||||
| "workspace.terminal.new"
|
| "workspace.terminal.new"
|
||||||
| "sidebar.toggle.right"
|
| "sidebar.toggle.right"
|
||||||
|
| "workspace.new"
|
||||||
| "worktree.new"
|
| "worktree.new"
|
||||||
| "worktree.archive";
|
| "worktree.archive";
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ export type KeyboardActionDefinition =
|
|||||||
| { id: "workspace.pane.close"; scope: KeyboardActionScope }
|
| { id: "workspace.pane.close"; scope: KeyboardActionScope }
|
||||||
| { id: "workspace.terminal.new"; scope: KeyboardActionScope }
|
| { id: "workspace.terminal.new"; scope: KeyboardActionScope }
|
||||||
| { id: "sidebar.toggle.right"; scope: KeyboardActionScope }
|
| { id: "sidebar.toggle.right"; scope: KeyboardActionScope }
|
||||||
|
| { id: "workspace.new"; scope: KeyboardActionScope }
|
||||||
| { id: "worktree.new"; scope: KeyboardActionScope }
|
| { id: "worktree.new"; scope: KeyboardActionScope }
|
||||||
| { id: "worktree.archive"; scope: KeyboardActionScope };
|
| { id: "worktree.archive"; scope: KeyboardActionScope };
|
||||||
|
|
||||||
|
|||||||
@@ -128,6 +128,18 @@ describe("keyboard-shortcuts", () => {
|
|||||||
context: { isMac: true },
|
context: { isMac: true },
|
||||||
action: "agent.new",
|
action: "agent.new",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "matches Cmd+N to create new workspace on mac",
|
||||||
|
event: { key: "n", code: "KeyN", metaKey: true },
|
||||||
|
context: { isMac: true, commandCenterOpen: false },
|
||||||
|
action: "workspace.new",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "matches Ctrl+N to create new workspace on non-mac",
|
||||||
|
event: { key: "n", code: "KeyN", ctrlKey: true },
|
||||||
|
context: { isMac: false, commandCenterOpen: false, focusScope: "other" },
|
||||||
|
action: "workspace.new",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "matches question-mark shortcut to toggle the shortcuts dialog",
|
name: "matches question-mark shortcut to toggle the shortcuts dialog",
|
||||||
event: { key: "?", code: "Slash", shiftKey: true },
|
event: { key: "?", code: "Slash", shiftKey: true },
|
||||||
@@ -564,6 +576,7 @@ describe("keyboard-shortcut help sections", () => {
|
|||||||
context: { isMac: true, isDesktop: true },
|
context: { isMac: true, isDesktop: true },
|
||||||
expectedKeys: {
|
expectedKeys: {
|
||||||
"new-agent": ["mod", "shift", "O"],
|
"new-agent": ["mod", "shift", "O"],
|
||||||
|
"new-workspace": ["mod", "N"],
|
||||||
"workspace-tab-new": ["mod", "T"],
|
"workspace-tab-new": ["mod", "T"],
|
||||||
"workspace-jump-index": ["mod", "1-9"],
|
"workspace-jump-index": ["mod", "1-9"],
|
||||||
"workspace-tab-jump-index": ["mod", "alt", "1-9"],
|
"workspace-tab-jump-index": ["mod", "alt", "1-9"],
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ const SHORTCUT_HELP_SECTION_LABEL_KEYS: Record<ShortcutSectionId, string> = {
|
|||||||
|
|
||||||
const SHORTCUT_HELP_LABEL_KEYS: Record<string, string> = {
|
const SHORTCUT_HELP_LABEL_KEYS: Record<string, string> = {
|
||||||
"new-agent": "settings.shortcuts.help.openProject",
|
"new-agent": "settings.shortcuts.help.openProject",
|
||||||
|
"new-workspace": "settings.shortcuts.help.newWorkspace",
|
||||||
"new-worktree": "settings.shortcuts.help.newWorktree",
|
"new-worktree": "settings.shortcuts.help.newWorktree",
|
||||||
"archive-worktree": "settings.shortcuts.help.archiveWorktree",
|
"archive-worktree": "settings.shortcuts.help.archiveWorktree",
|
||||||
"workspace-tab-new": "settings.shortcuts.help.newTab",
|
"workspace-tab-new": "settings.shortcuts.help.newTab",
|
||||||
@@ -190,6 +191,32 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// --- New workspace ---
|
||||||
|
{
|
||||||
|
id: "workspace-new-cmd-n-mac",
|
||||||
|
action: "workspace.new",
|
||||||
|
combo: "Cmd+N",
|
||||||
|
when: { mac: true, commandCenter: false },
|
||||||
|
help: {
|
||||||
|
id: "new-workspace",
|
||||||
|
section: "projects",
|
||||||
|
label: "New workspace",
|
||||||
|
keys: ["mod", "N"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "workspace-new-ctrl-n-non-mac",
|
||||||
|
action: "workspace.new",
|
||||||
|
combo: "Ctrl+N",
|
||||||
|
when: { mac: false, commandCenter: false, terminal: false },
|
||||||
|
help: {
|
||||||
|
id: "new-workspace",
|
||||||
|
section: "projects",
|
||||||
|
label: "New workspace",
|
||||||
|
keys: ["mod", "N"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// --- New worktree ---
|
// --- New worktree ---
|
||||||
{
|
{
|
||||||
id: "worktree-new-cmd-o-mac",
|
id: "worktree-new-cmd-o-mac",
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ describe("routeKeyboardShortcut — dispatch passthroughs", () => {
|
|||||||
it.each([
|
it.each([
|
||||||
["agent.interrupt", { id: "agent.interrupt", scope: "global" }],
|
["agent.interrupt", { id: "agent.interrupt", scope: "global" }],
|
||||||
["workspace.tab.new", { id: "workspace.tab.new", scope: "workspace" }],
|
["workspace.tab.new", { id: "workspace.tab.new", scope: "workspace" }],
|
||||||
|
["workspace.new", { id: "workspace.new", scope: "sidebar" }],
|
||||||
["worktree.archive", { id: "worktree.archive", scope: "sidebar" }],
|
["worktree.archive", { id: "worktree.archive", scope: "sidebar" }],
|
||||||
["worktree.new", { id: "worktree.new", scope: "sidebar" }],
|
["worktree.new", { id: "worktree.new", scope: "sidebar" }],
|
||||||
["workspace.terminal.new", { id: "workspace.terminal.new", scope: "workspace" }],
|
["workspace.terminal.new", { id: "workspace.terminal.new", scope: "workspace" }],
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ const NONE: ShortcutAction = { kind: "none" };
|
|||||||
const PASSTHROUGH_DISPATCH: Record<string, KeyboardActionDefinition> = {
|
const PASSTHROUGH_DISPATCH: Record<string, KeyboardActionDefinition> = {
|
||||||
"agent.interrupt": { id: "agent.interrupt", scope: "global" },
|
"agent.interrupt": { id: "agent.interrupt", scope: "global" },
|
||||||
"workspace.tab.new": { id: "workspace.tab.new", scope: "workspace" },
|
"workspace.tab.new": { id: "workspace.tab.new", scope: "workspace" },
|
||||||
|
"workspace.new": { id: "workspace.new", scope: "sidebar" },
|
||||||
"worktree.archive": { id: "worktree.archive", scope: "sidebar" },
|
"worktree.archive": { id: "worktree.archive", scope: "sidebar" },
|
||||||
"worktree.new": { id: "worktree.new", scope: "sidebar" },
|
"worktree.new": { id: "worktree.new", scope: "sidebar" },
|
||||||
"workspace.terminal.new": { id: "workspace.terminal.new", scope: "workspace" },
|
"workspace.terminal.new": { id: "workspace.terminal.new", scope: "workspace" },
|
||||||
|
|||||||
Reference in New Issue
Block a user