[Init] Initial commit - NetMesh terminal manager
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
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
This commit is contained in:
704
components/terminalLayer/TerminalLayerTabBridge.tsx
Normal file
704
components/terminalLayer/TerminalLayerTabBridge.tsx
Normal file
@@ -0,0 +1,704 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useSyncExternalStore } from 'react';
|
||||
|
||||
import { useActiveTabId } from '../../application/state/activeTabStore';
|
||||
import { sessionCapabilitiesStore } from '../../application/state/sessionCapabilitiesStore';
|
||||
import { useSystemManagerBackend } from '../../application/state/useSystemManagerBackend';
|
||||
import { isTerminalSessionEligibleForSftpReuse } from '../../application/state/terminalConnectionReuse';
|
||||
import { resolveSystemSidebarSession } from '../../domain/systemManager/resolveSystemSession';
|
||||
import type { TerminalContextReader } from '../../domain/terminalContextRead';
|
||||
import { useSystemCapabilitiesWarmup } from '../../application/state/useSystemManager';
|
||||
import { cn } from '../../lib/utils';
|
||||
import type { Host, TerminalSession, Workspace } from '../../types';
|
||||
import { resolveTerminalHibernateEnabled } from '../../domain/terminalHibernate';
|
||||
import { shouldMeasureTerminalLayerLayout } from '../terminalPaneVisibility';
|
||||
import { TerminalLayerView } from './TerminalLayerView';
|
||||
import { useTerminalAiContexts } from '../../application/state/useTerminalAiContexts';
|
||||
import { useTerminalLayerEffects } from './useTerminalLayerEffects';
|
||||
import { useTerminalThemePanelState } from './useTerminalThemePanelState';
|
||||
import { useManualTerminalChromeSurfaceInjection } from '../../application/state/useManualTerminalChromeSurfaceInjection';
|
||||
import { sidePanelLiveStore, type SidePanelLiveSnapshot } from '../../application/state/sidePanelLiveStore';
|
||||
import { terminalCwdStore } from '../../application/state/terminalCwdStore';
|
||||
import { useTerminalWorkspaceLayout } from './useTerminalWorkspaceLayout';
|
||||
import type { SidePanelTab } from './TerminalLayerSupport';
|
||||
import {
|
||||
collectSidePanelPanes,
|
||||
type SidePanelLayout,
|
||||
} from '../../domain/sidePanelLayout';
|
||||
|
||||
type StableRef = React.MutableRefObject<Record<string, any>>;
|
||||
|
||||
export function TerminalLayerTabBridge({ stableRef }: { stableRef: StableRef }) {
|
||||
const s = stableRef.current;
|
||||
const activeTabId = useActiveTabId();
|
||||
// Subscribe to cwd store so OSC 7 updates reach the side-panel live snapshot
|
||||
// without TerminalLayerInner setState.
|
||||
const terminalCwdVersion = useSyncExternalStore(
|
||||
terminalCwdStore.subscribe,
|
||||
terminalCwdStore.getVersion,
|
||||
terminalCwdStore.getVersion,
|
||||
);
|
||||
const systemBackend = useSystemManagerBackend();
|
||||
const terminalContextReadersRef = useRef<Map<string, TerminalContextReader>>(new Map());
|
||||
|
||||
s.activeTabIdRef.current = activeTabId;
|
||||
|
||||
const workspaceById = s.workspaceById as Map<string, Workspace>;
|
||||
const sessions = s.sessions as TerminalSession[];
|
||||
const sessionHostsMap = s.sessionHostsMap as Map<string, Host>;
|
||||
const sftpHostForTab = s.sftpHostForTab as Map<string, Host>;
|
||||
const sidePanelOpenTabs = s.sidePanelOpenTabs as Map<string, SidePanelTab>;
|
||||
const sidePanelLayouts = s.sidePanelLayouts as Map<string, SidePanelLayout>;
|
||||
const showHostTreeSidebar = s.showHostTreeSidebar as boolean | undefined;
|
||||
|
||||
const activeWorkspace = useMemo(
|
||||
() => (activeTabId ? workspaceById.get(activeTabId) : undefined),
|
||||
[activeTabId, workspaceById],
|
||||
);
|
||||
const activeSession = useMemo(
|
||||
() => sessions.find((session) => session.id === activeTabId),
|
||||
[activeTabId, sessions],
|
||||
);
|
||||
const isFocusMode = activeWorkspace?.viewMode === 'focus';
|
||||
const focusedSessionId = activeWorkspace?.focusedSessionId;
|
||||
const hibernateHiddenTabs = resolveTerminalHibernateEnabled(s.terminalSettings);
|
||||
const localWorkspaceIds = useMemo(() => new Set(
|
||||
sessions
|
||||
.filter((session) => session.workspaceId && sessionHostsMap.get(session.id)?.protocol === 'local')
|
||||
.map((session) => session.workspaceId as string),
|
||||
), [sessionHostsMap, sessions]);
|
||||
const shouldKeepHiddenWorkspaceLaidOut = useCallback(
|
||||
(workspace: Workspace) => !hibernateHiddenTabs || localWorkspaceIds.has(workspace.id),
|
||||
[hibernateHiddenTabs, localWorkspaceIds],
|
||||
);
|
||||
const keepHiddenLayoutActive = !hibernateHiddenTabs || localWorkspaceIds.size > 0;
|
||||
const effectiveFocusedSessionId = useMemo((): string | null => {
|
||||
if (activeWorkspace) {
|
||||
if (focusedSessionId) return focusedSessionId;
|
||||
return sessions.find((session) => session.workspaceId === activeWorkspace.id)?.id ?? null;
|
||||
}
|
||||
return activeSession?.id ?? null;
|
||||
}, [activeSession?.id, activeWorkspace, focusedSessionId, sessions]);
|
||||
|
||||
s.activeWorkspaceRef.current = activeWorkspace;
|
||||
s.activeSessionRef.current = activeSession;
|
||||
s.focusedSessionIdRef.current = focusedSessionId;
|
||||
|
||||
const isVisible = Boolean(activeSession || activeWorkspace || s.draggingSessionId);
|
||||
const isTerminalLayerVisible = isVisible || !!s.draggingSessionId;
|
||||
|
||||
const {
|
||||
activeResizers,
|
||||
computeSplitHint,
|
||||
dropHint,
|
||||
findSplitNode,
|
||||
handleWorkspaceDrop,
|
||||
resizing,
|
||||
setDropHint,
|
||||
setResizing,
|
||||
setWorkspaceArea,
|
||||
workspaceArea,
|
||||
workspaceInnerRef,
|
||||
workspaceOuterRef,
|
||||
workspaceOverlayRef,
|
||||
workspaceRectsById,
|
||||
} = useTerminalWorkspaceLayout({
|
||||
activeSession,
|
||||
activeWorkspace,
|
||||
isFocusMode,
|
||||
shouldKeepHiddenWorkspaceLaidOut,
|
||||
onAddSessionToWorkspace: s.onAddSessionToWorkspace,
|
||||
onCreateWorkspaceFromSessions: s.onCreateWorkspaceFromSessions,
|
||||
onSetDraggingSessionId: s.onSetDraggingSessionId,
|
||||
onUpdateSplitSizes: s.onUpdateSplitSizes,
|
||||
sessions,
|
||||
workspaces: s.workspaces,
|
||||
});
|
||||
|
||||
const isSidePanelOpenForCurrentTab = activeTabId ? sidePanelOpenTabs.has(activeTabId) : false;
|
||||
const activeSidePanelTab = activeTabId ? sidePanelOpenTabs.get(activeTabId) ?? null : null;
|
||||
const activeSidePanelLayout = activeTabId ? sidePanelLayouts.get(activeTabId) ?? null : null;
|
||||
const activeSidePanelTools = useMemo(
|
||||
() => new Set(activeSidePanelLayout ? collectSidePanelPanes(activeSidePanelLayout.root).map((pane) => pane.tool) : []),
|
||||
[activeSidePanelLayout],
|
||||
);
|
||||
const isSftpOpenForCurrentTab = activeSidePanelTools.has('sftp');
|
||||
|
||||
const activeHostIdForSidebar = useMemo(() => {
|
||||
const sessionId = activeWorkspace ? focusedSessionId : activeSession?.id;
|
||||
if (!sessionId) return null;
|
||||
return sessionHostsMap.get(sessionId)?.id
|
||||
?? sessions.find((session) => session.id === sessionId)?.hostId
|
||||
?? null;
|
||||
}, [activeWorkspace, focusedSessionId, activeSession, sessionHostsMap, sessions]);
|
||||
|
||||
const sftpActiveHost = useMemo((): Host | null => {
|
||||
if (!isSftpOpenForCurrentTab || !activeTabId) return null;
|
||||
if (activeWorkspace && focusedSessionId) {
|
||||
return sessionHostsMap.get(focusedSessionId) ?? sftpHostForTab.get(activeTabId) ?? null;
|
||||
}
|
||||
if (activeSession) {
|
||||
return sessionHostsMap.get(activeSession.id) ?? sftpHostForTab.get(activeTabId) ?? null;
|
||||
}
|
||||
return sftpHostForTab.get(activeTabId) ?? null;
|
||||
}, [activeSession, activeTabId, activeWorkspace, focusedSessionId, isSftpOpenForCurrentTab, sessionHostsMap, sftpHostForTab]);
|
||||
|
||||
// Keep the same-endpoint SSH session id across disconnected/connecting so
|
||||
// SftpSidePanel can observe status transitions and rebind after Start over.
|
||||
// Transport reuse for openSftp still requires status === "connected".
|
||||
const activeTerminalSessionIdForSftp = useMemo((): string | null => {
|
||||
if (!isSftpOpenForCurrentTab || !sftpActiveHost) return null;
|
||||
const sessionId = activeWorkspace ? focusedSessionId : activeSession?.id;
|
||||
if (!sessionId) return null;
|
||||
const session = sessions.find((candidate) => candidate.id === sessionId);
|
||||
if (!session || !isTerminalSessionEligibleForSftpReuse(session)) return null;
|
||||
const sessionHost = sessionHostsMap.get(session.id);
|
||||
if (!sessionHost) return null;
|
||||
const sameEndpoint =
|
||||
sessionHost.hostname === sftpActiveHost.hostname
|
||||
&& (sessionHost.port || 22) === (sftpActiveHost.port || 22)
|
||||
&& (sessionHost.username || 'root') === (sftpActiveHost.username || 'root');
|
||||
return sameEndpoint ? session.id : null;
|
||||
}, [activeSession?.id, activeWorkspace, focusedSessionId, isSftpOpenForCurrentTab, sessions, sessionHostsMap, sftpActiveHost]);
|
||||
|
||||
const linkedTerminalSessionIdForSftp = useMemo((): string | null => {
|
||||
if (!isSftpOpenForCurrentTab) return null;
|
||||
if (activeTerminalSessionIdForSftp) return activeTerminalSessionIdForSftp;
|
||||
return activeWorkspace ? (focusedSessionId ?? null) : (activeSession?.id ?? null);
|
||||
}, [
|
||||
activeSession?.id,
|
||||
activeTerminalSessionIdForSftp,
|
||||
activeWorkspace,
|
||||
focusedSessionId,
|
||||
isSftpOpenForCurrentTab,
|
||||
]);
|
||||
|
||||
// Recomputed when terminalCwdVersion changes (useSyncExternalStore above).
|
||||
const activeTerminalCwd = linkedTerminalSessionIdForSftp
|
||||
? (
|
||||
terminalCwdStore.getCwd(linkedTerminalSessionIdForSftp)
|
||||
?? s.terminalRendererCwdBySessionRef.current.get(linkedTerminalSessionIdForSftp)
|
||||
?? null
|
||||
)
|
||||
: null;
|
||||
const activeTerminalCwdSource = linkedTerminalSessionIdForSftp
|
||||
? (
|
||||
terminalCwdStore.getSource(linkedTerminalSessionIdForSftp)
|
||||
?? s.terminalRendererCwdSourceBySessionRef.current.get(linkedTerminalSessionIdForSftp)
|
||||
)
|
||||
: undefined;
|
||||
const activeTerminalCwdTrusted = activeTerminalCwdSource === 'osc7'
|
||||
|| activeTerminalCwdSource === 'backend-strict';
|
||||
void terminalCwdVersion;
|
||||
|
||||
const historySessionId = effectiveFocusedSessionId;
|
||||
const activeTerminalSessionForSystem = useMemo(
|
||||
() => resolveSystemSidebarSession(sessions, activeWorkspace, focusedSessionId, activeSession),
|
||||
[activeSession, activeWorkspace, focusedSessionId, sessions],
|
||||
);
|
||||
const activeSystemSessionHost = useMemo((): Host | null => {
|
||||
const id = activeTerminalSessionForSystem?.id;
|
||||
if (!id) return null;
|
||||
return sessionHostsMap.get(id) ?? null;
|
||||
}, [activeTerminalSessionForSystem?.id, sessionHostsMap]);
|
||||
|
||||
const systemWarmupSessionIds = useMemo(() => {
|
||||
if (!activeTabId || !activeSidePanelTools.has('system')) return [];
|
||||
const session = activeTerminalSessionForSystem;
|
||||
if (!session || session.status !== 'connected') return [];
|
||||
return [session.id];
|
||||
}, [activeSidePanelTools, activeTabId, activeTerminalSessionForSystem]);
|
||||
|
||||
useSystemCapabilitiesWarmup(
|
||||
systemWarmupSessionIds,
|
||||
systemBackend,
|
||||
systemWarmupSessionIds.length > 0,
|
||||
(s.terminalSettings?.systemManagerProcessRefreshInterval ?? 3) * 1000,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
sessionCapabilitiesStore.prune(new Set(sessions.map((session) => session.id)));
|
||||
}, [sessions]);
|
||||
|
||||
const focusedHost = useMemo((): Host | null => {
|
||||
if (!historySessionId) return null;
|
||||
return sessionHostsMap.get(historySessionId) ?? null;
|
||||
}, [historySessionId, sessionHostsMap]);
|
||||
|
||||
const themeState = useTerminalThemePanelState({
|
||||
activeSession,
|
||||
activeSidePanelTab: activeSidePanelTools.has('theme') ? 'theme' : activeSidePanelTab,
|
||||
activeWorkspace,
|
||||
followAppTerminalTheme: s.followAppTerminalTheme,
|
||||
focusedSessionId,
|
||||
fontSize: s.fontSize,
|
||||
hostMap: s.hostMap,
|
||||
isSidePanelOpenForCurrentTab,
|
||||
isVisible,
|
||||
onUpdateHost: s.onUpdateHost,
|
||||
onUpdateTerminalFontFamilyId: s.onUpdateTerminalFontFamilyId,
|
||||
onUpdateTerminalFontSize: s.onUpdateTerminalFontSize,
|
||||
onUpdateSessionFontSize: s.onUpdateSessionFontSize,
|
||||
onClearSessionFontSizeOverride: s.onClearSessionFontSizeOverride,
|
||||
onUpdateTerminalFontWeight: s.onUpdateTerminalFontWeight,
|
||||
onUpdateTerminalThemeId: s.onUpdateTerminalThemeId,
|
||||
pickTheme: s.pickTerminalTheme,
|
||||
clearIntent: s.clearThemeIntent,
|
||||
resolveFocusedAppearance: s.resolveSessionAppearance,
|
||||
sessionHostsMap,
|
||||
terminalFontFamilyId: s.terminalFontFamilyId,
|
||||
terminalSettings: s.terminalSettings,
|
||||
terminalTheme: s.terminalTheme,
|
||||
});
|
||||
|
||||
useManualTerminalChromeSurfaceInjection(
|
||||
themeState.resolvedPreviewTheme,
|
||||
!s.followAppTerminalTheme && isTerminalLayerVisible,
|
||||
);
|
||||
|
||||
const sidePanelLiveSnapshot = useMemo<SidePanelLiveSnapshot>(() => ({
|
||||
sftpActiveHost,
|
||||
activeTerminalSessionIdForSftp,
|
||||
activeTerminalCwd,
|
||||
activeTerminalCwdTrusted,
|
||||
activeWorkspace,
|
||||
activeTerminalSessionForSystem: activeTerminalSessionForSystem ?? null,
|
||||
activeSystemSessionHost,
|
||||
focusedHost,
|
||||
focusedSessionId: effectiveFocusedSessionId,
|
||||
historySessionId,
|
||||
resolvedPreviewTheme: themeState.resolvedPreviewTheme,
|
||||
previewedOrVisibleThemeId: themeState.previewedOrVisibleThemeId,
|
||||
focusedFontFamilyId: themeState.focusedFontFamilyId,
|
||||
focusedFontFamilyOverridden: themeState.focusedFontFamilyOverridden,
|
||||
focusedFontSize: themeState.focusedFontSize,
|
||||
focusedFontSizeOverridden: themeState.focusedFontSizeOverridden,
|
||||
focusedFontWeight: themeState.focusedFontWeight,
|
||||
focusedFontWeightOverridden: themeState.focusedFontWeightOverridden,
|
||||
focusedThemeOverridden: themeState.focusedThemeOverridden,
|
||||
}), [
|
||||
activeSystemSessionHost,
|
||||
activeTerminalCwd,
|
||||
activeTerminalCwdTrusted,
|
||||
activeTerminalSessionForSystem,
|
||||
activeTerminalSessionIdForSftp,
|
||||
activeWorkspace,
|
||||
effectiveFocusedSessionId,
|
||||
focusedHost,
|
||||
historySessionId,
|
||||
sftpActiveHost,
|
||||
themeState.focusedFontFamilyId,
|
||||
themeState.focusedFontFamilyOverridden,
|
||||
themeState.focusedFontSize,
|
||||
themeState.focusedFontSizeOverridden,
|
||||
themeState.focusedFontWeight,
|
||||
themeState.focusedFontWeightOverridden,
|
||||
themeState.focusedThemeOverridden,
|
||||
themeState.previewedOrVisibleThemeId,
|
||||
themeState.resolvedPreviewTheme,
|
||||
]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
sidePanelLiveStore.update(sidePanelLiveSnapshot);
|
||||
}, [sidePanelLiveSnapshot]);
|
||||
|
||||
const { aiContextsByTabId, resolveAIExecutorContext } = useTerminalAiContexts({
|
||||
hosts: s.hosts,
|
||||
hostsRef: s.hostsRef,
|
||||
portForwardingRules: s.portForwardingRules,
|
||||
portForwardingRulesRef: s.portForwardingRulesRef,
|
||||
mountedAiTabIds: s.mountedAiTabIds,
|
||||
sessionHostsMap,
|
||||
sessions,
|
||||
sessionsRef: s.sessionsRef,
|
||||
terminalContextReadersRef,
|
||||
workspaces: s.workspaces,
|
||||
workspacesRef: s.workspacesRef,
|
||||
});
|
||||
|
||||
const handleTerminalContextReaderChange = React.useCallback((
|
||||
sessionId: string,
|
||||
reader: TerminalContextReader | null,
|
||||
) => {
|
||||
if (reader) {
|
||||
terminalContextReadersRef.current.set(sessionId, reader);
|
||||
} else {
|
||||
terminalContextReadersRef.current.delete(sessionId);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const prevFocusedSessionIdRef = useRef<string | undefined>(undefined);
|
||||
|
||||
useTerminalLayerEffects({
|
||||
activeSidePanelTab,
|
||||
activeSidePanelLayout,
|
||||
activeTabId,
|
||||
activeTabIdRef: s.activeTabIdRef,
|
||||
activeWorkspace,
|
||||
activityTrackedSessions: s.activityTrackedSessions,
|
||||
cancelAnimationFrame,
|
||||
ChunkedEscapeFilter: s.ChunkedEscapeFilter,
|
||||
clearTimeout,
|
||||
document,
|
||||
dropHint,
|
||||
effectiveHosts: s.effectiveHosts,
|
||||
filterTabsMap: s.filterTabsMap,
|
||||
onConnectToHost: s.onConnectToHost,
|
||||
focusedSessionId,
|
||||
getSessionActivityIdsToClear: s.getSessionActivityIdsToClear,
|
||||
handleToggleAiFromTopBar: s.handleToggleAiFromTopBar,
|
||||
handleToggleSystemFromTopBar: s.handleToggleSystemFromTopBar,
|
||||
handleToggleScriptsSidePanel: s.handleToggleScriptsSidePanel,
|
||||
handleToggleSidePanel: s.handleToggleSidePanel,
|
||||
hasNotifiableTerminalOutput: s.hasNotifiableTerminalOutput,
|
||||
isComposeBarOpen: s.isComposeBarOpen,
|
||||
isFocusMode,
|
||||
isTerminalLayerVisible,
|
||||
shouldMeasureTerminalLayerLayout: shouldMeasureTerminalLayerLayout({
|
||||
isTerminalLayerVisible,
|
||||
keepHiddenLayoutActive,
|
||||
workspaceArea,
|
||||
}),
|
||||
lastSidePanelTabRef: s.lastSidePanelTabRef,
|
||||
Map,
|
||||
Math,
|
||||
onSessionData: s.onSessionData,
|
||||
onSplitSessionRef: s.onSplitSessionRef,
|
||||
onToggleBroadcastRef: s.onToggleBroadcastRef,
|
||||
onToggleWorkspaceViewModeRef: s.onToggleWorkspaceViewModeRef,
|
||||
prevFocusedSessionIdRef,
|
||||
refocusActiveTerminalSession: s.refocusActiveTerminalSession,
|
||||
requestAnimationFrame,
|
||||
ResizeObserver,
|
||||
sessionActivityStore: s.sessionActivityStore,
|
||||
sessionHostsMap,
|
||||
sessions,
|
||||
Set,
|
||||
setDropHint,
|
||||
setSftpHostForTab: s.setSftpHostForTab,
|
||||
setSftpInitialLocationForTab: s.setSftpInitialLocationForTab,
|
||||
setSftpPendingUploadsForTab: s.setSftpPendingUploadsForTab,
|
||||
setAiMountedTabIds: s.setAiMountedTabIds,
|
||||
setNotesMountedTabIds: s.setNotesMountedTabIds,
|
||||
setScriptsMountedTabIds: s.setScriptsMountedTabIds,
|
||||
setSystemMountedTabIds: s.setSystemMountedTabIds,
|
||||
setThemeMountedTabIds: s.setThemeMountedTabIds,
|
||||
setSidePanelOpenTabs: s.setSidePanelOpenTabs,
|
||||
setSidePanelLayouts: s.setSidePanelLayouts,
|
||||
setTimeout,
|
||||
setWorkspaceArea,
|
||||
sidePanelPosition: s.sidePanelPosition,
|
||||
sidePanelWidth: s.sidePanelWidth,
|
||||
sftpActiveHost,
|
||||
sftpHostForTab,
|
||||
sftpPaneClosedTabIdsRef: s.sftpPaneClosedTabIdsRef,
|
||||
shouldMarkSessionActivity: s.shouldMarkSessionActivity,
|
||||
sidePanelOpenTabs,
|
||||
splitHorizontalHandlersRef: s.splitHorizontalHandlersRef,
|
||||
splitVerticalHandlersRef: s.splitVerticalHandlersRef,
|
||||
terminalRendererCwdBySessionRef: s.terminalRendererCwdBySessionRef,
|
||||
toggleScriptsSidePanelRef: s.toggleScriptsSidePanelRef,
|
||||
toggleSidePanelRef: s.toggleSidePanelRef,
|
||||
validAIScopeTargetIds: s.validAIScopeTargetIds,
|
||||
validSessionActivityIds: s.validSessionActivityIds,
|
||||
window,
|
||||
workspaceBroadcastHandlersRef: s.workspaceBroadcastHandlersRef,
|
||||
workspaceFocusHandlersRef: s.workspaceFocusHandlersRef,
|
||||
workspaceInnerRef,
|
||||
workspaces: s.workspaces,
|
||||
});
|
||||
|
||||
const ctx = useMemo(() => ({
|
||||
accentMode: s.accentMode,
|
||||
activeHostIdForSidebar,
|
||||
activeResizers,
|
||||
activeSidePanelTab,
|
||||
activeSidePanelLayout,
|
||||
activeTabId,
|
||||
activeTerminalCwd,
|
||||
activeTerminalCwdTrusted,
|
||||
activeTerminalSessionIdForSftp,
|
||||
activeWorkspace,
|
||||
AIChatPanelsHost: s.AIChatPanelsHost,
|
||||
AISidePanelStateRoot: s.AISidePanelStateRoot,
|
||||
aiContextsByTabId,
|
||||
Array: s.Array,
|
||||
Button: s.Button,
|
||||
cn,
|
||||
composeBarThemeColors: themeState.composeBarThemeColors,
|
||||
computeSplitHint,
|
||||
customAccent: s.customAccent,
|
||||
customGroups: s.customGroups,
|
||||
draggingSessionId: s.draggingSessionId,
|
||||
dropHint,
|
||||
editorWordWrap: s.editorWordWrap,
|
||||
effectiveHosts: s.effectiveHosts,
|
||||
findSplitNode,
|
||||
focusedFontFamilyId: themeState.focusedFontFamilyId,
|
||||
focusedFontFamilyOverridden: themeState.focusedFontFamilyOverridden,
|
||||
focusedFontSize: themeState.focusedFontSize,
|
||||
focusedFontSizeOverridden: themeState.focusedFontSizeOverridden,
|
||||
focusedFontWeight: themeState.focusedFontWeight,
|
||||
focusedFontWeightOverridden: themeState.focusedFontWeightOverridden,
|
||||
focusedHost,
|
||||
focusedSessionId,
|
||||
focusedThemeOverridden: themeState.focusedThemeOverridden,
|
||||
FolderTree: s.FolderTree,
|
||||
followAppTerminalTheme: s.followAppTerminalTheme,
|
||||
handleHistoryPaste: s.handleHistoryPaste,
|
||||
handleHistoryDelete: s.handleHistoryDelete,
|
||||
handleHistoryRun: s.handleHistoryRun,
|
||||
handleFocusSidePanelPane: s.handleFocusSidePanelPane,
|
||||
handleMagnifySidePanelPane: s.handleMagnifySidePanelPane,
|
||||
handleRestoreMagnifiedPane: s.handleRestoreMagnifiedPane,
|
||||
handleMagnifyTerminalPane: s.handleMagnifyTerminalPane,
|
||||
handleTerminalPaneInteraction: s.handleTerminalPaneInteraction,
|
||||
handleSplitSidePanelPane: s.handleSplitSidePanelPane,
|
||||
handleCloseSidePanelPane: s.handleCloseSidePanelPane,
|
||||
handleResizeSidePanelSplit: s.handleResizeSidePanelSplit,
|
||||
fontSize: s.fontSize,
|
||||
getTerminalCwd: s.getTerminalCwd,
|
||||
handleAddKnownHost: s.handleAddKnownHost,
|
||||
handleAddSelectionToAI: s.handleAddSelectionToAI,
|
||||
handleBroadcastInput: s.handleBroadcastInput,
|
||||
handleCloseSession: s.handleCloseSession,
|
||||
handleCloseSidePanel: s.handleCloseSidePanel,
|
||||
handleCommandExecuted: s.handleCommandExecuted,
|
||||
handleCommandSubmitted: s.handleCommandSubmitted,
|
||||
handleComposeSend: s.handleComposeSend,
|
||||
handleFontFamilyChangeForFocusedSession: themeState.handleFontFamilyChangeForFocusedSession,
|
||||
handleFontFamilyResetForFocusedSession: themeState.handleFontFamilyResetForFocusedSession,
|
||||
handleFontSizeChangeForFocusedSession: themeState.handleFontSizeChangeForFocusedSession,
|
||||
handleFontSizeResetForFocusedSession: themeState.handleFontSizeResetForFocusedSession,
|
||||
handleFontWeightChangeForFocusedSession: themeState.handleFontWeightChangeForFocusedSession,
|
||||
handleFontWeightResetForFocusedSession: themeState.handleFontWeightResetForFocusedSession,
|
||||
handleOpenAI: s.handleOpenAI,
|
||||
handleOpenNotes: s.handleOpenNotes,
|
||||
handleOpenSystem: s.handleOpenSystem,
|
||||
handleOpenHistory: s.handleOpenHistory,
|
||||
handleOpenScripts: s.handleOpenScripts,
|
||||
activeTerminalSessionForSystem,
|
||||
activeSystemSessionHost,
|
||||
handleOpenSftp: s.handleOpenSftp,
|
||||
handleOpenTheme: s.handleOpenTheme,
|
||||
handleBackFromNotes: s.handleBackFromNotes,
|
||||
handleOpenHostFromNotes: s.handleOpenHostFromNotes,
|
||||
History: s.History,
|
||||
historySessionId,
|
||||
HistorySidePanel: s.HistorySidePanel,
|
||||
// remoteHistory / shellHistory are owned by History side-panel slot stores
|
||||
hibernateHiddenTabs,
|
||||
handleOsDetected: s.handleOsDetected,
|
||||
handlePendingTerminalSelectionConsumed: s.handlePendingTerminalSelectionConsumed,
|
||||
handlePendingUploadHandled: s.handlePendingUploadHandled,
|
||||
handleSessionExit: s.handleSessionExit,
|
||||
handleSftpCurrentPathChange: s.handleSftpCurrentPathChange,
|
||||
handleSftpActiveTransfersChange: s.handleSftpActiveTransfersChange,
|
||||
handleSftpActiveExternalEditsChange: s.handleSftpActiveExternalEditsChange,
|
||||
handleSftpInitialLocationApplied: s.handleSftpInitialLocationApplied,
|
||||
persistSidePanelWidth: s.persistSidePanelWidth,
|
||||
setSidePanelWidth: s.setSidePanelWidth,
|
||||
handleSnippetClickForFocusedSession: s.handleSnippetClickForFocusedSession,
|
||||
handleSnippetFromPanel: s.handleSnippetFromPanel,
|
||||
handleRunScriptFromPanel: s.handleRunScriptFromPanel,
|
||||
handleRunScriptOnWorkspace: s.handleRunScriptOnWorkspace,
|
||||
handleStartRecordingFromPanel: s.handleStartRecordingFromPanel,
|
||||
handleStopScriptRun: s.handleStopScriptRun,
|
||||
handlePauseScriptRun: s.handlePauseScriptRun,
|
||||
handleResumeScriptRun: s.handleResumeScriptRun,
|
||||
handleSnippetExecutorChange: s.handleSnippetExecutorChange,
|
||||
handleBroadcastInterruptPriorityChange: s.handleBroadcastInterruptPriorityChange,
|
||||
handleProgrammaticCommandLogRewriteChange: s.handleProgrammaticCommandLogRewriteChange,
|
||||
handleStatusChange: s.handleStatusChange,
|
||||
handleTerminalCwdChange: s.handleTerminalCwdChange,
|
||||
handleTerminalTitleChange: s.handleTerminalTitleChange,
|
||||
handleTerminalBell: s.handleTerminalBell,
|
||||
handleTerminalOutput: s.handleTerminalOutput,
|
||||
handleTerminalDataCapture: s.handleTerminalDataCapture,
|
||||
handleTerminalContextReaderChange,
|
||||
handleTerminalFontSizeChange: s.handleTerminalFontSizeChange,
|
||||
handleThemeChangeForFocusedSession: themeState.handleThemeChangeForFocusedSession,
|
||||
handleThemeResetForFocusedSession: themeState.handleThemeResetForFocusedSession,
|
||||
handleToggleSftpFromBar: s.handleToggleSftpFromBar,
|
||||
handleToggleWorkspaceComposeBar: s.handleToggleWorkspaceComposeBar,
|
||||
handleUpdateHost: s.handleUpdateHost,
|
||||
handleWorkspaceDrop,
|
||||
hosts: s.hosts,
|
||||
hotkeyScheme: s.hotkeyScheme,
|
||||
disableTerminalFontZoom: s.disableTerminalFontZoom,
|
||||
restoreTerminalCwd: s.restoreTerminalCwd,
|
||||
identities: s.identities,
|
||||
isBroadcastEnabled: s.isBroadcastEnabled,
|
||||
isComposeBarOpen: s.isComposeBarOpen,
|
||||
isFocusMode,
|
||||
isSidePanelOpenForCurrentTab,
|
||||
isTerminalLayerVisible,
|
||||
keyBindings: s.keyBindings,
|
||||
keys: s.keys,
|
||||
knownHosts: s.knownHosts,
|
||||
MessageSquare: s.MessageSquare,
|
||||
magnifiedPane: s.magnifiedPane,
|
||||
mountedAiTabIds: s.mountedAiTabIds,
|
||||
mountedSftpTabIds: s.mountedSftpTabIds,
|
||||
notesMountedTabIds: s.notesMountedTabIds,
|
||||
notesOpenNoteByTab: s.notesOpenNoteByTab,
|
||||
NotesManager: s.NotesManager,
|
||||
scriptsMountedTabIds: s.scriptsMountedTabIds,
|
||||
systemMountedTabIds: s.systemMountedTabIds,
|
||||
themeMountedTabIds: s.themeMountedTabIds,
|
||||
onConnectToHost: s.onConnectToHost,
|
||||
onCreateLocalTerminal: s.onCreateLocalTerminal,
|
||||
onHotkeyAction: s.onHotkeyAction,
|
||||
onReorderWorkspaceSessions: s.onReorderWorkspaceSessions,
|
||||
onReorderTabs: s.onReorderTabs,
|
||||
onCopySession: s.onCopySession,
|
||||
onDuplicateSession: s.onDuplicateSession,
|
||||
onCopySessionToNewWindow: s.onCopySessionToNewWindow,
|
||||
onUpdateSessionRestoreCwd: s.onUpdateSessionRestoreCwd,
|
||||
onUpdateSessionDynamicTitle: s.onUpdateSessionDynamicTitle,
|
||||
onUpdateSessionCodingCliProvider: s.onUpdateSessionCodingCliProvider,
|
||||
onRequestAddToWorkspace: s.onRequestAddToWorkspace,
|
||||
onAppendHostToWorkspace: s.onAppendHostToWorkspace,
|
||||
onSetWorkspaceFocusedSession: s.onSetWorkspaceFocusedSession,
|
||||
onStartSessionRename: s.onStartSessionRename,
|
||||
onSubmitSessionRename: s.onSubmitSessionRename,
|
||||
onRemoveSessionFromWorkspace: s.onRemoveSessionFromWorkspace,
|
||||
onOpenVaultNoteFromChat: s.onOpenVaultNoteFromChat,
|
||||
onOpenVaultHostFromChat: s.onOpenVaultHostFromChat,
|
||||
onOpenVaultSectionFromChat: s.onOpenVaultSectionFromChat,
|
||||
onOpenVaultSnippetFromChat: s.onOpenVaultSnippetFromChat,
|
||||
onStartSessionDrag: s.onStartSessionDrag,
|
||||
onEndSessionDrag: s.onEndSessionDrag,
|
||||
onSplitSession: s.onSplitSession,
|
||||
onToggleWorkspaceViewMode: s.onToggleWorkspaceViewMode,
|
||||
Palette: s.Palette,
|
||||
PanelLeft: s.PanelLeft,
|
||||
PanelRight: s.PanelRight,
|
||||
pendingTerminalSelectionForAI: s.pendingTerminalSelectionForAI,
|
||||
previewedOrVisibleThemeId: themeState.previewedOrVisibleThemeId,
|
||||
refocusActiveTerminalSession: s.refocusActiveTerminalSession,
|
||||
refocusTerminalSession: s.refocusTerminalSession,
|
||||
resizing,
|
||||
resolveAIExecutorContext,
|
||||
resolvedPreviewTheme: themeState.resolvedPreviewTheme,
|
||||
ScriptsSidePanel: s.ScriptsSidePanel,
|
||||
sessionChainHostsMap: s.sessionChainHostsMap,
|
||||
sessionHostsMap,
|
||||
resolvedSessionHostIds: s.resolvedSessionHostIds,
|
||||
sessionLogConfig: s.sessionLogConfig,
|
||||
sessionSudoAutofillPasswordsMap: s.sessionSudoAutofillPasswordsMap,
|
||||
sessionSudoAutofillCandidatesMap: s.sessionSudoAutofillCandidatesMap,
|
||||
sessions,
|
||||
setDropHint,
|
||||
setEditorWordWrap: s.setEditorWordWrap,
|
||||
setIsComposeBarOpen: s.setIsComposeBarOpen,
|
||||
setResizing,
|
||||
showHostTreeSidebar,
|
||||
setSidePanelPosition: s.setSidePanelPosition,
|
||||
setSftpFollowTerminalCwd: s.setSftpFollowTerminalCwd,
|
||||
sftpActiveHost,
|
||||
sftpHostForTab,
|
||||
sftpAutoSync: s.sftpAutoSync,
|
||||
sftpDefaultViewMode: s.sftpDefaultViewMode,
|
||||
sftpDoubleClickBehavior: s.sftpDoubleClickBehavior,
|
||||
sftpFollowTerminalCwd: s.sftpFollowTerminalCwd,
|
||||
sftpInitialLocationForTab: s.sftpInitialLocationForTab,
|
||||
sftpPendingUploadsForTab: s.sftpPendingUploadsForTab,
|
||||
sftpPaneClosedTabIdsRef: s.sftpPaneClosedTabIdsRef,
|
||||
sftpShowHiddenFiles: s.sftpShowHiddenFiles,
|
||||
SftpSidePanel: s.SftpSidePanel,
|
||||
sftpUseCompressedUpload: s.sftpUseCompressedUpload,
|
||||
sidePanelPosition: s.sidePanelPosition,
|
||||
sidePanelWidth: s.sidePanelWidth,
|
||||
sidePanelOpenTabs,
|
||||
sidePanelLayouts,
|
||||
snippetPackages: s.snippetPackages,
|
||||
snippets: s.snippets,
|
||||
updateSnippetPackages: s.updateSnippetPackages,
|
||||
updateSnippets: s.updateSnippets,
|
||||
splitHorizontalHandlersRef: s.splitHorizontalHandlersRef,
|
||||
splitVerticalHandlersRef: s.splitVerticalHandlersRef,
|
||||
sshDebugLogsEnabled: s.sshDebugLogsEnabled,
|
||||
t: s.t,
|
||||
TerminalComposeBar: s.TerminalComposeBar,
|
||||
terminalFontFamilyId: s.terminalFontFamilyId,
|
||||
TerminalPanesHost: s.TerminalPanesHost,
|
||||
terminalSettings: s.terminalSettings,
|
||||
terminalTheme: s.terminalTheme,
|
||||
terminalThemeId: s.terminalThemeId,
|
||||
resolveSessionAppearance: s.resolveSessionAppearance,
|
||||
hostMap: s.hostMap,
|
||||
ThemeSidePanel: s.ThemeSidePanel,
|
||||
Tooltip: s.Tooltip,
|
||||
TooltipContent: s.TooltipContent,
|
||||
TooltipTrigger: s.TooltipTrigger,
|
||||
updateHosts: s.updateHosts,
|
||||
validAIScopeTargetIds: s.validAIScopeTargetIds,
|
||||
workspaceBroadcastHandlersRef: s.workspaceBroadcastHandlersRef,
|
||||
workspaceById,
|
||||
isGlobalBroadcastEnabled: s.isGlobalBroadcastEnabled,
|
||||
canUseGlobalBroadcast: s.canUseGlobalBroadcast,
|
||||
onToggleGlobalBroadcast: s.onToggleGlobalBroadcastRef.current,
|
||||
// AI scope maintenance (merge/dissolve handoff) needs the full list; do not
|
||||
// rely on workspaceById alone — SidePanelStateRoot reads ctx.workspaces.
|
||||
workspaces: s.workspaces,
|
||||
workspaceFocusHandlersRef: s.workspaceFocusHandlersRef,
|
||||
workspaceInnerRef,
|
||||
workspaceOuterRef,
|
||||
workspaceOverlayRef,
|
||||
workspaceRectsById,
|
||||
X: s.X,
|
||||
Zap: s.Zap,
|
||||
// stableRef fields are intentionally omitted from deps — they update every parent render.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}), [
|
||||
activeHostIdForSidebar,
|
||||
activeResizers,
|
||||
activeSidePanelLayout,
|
||||
activeSidePanelTab,
|
||||
activeTabId,
|
||||
activeTerminalCwd,
|
||||
activeTerminalCwdTrusted,
|
||||
activeTerminalSessionIdForSftp,
|
||||
activeWorkspace,
|
||||
aiContextsByTabId,
|
||||
computeSplitHint,
|
||||
dropHint,
|
||||
focusedHost,
|
||||
focusedSessionId,
|
||||
s.restoreTerminalCwd,
|
||||
handleWorkspaceDrop,
|
||||
handleTerminalContextReaderChange,
|
||||
hibernateHiddenTabs,
|
||||
historySessionId,
|
||||
isFocusMode,
|
||||
isSidePanelOpenForCurrentTab,
|
||||
isTerminalLayerVisible,
|
||||
s.magnifiedPane,
|
||||
resizing,
|
||||
resolveAIExecutorContext,
|
||||
sessionHostsMap,
|
||||
sidePanelLayouts,
|
||||
s.resolvedSessionHostIds,
|
||||
sessions,
|
||||
s.terminalSettings,
|
||||
showHostTreeSidebar,
|
||||
sftpActiveHost,
|
||||
s.sftpFollowTerminalCwd,
|
||||
themeState,
|
||||
workspaceById,
|
||||
s.workspaces,
|
||||
workspaceInnerRef,
|
||||
workspaceOuterRef,
|
||||
workspaceOverlayRef,
|
||||
workspaceRectsById,
|
||||
s.terminalTheme,
|
||||
s.resolveSessionAppearance,
|
||||
s.hostMap,
|
||||
s.isGlobalBroadcastEnabled,
|
||||
s.canUseGlobalBroadcast,
|
||||
s.onToggleGlobalBroadcastRef,
|
||||
]);
|
||||
|
||||
return <TerminalLayerView ctx={ctx} />;
|
||||
}
|
||||
Reference in New Issue
Block a user