import { AlertDialog, AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog" import { Terminal } from "@xterm/xterm"; import { AttachAddon } from "@xterm/addon-attach"; import { FitAddon } from '@xterm/addon-fit'; import { useRef, useEffect, useState, useMemo } from "react"; import { sleep } from "@/lib/utils"; import "@xterm/xterm/css/xterm.css"; import { useParams } from 'react-router-dom'; import { Button } from "./ui/button"; import { toast } from "sonner"; import { FMCard } from "./fm"; import useTerminal from "@/hooks/useTerminal"; import { IconButton } from "./xui/icon-button"; interface XtermProps { wsUrl: string; setClose: React.Dispatch>; } const XtermComponent: React.FC = ({ wsUrl, setClose, ...props }) => { const terminalIdRef = useRef(null); const terminalRef = useRef(null); const wsRef = useRef(null); useEffect(() => { return () => { wsRef.current?.close(); terminalRef.current?.dispose(); }; }, []); useEffect(() => { terminalRef.current = new Terminal({ cursorBlink: true, fontSize: 16, }); const ws = new WebSocket(wsUrl); wsRef.current = ws; ws.binaryType = "arraybuffer"; ws.onopen = () => { onResize(); } ws.onclose = () => { terminalRef.current?.dispose(); setClose(true); } ws.onerror = (e) => { console.error(e); toast("Websocket error", { description: "View console for details.", }) } }, [wsUrl]); const fitAddon = useRef(new FitAddon()).current; const sendResize = useRef(false); const doResize = () => { if (!terminalIdRef.current) return; fitAddon.fit(); const dimensions = fitAddon.proposeDimensions(); if (dimensions) { const prefix = new Int8Array([1]); const resizeMessage = new TextEncoder().encode(JSON.stringify({ Rows: dimensions.rows, Cols: dimensions.cols, })); const msg = new Int8Array(prefix.length + resizeMessage.length); msg.set(prefix); msg.set(resizeMessage, prefix.length); wsRef.current?.send(msg); } }; const onResize = async () => { if (sendResize.current) return; sendResize.current = true; try { await sleep(1500); doResize(); } catch (error) { console.error('resize error', error); } finally { sendResize.current = false; } }; useEffect(() => { if (!wsRef.current || !terminalIdRef.current || !terminalRef.current) return; const attachAddon = new AttachAddon(wsRef.current); terminalRef.current.loadAddon(attachAddon); terminalRef.current.loadAddon(fitAddon); terminalRef.current.open(terminalIdRef.current); window.addEventListener('resize', onResize); return () => { window.removeEventListener('resize', onResize); if (wsRef.current) { wsRef.current.close(); } }; }, [wsRef.current, terminalRef.current, terminalIdRef.current]); return
; }; export const TerminalPage = () => { const { id } = useParams<{ id: string }>(); const [open, setOpen] = useState(false); const terminal = useTerminal(id ? parseInt(id) : undefined); return (

{`Terminal (${id})`}

{terminal?.session_id ? :

The server does not exist, or have not been connected yet.

} Session completed You may close this window now.
) } export const TerminalButton = ({ id }: { id: number }) => { const handleOpenNewTab = () => { window.open(`/dashboard/terminal/${id}`, '_blank'); }; return ( ) }