feat: enhance CLI stream viewer with active execution synchronization and improved tab UI

This commit is contained in:
catlog22
2026-01-04 23:17:58 +08:00
parent 4fb6b2d1de
commit 33f2aef4e6
2 changed files with 96 additions and 2 deletions

View File

@@ -206,21 +206,35 @@
/* ===== Tab Bar ===== */
.cli-stream-tabs {
display: flex;
flex-wrap: nowrap;
gap: 2px;
padding: 8px 12px;
border-bottom: 1px solid hsl(var(--border));
background: hsl(var(--muted) / 0.2);
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: thin;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
}
/* Show scrollbar on hover for better UX */
.cli-stream-tabs::-webkit-scrollbar {
height: 4px;
height: 6px;
}
.cli-stream-tabs::-webkit-scrollbar-track {
background: transparent;
}
.cli-stream-tabs::-webkit-scrollbar-thumb {
background: hsl(var(--border));
border-radius: 2px;
border-radius: 3px;
transition: background 0.2s;
}
.cli-stream-tabs::-webkit-scrollbar-thumb:hover {
background: hsl(var(--muted-foreground) / 0.5);
}
.cli-stream-tab {
@@ -235,6 +249,7 @@
color: hsl(var(--muted-foreground));
cursor: pointer;
white-space: nowrap;
flex-shrink: 0;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

View File

@@ -12,6 +12,82 @@ let searchFilter = ''; // Search filter for output content
const MAX_OUTPUT_LINES = 5000; // Prevent memory issues
// ===== State Synchronization =====
/**
* Sync active executions from server
* Called on initialization to recover state when view is opened mid-execution
*/
async function syncActiveExecutions() {
// Only sync in server mode
if (!window.SERVER_MODE) return;
try {
const response = await fetch('/api/cli/active');
if (!response.ok) return;
const { executions } = await response.json();
if (!executions || executions.length === 0) return;
executions.forEach(exec => {
// Skip if already tracked (avoid overwriting live data)
if (cliStreamExecutions[exec.id]) return;
// Rebuild execution state
cliStreamExecutions[exec.id] = {
tool: exec.tool || 'cli',
mode: exec.mode || 'analysis',
output: [],
status: exec.status || 'running',
startTime: exec.startTime || Date.now(),
endTime: null
};
// Add system start message
cliStreamExecutions[exec.id].output.push({
type: 'system',
content: `[${new Date(exec.startTime).toLocaleTimeString()}] CLI execution started: ${exec.tool} (${exec.mode} mode)`,
timestamp: exec.startTime
});
// Fill historical output (limit to last MAX_OUTPUT_LINES)
if (exec.output) {
const lines = exec.output.split('\n');
const startIndex = Math.max(0, lines.length - MAX_OUTPUT_LINES + 1);
lines.slice(startIndex).forEach(line => {
if (line.trim()) {
cliStreamExecutions[exec.id].output.push({
type: 'stdout',
content: line,
timestamp: Date.now()
});
}
});
}
});
// Update UI if we recovered any executions
if (executions.length > 0) {
// Set active tab to first running execution
const runningExec = executions.find(e => e.status === 'running');
if (runningExec && !activeStreamTab) {
activeStreamTab = runningExec.id;
}
renderStreamTabs();
updateStreamBadge();
// If viewer is open, render content
if (isCliStreamViewerOpen) {
renderStreamContent(activeStreamTab);
}
}
console.log(`[CLI Stream] Synced ${executions.length} active execution(s)`);
} catch (e) {
console.error('[CLI Stream] Sync failed:', e);
}
}
// ===== Initialization =====
function initCliStreamViewer() {
// Initialize keyboard shortcuts
@@ -39,6 +115,9 @@ function initCliStreamViewer() {
if (content) {
content.addEventListener('scroll', handleStreamContentScroll);
}
// Sync active executions from server (recover state for mid-execution joins)
syncActiveExecutions();
}
// ===== Panel Control =====