fix: move macOS split pane navigation to Cmd+Option+Arrow

This commit is contained in:
LukeGus committed 2026-10-16 23:28:24 -05:00
1 parent 6bacb17390
commit 9949d0711e
5 files changed
+86 -13

No files matched your search

+1
View File
@@ -11,6 +11,7 @@
- The Docker Compose files no longer start a guacd container. npm run dev -- --guacd also points remote desktop at the guacd it starts
- The side menu now opens over the page when it expands on hover, so open sessions like remote desktop are not resized each time. Turn on Push content when it expands in Settings to get the old behavior back
- On macOS, moving between split panes is now Cmd+Option+Arrow, so Option+Arrow can move the cursor by word in a terminal
### Fixed
+4 -12
View File
@@ -125,6 +125,7 @@ import type {
} from "@/types/ui-types";
import { applyAccentColor, applyFontSize } from "@/lib/theme";
import { globalShortcutHandler } from "@/lib/global-shortcut-handler";
import { getPaneNavDirection } from "@/lib/pane-nav-hotkey";
import { getTabJumpDigit } from "@/lib/tab-jump-hotkey";
import { useTheme } from "@/components/theme-provider";
import {
@@ -829,21 +830,12 @@ export function AppShell({
}
}
// Alt+Arrow moves between panes of the active split
if (e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey) {
const direction = (
{
ArrowLeft: "left",
ArrowRight: "right",
ArrowUp: "up",
ArrowDown: "down",
} as Record<string, NavDirection>
)[e.code];
if (direction && splitActionsRef.current?.navigate(direction)) {
// Alt+Arrow (Cmd+Option+Arrow on macOS) moves between panes of the active split
const paneDirection = getPaneNavDirection(e);
if (paneDirection && splitActionsRef.current?.navigate(paneDirection)) {
e.preventDefault();
return;
}
}
// Cmd+1..9 on macOS, Alt+1..9 elsewhere: jump directly to the tab at that position
const tabDigit = getTabJumpDigit(e);
+25
View File
@@ -0,0 +1,25 @@
import { isMacPlatform } from "./tab-jump-hotkey";
export type PaneNavDirection = "left" | "right" | "up" | "down";
const ARROWS: Record<string, PaneNavDirection> = {
ArrowLeft: "left",
ArrowRight: "right",
ArrowUp: "up",
ArrowDown: "down",
};
/**
* The split pane to move to for this key, if any. Cmd+Option+Arrow on macOS,
* where Option+Arrow moves the shell cursor by word, Alt+Arrow elsewhere.
*/
export function getPaneNavDirection(e: KeyboardEvent): PaneNavDirection | null {
const direction = ARROWS[e.code];
if (!direction || e.ctrlKey || e.shiftKey || !e.altKey) return null;
if (e.metaKey !== isMacPlatform()) return null;
return direction;
}
export function paneNavHotkeyLabel(): string {
return isMacPlatform() ? "Cmd+Option+Arrows" : "Alt+Arrows";
}
+2 -1
View File
@@ -18,6 +18,7 @@ import {
type SplitPresetId,
} from "./split-presets";
import { MAX_PANES, type PaneEdge } from "./split-tree";
import { paneNavHotkeyLabel } from "@/lib/pane-nav-hotkey";
function NodeThumb({ node }: { node: LayoutNode }) {
if (node.kind === "pane") {
@@ -129,7 +130,7 @@ export function SplitLayoutMenu({
<div className="flex flex-col gap-1 px-2 pb-1.5 text-xs text-muted-foreground">
<div className="flex items-center justify-between gap-2">
<span>{t("splitScreen.shortcutNavigate")}</span>
<kbd className="font-mono text-[10px]">Alt+Arrows</kbd>
<kbd className="font-mono text-[10px]">{paneNavHotkeyLabel()}</kbd>
</div>
<div className="flex items-center justify-between gap-2">
<span>{t("splitScreen.zoom")}</span>
+54
View File
@@ -0,0 +1,54 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
getPaneNavDirection,
paneNavHotkeyLabel,
} from "../../lib/pane-nav-hotkey";
function keyEvent(init: Partial<KeyboardEvent>): KeyboardEvent {
return {
code: "ArrowLeft",
altKey: false,
ctrlKey: false,
metaKey: false,
shiftKey: false,
...init,
} as KeyboardEvent;
}
afterEach(() => vi.unstubAllGlobals());
describe("pane-nav-hotkey", () => {
it("uses Alt+Arrow outside macOS", () => {
vi.stubGlobal("navigator", { platform: "Win32" });
expect(getPaneNavDirection(keyEvent({ altKey: true }))).toBe("left");
expect(
getPaneNavDirection(keyEvent({ code: "ArrowDown", altKey: true })),
).toBe("down");
expect(
getPaneNavDirection(keyEvent({ altKey: true, metaKey: true })),
).toBeNull();
expect(paneNavHotkeyLabel()).toBe("Alt+Arrows");
});
it("leaves Option+Arrow to the shell on macOS", () => {
vi.stubGlobal("navigator", { platform: "MacIntel" });
expect(getPaneNavDirection(keyEvent({ altKey: true }))).toBeNull();
expect(
getPaneNavDirection(
keyEvent({ code: "ArrowRight", altKey: true, metaKey: true }),
),
).toBe("right");
expect(paneNavHotkeyLabel()).toBe("Cmd+Option+Arrows");
});
it("ignores other keys and modifiers", () => {
vi.stubGlobal("navigator", { platform: "Win32" });
expect(
getPaneNavDirection(keyEvent({ code: "KeyA", altKey: true })),
).toBeNull();
expect(
getPaneNavDirection(keyEvent({ altKey: true, shiftKey: true })),
).toBeNull();
expect(getPaneNavDirection(keyEvent({}))).toBeNull();
});
});