From 11bc477f1faae07cd9cb2fdb9ca4814f4deec26d Mon Sep 17 00:00:00 2001 From: MacRimi Date: Thu, 11 Dec 2025 19:06:53 +0100 Subject: [PATCH] Update script-terminal-modal.tsx --- AppImage/components/script-terminal-modal.tsx | 215 ++++++++++++------ 1 file changed, 150 insertions(+), 65 deletions(-) diff --git a/AppImage/components/script-terminal-modal.tsx b/AppImage/components/script-terminal-modal.tsx index 1c1973f..ac5a3db 100644 --- a/AppImage/components/script-terminal-modal.tsx +++ b/AppImage/components/script-terminal-modal.tsx @@ -58,6 +58,8 @@ export function ScriptTerminalModal({ const [currentInteraction, setCurrentInteraction] = useState(null) const [interactionInput, setInteractionInput] = useState("") const checkConnectionInterval = useRef(null) + const reconnectTimeoutRef = useRef(null) + const reconnectAttemptsRef = useRef(0) const [isMobile, setIsMobile] = useState(false) const [isTablet, setIsTablet] = useState(false) @@ -69,11 +71,103 @@ export function ScriptTerminalModal({ const resizeBarRef = useRef(null) const modalHeightRef = useRef(600) // Ref para mantener el valor actualizado - // Debug visual para tablets - const [debugInfo, setDebugInfo] = useState([]) - const terminalContainerRef = useRef(null) + const attemptReconnect = useCallback(() => { + if (!isOpen || isComplete || reconnectAttemptsRef.current >= 3) { + return + } + + reconnectAttemptsRef.current++ + setConnectionStatus("connecting") + + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current) + } + + reconnectTimeoutRef.current = setTimeout(() => { + if (wsRef.current?.readyState !== WebSocket.OPEN && termRef.current) { + // Cerrar conexión anterior si existe + if (wsRef.current) { + wsRef.current.close() + } + + // Crear nueva conexión + const wsUrl = getScriptWebSocketUrl(sessionIdRef.current) + const ws = new WebSocket(wsUrl) + wsRef.current = ws + + ws.onopen = () => { + setConnectionStatus("online") + reconnectAttemptsRef.current = 0 + + // Reiniciar el script + const initMessage = { + script_path: scriptPath, + params: { + EXECUTION_MODE: "web", + }, + } + ws.send(JSON.stringify(initMessage)) + + // Redimensionar terminal + setTimeout(() => { + if (fitAddonRef.current && termRef.current && ws.readyState === WebSocket.OPEN) { + const cols = termRef.current.cols + const rows = termRef.current.rows + ws.send(JSON.stringify({ type: "resize", cols, rows })) + } + }, 100) + } + + ws.onmessage = (event) => { + try { + const msg = JSON.parse(event.data) + if (msg.type === "web_interaction" && msg.interaction) { + setIsWaitingNextInteraction(false) + if (waitingTimeoutRef.current) { + clearTimeout(waitingTimeoutRef.current) + } + setCurrentInteraction({ + type: msg.interaction.type, + id: msg.interaction.id, + title: msg.interaction.title || "", + message: msg.interaction.message || "", + options: msg.interaction.options, + default: msg.interaction.default, + }) + return + } + if (msg.type === "error") { + termRef.current?.writeln(`\x1b[31m${msg.message}\x1b[0m`) + return + } + } catch {} + termRef.current?.write(event.data) + setIsWaitingNextInteraction(false) + if (waitingTimeoutRef.current) { + clearTimeout(waitingTimeoutRef.current) + } + } + + ws.onerror = () => { + setConnectionStatus("offline") + } + + ws.onclose = (event) => { + setConnectionStatus("offline") + if (!isComplete && reconnectAttemptsRef.current < 3) { + // Intentar reconectar después de 2 segundos + reconnectTimeoutRef.current = setTimeout(attemptReconnect, 2000) + } else { + setIsComplete(true) + setExitCode(event.code === 1000 ? 0 : 1) + } + } + } + }, 1000) + }, [isOpen, isComplete, scriptPath]) + const sendKey = useCallback((key: string) => { if (!termRef.current) return @@ -293,6 +387,9 @@ export function ScriptTerminalModal({ if (waitingTimeoutRef.current) { clearTimeout(waitingTimeoutRef.current) } + if (reconnectTimeoutRef.current) { + clearTimeout(reconnectTimeoutRef.current) + } if (wsRef.current) { wsRef.current.close() wsRef.current = null @@ -303,6 +400,7 @@ export function ScriptTerminalModal({ } sessionIdRef.current = Math.random().toString(36).substring(2, 8) + reconnectAttemptsRef.current = 0 setIsComplete(false) setExitCode(null) setInteractionInput("") @@ -326,10 +424,32 @@ export function ScriptTerminalModal({ const handleResize = () => updateDeviceType() window.addEventListener("resize", handleResize) + // Detectar cuando la app vuelve a estar visible (desbloqueo de pantalla) + const handleVisibilityChange = () => { + if (!document.hidden && isOpen) { + // La pantalla se desbloqueó, verificar conexión + if (wsRef.current?.readyState !== WebSocket.OPEN && !isComplete) { + attemptReconnect() + } + } + } + + // Detectar cuando la app vuelve desde background + const handleFocus = () => { + if (isOpen && wsRef.current?.readyState !== WebSocket.OPEN && !isComplete) { + attemptReconnect() + } + } + + document.addEventListener("visibilitychange", handleVisibilityChange) + window.addEventListener("focus", handleFocus) + return () => { window.removeEventListener("resize", handleResize) + document.removeEventListener("visibilitychange", handleVisibilityChange) + window.removeEventListener("focus", handleFocus) } - }, []) + }, [isOpen, isComplete, attemptReconnect]) const getScriptWebSocketUrl = (sid: string): string => { if (typeof window === "undefined") { @@ -394,80 +514,54 @@ export function ScriptTerminalModal({ e.preventDefault() e.stopPropagation() - const debugMsg = `[${new Date().toLocaleTimeString()}] Resize start - Type: ${e.type}, isMobile: ${isMobile}, isTablet: ${isTablet}` - console.log(debugMsg) - setDebugInfo(prev => [...prev.slice(-4), debugMsg]) - setIsResizing(true) - // Detectar si es touch o mouse const clientY = "touches" in e ? e.touches[0].clientY : e.clientY const startY = clientY const startHeight = modalHeight - const debugMsg2 = `Start Y: ${startY}, Start height: ${startHeight}` - console.log(debugMsg2) - setDebugInfo(prev => [...prev.slice(-4), debugMsg2]) - - let moveCount = 0 - const handleMove = (moveEvent: MouseEvent | TouchEvent) => { - moveEvent.preventDefault() - moveEvent.stopPropagation() - const currentY = "touches" in moveEvent ? moveEvent.touches[0].clientY : moveEvent.clientY const deltaY = currentY - startY - const newHeight = Math.max(300, Math.min(window.innerHeight - 100, startHeight + deltaY)) + const newHeight = Math.max(300, Math.min(window.innerHeight - 50, startHeight + deltaY)) - moveCount++ - if (moveCount % 5 === 0) { - console.log(`Move #${moveCount} - currentY: ${currentY}, deltaY: ${deltaY}, newHeight: ${newHeight}`) - } - - // Actualizar tanto el state como el ref + // Actualizar directamente sin requestAnimationFrame para mayor fluidez modalHeightRef.current = newHeight setModalHeight(newHeight) - - if (fitAddonRef.current && termRef.current && wsRef.current?.readyState === WebSocket.OPEN) { - setTimeout(() => { - if (fitAddonRef.current && termRef.current) { - fitAddonRef.current.fit() - wsRef.current?.send( - JSON.stringify({ - type: "resize", - cols: termRef.current.cols, - rows: termRef.current.rows, - }), - ) - } - }, 10) - } } const handleEnd = () => { - // Usar el ref que tiene el valor actualizado const finalHeight = modalHeightRef.current - const debugMsg3 = `Resize end - Final height: ${finalHeight}, Total moves: ${moveCount}` - console.log(debugMsg3) - setDebugInfo(prev => [...prev.slice(-4), debugMsg3]) - setIsResizing(false) - document.removeEventListener("mousemove", handleMove as any, false) - document.removeEventListener("mouseup", handleEnd, false) - document.removeEventListener("touchmove", handleMove as any, false) - document.removeEventListener("touchend", handleEnd, false) - document.removeEventListener("touchcancel", handleEnd, false) + document.removeEventListener("mousemove", handleMove as any) + document.removeEventListener("mouseup", handleEnd) + document.removeEventListener("touchmove", handleMove as any) + document.removeEventListener("touchend", handleEnd) + document.removeEventListener("touchcancel", handleEnd) - // Guardar usando el valor del ref localStorage.setItem("scriptModalHeight", finalHeight.toString()) + + // Ajustar terminal al final del resize + if (fitAddonRef.current && termRef.current && wsRef.current?.readyState === WebSocket.OPEN) { + setTimeout(() => { + fitAddonRef.current?.fit() + wsRef.current?.send( + JSON.stringify({ + type: "resize", + cols: termRef.current.cols, + rows: termRef.current.rows, + }), + ) + }, 100) + } } - document.addEventListener("mousemove", handleMove as any, false) - document.addEventListener("mouseup", handleEnd, false) - document.addEventListener("touchmove", handleMove as any, { passive: false, capture: false }) - document.addEventListener("touchend", handleEnd, false) - document.addEventListener("touchcancel", handleEnd, false) + document.addEventListener("mousemove", handleMove as any) + document.addEventListener("mouseup", handleEnd) + document.addEventListener("touchmove", handleMove as any, { passive: false }) + document.addEventListener("touchend", handleEnd) + document.addEventListener("touchcancel", handleEnd) } const sendCommand = (command: string) => { @@ -498,15 +592,6 @@ export function ScriptTerminalModal({ - {/* Debug panel - solo visible en tablets */} - {isTablet && debugInfo.length > 0 && ( -
- {debugInfo.map((info, i) => ( -
{info}
- ))} -
- )} -