feat: add per-terminal status indicators and increase terminal init delay to 600ms for stability

This commit is contained in:
2026-03-21 22:28:00 +08:00
parent 5907fb74e4
commit 666dc3cfe8
2 changed files with 69 additions and 5 deletions

View File

@@ -232,6 +232,7 @@
let terminals = []; let terminals = [];
let websockets = []; let websockets = [];
let fitAddons = []; let fitAddons = [];
let statusIndicators = [];
let currentLayout = { cols: 1, rows: 1 }; let currentLayout = { cols: 1, rows: 1 };
let currentBrightness = 0; let currentBrightness = 0;
@@ -413,12 +414,23 @@
pane.style.overflow = 'hidden'; pane.style.overflow = 'hidden';
pane.style.margin = '0'; pane.style.margin = '0';
pane.style.padding = '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); terminalsContainer.appendChild(pane);
try { try {
const ws = new WebSocket(wsUrl); const ws = new WebSocket(wsUrl);
websockets.push(ws); websockets.push(ws);
const { term, fitAddon } = createTerminal(pane, ws); statusIndicators.push(statusIndicator);
const { term, fitAddon } = createTerminal(pane, ws, statusIndicator);
terminals.push(term); terminals.push(term);
fitAddons.push(fitAddon); fitAddons.push(fitAddon);
} catch (err) { } catch (err) {
@@ -426,6 +438,7 @@
terminals.push(null); terminals.push(null);
websockets.push(null); websockets.push(null);
fitAddons.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 isDark = currentBrightness < 50;
const bgValue = Math.round((currentBrightness / 100) * 255); const bgValue = Math.round((currentBrightness / 100) * 255);
const bgColor = `rgb(${bgValue}, ${bgValue}, ${bgValue})`; const bgColor = `rgb(${bgValue}, ${bgValue}, ${bgValue})`;
@@ -505,10 +518,17 @@
ws.send(`\x1b[8;${term.rows};${term.cols}t`); ws.send(`\x1b[8;${term.rows};${term.cols}t`);
} }
term.focus(); term.focus();
}, 100); }, 600);
ws.binaryType = 'arraybuffer'; ws.binaryType = 'arraybuffer';
ws.onopen = () => {
if (statusIndicator) {
statusIndicator.className = 'status-indicator connected';
statusIndicator.title = 'Connected';
}
};
ws.onmessage = (event) => { ws.onmessage = (event) => {
if (event.data instanceof ArrayBuffer) { if (event.data instanceof ArrayBuffer) {
const text = new TextDecoder().decode(event.data); const text = new TextDecoder().decode(event.data);
@@ -519,11 +539,18 @@
}; };
ws.onclose = () => { ws.onclose = () => {
if (statusIndicator) {
statusIndicator.className = 'status-indicator disconnected';
statusIndicator.title = 'Disconnected';
}
term.write('\r\n\x1b[33m[Connection closed]\x1b[0m\r\n'); term.write('\r\n\x1b[33m[Connection closed]\x1b[0m\r\n');
}; };
ws.onerror = (error) => { ws.onerror = () => {
console.error('WebSocket error:', error); if (statusIndicator) {
statusIndicator.className = 'status-indicator disconnected';
statusIndicator.title = 'Connection error';
}
}; };
term.onData((data) => { term.onData((data) => {

View File

@@ -106,6 +106,43 @@ html, body {
padding: 0 !important; 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 { .terminal-divider {
height: 4px; height: 4px;
background: var(--bg-secondary); background: var(--bg-secondary);