mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-10 02:24:35 +08:00
feat: enhance CLI stream viewer with active execution synchronization and improved tab UI
This commit is contained in:
@@ -206,21 +206,35 @@
|
|||||||
/* ===== Tab Bar ===== */
|
/* ===== Tab Bar ===== */
|
||||||
.cli-stream-tabs {
|
.cli-stream-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
border-bottom: 1px solid hsl(var(--border));
|
border-bottom: 1px solid hsl(var(--border));
|
||||||
background: hsl(var(--muted) / 0.2);
|
background: hsl(var(--muted) / 0.2);
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Show scrollbar on hover for better UX */
|
||||||
.cli-stream-tabs::-webkit-scrollbar {
|
.cli-stream-tabs::-webkit-scrollbar {
|
||||||
height: 4px;
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cli-stream-tabs::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cli-stream-tabs::-webkit-scrollbar-thumb {
|
.cli-stream-tabs::-webkit-scrollbar-thumb {
|
||||||
background: hsl(var(--border));
|
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 {
|
.cli-stream-tab {
|
||||||
@@ -235,6 +249,7 @@
|
|||||||
color: hsl(var(--muted-foreground));
|
color: hsl(var(--muted-foreground));
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,82 @@ let searchFilter = ''; // Search filter for output content
|
|||||||
|
|
||||||
const MAX_OUTPUT_LINES = 5000; // Prevent memory issues
|
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 =====
|
// ===== Initialization =====
|
||||||
function initCliStreamViewer() {
|
function initCliStreamViewer() {
|
||||||
// Initialize keyboard shortcuts
|
// Initialize keyboard shortcuts
|
||||||
@@ -39,6 +115,9 @@ function initCliStreamViewer() {
|
|||||||
if (content) {
|
if (content) {
|
||||||
content.addEventListener('scroll', handleStreamContentScroll);
|
content.addEventListener('scroll', handleStreamContentScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync active executions from server (recover state for mid-execution joins)
|
||||||
|
syncActiveExecutions();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Panel Control =====
|
// ===== Panel Control =====
|
||||||
|
|||||||
Reference in New Issue
Block a user