feat: add per-terminal status indicators and increase terminal init delay to 600ms for stability
This commit is contained in:
@@ -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) => {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user