diff --git a/AppImage/components/terminal-panel.tsx b/AppImage/components/terminal-panel.tsx index e1896e4..5b0ded1 100644 --- a/AppImage/components/terminal-panel.tsx +++ b/AppImage/components/terminal-panel.tsx @@ -288,8 +288,6 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl cursorBlink: true, scrollback: 2000, disableStdin: false, - cols: isMobile ? 40 : layout === "grid" ? 60 : 120, - rows: isMobile ? 20 : layout === "grid" ? 15 : 30, theme: { background: "#000000", foreground: "#ffffff", @@ -325,13 +323,23 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl if (xtermViewport) xtermViewport.style.padding = "0" if (xtermScreen) xtermScreen.style.padding = "0" fitAddon.fit() + console.log(`[v0] Terminal fitted: ${term.cols}x${term.rows}`) - const cols = term.cols - const rows = term.rows - if (ws.readyState === WebSocket.OPEN) { - ws.send(`\x1b[8;${rows};${cols}t`) - } - }, 10) + // Send resize to backend via HTTP + const apiUrl = getApiUrl() + fetch(`${apiUrl}/api/terminal/${terminal.id}/resize`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ cols: term.cols, rows: term.rows }), + }) + .then((res) => res.json()) + .then((data) => { + console.log(`[v0] Backend PTY resized:`, data) + }) + .catch((err) => { + console.error(`[v0] Error resizing backend PTY:`, err) + }) + }, 100) const wsUrl = websocketUrl || getWebSocketUrl() const ws = new WebSocket(wsUrl) @@ -367,9 +375,17 @@ export const TerminalPanel: React.FC = ({ websocketUrl, onCl fitAddon.fit() const cols = term.cols const rows = term.rows - if (ws.readyState === WebSocket.OPEN) { - ws.send(`\x1b[8;${rows};${cols}t`) - } + console.log(`[v0] Window resized, terminal now: ${cols}x${rows}`) + + // Send resize to backend via HTTP + const apiUrl = getApiUrl() + fetch(`${apiUrl}/api/terminal/${terminal.id}/resize`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ cols, rows }), + }).catch((err) => { + console.error(`[v0] Error resizing backend PTY:`, err) + }) } catch { // Ignore resize errors } diff --git a/AppImage/scripts/flask_terminal_routes.py b/AppImage/scripts/flask_terminal_routes.py index 8a5cd81..dbe06ec 100644 --- a/AppImage/scripts/flask_terminal_routes.py +++ b/AppImage/scripts/flask_terminal_routes.py @@ -20,79 +20,8 @@ import requests terminal_bp = Blueprint('terminal', __name__) sock = Sock() -# Active terminal sessions -active_sessions = {} - -@terminal_bp.route('/api/terminal/health', methods=['GET']) -def terminal_health(): - """Health check for terminal service""" - return {'success': True, 'active_sessions': len(active_sessions)} - -@terminal_bp.route('/api/terminal/search-command', methods=['GET']) -def search_command(): - """Proxy endpoint for cheat.sh API to avoid CORS issues""" - query = request.args.get('q', '') - - if not query or len(query) < 2: - return jsonify({'error': 'Query too short'}), 400 - - try: - url = f'https://cht.sh/{query.replace(" ", "+")}?QT' - headers = { - 'User-Agent': 'curl/7.68.0' - } - - response = requests.get(url, headers=headers, timeout=10) - - if response.status_code == 200: - content = response.text - examples = [] - current_description = [] - - for line in content.split('\n'): - stripped = line.strip() - - # Ignorar líneas vacías - if not stripped: - continue - - # Si es un comentario - if stripped.startswith('#'): - # Acumular descripciones - current_description.append(stripped[1:].strip()) - # Si no es comentario, es un comando - elif stripped and not stripped.startswith('http'): - # Unir las descripciones acumuladas - description = ' '.join(current_description) if current_description else '' - - examples.append({ - 'description': description, - 'command': stripped - }) - - # Resetear descripciones para el siguiente comando - current_description = [] - - return jsonify({ - 'success': True, - 'examples': examples - }) - else: - return jsonify({ - 'success': False, - 'error': f'API returned status {response.status_code}' - }), response.status_code - - except requests.Timeout: - return jsonify({ - 'success': False, - 'error': 'Request timeout' - }), 504 - except Exception as e: - return jsonify({ - 'success': False, - 'error': str(e) - }), 500 +# Active terminal sessions - now stores session by session_id string +sessions = {} def set_winsize(fd, rows, cols): """Set terminal window size""" @@ -121,6 +50,33 @@ def read_and_forward_output(master_fd, ws): print(f"Error reading from PTY: {e}") break +@terminal_bp.route('/api/terminal/health', methods=['GET']) +def terminal_health(): + """Health check for terminal service""" + return {'success': True, 'active_sessions': len(sessions)} + +@terminal_bp.route('/api/terminal//resize', methods=['POST']) +def resize_terminal(session_id): + """Resize the PTY for a given terminal session.""" + if session_id not in sessions: + return jsonify({'error': 'Session not found'}), 404 + + try: + data = request.get_json() + cols = int(data.get('cols', 120)) + rows = int(data.get('rows', 30)) + + # Resize the PTY to match the frontend terminal dimensions + master_fd = sessions[session_id]['master_fd'] + set_winsize(master_fd, rows, cols) + + print(f"[v0] Terminal {session_id} resized to {cols}x{rows}") + + return jsonify({'status': 'success', 'cols': cols, 'rows': rows}) + except Exception as e: + print(f"Error resizing terminal {session_id}: {e}") + return jsonify({'error': str(e)}), 500 + @sock.route('/ws/terminal') def terminal_websocket(ws): """WebSocket endpoint for terminal sessions""" @@ -139,8 +95,8 @@ def terminal_websocket(ws): env=dict(os.environ, TERM='xterm-256color', PS1='\\u@\\h:\\w\\$ ') ) - session_id = id(ws) - active_sessions[session_id] = { + session_id = str(int(time.time() * 1000)) + sessions[session_id] = { 'process': shell_process, 'master_fd': master_fd } @@ -149,8 +105,10 @@ def terminal_websocket(ws): flags = fcntl.fcntl(master_fd, fcntl.F_GETFL) fcntl.fcntl(master_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) - # Set initial terminal size - set_winsize(master_fd, 30, 120) + set_winsize(master_fd, 30, 80) + + # Send session_id to frontend + ws.send(f"\x1b]0;SESSION_ID:{session_id}\x07") # Start thread to read PTY output and forward to WebSocket output_thread = threading.Thread( @@ -176,7 +134,7 @@ def terminal_websocket(ws): if len(parts) >= 2: rows, cols = int(parts[0]), int(parts[1]) set_winsize(master_fd, rows, cols) - print(f"[Terminal] Resized to {rows}x{cols}") + print(f"[v0] Terminal resized via WebSocket to {rows}x{cols}") continue except Exception as e: print(f"Error parsing resize: {e}") @@ -216,8 +174,8 @@ def terminal_websocket(ws): except: pass - if session_id in active_sessions: - del active_sessions[session_id] + if session_id in sessions: + del sessions[session_id] def init_terminal_routes(app): """Initialize terminal routes with Flask app"""