diff --git a/AppImage/components/terminal-panel.tsx b/AppImage/components/terminal-panel.tsx index fd12fe33..1455dca4 100644 --- a/AppImage/components/terminal-panel.tsx +++ b/AppImage/components/terminal-panel.tsx @@ -16,6 +16,7 @@ import { Grid2X2, GripHorizontal, ChevronDown, + Clipboard, } from "lucide-react" import { DropdownMenu, @@ -578,6 +579,19 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl prev.map((t) => (t.id === terminal.id ? { ...t, isConnected: true, term, ws, fitAddon, pingInterval } : t)), ) syncSizeWithBackend() + + // Auto-refresh for mobile/VPN: send a newline after 1 second to ensure connection is active + // This fixes the issue where mobile connections sometimes don't fully initialize + const isMobileDevice = window.innerWidth < 768 || + ('ontouchstart' in window && navigator.maxTouchPoints > 0) + if (isMobileDevice) { + setTimeout(() => { + if (ws.readyState === WebSocket.OPEN) { + // Send empty string to trigger a refresh without executing any command + ws.send('\n') + } + }, 1000) + } } ws.onmessage = (event) => { @@ -724,13 +738,39 @@ const handleClose = () => { e.preventDefault() e.stopPropagation() } - + const activeTerminal = terminals.find((t) => t.id === activeTerminalId) if (activeTerminal?.ws && activeTerminal.ws.readyState === WebSocket.OPEN) { activeTerminal.ws.send(seq) } } - + + // Paste from clipboard - essential for mobile devices + const handlePaste = async (e?: React.MouseEvent | React.TouchEvent) => { + if (e) { + e.preventDefault() + e.stopPropagation() + } + + try { + const text = await navigator.clipboard.readText() + if (text) { + const activeTerminal = terminals.find((t) => t.id === activeTerminalId) + if (activeTerminal?.ws && activeTerminal.ws.readyState === WebSocket.OPEN) { + // Send text character by character to handle special characters properly + activeTerminal.ws.send(text) + } + } + } catch (err) { + console.warn('[Terminal] Clipboard access denied:', err) + // Fallback: show a message in terminal + const activeTerminal = terminals.find((t) => t.id === activeTerminalId) + if (activeTerminal?.term) { + activeTerminal.term.writeln('\r\n\x1b[33m[INFO] Clipboard access denied. Please allow clipboard permissions.\x1b[0m') + } + } + } + const getLayoutClass = () => { const count = terminals.length if (isMobile || count === 1) return "grid grid-cols-1" @@ -1032,6 +1072,16 @@ const handleClose = () => { + )}