From 1c83e5eeab798724c8d126f47f41883d1c506e73 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Sat, 6 Dec 2025 18:36:34 +0100 Subject: [PATCH] Update AppImage --- AppImage/components/script-terminal-modal.tsx | 178 +++++++++++++++--- AppImage/components/terminal-panel.tsx | 164 ++++++++-------- AppImage/scripts/flask_terminal_routes.py | 44 +---- 3 files changed, 236 insertions(+), 150 deletions(-) diff --git a/AppImage/components/script-terminal-modal.tsx b/AppImage/components/script-terminal-modal.tsx index 808b2f1..3368b38 100644 --- a/AppImage/components/script-terminal-modal.tsx +++ b/AppImage/components/script-terminal-modal.tsx @@ -1,13 +1,16 @@ "use client" +import type React from "react" + import { useState, useEffect, useRef } from "react" import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" -import { X, CheckCircle2, XCircle, Loader2 } from "lucide-react" +import { CheckCircle2, XCircle, Loader2, Activity, GripHorizontal } from "lucide-react" import { TerminalPanel } from "./terminal-panel" import { API_PORT } from "@/lib/api-config" +import { useIsMobile } from "@/hooks/use-mobile" interface WebInteraction { type: "yesno" | "menu" | "msgbox" | "input" | "inputbox" @@ -43,6 +46,14 @@ export function ScriptTerminalModal({ const [currentInteraction, setCurrentInteraction] = useState(null) const [interactionInput, setInteractionInput] = useState("") const wsRef = useRef(null) + const [isConnected, setIsConnected] = useState(false) + const checkConnectionInterval = useRef(null) + const isMobile = useIsMobile() + + const [modalHeight, setModalHeight] = useState(80) + const [isResizing, setIsResizing] = useState(false) + const startYRef = useRef(0) + const startHeightRef = useRef(80) useEffect(() => { if (open) { @@ -50,9 +61,62 @@ export function ScriptTerminalModal({ setExitCode(null) setInteractionInput("") setCurrentInteraction(null) + setIsConnected(false) + + checkConnectionInterval.current = setInterval(() => { + if (wsRef.current) { + setIsConnected(wsRef.current.readyState === WebSocket.OPEN) + } + }, 500) + } + + return () => { + if (checkConnectionInterval.current) { + clearInterval(checkConnectionInterval.current) + } } }, [open]) + const handleResizeStart = (e: React.MouseEvent | React.TouchEvent) => { + if (isMobile) return + + setIsResizing(true) + startYRef.current = "touches" in e ? e.touches[0].clientY : e.clientY + startHeightRef.current = modalHeight + + e.preventDefault() + } + + useEffect(() => { + if (!isResizing) return + + const handleResizeMove = (e: MouseEvent | TouchEvent) => { + const currentY = "touches" in e ? e.touches[0].clientY : e.clientY + const deltaY = startYRef.current - currentY + const viewportHeight = window.innerHeight + const deltaVh = (deltaY / viewportHeight) * 100 + + const newHeight = Math.min(Math.max(startHeightRef.current + deltaVh, 50), 95) + setModalHeight(newHeight) + } + + const handleResizeEnd = () => { + setIsResizing(false) + } + + document.addEventListener("mousemove", handleResizeMove) + document.addEventListener("mouseup", handleResizeEnd) + document.addEventListener("touchmove", handleResizeMove) + document.addEventListener("touchend", handleResizeEnd) + + return () => { + document.removeEventListener("mousemove", handleResizeMove) + document.removeEventListener("mouseup", handleResizeEnd) + document.removeEventListener("touchmove", handleResizeMove) + document.removeEventListener("touchend", handleResizeEnd) + } + }, [isResizing]) + const getScriptWebSocketUrl = (): string => { if (typeof window === "undefined") { return `ws://localhost:${API_PORT}/ws/script/${sessionId}` @@ -67,6 +131,7 @@ export function ScriptTerminalModal({ const handleWebSocketCreated = (ws: WebSocket) => { wsRef.current = ws + setIsConnected(ws.readyState === WebSocket.OPEN) } const handleWebInteraction = (interaction: WebInteraction) => { @@ -92,32 +157,41 @@ export function ScriptTerminalModal({ setInteractionInput("") } + const handleCloseModal = () => { + if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) { + wsRef.current.close() + } + if (checkConnectionInterval.current) { + clearInterval(checkConnectionInterval.current) + } + onClose() + } + return ( <> - - + + e.preventDefault()} + onEscapeKeyDown={(e) => e.preventDefault()} + > {title} - {/* Header */} -
-
- {isComplete ? ( - exitCode === 0 ? ( - - ) : ( - - ) +
+ {isComplete ? ( + exitCode === 0 ? ( + ) : ( - - )} -
-

{title}

- {description &&

{description}

} -
+ + ) + ) : ( + + )} +
+

