fix(workspace): suppress pane focus when moving tabs between panes
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user