/** * ProxmoxPanel — Terminal page logic * xterm.js + WebSocket PTY * Chargé uniquement sur terminal.html */ document.addEventListener('DOMContentLoaded', () => { // Les classes Terminal et FitAddon sont exposées par xterm.iife.js if (typeof Terminal === 'undefined' || typeof FitAddon === 'undefined') { console.error('xterm.js not loaded') return } const term = new Terminal({ cursorBlink: true, fontFamily: '"JetBrains Mono", "Fira Code", "Cascadia Code", monospace', fontSize: 14, theme: { background: 'var(--bg-secondary, #1a1a2e)', foreground: 'var(--text-primary, #e2e8f0)', cursor: 'var(--accent-primary, #6366f1)', }, }) const fitAddon = new FitAddon() term.loadAddon(fitAddon) const container = document.getElementById('terminal-container') if (!container) return term.open(container) fitAddon.fit() // Connexion WebSocket PTY const proto = location.protocol === 'https:' ? 'wss' : 'ws' const token = localStorage.getItem('pxp_token') const ws = new WebSocket(`${proto}://${location.host}/ws/terminal?token=${encodeURIComponent(token || '')}`) ws.binaryType = 'arraybuffer' const statusEl = document.getElementById('terminal-status') function setStatus(text, cls) { if (statusEl) { statusEl.textContent = text statusEl.className = 'terminal-status ' + (cls || '') } } setStatus('Connexion…', 'connecting') ws.onopen = () => { setStatus('Connecté', 'connected') // Envoyer la taille initiale du terminal sendResize() } ws.onmessage = (e) => { if (e.data instanceof ArrayBuffer) { term.write(new Uint8Array(e.data)) } else { term.write(e.data) } } ws.onclose = () => { setStatus('Déconnecté', 'disconnected') term.writeln('\r\n\x1b[31m[Connexion terminée]\x1b[0m') } ws.onerror = () => { setStatus('Erreur', 'error') } // Envoyer l'input utilisateur au serveur term.onData((data) => { if (ws.readyState === WebSocket.OPEN) { ws.send(data) } }) // Envoyer la taille du terminal lors du redimensionnement function sendResize() { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'resize', cols: term.cols, rows: term.rows, })) } } const resizeObserver = new ResizeObserver(() => { fitAddon.fit() sendResize() }) resizeObserver.observe(container) // Nettoyage quand Swup navigue hors de la page document.addEventListener('swup:page:view', () => { if (!document.getElementById('terminal-container')) { ws.close() resizeObserver.disconnect() } }) // Bouton "Effacer" const clearBtn = document.getElementById('terminal-clear') if (clearBtn) { clearBtn.addEventListener('click', () => term.clear()) } })