From 666dc3cfe884659a40a4c64ea6b5fdfa86207e8c Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sat, 21 Mar 2026 22:28:00 +0800 Subject: [PATCH] feat: add per-terminal status indicators and increase terminal init delay to 600ms for stability --- src/frontend/index.html | 37 ++++++++++++++++++++++++++++++++----- src/frontend/styles.css | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/frontend/index.html b/src/frontend/index.html index 62939f9..745eb6e 100644 --- a/src/frontend/index.html +++ b/src/frontend/index.html @@ -232,6 +232,7 @@ let terminals = []; let websockets = []; let fitAddons = []; + let statusIndicators = []; let currentLayout = { cols: 1, rows: 1 }; let currentBrightness = 0; @@ -413,12 +414,23 @@ pane.style.overflow = 'hidden'; pane.style.margin = '0'; pane.style.padding = '0'; + pane.style.position = 'relative'; + + const statusDiv = document.createElement('div'); + statusDiv.className = 'pane-status'; + const statusIndicator = document.createElement('span'); + statusIndicator.className = 'status-indicator connecting'; + statusIndicator.title = 'Connecting...'; + statusDiv.appendChild(statusIndicator); + pane.appendChild(statusDiv); + terminalsContainer.appendChild(pane); try { const ws = new WebSocket(wsUrl); websockets.push(ws); - const { term, fitAddon } = createTerminal(pane, ws); + statusIndicators.push(statusIndicator); + const { term, fitAddon } = createTerminal(pane, ws, statusIndicator); terminals.push(term); fitAddons.push(fitAddon); } catch (err) { @@ -426,6 +438,7 @@ terminals.push(null); websockets.push(null); fitAddons.push(null); + statusIndicators.push(null); } } @@ -449,7 +462,7 @@ }); } - function createTerminal(container, ws) { + function createTerminal(container, ws, statusIndicator) { const isDark = currentBrightness < 50; const bgValue = Math.round((currentBrightness / 100) * 255); const bgColor = `rgb(${bgValue}, ${bgValue}, ${bgValue})`; @@ -505,10 +518,17 @@ ws.send(`\x1b[8;${term.rows};${term.cols}t`); } term.focus(); - }, 100); + }, 600); ws.binaryType = 'arraybuffer'; + ws.onopen = () => { + if (statusIndicator) { + statusIndicator.className = 'status-indicator connected'; + statusIndicator.title = 'Connected'; + } + }; + ws.onmessage = (event) => { if (event.data instanceof ArrayBuffer) { const text = new TextDecoder().decode(event.data); @@ -519,11 +539,18 @@ }; ws.onclose = () => { + if (statusIndicator) { + statusIndicator.className = 'status-indicator disconnected'; + statusIndicator.title = 'Disconnected'; + } term.write('\r\n\x1b[33m[Connection closed]\x1b[0m\r\n'); }; - ws.onerror = (error) => { - console.error('WebSocket error:', error); + ws.onerror = () => { + if (statusIndicator) { + statusIndicator.className = 'status-indicator disconnected'; + statusIndicator.title = 'Connection error'; + } }; term.onData((data) => { diff --git a/src/frontend/styles.css b/src/frontend/styles.css index b6cd9b2..4cbca48 100644 --- a/src/frontend/styles.css +++ b/src/frontend/styles.css @@ -106,6 +106,43 @@ html, body { padding: 0 !important; } +.pane-status { + position: absolute; + top: 6px; + right: 6px; + z-index: 10; + pointer-events: none; +} + +.status-indicator { + width: 10px; + height: 10px; + border-radius: 50%; + display: block; + transition: background 0.3s, box-shadow 0.3s; +} + +.status-indicator.connected { + background: #50c878; + box-shadow: 0 0 6px #50c878; +} + +.status-indicator.connecting { + background: #f0a050; + box-shadow: 0 0 6px #f0a050; + animation: pulse 1s infinite; +} + +.status-indicator.disconnected { + background: #d35252; + box-shadow: 0 0 6px #d35252; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + .terminal-divider { height: 4px; background: var(--bg-secondary);