diff --git a/src/App.tsx b/src/App.tsx index a0fabb0..bb3669f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ import { Box, Paper, ThemeProvider, List, ListItem, ListItemButton, ListItemText, SwipeableDrawer, ListItemIcon, IconButton, AppBar, Toolbar, PaletteMode, createTheme, useMediaQuery, useTheme } from "@mui/material"; import React, { Dispatch, ReactNode } from "react"; -import { BrowserRouter, Routes, Route, Navigate, useSearchParams } from "react-router-dom"; +import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; import { ApiWrapper, getDesignTokens, GlobalUserInfo } from "./common"; import { LoginPage } from "./login"; import ServersBoard from "./servers"; @@ -14,7 +14,6 @@ import { SignupPage } from "./signup"; import Brightness4Icon from '@mui/icons-material/Brightness4'; import Brightness7Icon from '@mui/icons-material/Brightness7'; import Cookies from 'js-cookie' -import TerminalComponent from "./terminal"; const ColorModeContext = React.createContext({ toggleColorMode: () => { } }); @@ -113,7 +112,6 @@ export default function App() { }), [], ); - const [searchParams, setSearchParams] = useSearchParams(); const theme = React.useMemo(() => createTheme(getDesignTokens(mode)), [mode]); @@ -131,7 +129,6 @@ export default function App() { } /> } /> } /> - } /> } /> diff --git a/src/common.tsx b/src/common.tsx index 3c71fba..a3f6772 100644 --- a/src/common.tsx +++ b/src/common.tsx @@ -264,12 +264,8 @@ export function ActionItem(p: { action: ActionInfo, identifierSubstring?: string setFormData(args.formData) } - function createTerminalWindow(websocket: string){ - window.open(`/terminal?ws=${encodeURIComponent(websocket)}`, 'Terminal', 'width=800,height=600'); - } - return (<> - + { setForm(false); setFormData({}); }} open={form} diff --git a/src/terminal.tsx b/src/terminal.tsx index eaaeaad..6325466 100644 --- a/src/terminal.tsx +++ b/src/terminal.tsx @@ -1,83 +1,104 @@ +// terminal.tsx // src/components/Terminal.tsx import React, { useEffect, useRef } from 'react'; import { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; import 'xterm/css/xterm.css'; - -const useTerminalResize = (terminalRef: React.RefObject, fitAddon: FitAddon) => { +const useTerminalResize = (terminalRef: React.RefObject, fitAddon: FitAddon, newWindow: Window | null) => { useEffect(() => { - const handleResize = () => { - if (fitAddon && terminalRef.current) { - fitAddon.fit(); + const handleResize = () => { + if (fitAddon && terminalRef.current) { + fitAddon.fit(); + } + }; + + if (newWindow) { + newWindow.addEventListener('resize', handleResize); + } else { + window.addEventListener('resize', handleResize); } - }; - - window.addEventListener('resize', handleResize); - - // Fit the terminal initially - handleResize(); - - return () => { - window.removeEventListener('resize', handleResize); - }; - }, [terminalRef, fitAddon]); - }; + handleResize(); -function TerminalComponent (p: {websocket: string|null}) { - const {websocket} = p + return () => { + if (newWindow) { + newWindow.removeEventListener('resize', handleResize); + } else { + window.removeEventListener('resize', handleResize); + } + }; + }, [terminalRef, fitAddon, newWindow]); +}; + +function TerminalComponent(p: { websocket: string | null }) { + const { websocket } = p; const terminalRef = useRef(null); const terminal = useRef(null); const fitAddon = useRef(null); + const newWindow = useRef(null); useEffect(() => { - if (terminalRef.current) { - terminal.current = new Terminal(); - fitAddon.current = new FitAddon(); - terminal.current.loadAddon(fitAddon.current); - terminal.current.open(terminalRef.current); - fitAddon.current.fit(); - if (websocket == null){ - - return ()=>{ - terminal.current?.dispose(); + newWindow.current = window.open('', '', 'width=800,height=600'); + + if (newWindow.current) { + newWindow.current.document.title = "Terminal"; + const terminalDiv = newWindow.current.document.createElement('div'); + terminalDiv.style.width = '100%'; + terminalDiv.style.height = '100%'; + newWindow.current.document.body.appendChild(terminalDiv); + + terminalRef.current = terminalDiv; + + if (terminalRef.current) { + terminal.current = new Terminal(); + fitAddon.current = new FitAddon(); + terminal.current.loadAddon(fitAddon.current); + terminal.current.open(terminalRef.current); + fitAddon.current.fit(); + + if (websocket == null) { + return () => { + terminal.current?.dispose(); + newWindow.current?.close(); + }; } + + const socket = new WebSocket(websocket); + + socket.onerror = (ev) => { + console.log(ev); + }; + + socket.addEventListener('open', () => { + socket.send(JSON.stringify({ CommandType: 'resize', Arguments: `${terminal.current?.cols}x${terminal.current?.rows}` })); + }); + + terminal.current.onResize(({ cols, rows }) => { + socket.send(JSON.stringify({ CommandType: 'resize', Arguments: `${cols}x${rows}` })); + }); + + socket.addEventListener('message', (event) => { + terminal.current?.write(JSON.parse(event.data)); + }); + + terminal.current.onData((data) => { + socket.send(JSON.stringify({ CommandType: 'insert', Arguments: data })); + }); + + return () => { + console.log("closed websocket"); + terminal.current?.dispose(); + socket.close(); + newWindow.current?.close(); + }; } - - const socket = new WebSocket(websocket); - - socket.onerror = (ev)=>{ - console.log(ev) - } - - socket.addEventListener('open', () => { - socket.send(JSON.stringify({CommandType: 'resize', Arguments: `${terminal.current?.cols}x${terminal.current?.rows}`})); - }); - - terminal.current.onResize(({cols, rows})=>{ - socket.send(JSON.stringify({CommandType: 'resize', Arguments: `${cols}x${rows}`})); - }) - - socket.addEventListener('message', (event) => { - terminal.current?.write(JSON.parse(event.data)); - }); - - terminal.current.onData((data) => { - socket.send(JSON.stringify({CommandType: 'insert', Arguments: data})); - }); - - return () => { - console.log("closed websocket") - terminal.current?.dispose(); - socket.close(); - }; } }, []); - useTerminalResize(terminalRef, fitAddon.current!) + useTerminalResize(terminalRef, fitAddon.current!, newWindow.current); - return
; + return null; }; export default TerminalComponent;