mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-26 21:55:50 +08:00
123 lines
3.3 KiB
TypeScript
123 lines
3.3 KiB
TypeScript
import { useCallback, useEffect, useRef } from 'react';
|
|
import type { FitAddon } from '@xterm/addon-fit';
|
|
import type { Terminal } from '@xterm/xterm';
|
|
|
|
import type { UseShellRuntimeOptions, UseShellRuntimeResult } from '../types/types';
|
|
|
|
import { useShellConnection } from './useShellConnection';
|
|
import { useShellTerminal } from './useShellTerminal';
|
|
|
|
export function useShellRuntime({
|
|
selectedProject,
|
|
selectedSession,
|
|
initialCommand,
|
|
isPlainShell,
|
|
minimal,
|
|
autoConnect,
|
|
isRestarting,
|
|
onProcessComplete,
|
|
onOutputRef,
|
|
}: UseShellRuntimeOptions): UseShellRuntimeResult {
|
|
const terminalContainerRef = useRef<HTMLDivElement>(null);
|
|
const terminalRef = useRef<Terminal | null>(null);
|
|
const fitAddonRef = useRef<FitAddon | null>(null);
|
|
const wsRef = useRef<WebSocket | null>(null);
|
|
|
|
const selectedProjectRef = useRef(selectedProject);
|
|
const selectedSessionRef = useRef(selectedSession);
|
|
const initialCommandRef = useRef(initialCommand);
|
|
const isPlainShellRef = useRef(isPlainShell);
|
|
const onProcessCompleteRef = useRef(onProcessComplete);
|
|
const lastSessionIdRef = useRef<string | null>(selectedSession?.id ?? null);
|
|
|
|
// Keep mutable values in refs so websocket handlers always read current data.
|
|
useEffect(() => {
|
|
selectedProjectRef.current = selectedProject;
|
|
selectedSessionRef.current = selectedSession;
|
|
initialCommandRef.current = initialCommand;
|
|
isPlainShellRef.current = isPlainShell;
|
|
onProcessCompleteRef.current = onProcessComplete;
|
|
}, [selectedProject, selectedSession, initialCommand, isPlainShell, onProcessComplete]);
|
|
|
|
const closeSocket = useCallback(() => {
|
|
const activeSocket = wsRef.current;
|
|
if (!activeSocket) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
activeSocket.readyState === WebSocket.OPEN ||
|
|
activeSocket.readyState === WebSocket.CONNECTING
|
|
) {
|
|
activeSocket.close();
|
|
}
|
|
|
|
wsRef.current = null;
|
|
}, []);
|
|
|
|
const { isInitialized, clearTerminalScreen, disposeTerminal } = useShellTerminal({
|
|
terminalContainerRef,
|
|
terminalRef,
|
|
fitAddonRef,
|
|
wsRef,
|
|
selectedProject,
|
|
minimal,
|
|
isRestarting,
|
|
closeSocket,
|
|
});
|
|
|
|
const { isConnected, isConnecting, connectToShell, disconnectFromShell } = useShellConnection({
|
|
wsRef,
|
|
terminalRef,
|
|
fitAddonRef,
|
|
selectedProjectRef,
|
|
selectedSessionRef,
|
|
initialCommandRef,
|
|
isPlainShellRef,
|
|
onProcessCompleteRef,
|
|
isInitialized,
|
|
autoConnect,
|
|
closeSocket,
|
|
clearTerminalScreen,
|
|
onOutputRef,
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (!isRestarting) {
|
|
return;
|
|
}
|
|
|
|
disconnectFromShell();
|
|
disposeTerminal();
|
|
}, [disconnectFromShell, disposeTerminal, isRestarting]);
|
|
|
|
useEffect(() => {
|
|
if (selectedProject) {
|
|
return;
|
|
}
|
|
|
|
disconnectFromShell();
|
|
disposeTerminal();
|
|
}, [disconnectFromShell, disposeTerminal, selectedProject]);
|
|
|
|
useEffect(() => {
|
|
const currentSessionId = selectedSession?.id ?? null;
|
|
if (lastSessionIdRef.current !== currentSessionId && isInitialized) {
|
|
disconnectFromShell();
|
|
}
|
|
|
|
lastSessionIdRef.current = currentSessionId;
|
|
}, [disconnectFromShell, isInitialized, selectedSession?.id]);
|
|
|
|
return {
|
|
terminalContainerRef,
|
|
terminalRef,
|
|
wsRef,
|
|
isConnected,
|
|
isInitialized,
|
|
isConnecting,
|
|
connectToShell,
|
|
disconnectFromShell,
|
|
};
|
|
}
|