fix(workspace): suppress pane focus when moving tabs between panes

This commit is contained in:
Mohamed Boudra
2026-03-23 11:30:35 +07:00
parent d6977352aa
commit 4b4e55a246
5 changed files with 35 additions and 27 deletions
@@ -9,6 +9,7 @@ import {
type ReactNode,
type SetStateAction,
} from "react";
import { useStableEvent } from "@/hooks/use-stable-event";
import {
DndContext,
DragOverlay,
@@ -714,6 +715,7 @@ function SplitPaneView({
}: SplitPaneViewProps) {
const { theme } = useUnistyles();
const paneRef = useRef<View | null>(null);
const stableOnFocusPane = useStableEvent(onFocusPane);
const paneState = useMemo(
() =>
deriveWorkspacePaneState({
@@ -743,7 +745,7 @@ function SplitPaneView({
tab: activeTabDescriptor,
})
: null,
[activeTabDescriptor, buildPaneContentModel, pane.id],
[activeTabDescriptor, buildPaneContentModel, isFocused, pane.id],
);
useEffect(() => {
@@ -764,14 +766,14 @@ function SplitPaneView({
if (!shouldFocusPaneFromEventTarget(event.target)) {
return;
}
onFocusPane(pane.id);
stableOnFocusPane(pane.id);
};
const handlePaneFocusIn = (event: FocusEvent) => {
if (!shouldFocusPaneFromEventTarget(event.target)) {
return;
}
onFocusPane(pane.id);
stableOnFocusPane(pane.id);
};
paneElement.addEventListener("pointerdown", handlePanePointerDown, true);
@@ -781,7 +783,7 @@ function SplitPaneView({
paneElement.removeEventListener("pointerdown", handlePanePointerDown, true);
paneElement.removeEventListener("focusin", handlePaneFocusIn, true);
};
}, [onFocusPane, pane.id]);
}, [stableOnFocusPane, pane.id]);
return (
<View ref={paneRef} collapsable={false} style={styles.pane}>
@@ -24,6 +24,7 @@ interface TerminalPaneProps {
serverId: string;
cwd: string;
terminalId: string;
isPaneFocused: boolean;
}
const TERMINAL_REFIT_DELAYS_MS = [0, 48, 144, 320];
@@ -82,6 +83,7 @@ export function TerminalPane({
serverId,
cwd,
terminalId,
isPaneFocused,
}: TerminalPaneProps) {
const isScreenFocused = useIsFocused();
const { theme } = useUnistyles();
@@ -127,7 +129,7 @@ export function TerminalPane({
}, []);
useEffect(() => {
if (isMobile || !isScreenFocused || !terminalId) {
if (isMobile || !isScreenFocused || !isPaneFocused || !terminalId) {
lastAutoFocusKeyRef.current = null;
return;
}
@@ -139,7 +141,7 @@ export function TerminalPane({
lastAutoFocusKeyRef.current = nextFocusKey;
requestTerminalFocus();
}, [isMobile, isScreenFocused, requestTerminalFocus, scopeKey, terminalId]);
}, [isMobile, isPaneFocused, isScreenFocused, requestTerminalFocus, scopeKey, terminalId]);
const clearKeyboardRefitTimeouts = useCallback(() => {
if (keyboardRefitTimeoutsRef.current.length === 0) {
+11 -11
View File
@@ -439,7 +439,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-split-right-cmd-backslash",
action: "workspace.pane.split.right",
combo: "Cmd+\\",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-split-right",
section: "tabs-panes",
@@ -451,7 +451,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-split-down-cmd-shift-backslash",
action: "workspace.pane.split.down",
combo: "Cmd+Shift+\\",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-split-down",
section: "tabs-panes",
@@ -463,7 +463,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-focus-left-cmd-shift-left",
action: "workspace.pane.focus.left",
combo: "Cmd+Shift+ArrowLeft",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-focus-left",
section: "tabs-panes",
@@ -475,7 +475,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-focus-right-cmd-shift-right",
action: "workspace.pane.focus.right",
combo: "Cmd+Shift+ArrowRight",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-focus-right",
section: "tabs-panes",
@@ -487,7 +487,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-focus-up-cmd-shift-up",
action: "workspace.pane.focus.up",
combo: "Cmd+Shift+ArrowUp",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-focus-up",
section: "tabs-panes",
@@ -499,7 +499,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-focus-down-cmd-shift-down",
action: "workspace.pane.focus.down",
combo: "Cmd+Shift+ArrowDown",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-focus-down",
section: "tabs-panes",
@@ -511,7 +511,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-move-tab-left-cmd-shift-alt-left",
action: "workspace.pane.move-tab.left",
combo: "Cmd+Alt+Shift+ArrowLeft",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-move-tab-left",
section: "tabs-panes",
@@ -523,7 +523,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-move-tab-right-cmd-shift-alt-right",
action: "workspace.pane.move-tab.right",
combo: "Cmd+Alt+Shift+ArrowRight",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-move-tab-right",
section: "tabs-panes",
@@ -535,7 +535,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-move-tab-up-cmd-shift-alt-up",
action: "workspace.pane.move-tab.up",
combo: "Cmd+Alt+Shift+ArrowUp",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-move-tab-up",
section: "tabs-panes",
@@ -547,7 +547,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-move-tab-down-cmd-shift-alt-down",
action: "workspace.pane.move-tab.down",
combo: "Cmd+Alt+Shift+ArrowDown",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-move-tab-down",
section: "tabs-panes",
@@ -559,7 +559,7 @@ const SHORTCUT_BINDINGS: readonly ShortcutBinding[] = [
id: "workspace-pane-close-cmd-shift-w",
action: "workspace.pane.close",
combo: "Cmd+Shift+W",
when: { mac: true, terminal: false, commandCenter: false },
when: { mac: true, commandCenter: false },
help: {
id: "workspace-pane-close",
section: "tabs-panes",
+2 -1
View File
@@ -49,7 +49,7 @@ function useTerminalPanelDescriptor(
function TerminalPanel() {
const isFocused = useIsFocused();
const { serverId, workspaceId, target } = usePaneContext();
const { serverId, workspaceId, target, isPaneFocused } = usePaneContext();
invariant(target.kind === "terminal", "TerminalPanel requires terminal target");
if (!isFocused) {
@@ -61,6 +61,7 @@ function TerminalPanel() {
serverId={serverId}
cwd={workspaceId}
terminalId={target.terminalId}
isPaneFocused={isPaneFocused}
/>
);
}
@@ -74,6 +74,7 @@ import type { ListTerminalsResponse } from "@server/shared/messages";
import { upsertTerminalListEntry } from "@/utils/terminal-list";
import { confirmDialog } from "@/utils/confirm-dialog";
import { useArchiveAgent } from "@/hooks/use-archive-agent";
import { useStableEvent } from "@/hooks/use-stable-event";
import { buildProviderCommand } from "@/utils/provider-command-templates";
import { generateDraftId } from "@/stores/draft-keys";
import {
@@ -740,6 +741,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
const splitWorkspacePaneEmpty = useWorkspaceLayoutStore((state) => state.splitPaneEmpty);
const moveWorkspaceTabToPane = useWorkspaceLayoutStore((state) => state.moveTabToPane);
const focusWorkspacePane = useWorkspaceLayoutStore((state) => state.focusPane);
const paneFocusSuppressedRef = useRef(false);
const resizeWorkspaceSplit = useWorkspaceLayoutStore((state) => state.resizeSplit);
const reorderWorkspaceTabsInPane = useWorkspaceLayoutStore((state) => state.reorderTabsInPane);
const pinnedAgentIds = useWorkspaceLayoutStore((state) =>
@@ -1556,7 +1558,11 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
const activePaneTabId = focusedPaneTabState.activeTabId;
const adjacentPaneId = findAdjacentPane(workspaceLayout.root, focusedPane.id, direction);
if (activePaneTabId && adjacentPaneId) {
paneFocusSuppressedRef.current = true;
moveWorkspaceTabToPane(persistenceKey, activePaneTabId, adjacentPaneId);
requestAnimationFrame(() => {
paneFocusSuppressedRef.current = false;
});
}
}
return true;
@@ -1790,15 +1796,12 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
[buildPaneContentModel],
);
const handleFocusPane = useCallback(
function handleFocusPane(paneId: string) {
if (!persistenceKey) {
return;
}
focusWorkspacePane(persistenceKey, paneId);
},
[focusWorkspacePane, persistenceKey],
);
const handleFocusPane = useStableEvent(function handleFocusPane(paneId: string) {
if (!persistenceKey || paneFocusSuppressedRef.current) {
return;
}
focusWorkspacePane(persistenceKey, paneId);
});
const handleSplitPane = useCallback(
function handleSplitPane(input: {