import { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import '@xterm/xterm/css/xterm.css'; import type { Project, ProjectSession } from '../../../types/app'; import { SHELL_RESTART_DELAY_MS } from '../constants/constants'; import { useShellRuntime } from '../hooks/useShellRuntime'; import { getSessionDisplayName } from '../utils/auth'; import ShellConnectionOverlay from './subcomponents/ShellConnectionOverlay'; import ShellEmptyState from './subcomponents/ShellEmptyState'; import ShellHeader from './subcomponents/ShellHeader'; import ShellMinimalView from './subcomponents/ShellMinimalView'; import TerminalShortcutsPanel from './subcomponents/TerminalShortcutsPanel'; type ShellProps = { selectedProject?: Project | null; selectedSession?: ProjectSession | null; initialCommand?: string | null; isPlainShell?: boolean; onProcessComplete?: ((exitCode: number) => void) | null; minimal?: boolean; autoConnect?: boolean; isActive?: boolean; }; export default function Shell({ selectedProject = null, selectedSession = null, initialCommand = null, isPlainShell = false, onProcessComplete = null, minimal = false, autoConnect = false, isActive, }: ShellProps) { const { t } = useTranslation('chat'); const [isRestarting, setIsRestarting] = useState(false); // Keep the public API stable for existing callers that still pass `isActive`. void isActive; const { terminalContainerRef, terminalRef, wsRef, isConnected, isInitialized, isConnecting, authUrl, authUrlVersion, connectToShell, disconnectFromShell, openAuthUrlInBrowser, copyAuthUrlToClipboard, } = useShellRuntime({ selectedProject, selectedSession, initialCommand, isPlainShell, minimal, autoConnect, isRestarting, onProcessComplete, }); const sessionDisplayName = useMemo(() => getSessionDisplayName(selectedSession), [selectedSession]); const sessionDisplayNameShort = useMemo( () => (sessionDisplayName ? sessionDisplayName.slice(0, 30) : null), [sessionDisplayName], ); const sessionDisplayNameLong = useMemo( () => (sessionDisplayName ? sessionDisplayName.slice(0, 50) : null), [sessionDisplayName], ); const handleRestartShell = useCallback(() => { setIsRestarting(true); window.setTimeout(() => { setIsRestarting(false); }, SHELL_RESTART_DELAY_MS); }, []); if (!selectedProject) { return ( ); } if (minimal) { return ( ); } const readyDescription = isPlainShell ? t('shell.runCommand', { command: initialCommand || t('shell.defaultCommand'), projectName: selectedProject.displayName, }) : selectedSession ? t('shell.resumeSession', { displayName: sessionDisplayNameLong }) : t('shell.startSession'); const connectingDescription = isPlainShell ? t('shell.runCommand', { command: initialCommand || t('shell.defaultCommand'), projectName: selectedProject.displayName, }) : t('shell.startCli', { projectName: selectedProject.displayName }); const overlayMode = !isInitialized ? 'loading' : isConnecting ? 'connecting' : !isConnected ? 'connect' : null; const overlayDescription = overlayMode === 'connecting' ? connectingDescription : readyDescription; return (
{overlayMode && ( )}
); }