Files
Netcatty/components/terminalPaneVisibility.ts
T
2026-07-02 01:00:32 +08:00

186 lines
4.9 KiB
TypeScript

import { activeTabStore } from "../application/state/activeTabStore";
import type { Workspace } from "../types";
export const HIDDEN_TERMINAL_PANE_SNAPSHOT = "hidden";
export type TerminalPaneSnapshot =
| typeof HIDDEN_TERMINAL_PANE_SNAPSHOT
| `solo|${string}`
| `workspace|split|${string}`
| `workspace|focus|${string}|${string}`;
export type TerminalPaneFocusSnapshot = "na" | "focused" | "unfocused";
export type TerminalPaneHiddenSize = {
width: number;
height: number;
};
export type TerminalPaneStyle = {
left?: string | number;
top?: string | number;
width?: string | number;
height?: string | number;
visibility?: string;
pointerEvents?: string;
};
export function resolveHiddenTerminalPaneStyle<T extends TerminalPaneStyle>(
layoutStyle: T,
lastVisibleSize: TerminalPaneHiddenSize | null,
): T {
return {
...layoutStyle,
visibility: "hidden",
pointerEvents: "none",
...(lastVisibleSize
? {
width: `${lastVisibleSize.width}px`,
height: `${lastVisibleSize.height}px`,
}
: {}),
};
}
interface GetTerminalPaneSnapshotOptions {
activeTabId: string | null;
sessionId: string;
sessionWorkspaceId?: string;
workspaceById: Map<string, Workspace>;
isTerminalLayerVisible: boolean;
}
export function getTerminalPaneSnapshot({
activeTabId,
sessionId,
sessionWorkspaceId,
workspaceById,
isTerminalLayerVisible,
}: GetTerminalPaneSnapshotOptions): TerminalPaneSnapshot {
if (!isTerminalLayerVisible || !activeTabId) {
return HIDDEN_TERMINAL_PANE_SNAPSHOT;
}
const activeWorkspace = workspaceById.get(activeTabId);
if (activeWorkspace) {
if (sessionWorkspaceId !== activeWorkspace.id) {
return HIDDEN_TERMINAL_PANE_SNAPSHOT;
}
const focusedSessionId = activeWorkspace.focusedSessionId ?? "";
if (activeWorkspace.viewMode === "focus") {
return sessionId === focusedSessionId
? `workspace|focus|${activeWorkspace.id}|${focusedSessionId}`
: HIDDEN_TERMINAL_PANE_SNAPSHOT;
}
return `workspace|split|${activeWorkspace.id}`;
}
return activeTabId === sessionId
? `solo|${sessionId}`
: HIDDEN_TERMINAL_PANE_SNAPSHOT;
}
export function parseTerminalPaneSnapshot(snapshot: TerminalPaneSnapshot): {
isVisible: boolean;
mode: "hidden" | "solo" | "split" | "focus";
workspaceId: string | null;
focusedSessionId: string | null;
} {
if (snapshot === HIDDEN_TERMINAL_PANE_SNAPSHOT) {
return {
isVisible: false,
mode: "hidden",
workspaceId: null,
focusedSessionId: null,
};
}
const parts = snapshot.split("|");
if (parts[0] === "solo") {
return {
isVisible: true,
mode: "solo",
workspaceId: null,
focusedSessionId: null,
};
}
if (parts[1] === "focus") {
return {
isVisible: true,
mode: "focus",
workspaceId: parts[2] || null,
focusedSessionId: parts[3] || null,
};
}
return {
isVisible: true,
mode: "split",
workspaceId: parts[2] || null,
focusedSessionId: null,
};
}
export function getTerminalPaneFocusSnapshot({
activeTabId: activeTabIdOverride,
sessionId,
sessionWorkspaceId,
workspaceById,
}: {
activeTabId?: string | null;
sessionId: string;
sessionWorkspaceId?: string;
workspaceById: Map<string, Workspace>;
}): TerminalPaneFocusSnapshot {
const activeTabId = activeTabIdOverride ?? activeTabStore.getActiveTabId();
if (!activeTabId) return "na";
const activeWorkspace = workspaceById.get(activeTabId);
if (!activeWorkspace || activeWorkspace.viewMode === "focus") return "na";
if (sessionWorkspaceId !== activeWorkspace.id) return "na";
return activeWorkspace.focusedSessionId === sessionId ? "focused" : "unfocused";
}
/** Combined visibility + focus snapshot for a single useSyncExternalStore subscription. */
export function getTerminalPaneRenderSnapshot(
options: GetTerminalPaneSnapshotOptions,
): string {
const pane = getTerminalPaneSnapshot(options);
if (pane === HIDDEN_TERMINAL_PANE_SNAPSHOT) {
return HIDDEN_TERMINAL_PANE_SNAPSHOT;
}
const focus = getTerminalPaneFocusSnapshot({
activeTabId: options.activeTabId,
sessionId: options.sessionId,
sessionWorkspaceId: options.sessionWorkspaceId,
workspaceById: options.workspaceById,
});
return `${pane}|${focus}`;
}
export function parseTerminalPaneRenderSnapshot(snapshot: string): {
paneState: ReturnType<typeof parseTerminalPaneSnapshot>;
isFocusedPane: boolean;
} {
if (snapshot === HIDDEN_TERMINAL_PANE_SNAPSHOT) {
return {
paneState: parseTerminalPaneSnapshot(HIDDEN_TERMINAL_PANE_SNAPSHOT),
isFocusedPane: false,
};
}
const focusSep = snapshot.lastIndexOf("|");
const focusToken = snapshot.slice(focusSep + 1);
const paneSnapshot = snapshot.slice(0, focusSep) as TerminalPaneSnapshot;
const paneState = parseTerminalPaneSnapshot(paneSnapshot);
return {
paneState,
isFocusedPane: focusToken === "focused",
};
}