/** * Terminal Manager - Frontend for xterm.js terminals */ class TerminalManager { constructor() { this.terminals = new Map(); // terminalId -> { terminal, ws, fitAddon, container, mode, ready } this.activeTerminalId = null; this.xtermLoaded = false; this.terminalsContainer = null; this.terminalTabsContainer = null; // Bind methods this.createTerminal = this.createTerminal.bind(this); this.switchToTerminal = this.switchToTerminal.bind(this); this.closeTerminal = this.closeTerminal.bind(this); this.setMode = this.setMode.bind(this); this.clearScreen = this.clearScreen.bind(this); } /** * Load xterm.js CSS dynamically */ async loadXTerm() { if (this.xtermLoaded) return; try { // Load xterm.js CSS const cssLink = document.createElement('link'); cssLink.rel = 'stylesheet'; cssLink.href = 'https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/css/xterm.css'; document.head.appendChild(cssLink); // Load xterm.js library await this.loadScript('https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.js'); // Load addons await this.loadScript('https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0.10.0/lib/addon-fit.js'); await this.loadScript('https://cdn.jsdelivr.net/npm/@xterm/addon-web-links@0.11.0/lib/addon-web-links.js'); this.xtermLoaded = true; console.log('[TerminalManager] xterm.js loaded'); } catch (error) { console.error('[TerminalManager] Failed to load xterm.js:', error); throw error; } } /** * Helper to load script dynamically */ loadScript(src) { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = src; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); } /** * Initialize terminal UI */ async initialize() { // Load xterm.js await this.loadXTerm(); // Get container elements this.terminalsContainer = document.getElementById('terminals-container'); this.terminalTabsContainer = document.getElementById('terminal-tabs'); // Check for restore state await this.checkForRestore(); // Set up keyboard shortcuts this.setupKeyboardShortcuts(); } /** * Check for saved terminal state and offer restore */ async checkForRestore() { try { const res = await fetch('/claude/api/claude/terminal-restore'); const data = await res.json(); if (data.success && data.state && data.state.terminals && data.state.terminals.length > 0) { this.showRestorePrompt(data.state); } } catch (error) { console.error('[TerminalManager] Error checking for restore:', error); } } /** * Show restore prompt toast */ showRestorePrompt(state) { const terminalCount = state.terminals.length; const toast = document.createElement('div'); toast.className = 'toast-notification toast-info restore-toast'; toast.innerHTML = `
No sessions found. Create a new one to get started!