diff --git a/dev-dashboard/frontend/src/App.svelte b/dev-dashboard/frontend/src/App.svelte
index edbc6fe918..d097ecdb3d 100644
--- a/dev-dashboard/frontend/src/App.svelte
+++ b/dev-dashboard/frontend/src/App.svelte
@@ -80,10 +80,52 @@
}
}
+ function selectNeighborWorktree(direction: -1 | 1) {
+ const selectable = visibleWorktrees.filter(
+ (w) => w.path !== "(here)" && w.branch !== "main" && !removingBranches.has(w.branch)
+ );
+ if (selectable.length === 0) return;
+ if (!selectedBranch) {
+ selectedBranch = selectable[direction === 1 ? 0 : selectable.length - 1].branch;
+ return;
+ }
+ const idx = selectable.findIndex((w) => w.branch === selectedBranch);
+ const next = idx + direction;
+ if (next >= 0 && next < selectable.length) {
+ selectedBranch = selectable[next].branch;
+ }
+ }
+
+ function handleKeydown(e: KeyboardEvent) {
+ // Ignore shortcuts when a dialog is open (let dialog handle its own keys)
+ if (showCreateDialog || removeBranch) return;
+
+ const mod = e.metaKey || e.ctrlKey;
+ if (!mod) return;
+
+ if (e.key === "ArrowUp") {
+ e.preventDefault();
+ selectNeighborWorktree(-1);
+ } else if (e.key === "ArrowDown") {
+ e.preventDefault();
+ selectNeighborWorktree(1);
+ } else if (e.key === "k" || e.key === "K") {
+ e.preventDefault();
+ if (!creating) showCreateDialog = true;
+ } else if (e.key === "d" || e.key === "D") {
+ e.preventDefault();
+ if (selectedBranch && !isMain) removeBranch = selectedBranch;
+ }
+ }
+
onMount(() => {
refresh();
const interval = setInterval(refresh, 5000);
- return () => clearInterval(interval);
+ window.addEventListener("keydown", handleKeydown);
+ return () => {
+ clearInterval(interval);
+ window.removeEventListener("keydown", handleKeydown);
+ };
});
@@ -95,10 +137,15 @@
class="h-8 px-2 gap-1.5 rounded-md border border-edge bg-surface text-accent text-xs flex items-center justify-center cursor-pointer hover:bg-hover disabled:opacity-50 disabled:cursor-not-allowed"
onclick={() => (showCreateDialog = true)}
disabled={creating}
- title="New Worktree"
+ title="New Worktree (Cmd+K)"
>+ New
(selectedBranch = b)} onremove={(b) => (removeBranch = b)} />
+
+
NavigateCmd+Up/Down
+
New worktreeCmd+K
+
RemoveCmd+D
+
diff --git a/dev-dashboard/frontend/src/lib/Terminal.svelte b/dev-dashboard/frontend/src/lib/Terminal.svelte
index 6a975858f6..de2b458e3e 100644
--- a/dev-dashboard/frontend/src/lib/Terminal.svelte
+++ b/dev-dashboard/frontend/src/lib/Terminal.svelte
@@ -56,6 +56,17 @@
}
});
+ // Let app-level shortcuts (Cmd+Arrow, Cmd+N, Cmd+D) bubble up instead of
+ // being consumed by xterm. Return false → xterm ignores the event.
+ term.attachCustomKeyEventHandler((e: KeyboardEvent) => {
+ if (e.type !== "keydown") return true;
+ const mod = e.metaKey || e.ctrlKey;
+ if (mod && (e.key === "ArrowUp" || e.key === "ArrowDown")) return false;
+ if (mod && (e.key === "k" || e.key === "K")) return false;
+ if (mod && (e.key === "d" || e.key === "D")) return false;
+ return true;
+ });
+
requestAnimationFrame(() => fitAddon.fit());
const protocol = location.protocol === "https:" ? "wss:" : "ws:";