Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
184 lines
5.7 KiB
TypeScript
184 lines
5.7 KiB
TypeScript
import React, { memo, useMemo } from 'react';
|
|
|
|
import { useActiveTabId } from '../state/activeTabStore';
|
|
import { useAppearanceChromeStore } from '../state/appearanceChromeStore';
|
|
import type { EditorTabChrome } from '../state/editorTabStore';
|
|
import type { LogView } from '../state/logViewState';
|
|
import { useManualTerminalChromeSurfaceInjection } from '../state/useManualTerminalChromeSurfaceInjection';
|
|
import { TerminalHostTreeSidebar } from '../../components/terminalLayer/TerminalHostTreeSidebar';
|
|
import type {
|
|
ResolvedAppearance,
|
|
TerminalAppearanceHostScope,
|
|
} from '../../domain/terminalAppearanceRuntime';
|
|
import type { GroupConfig, Host, TerminalSession, TerminalTheme, Workspace } from '../../types';
|
|
import { resolveActiveChromeTheme } from './activeChromeTheme';
|
|
import {
|
|
isHostTreeWorkTabSurface,
|
|
resolveWorkTabActiveHostId,
|
|
} from './workTabSurface';
|
|
|
|
interface AppHostTreeLayerProps {
|
|
enabled: boolean;
|
|
hosts: Host[];
|
|
customGroups: string[];
|
|
groupConfigs: GroupConfig[];
|
|
sessions: TerminalSession[];
|
|
workspaces: Workspace[];
|
|
editorTabs: readonly EditorTabChrome[];
|
|
logViews: readonly LogView[];
|
|
orderedTabs: readonly string[];
|
|
currentTerminalTheme: TerminalTheme;
|
|
followAppTerminalTheme: boolean;
|
|
hostById: ReadonlyMap<string, Host>;
|
|
themeById: ReadonlyMap<string, TerminalTheme>;
|
|
resolveSessionAppearance?: (hostScope: TerminalAppearanceHostScope) => ResolvedAppearance;
|
|
onConnect: (host: Host) => void;
|
|
onNewHost?: (defaultGroup?: string) => void;
|
|
onEditHost?: (host: Host) => void;
|
|
onCreateLocalTerminal?: () => void;
|
|
}
|
|
|
|
export function getAppHostTreeLayerStyle(surfaceVisible: boolean): React.CSSProperties {
|
|
return {
|
|
visibility: surfaceVisible ? 'visible' : 'hidden',
|
|
pointerEvents: surfaceVisible ? 'auto' : 'none',
|
|
zIndex: surfaceVisible ? 30 : 0,
|
|
};
|
|
}
|
|
|
|
function appHostTreeLayerAreEqual(
|
|
prev: AppHostTreeLayerProps,
|
|
next: AppHostTreeLayerProps,
|
|
): boolean {
|
|
return prev.enabled === next.enabled
|
|
&& prev.hosts === next.hosts
|
|
&& prev.customGroups === next.customGroups
|
|
&& prev.groupConfigs === next.groupConfigs
|
|
&& prev.sessions === next.sessions
|
|
&& prev.workspaces === next.workspaces
|
|
&& prev.editorTabs === next.editorTabs
|
|
&& prev.logViews === next.logViews
|
|
&& prev.orderedTabs === next.orderedTabs
|
|
// accentMode / customAccent intentionally omitted — read from
|
|
// appearanceChromeStore so accent drag does not rebuild the App shell.
|
|
&& prev.currentTerminalTheme === next.currentTerminalTheme
|
|
&& prev.followAppTerminalTheme === next.followAppTerminalTheme
|
|
&& prev.hostById === next.hostById
|
|
&& prev.themeById === next.themeById
|
|
&& prev.resolveSessionAppearance === next.resolveSessionAppearance
|
|
&& prev.onConnect === next.onConnect
|
|
&& prev.onNewHost === next.onNewHost
|
|
&& prev.onEditHost === next.onEditHost
|
|
&& prev.onCreateLocalTerminal === next.onCreateLocalTerminal;
|
|
}
|
|
|
|
const AppHostTreeLayerInner: React.FC<AppHostTreeLayerProps> = ({
|
|
enabled,
|
|
hosts,
|
|
customGroups,
|
|
groupConfigs,
|
|
sessions,
|
|
workspaces,
|
|
editorTabs,
|
|
logViews,
|
|
orderedTabs,
|
|
currentTerminalTheme,
|
|
followAppTerminalTheme,
|
|
hostById,
|
|
themeById,
|
|
resolveSessionAppearance,
|
|
onConnect,
|
|
onNewHost,
|
|
onEditHost,
|
|
onCreateLocalTerminal,
|
|
}) => {
|
|
const activeTabId = useActiveTabId();
|
|
const { accentMode, customAccent } = useAppearanceChromeStore();
|
|
const sessionIds = useMemo(() => new Set(sessions.map((session) => session.id)), [sessions]);
|
|
const workspaceIds = useMemo(() => new Set(workspaces.map((workspace) => workspace.id)), [workspaces]);
|
|
const logViewIds = useMemo(() => new Set(logViews.map((logView) => logView.id)), [logViews]);
|
|
const sessionById = useMemo(
|
|
() => new Map(sessions.map((session) => [session.id, session])),
|
|
[sessions],
|
|
);
|
|
const workspaceById = useMemo(
|
|
() => new Map(workspaces.map((workspace) => [workspace.id, workspace])),
|
|
[workspaces],
|
|
);
|
|
const surfaceVisible = isHostTreeWorkTabSurface({
|
|
enabled,
|
|
activeTabId,
|
|
logViewIds,
|
|
orderedTabs,
|
|
sessionIds,
|
|
workspaceIds,
|
|
});
|
|
|
|
const activeHostId = useMemo(() => resolveWorkTabActiveHostId({
|
|
activeTabId,
|
|
editorTabs,
|
|
sessions,
|
|
workspaces,
|
|
}), [activeTabId, editorTabs, sessions, workspaces]);
|
|
|
|
const hostTreeTheme = useMemo(() => (
|
|
resolveActiveChromeTheme({
|
|
accentMode,
|
|
activeTabId,
|
|
currentTerminalTheme,
|
|
customAccent,
|
|
editorTabs,
|
|
followAppTerminalTheme,
|
|
hostById,
|
|
logViews,
|
|
resolveSessionAppearance,
|
|
sessionById,
|
|
themeById,
|
|
workspaceById,
|
|
}) ?? currentTerminalTheme
|
|
), [
|
|
accentMode,
|
|
activeTabId,
|
|
currentTerminalTheme,
|
|
customAccent,
|
|
editorTabs,
|
|
followAppTerminalTheme,
|
|
hostById,
|
|
logViews,
|
|
resolveSessionAppearance,
|
|
sessionById,
|
|
themeById,
|
|
workspaceById,
|
|
]);
|
|
|
|
useManualTerminalChromeSurfaceInjection(
|
|
hostTreeTheme,
|
|
!followAppTerminalTheme && surfaceVisible,
|
|
);
|
|
|
|
return (
|
|
<div
|
|
className="absolute left-0 top-0 bottom-0 flex min-h-0"
|
|
data-section="app-host-tree-layer"
|
|
style={getAppHostTreeLayerStyle(surfaceVisible)}
|
|
>
|
|
<TerminalHostTreeSidebar
|
|
enabled={enabled}
|
|
surfaceVisible={surfaceVisible}
|
|
hosts={hosts}
|
|
customGroups={customGroups}
|
|
groupConfigs={groupConfigs}
|
|
resolvedPreviewTheme={hostTreeTheme}
|
|
activeHostId={activeHostId}
|
|
onConnect={onConnect}
|
|
onNewHost={onNewHost}
|
|
onEditHost={onEditHost}
|
|
onCreateLocalTerminal={onCreateLocalTerminal}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const AppHostTreeLayer = memo(AppHostTreeLayerInner, appHostTreeLayerAreEqual);
|
|
AppHostTreeLayer.displayName = 'AppHostTreeLayer';
|