diff --git a/src/components/TabBar.tsx b/src/components/TabBar.tsx index aca42a6..5ffdc6f 100644 --- a/src/components/TabBar.tsx +++ b/src/components/TabBar.tsx @@ -6,7 +6,12 @@ import { LayoutControls } from "./LayoutControls"; import { useEffect } from "react"; export function TabBar() { - const { state, setActiveWorkspace, createWorkspace } = useGrimoire(); + const { + state, + setActiveWorkspace, + createWorkspace, + createWorkspaceWithNumber, + } = useGrimoire(); const { workspaces, activeWorkspaceId } = state; const handleNewTab = () => { @@ -18,24 +23,39 @@ export function TabBar() { (a, b) => a.number - b.number, ); - // Keyboard shortcut: Cmd+1-9 to switch workspaces by position + // Keyboard shortcut: Cmd+1-9 to switch (or create) workspaces by number useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Check for Cmd/Ctrl + number key (1-9) if ((e.metaKey || e.ctrlKey) && e.key >= "1" && e.key <= "9") { - const index = Number.parseInt(e.key, 10) - 1; // Convert key to array index - const targetWorkspace = sortedWorkspaces[index]; + e.preventDefault(); // Prevent browser default (like Cmd+1 = first tab) + + const desiredNumber = Number.parseInt(e.key, 10); + + // Safety check: ensure valid workspace number (1-9) + if (desiredNumber < 1 || desiredNumber > 9) { + return; + } + + // Find workspace with this number + const targetWorkspace = sortedWorkspaces.find( + (ws) => ws.number === desiredNumber, + ); if (targetWorkspace) { - e.preventDefault(); // Prevent browser default (like Cmd+1 = first tab) + // Workspace exists - switch to it setActiveWorkspace(targetWorkspace.id); + } else { + // Workspace doesn't exist - create it and switch to it + createWorkspaceWithNumber(desiredNumber); + // Note: We don't need to explicitly switch - createWorkspace sets it as active } } }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); - }, [sortedWorkspaces, setActiveWorkspace]); + }, [sortedWorkspaces, setActiveWorkspace, createWorkspaceWithNumber]); return ( <> diff --git a/src/core/state.ts b/src/core/state.ts index ea5a25f..66de270 100644 --- a/src/core/state.ts +++ b/src/core/state.ts @@ -142,6 +142,32 @@ export const useGrimoire = () => { }); }, [setState]); + const createWorkspaceWithNumber = useCallback( + (number: number) => { + setState((prev) => { + // Check if we're leaving an empty workspace and should auto-remove it + const currentWorkspace = prev.workspaces[prev.activeWorkspaceId]; + const shouldDeleteCurrent = + currentWorkspace && + currentWorkspace.windowIds.length === 0 && + Object.keys(prev.workspaces).length > 1; + + if (shouldDeleteCurrent) { + // Delete the empty workspace, then create new one + const afterDelete = Logic.deleteWorkspace( + prev, + prev.activeWorkspaceId, + ); + return Logic.createWorkspace(afterDelete, number); + } + + // Normal workspace creation + return Logic.createWorkspace(prev, number); + }); + }, + [setState], + ); + const addWindow = useCallback( (appId: AppId, props: any, commandString?: string, customTitle?: string) => setState((prev) => @@ -250,6 +276,7 @@ export const useGrimoire = () => { locale: state.locale || browserLocale, activeWorkspace: state.workspaces[state.activeWorkspaceId], createWorkspace, + createWorkspaceWithNumber, addWindow, updateWindow, removeWindow,