diff --git a/AppImage/components/script-terminal-modal.tsx b/AppImage/components/script-terminal-modal.tsx index 8afebfc..004c53f 100644 --- a/AppImage/components/script-terminal-modal.tsx +++ b/AppImage/components/script-terminal-modal.tsx @@ -9,8 +9,7 @@ import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { CheckCircle2, XCircle, Loader2, Activity, GripHorizontal } from "lucide-react" import { TerminalPanel } from "./terminal-panel" -const API_PORT = - typeof window !== "undefined" && process.env.NEXT_PUBLIC_API_PORT ? process.env.NEXT_PUBLIC_API_PORT : "8008" +import { API_PORT } from "@/lib/api-config" import { useIsMobile } from "@/hooks/use-mobile" interface WebInteraction { @@ -59,28 +58,6 @@ export function ScriptTerminalModal({ const startYRef = useRef(0) const startHeightRef = useRef(80) - const terminalRef = useRef(null) - - useEffect(() => { - if (!terminalRef.current) return - - const resizeObserver = new ResizeObserver(() => { - // Notificar a la terminal que necesita redimensionarse - const event = new CustomEvent("terminal-resize-needed") - window.dispatchEvent(event) - }) - - resizeObserver.observe(terminalRef.current) - - return () => { - resizeObserver.disconnect() - } - }, []) - - const handleTerminalResize = () => { - // Este callback será usado por TerminalPanel para saber cuándo redimensionar - } - useEffect(() => { if (open) { setIsComplete(false) @@ -212,6 +189,20 @@ export function ScriptTerminalModal({ onClose() } + useEffect(() => { + const handleResize = () => { + if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) { + wsRef.current.send(JSON.stringify({ type: "resize" })) + } + } + + window.addEventListener("resize", handleResize) + + return () => { + window.removeEventListener("resize", handleResize) + } + }, [open]) + return ( <> @@ -239,7 +230,7 @@ export function ScriptTerminalModal({ -
+
{isWaitingNextInteraction && !currentInteraction && ( diff --git a/AppImage/components/terminal-panel.tsx b/AppImage/components/terminal-panel.tsx index c4be060..fd62d68 100644 --- a/AppImage/components/terminal-panel.tsx +++ b/AppImage/components/terminal-panel.tsx @@ -2,27 +2,14 @@ import type React from "react" import { useEffect, useRef, useState } from "react" -const API_PORT = - typeof window !== "undefined" && process.env.NEXT_PUBLIC_API_PORT ? process.env.NEXT_PUBLIC_API_PORT : "8008" -import { - Activity, - Trash2, - X, - Search, - Send, - Lightbulb, - Terminal, - Plus, - AlignJustify, - Grid2X2, - GripHorizontal, -} from "lucide-react" +import { API_PORT } from "../lib/api-config" +import { fetchApi } from "@/lib/api-config" // Cambiando import para usar fetchApi directamente +import { X, Search, Send, Lightbulb, Terminal, Plus, GripHorizontal } from "lucide-react" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs" import type { CheatSheetResult } from "@/lib/cheat-sheet-result" // Declare CheatSheetResult here -import { ResizablePanelGroup, ResizablePanel } from "@/components/ui/resizable-panel-group" // Added import for ResizablePanelGroup and ResizablePanel type TerminalPanelProps = { websocketUrl?: string @@ -32,7 +19,6 @@ type TerminalPanelProps = { onWebSocketCreated?: (ws: WebSocket) => void onTerminalOutput?: () => void isScriptModal?: boolean - onResizeNeeded?: () => void // Added prop for notifying when resize is needed } interface TerminalInstance { @@ -147,7 +133,6 @@ export function TerminalPanel({ onWebSocketCreated, onTerminalOutput, isScriptModal = false, - onResizeNeeded, }: TerminalPanelProps) { const [terminals, setTerminals] = useState([]) const [activeTerminalId, setActiveTerminalId] = useState("") @@ -163,6 +148,7 @@ export function TerminalPanel({ const [useOnline, setUseOnline] = useState(true) const containerRefs = useRef<{ [key: string]: HTMLDivElement | null }>({}) + const panelRef = useRef(null) useEffect(() => { const updateDeviceType = () => { @@ -244,17 +230,11 @@ export function TerminalPanel({ const searchEndpoint = `/api/terminal/search-command?q=${encodeURIComponent(query)}` - const response = await fetch(searchEndpoint, { + const data = await fetchApi<{ success: boolean; examples: any[] }>(searchEndpoint, { method: "GET", signal: AbortSignal.timeout(10000), }) - if (!response.ok) { - throw new Error("Network response was not ok") - } - - const data = await response.json() - if (!data.success || !data.examples || data.examples.length === 0) { throw new Error("No examples found") } @@ -333,6 +313,11 @@ export function TerminalPanel({ delete containerRefs.current[id] } + const handleCloseTab = (e: React.MouseEvent, id: string) => { + e.stopPropagation() + closeTerminal(id) + } + useEffect(() => { terminals.forEach((terminal) => { const container = containerRefs.current[terminal.id] @@ -370,10 +355,14 @@ export function TerminalPanel({ }, [terminalHeight, layout, terminals, isMobile]) useEffect(() => { - if (onResizeNeeded && terminals.length > 0) { - const terminal = terminals[0] - if (terminal.term && terminal.fitAddon && terminal.isConnected) { - const triggerResize = () => { + if (!isScriptModal) return + + const mainContainer = containerRefs.current["main"] + if (!mainContainer) return + + const resizeObserver = new ResizeObserver(() => { + terminals.forEach((terminal) => { + if (terminal.term && terminal.fitAddon && terminal.isConnected) { try { setTimeout(() => { terminal.fitAddon?.fit() @@ -388,28 +377,20 @@ export function TerminalPanel({ }), ) } - }, 100) + }, 50) } catch (err) { - console.warn("[Terminal] resize failed:", err) + // Silently handle resize errors } } + }) + }) - // Llamar automáticamente cuando sea necesario - const resizeObserver = new ResizeObserver(() => { - triggerResize() - }) + resizeObserver.observe(mainContainer) - const terminalElement = document.querySelector(".xterm") - if (terminalElement) { - resizeObserver.observe(terminalElement) - } - - return () => { - resizeObserver.disconnect() - } - } + return () => { + resizeObserver.disconnect() } - }, [terminals, onResizeNeeded]) + }, [terminals, isScriptModal]) const initializeTerminal = async (terminal: TerminalInstance, container: HTMLDivElement) => { const [TerminalClass, FitAddonClass] = await Promise.all([ @@ -655,99 +636,61 @@ export function TerminalPanel({ const activeTerminal = terminals.find((t) => t.id === activeTerminalId) return ( -
+
{!isScriptModal && ( -
-
- -
- {terminals.length} / 4 terminals -
- -
- {!isMobile && terminals.length > 1 && ( - <> - - - - )} - + ))} +
+ - - - +
)} {/* Terminal Tabs */} -
- {terminals.map((terminal) => ( - - ))} -
+ {!isScriptModal && ( +
+ {terminals.map((terminal) => ( + + ))} +
+ )} {isScriptModal && (
@@ -755,108 +698,89 @@ export function TerminalPanel({
)} - - -
- {!isScriptModal && ( - + )} + + ))} + + {terminals.map((terminal) => ( + +
(containerRefs.current[terminal.id] = el)} + className="w-full h-full flex-1 bg-black overflow-hidden" + /> + + ))} + + ) : ( +
+ {terminals.map((terminal) => ( +
1 && activeTerminalId === terminal.id ? "ring-2 ring-blue-500" : "" }`} > - Terminal {1} - - )} -
- -
{ - containerRefs.current["main"] = el - }} - className={`overflow-hidden flex flex-col ${isMobile ? "flex-1 h-[60vh]" : "overflow-hidden"} w-full max-w-full`} - style={ - isScriptModal - ? { height: "100%", flexShrink: 0 } - : !isMobile || isTablet - ? { height: `${terminalHeight}px`, flexShrink: 0 } - : undefined - } - > - {isMobile ? ( - - - {terminals.map((terminal) => ( - - {terminal.title} - {terminals.length > 1 && ( - - )} - - ))} - - {terminals.map((terminal) => ( - + - {terminals.length > 1 && ( - - )} -
-
(containerRefs.current[terminal.id] = el)} - onClick={() => setActiveTerminalId(terminal.id)} - className="flex-1 w-full max-w-full bg-black overflow-hidden cursor-pointer" - data-terminal-container - /> -
- ))} + {terminal.title} + + {terminals.length > 1 && ( + + )} +
+
(containerRefs.current[terminal.id] = el)} + onClick={() => setActiveTerminalId(terminal.id)} + className="flex-1 w-full max-w-full bg-black overflow-hidden cursor-pointer" + data-terminal-container + />
- )} + ))}
- - + )} +
{!isScriptModal && (isTablet || (!isMobile && !isTablet)) && terminals.length > 0 && (
(endpoint: string, options?: RequestInit): Prom if (token) { headers["Authorization"] = `Bearer ${token}` + console.log("[v0] fetchApi:", endpoint, "- Authorization header ADDED") + } else { + console.log("[v0] fetchApi:", endpoint, "- NO TOKEN - Request will fail if endpoint is protected") } try { @@ -93,8 +108,11 @@ export async function fetchApi(endpoint: string, options?: RequestInit): Prom cache: "no-store", }) + console.log("[v0] fetchApi:", endpoint, "- Response status:", response.status) + if (!response.ok) { if (response.status === 401) { + console.error("[v0] fetchApi: 401 UNAUTHORIZED -", endpoint, "- Token present:", !!token) throw new Error(`Unauthorized: ${endpoint}`) } throw new Error(`API request failed: ${response.status} ${response.statusText}`) @@ -102,7 +120,7 @@ export async function fetchApi(endpoint: string, options?: RequestInit): Prom return response.json() } catch (error) { - console.error("API fetch error for", endpoint, ":", error) + console.error("[v0] fetchApi error for", endpoint, ":", error) throw error } }