diff --git a/src/App.svelte b/src/App.svelte index 5c54df2..bfab03a 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -5940,6 +5940,67 @@ // ── Event handlers ───────────────────────────────────────────────────────── + // ── Main view shortcuts ──────────────────────────────────────────────────── + // Ctrl/Cmd + 1…4 jump to the four main views in the order of the tab bar. + const mainViewShortcuts: AppView[] = ["management", "repository", "review-center", "issues"]; + + function mainViewShortcutIndex(event: KeyboardEvent): number { + if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey) return 0; + const code = event.code ?? ""; + const digit = code.startsWith("Digit") + ? Number(code.slice(5)) + : code.startsWith("Numpad") + ? Number(code.slice(6)) + : Number(event.key); + if (!Number.isInteger(digit) || digit < 1 || digit > mainViewShortcuts.length) return 0; + return digit; + } + + // Capture phase: dialogs and editors trap keys on their own nodes, and the + // view shortcut must not depend on the event reaching window afterwards. + function handleViewShortcutKeydown(event: KeyboardEvent) { + const index = mainViewShortcutIndex(event); + if (index === 0) return; + if (import.meta.env.DEV) { + console.debug("[gitty] view shortcut", { key: event.key, code: event.code, index, blocked: mainViewSwitchBlocked(), activeView }); + } + event.preventDefault(); + if (!event.repeat) switchMainView(index); + } + + // Anything modal owns the keyboard, so the view stays where it is. + function mainViewSwitchBlocked(): boolean { + return isBusy + || commandPaletteOpen || helpOpen || globalSearchOpen || appSettingsOpen + || cloneDialogOpen || initRepositoryDialogOpen || credDialogOpen + || compareDialogOpen || compareSelectOpen || fileHistoryDialogOpen + || resolveDialogOpen || interactiveRebaseOpen || reflogOpen || bisectOpen + || worktreeDialogOpen || submoduleDialogOpen || gitLfsDialogOpen + || linePatchOpen || aiReviewOpen + || pendingSubmoduleInitialization !== null || pendingDiscard !== null + || newBranchCommit !== null || renameBranchTarget !== null + || deleteBranchTarget !== null || commitNoteTarget !== null; + } + + function switchMainView(index: number) { + if (mainViewSwitchBlocked()) return; + const view = mainViewShortcuts[index - 1]; + if (!view || view === activeView) return; + closeRepoTabContextMenu(); + if (view === "management") { + openRepoManagement(); + } else if (view === "review-center") { + openReviewCenter(); + } else if (view === "issues") { + activeView = "issues"; + } else { + // Without an open repository the tab bar would ask for a folder; a + // keyboard shortcut should never pop up a native file dialog. + const path = repoTabs.find((tab) => sameRepoPath(tab.path, activeRepoPath))?.path ?? repoTabs[0]?.path; + if (path) void selectRepoTab(path); + } + } + function handleWindowKeydown(event: KeyboardEvent) { if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k") { event.preventDefault(); @@ -5992,7 +6053,7 @@ Gitty - + {#if sidebarSectionMenu && activeView === "repository"} {#if activeView === "repository"}
diff --git a/src/lib/components/HelpOverlay.svelte b/src/lib/components/HelpOverlay.svelte index 94ffc18..4271014 100644 --- a/src/lib/components/HelpOverlay.svelte +++ b/src/lib/components/HelpOverlay.svelte @@ -249,6 +249,7 @@ summary: "Die Hilfe ist überall erreichbar. Dialoge lassen sich konsistent schließen und Suchfelder direkt fokussieren.", commands: [ { command: "Ctrl + /", description: "Diese Hilfe öffnen" }, + { command: "Ctrl + 1 … 4", description: "Zwischen Dashboard, Repositories, Pull Requests und Issues & Boards wechseln" }, { command: "Ctrl + A", description: "Alle Dateien in der aktiven Statusliste („Ungestaged“ oder „Gestaged“) auswählen" }, { command: "Escape", description: "Aktuelles Overlay oder Dialogfenster schließen – in der Statusliste die aktuelle Auswahl aufheben" }, { command: "Tab / Shift + Tab", description: "Zwischen Bedienelementen wechseln" }, @@ -460,6 +461,7 @@ summary: "Help is available everywhere. Dialogs close consistently and search fields receive focus automatically.", commands: [ { command: "Ctrl + /", description: "Open this help center" }, + { command: "Ctrl + 1 … 4", description: "Switch between Dashboard, Repositories, Pull Requests and Issues & Boards" }, { command: "Ctrl + A", description: "Select every file in the focused status list (Unstaged or Staged)" }, { command: "Escape", description: "Close the current overlay or dialog – in the status list, clear the current selection" }, { command: "Tab / Shift + Tab", description: "Move between controls" },