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);