{title}

+ {description &&

{description}

}
-
@@ -129,13 +203,37 @@ export function ScriptTerminalModal({ }} onWebInteraction={handleWebInteraction} onWebSocketCreated={handleWebSocketCreated} + isScriptModal={true} />
- {/* Footer */} + {!isMobile && ( +
+ +
+ )} +
-
Session ID: {sessionId}
- {isComplete && } +
+ +
+ {isConnected ? "Online" : "Offline"} +
+ +
@@ -176,11 +274,18 @@ export function ScriptTerminalModal({ key={option.value} onClick={() => handleInteractionResponse(option.value)} variant="outline" - className="w-full justify-start" + className="w-full justify-start hover:bg-blue-600 hover:text-white" > {option.label} ))} + )} @@ -197,14 +302,29 @@ export function ScriptTerminalModal({ }} placeholder={currentInteraction.default || ""} /> - +
+ + +
)} {currentInteraction.type === "msgbox" && ( - )} diff --git a/AppImage/components/terminal-panel.tsx b/AppImage/components/terminal-panel.tsx index 3cd5ecf..fa61b79 100644 --- a/AppImage/components/terminal-panel.tsx +++ b/AppImage/components/terminal-panel.tsx @@ -29,6 +29,7 @@ type TerminalPanelProps = { initMessage?: Record onWebInteraction?: (interaction: any) => void onWebSocketCreated?: (ws: WebSocket) => void + isScriptModal?: boolean } interface TerminalInstance { @@ -141,6 +142,7 @@ export const TerminalPanel: React.FC = ({ initMessage, onWebInteraction, onWebSocketCreated, + isScriptModal = false, }) => { const [terminals, setTerminals] = useState([]) const [activeTerminalId, setActiveTerminalId] = useState("") @@ -246,8 +248,6 @@ export const TerminalPanel: React.FC = ({ throw new Error("No examples found") } - console.log("[v0] Received parsed examples from server:", data.examples.length) - const formattedResults: CheatSheetResult[] = data.examples.map((example: any) => ({ command: example.command, description: example.description || "", @@ -257,7 +257,6 @@ export const TerminalPanel: React.FC = ({ setUseOnline(true) setSearchResults(formattedResults) } catch (error) { - console.log("[v0] Error fetching from cheat.sh proxy, using offline commands:", error) const filtered = proxmoxCommands.filter( (item) => item.cmd.toLowerCase().includes(query.toLowerCase()) || @@ -442,7 +441,6 @@ export const TerminalPanel: React.FC = ({ } if (initMessage) { - console.log("[v0] TerminalPanel: Sending init message:", initMessage) ws.send(JSON.stringify(initMessage)) } else { term.writeln("\x1b[32mConnected to ProxMenux terminal.\x1b[0m") @@ -454,8 +452,7 @@ export const TerminalPanel: React.FC = ({ ws.onmessage = (event) => { try { const data = JSON.parse(event.data) - if (data.type === "web_interaction" && onWebInteraction) { - console.log("[v0] TerminalPanel: Intercepted web_interaction:", data.interaction) + if (data.type === "web_interaction" && data.interaction && onWebInteraction) { onWebInteraction(data.interaction) return // Don't write to terminal } @@ -466,8 +463,7 @@ export const TerminalPanel: React.FC = ({ term.write(event.data) } - ws.onerror = (error) => { - console.error("[v0] TerminalPanel: WebSocket error:", error) + ws.onerror = () => { setTerminals((prev) => prev.map((t) => (t.id === terminal.id ? { ...t, isConnected: false } : t))) term.writeln("\r\n\x1b[31m[ERROR] WebSocket connection error\x1b[0m") } @@ -603,80 +599,88 @@ export const TerminalPanel: React.FC = ({ return (
-
-
- -
- {terminals.length} / 4 terminals -
+ {!isScriptModal && ( +
+
+ +
+ {terminals.length} / 4 terminals +
-
- {!isMobile && terminals.length > 1 && ( - <> - - - - )} - - - - +
+ {!isMobile && terminals.length > 1 && ( + <> + + + + )} + + + + +
-
+ )} + + {isScriptModal && ( +
+ Connection Status +
+ )}