fix(web): only connect terminal ws when process is running

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
tao.chen
2026-07-03 16:15:51 +08:00
co-authored by Claude
parent 11ac0299a6
commit 75e807c154
2 changed files with 36 additions and 8 deletions
+13 -4
View File
@@ -2,7 +2,7 @@ import { FitAddon } from "@xterm/addon-fit";
import { Terminal } from "@xterm/xterm"; import { Terminal } from "@xterm/xterm";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useProcessWebSocket } from "@/lib/api/process"; import { useProcessStatus, useProcessWebSocket } from "@/lib/api/process";
interface TerminalPanelProps { interface TerminalPanelProps {
workspaceId: string | null; workspaceId: string | null;
@@ -16,7 +16,12 @@ export function TerminalPanel({ workspaceId }: TerminalPanelProps) {
const isDeadRef = useRef(false); const isDeadRef = useRef(false);
const [failed, setFailed] = useState(false); const [failed, setFailed] = useState(false);
const [isDead, setIsDead] = useState(false); const [isDead, setIsDead] = useState(false);
const { status, send, onData } = useProcessWebSocket(workspaceId); const { data: processStatus } = useProcessStatus(workspaceId);
const processRunning = processStatus?.running === true;
const { status, send, onData } = useProcessWebSocket(
workspaceId,
processRunning,
);
// Initialize xterm once and keep it alive across WS status changes. // Initialize xterm once and keep it alive across WS status changes.
useEffect(() => { useEffect(() => {
@@ -148,11 +153,15 @@ export function TerminalPanel({ workspaceId }: TerminalPanelProps) {
? "connecting…" ? "connecting…"
: null; : null;
const hint = processRunning
? statusHint
: "process not running — start it from the toolbar";
return ( return (
<section className="flex h-full w-full flex-col overflow-hidden bg-[#171717]"> <section className="flex h-full w-full flex-col overflow-hidden bg-[#171717]">
{statusHint && ( {hint && (
<div className="flex h-6 shrink-0 items-center px-2 text-[11px] text-muted-foreground"> <div className="flex h-6 shrink-0 items-center px-2 text-[11px] text-muted-foreground">
{statusHint} {hint}
</div> </div>
)} )}
<div ref={containerRef} className="min-h-0 flex-1 p-2" /> <div ref={containerRef} className="min-h-0 flex-1 p-2" />
+23 -4
View File
@@ -97,9 +97,10 @@ export interface ProcessWebSocket {
export function useProcessWebSocket( export function useProcessWebSocket(
workspaceId: string | null, workspaceId: string | null,
enabled: boolean = true,
): ProcessWebSocket { ): ProcessWebSocket {
const [status, setStatus] = useState<ProcessWebSocketStatus>(() => const [status, setStatus] = useState<ProcessWebSocketStatus>(() =>
workspaceId ? "connecting" : "idle", workspaceId && enabled ? "connecting" : "idle",
); );
const statusRef = useRef(status); const statusRef = useRef(status);
useEffect(() => { useEffect(() => {
@@ -133,14 +134,26 @@ export function useProcessWebSocket(
}, [clearReconnectTimeout]); }, [clearReconnectTimeout]);
useEffect(() => { useEffect(() => {
if (!workspaceId) { if (!workspaceId || !enabled) {
setStatus("idle"); setStatus("idle");
clearReconnectTimeout();
const socket = socketRef.current;
if (socket) {
socket.onclose = null;
socket.close();
socketRef.current = null;
}
reconnectAttemptsRef.current = 0;
lastFrameWasExitRef.current = false;
intentionalCloseRef.current = false;
serverCleanCloseRef.current = false;
return; return;
} }
reconnectAttemptsRef.current = 0; reconnectAttemptsRef.current = 0;
lastFrameWasExitRef.current = false; lastFrameWasExitRef.current = false;
intentionalCloseRef.current = false; intentionalCloseRef.current = false;
serverCleanCloseRef.current = false;
const openSocket = () => { const openSocket = () => {
const wsUrl = const wsUrl =
@@ -211,9 +224,15 @@ export function useProcessWebSocket(
openSocket(); openSocket();
return () => { return () => {
closeSocket(); clearReconnectTimeout();
const socket = socketRef.current;
if (socket) {
socket.onclose = null;
socket.close();
socketRef.current = null;
}
}; };
}, [workspaceId, closeSocket]); }, [workspaceId, enabled, clearReconnectTimeout]);
const send = useCallback((data: string) => { const send = useCallback((data: string) => {
const socket = socketRef.current; const socket = socketRef.current;