({
open: isOpen,
onClose,
scriptPath,
title,
description,
params = { EXECUTION_MODE: "web" },
}: ScriptTerminalModalProps)
| 52 | } |
| 53 | |
| 54 | export function ScriptTerminalModal({ |
| 55 | open: isOpen, |
| 56 | onClose, |
| 57 | scriptPath, |
| 58 | title, |
| 59 | description, |
| 60 | params = { EXECUTION_MODE: "web" }, |
| 61 | }: ScriptTerminalModalProps) { |
| 62 | const termRef = useRef<any>(null) |
| 63 | const wsRef = useRef<WebSocket | null>(null) |
| 64 | // Mirrors `isOpen` for use inside async closures (initializeTerminal) |
| 65 | // after dynamic imports resolve — captures the latest value without |
| 66 | // re-binding the closure. |
| 67 | const isOpenRef = useRef<boolean>(false) |
| 68 | const fitAddonRef = useRef<any>(null) |
| 69 | const sessionIdRef = useRef<string>(Math.random().toString(36).substring(2, 8)) |
| 70 | |
| 71 | const [connectionStatus, setConnectionStatus] = useState<"connecting" | "online" | "offline">("connecting") |
| 72 | const [isComplete, setIsComplete] = useState(false) |
| 73 | const [currentInteraction, setCurrentInteraction] = useState<WebInteraction | null>(null) |
| 74 | const [interactionInput, setInteractionInput] = useState("") |
| 75 | const checkConnectionInterval = useRef<NodeJS.Timeout | null>(null) |
| 76 | const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null) |
| 77 | const reconnectAttemptsRef = useRef(0) |
| 78 | const keepAliveIntervalRef = useRef<NodeJS.Timeout | null>(null) |
| 79 | const [isMobile, setIsMobile] = useState(false) |
| 80 | const [isTablet, setIsTablet] = useState(false) |
| 81 | |
| 82 | const [isWaitingNextInteraction, setIsWaitingNextInteraction] = useState(false) |
| 83 | const waitingTimeoutRef = useRef<NodeJS.Timeout | null>(null) |
| 84 | |
| 85 | const [modalHeight, setModalHeight] = useState(600) |
| 86 | const [isResizing, setIsResizing] = useState(false) |
| 87 | const resizeBarRef = useRef<HTMLDivElement>(null) |
| 88 | const modalHeightRef = useRef(600) |
| 89 | |
| 90 | const terminalContainerRef = useRef<HTMLDivElement>(null) |
| 91 | const paramsRef = useRef(params) |
| 92 | |
| 93 | // Keep paramsRef updated with latest params |
| 94 | useEffect(() => { |
| 95 | paramsRef.current = params |
| 96 | }, [params]) |
| 97 | |
| 98 | const attemptReconnect = useCallback(() => { |
| 99 | if (!isOpen || isComplete || reconnectAttemptsRef.current >= 3) { |
| 100 | return |
| 101 | } |
| 102 | |
| 103 | reconnectAttemptsRef.current++ |
| 104 | setConnectionStatus("connecting") |
| 105 | |
| 106 | if (reconnectTimeoutRef.current) { |
| 107 | clearTimeout(reconnectTimeoutRef.current) |
| 108 | } |
| 109 | |
| 110 | reconnectTimeoutRef.current = setTimeout(async () => { |
| 111 | if (wsRef.current?.readyState !== WebSocket.OPEN && termRef.current) { |
nothing calls this directly
no test coverage detected