feat: add status bar with connection indicator and cd path detection

This commit is contained in:
2026-03-21 22:49:14 +08:00
parent 666dc3cfe8
commit d6f3549a53
2 changed files with 66 additions and 25 deletions

View File

@@ -411,26 +411,32 @@
for (let i = 0; i < totalTerminals; i++) {
const pane = document.createElement('div');
pane.className = 'terminal-pane';
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 addressText = document.createElement('span');
addressText.className = 'pane-address';
addressText.textContent = shell;
statusDiv.appendChild(addressText);
const statusIndicator = document.createElement('span');
statusIndicator.className = 'status-indicator connecting';
statusIndicator.title = 'Connecting...';
statusDiv.appendChild(statusIndicator);
pane.appendChild(statusDiv);
const contentDiv = document.createElement('div');
contentDiv.className = 'terminal-content';
pane.appendChild(contentDiv);
terminalsContainer.appendChild(pane);
try {
const ws = new WebSocket(wsUrl);
websockets.push(ws);
statusIndicators.push(statusIndicator);
const { term, fitAddon } = createTerminal(pane, ws, statusIndicator);
const { term, fitAddon } = createTerminal(contentDiv, ws, statusIndicator, addressText);
terminals.push(term);
fitAddons.push(fitAddon);
} catch (err) {
@@ -462,7 +468,7 @@
});
}
function createTerminal(container, ws, statusIndicator) {
function createTerminal(container, ws, statusIndicator, addressText) {
const isDark = currentBrightness < 50;
const bgValue = Math.round((currentBrightness / 100) * 255);
const bgColor = `rgb(${bgValue}, ${bgValue}, ${bgValue})`;
@@ -530,12 +536,14 @@
};
ws.onmessage = (event) => {
let text;
if (event.data instanceof ArrayBuffer) {
const text = new TextDecoder().decode(event.data);
term.write(text);
text = new TextDecoder().decode(event.data);
} else {
term.write(event.data);
text = event.data;
}
term.write(text);
};
ws.onclose = () => {
@@ -554,6 +562,13 @@
};
term.onData((data) => {
if (addressText) {
const trimmed = data.trim();
const cdMatch = trimmed.match(/^cd\s+(.+)/i);
if (cdMatch && cdMatch[1]) {
addressText.textContent = cdMatch[1].trim();
}
}
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(data);
}

View File

@@ -89,29 +89,20 @@ html, body {
.terminal-pane {
flex: 1;
display: flex;
flex-direction: column;
background: var(--bg-primary);
overflow: hidden;
margin: 0;
padding: 0;
}
.terminal-pane .xterm {
height: 100%;
margin: 0 !important;
padding: 0 !important;
}
.terminal-pane .xterm-viewport {
margin: 0 !important;
padding: 0 !important;
}
.pane-status {
position: absolute;
top: 6px;
right: 6px;
z-index: 10;
pointer-events: none;
height: 20px;
display: flex;
align-items: center;
padding-right: 12px;
flex-shrink: 0;
}
.status-indicator {
@@ -120,6 +111,41 @@ html, body {
border-radius: 50%;
display: block;
transition: background 0.3s, box-shadow 0.3s;
margin-left: 8px;
flex-shrink: 0;
}
.pane-address {
font-size: 11px;
color: var(--text-secondary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.terminal-content {
flex: 1;
overflow: hidden;
}
.terminal-content .xterm {
height: 100%;
margin: 0 !important;
padding: 0 !important;
}
.terminal-content .xterm-viewport {
margin: 0 !important;
padding: 0 !important;
}
.status-indicator {
width: 6px;
height: 6px;
border-radius: 50%;
display: block;
transition: background 0.3s, box-shadow 0.3s;
}
.status-indicator.connected {