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; themeById: ReadonlyMap; 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 = ({ 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 (
); }; export const AppHostTreeLayer = memo(AppHostTreeLayerInner, appHostTreeLayerAreEqual); AppHostTreeLayer.displayName = 'AppHostTreeLayer';