diff --git a/ui/suite/css/theme-sentient.css b/ui/suite/css/theme-sentient.css index 5e75548..6704f34 100644 --- a/ui/suite/css/theme-sentient.css +++ b/ui/suite/css/theme-sentient.css @@ -1668,3 +1668,294 @@ [data-theme="sentient"] .task-card.status-failed::before { background: var(--error); } + +/* ============================================ */ +/* TASKMD PROGRESS TREE - PIXEL PERFECT */ +/* ============================================ */ + +/* Main tree container */ +[data-theme="sentient"] .taskmd-tree { + display: flex; + flex-direction: column; + gap: 0; +} + +/* Tree Section (Level 0) - Main sections like "Database & Models" */ +[data-theme="sentient"] .tree-section { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 8px; + margin: 8px 16px; + overflow: hidden; +} + +[data-theme="sentient"] .tree-section:last-child { + margin-bottom: 16px; +} + +/* Section row */ +[data-theme="sentient"] .tree-row { + display: flex; + align-items: center; + padding: 16px 20px; + cursor: pointer; + transition: background 0.15s; +} + +[data-theme="sentient"] .tree-row:hover { + background: var(--surface-hover); +} + +[data-theme="sentient"] .tree-level-0 { + padding-left: 20px; +} + +[data-theme="sentient"] .tree-level-1 { + padding-left: 40px; + background: var(--bg-secondary); + border-top: 1px solid var(--border); +} + +/* Section/Child name */ +[data-theme="sentient"] .tree-name { + font-size: 14px; + font-weight: 500; + color: var(--text); +} + +[data-theme="sentient"] .tree-level-1 .tree-name { + font-size: 13px; + color: var(--text-secondary); +} + +/* View Details link */ +[data-theme="sentient"] .tree-view-details { + font-size: 12px; + color: var(--text-tertiary); + margin-left: 12px; + margin-right: auto; + cursor: pointer; + transition: color 0.15s; +} + +[data-theme="sentient"] .tree-view-details:hover { + color: var(--accent); +} + +/* Step badge - green pill */ +[data-theme="sentient"] .tree-step-badge { + padding: 4px 12px; + background: var(--accent); + color: var(--bg); + border-radius: 4px; + font-size: 11px; + font-weight: 600; + margin-right: 12px; + white-space: nowrap; +} + +[data-theme="sentient"] .tree-section.pending .tree-step-badge, +[data-theme="sentient"] .tree-child.pending .tree-step-badge { + background: var(--surface-active); + color: var(--text-tertiary); +} + +/* Status text */ +[data-theme="sentient"] .tree-status { + font-size: 12px; + color: var(--text-tertiary); + min-width: 80px; + text-align: right; +} + +[data-theme="sentient"] .tree-status.completed { + color: var(--text-secondary); +} + +[data-theme="sentient"] .tree-status.running { + color: var(--accent); +} + +[data-theme="sentient"] .tree-status.failed { + color: var(--error); +} + +/* Tree children container */ +[data-theme="sentient"] .tree-children { + display: none; + background: var(--bg); +} + +[data-theme="sentient"] .tree-section.expanded .tree-children { + display: block; +} + +/* Tree child row */ +[data-theme="sentient"] .tree-child { + border-bottom: 1px solid var(--border-light); +} + +[data-theme="sentient"] .tree-child:last-child { + border-bottom: none; +} + +/* Indent line */ +[data-theme="sentient"] .tree-indent { + width: 20px; + height: 1px; + background: var(--border); + margin-right: 12px; +} + +/* Tree items container */ +[data-theme="sentient"] .tree-items { + display: none; + padding: 8px 0 16px 0; + background: var(--bg); +} + +[data-theme="sentient"] .tree-child.expanded .tree-items { + display: block; +} + +/* Section-level items */ +[data-theme="sentient"] .tree-children > .tree-item { + padding-left: 40px; +} + +/* Individual item row */ +[data-theme="sentient"] .tree-item { + display: flex; + align-items: center; + padding: 10px 20px 10px 60px; + min-height: 36px; +} + +[data-theme="sentient"] .tree-item.running { + background: rgba(var(--accent-rgb), 0.03); +} + +/* Item dot indicator - the colored dots */ +[data-theme="sentient"] .tree-item-dot { + width: 8px; + height: 8px; + border-radius: 50%; + margin-right: 12px; + flex-shrink: 0; + background: var(--text-muted); + transition: all 0.2s; +} + +[data-theme="sentient"] .tree-item-dot.completed { + background: var(--accent); +} + +[data-theme="sentient"] .tree-item-dot.running { + background: var(--accent); + box-shadow: 0 0 8px var(--accent-glow); + animation: sentient-dot-pulse 1.5s ease-in-out infinite; +} + +[data-theme="sentient"] .tree-item-dot.pending { + background: var(--text-muted); +} + +[data-theme="sentient"] .tree-item-dot.failed { + background: var(--error); +} + +@keyframes sentient-dot-pulse { + 0%, + 100% { + opacity: 1; + box-shadow: 0 0 8px var(--accent-glow); + } + 50% { + opacity: 0.6; + box-shadow: 0 0 4px rgba(var(--accent-rgb), 0.2); + } +} + +/* Item name */ +[data-theme="sentient"] .tree-item-name { + flex: 1; + font-size: 13px; + color: var(--text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +[data-theme="sentient"] .tree-item.completed .tree-item-name { + color: var(--text-tertiary); +} + +[data-theme="sentient"] .tree-item.running .tree-item-name { + color: var(--text); +} + +/* Item duration */ +[data-theme="sentient"] .tree-item-duration { + font-size: 12px; + color: var(--text-muted); + margin-right: 8px; + white-space: nowrap; +} + +/* Item checkmark */ +[data-theme="sentient"] .tree-item-check { + font-size: 14px; + width: 20px; + text-align: center; + flex-shrink: 0; +} + +[data-theme="sentient"] .tree-item-check.completed { + color: var(--success); +} + +[data-theme="sentient"] .tree-item-check.running { + color: var(--accent); +} + +[data-theme="sentient"] .tree-item-check.pending { + color: transparent; +} + +/* Progress bar under running sections */ +[data-theme="sentient"] .tree-progress-bar-container { + display: flex; + align-items: center; + gap: 12px; + padding: 0 20px 16px 20px; + background: transparent; + position: relative; +} + +[data-theme="sentient"] .tree-progress-bar-container::before { + content: ""; + position: absolute; + left: 20px; + right: 70px; + height: 3px; + background: var(--border); + border-radius: 2px; +} + +[data-theme="sentient"] .tree-progress-bar { + flex: 1; + height: 3px; + background: var(--accent); + border-radius: 2px; + transition: width 0.3s ease-out; + position: relative; + z-index: 1; +} + +[data-theme="sentient"] .tree-progress-percent { + font-size: 11px; + font-weight: 600; + color: var(--accent); + min-width: 36px; + text-align: right; +} diff --git a/ui/suite/index.html b/ui/suite/index.html index 4e9ad28..fe93e11 100644 --- a/ui/suite/index.html +++ b/ui/suite/index.html @@ -26,6 +26,7 @@ + diff --git a/ui/suite/tasks/autotask.html b/ui/suite/tasks/autotask.html index 562e0f9..aaefb0e 100644 --- a/ui/suite/tasks/autotask.html +++ b/ui/suite/tasks/autotask.html @@ -476,4 +476,6 @@ Examples:
+ + diff --git a/ui/suite/tasks/autotask.js b/ui/suite/tasks/autotask.js index 02361cc..8aaa8b9 100644 --- a/ui/suite/tasks/autotask.js +++ b/ui/suite/tasks/autotask.js @@ -206,6 +206,11 @@ function initTaskProgressWebSocket() { } function handleTaskProgressMessage(data) { + // Forward to ProgressPanel if available + if (typeof ProgressPanel !== "undefined" && ProgressPanel.manifest) { + ProgressPanel.handleProgressUpdate(data); + } + console.log("Task progress:", data); switch (data.type) { @@ -394,11 +399,24 @@ function loadIntentDetail(intentId) { `; - // Fetch intent details - fetch(`/api/autotask/${intentId}`) - .then((response) => response.json()) - .then((data) => { - renderIntentDetail(data); + // Fetch intent details and manifest in parallel + Promise.all([ + fetch(`/api/autotask/${intentId}`).then((r) => r.json()), + fetch(`/api/autotask/${intentId}/manifest`) + .then((r) => r.json()) + .catch(() => null), + ]) + .then(([taskData, manifestData]) => { + renderIntentDetail(taskData); + + // If manifest exists, initialize ProgressPanel + if (manifestData && manifestData.success && manifestData.manifest) { + if (typeof ProgressPanel !== "undefined") { + ProgressPanel.manifest = manifestData.manifest; + ProgressPanel.init(intentId); + ProgressPanel.render(); + } + } }) .catch((error) => { console.error("Failed to load intent detail:", error); @@ -590,6 +608,11 @@ function toggleLogEntry(header) { } function closeDetailPanel() { + // Clean up ProgressPanel if active + if (typeof ProgressPanel !== "undefined") { + ProgressPanel.destroy(); + } + AutoTaskState.selectedIntentId = null; document.querySelectorAll(".intent-card").forEach((card) => { diff --git a/ui/suite/tasks/progress-panel.css b/ui/suite/tasks/progress-panel.css new file mode 100644 index 0000000..3c8e7b7 --- /dev/null +++ b/ui/suite/tasks/progress-panel.css @@ -0,0 +1,555 @@ +.progress-panel { + display: flex; + flex-direction: column; + gap: 24px; + padding: 20px; + background: var(--bg-primary, #0a0a0f); + color: var(--text-primary, #e0e0e0); + font-family: + "Inter", + -apple-system, + BlinkMacSystemFont, + sans-serif; + border-radius: 12px; + border: 1px solid var(--border-color, #1a1a24); + height: 100%; + min-height: 0; + overflow: hidden; +} + +.status-section { + background: var(--bg-secondary, #111118); + border-radius: 8px; + padding: 16px 20px; + border: 1px solid var(--border-color, #1a1a24); + flex-shrink: 0; + min-height: 120px; +} + +.status-header { + margin-bottom: 12px; +} + +.status-label { + font-size: 11px; + font-weight: 600; + letter-spacing: 1.2px; + color: var(--text-muted, #666); + text-transform: uppercase; +} + +.status-content { + display: flex; + flex-direction: column; + gap: 8px; +} + +.status-title-row { + display: flex; + align-items: center; + justify-content: space-between; +} + +.status-title { + font-size: 16px; + font-weight: 500; + color: var(--text-primary, #e0e0e0); +} + +.status-time { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; +} + +.runtime-label, +.estimated-label { + color: var(--text-muted, #666); +} + +.runtime-value, +.estimated-value { + color: var(--text-primary, #e0e0e0); +} + +.status-indicator { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--text-muted, #666); +} + +.status-indicator.active { + background: var(--accent-yellow, #d4e94c); + box-shadow: 0 0 8px var(--accent-yellow, #d4e94c); +} + +.status-current-action { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 0; +} + +.action-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--text-muted, #666); + flex-shrink: 0; +} + +.action-dot.active { + background: var(--accent-yellow, #d4e94c); +} + +.action-text { + font-size: 14px; + color: var(--text-primary, #e0e0e0); + flex: 1; +} + +.estimated-time { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + margin-left: auto; +} + +.settings-icon { + color: var(--text-muted, #666); + cursor: pointer; + transition: color 0.2s; +} + +.settings-icon:hover { + color: var(--text-primary, #e0e0e0); +} + +.status-decision-point { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 0; + opacity: 0.7; +} + +.decision-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--text-muted, #444); + flex-shrink: 0; +} + +.decision-dot.pending { + background: var(--text-muted, #444); + border: 1px dashed var(--text-muted, #666); +} + +.decision-text { + font-size: 13px; + color: var(--text-muted, #888); +} + +.decision-badge { + font-size: 12px; + padding: 4px 10px; + background: var(--bg-tertiary, #1a1a24); + border-radius: 4px; + color: var(--text-muted, #888); + margin-left: auto; +} + +.progress-log-section { + background: var(--bg-secondary, #111118); + border-radius: 8px; + border: 1px solid var(--border-color, #1a1a24); + overflow: hidden; + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; +} + +.progress-log-header { + padding: 14px 20px; + border-bottom: 1px solid var(--border-color, #1a1a24); + flex-shrink: 0; +} + +.progress-log-label { + font-size: 11px; + font-weight: 600; + letter-spacing: 1.2px; + color: var(--text-muted, #666); + text-transform: uppercase; +} + +.progress-log-content { + padding: 0; + flex: 1; + min-height: 0; + overflow-y: auto; + overflow-x: hidden; +} + +.log-section { + border-bottom: 1px solid var(--border-color, #1a1a24); + min-height: 48px; +} + +.log-section:last-child { + border-bottom: none; +} + +.log-section-header { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 20px; + cursor: pointer; + transition: background 0.2s; + min-height: 48px; +} + +.log-section-header:hover { + background: var(--bg-hover, #151520); +} + +.section-indicator { + width: 10px; + height: 10px; + border-radius: 50%; + flex-shrink: 0; +} + +.section-indicator.completed { + background: var(--accent-yellow, #d4e94c); +} + +.section-indicator.running { + background: var(--accent-blue, #4c9ee9); + animation: pulse 1.5s infinite; +} + +.section-indicator.pending { + background: var(--text-muted, #444); +} + +.section-indicator.failed { + background: var(--accent-red, #e94c4c); +} + +@keyframes pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +.section-name { + font-size: 14px; + font-weight: 500; + color: var(--text-primary, #e0e0e0); +} + +.section-details-link { + font-size: 12px; + color: var(--text-muted, #666); + cursor: pointer; + transition: color 0.2s; +} + +.section-details-link:hover { + color: var(--accent-yellow, #d4e94c); +} + +.section-step-badge { + margin-left: auto; + font-size: 12px; + padding: 4px 10px; + background: var(--accent-yellow, #d4e94c); + color: var(--bg-primary, #0a0a0f); + border-radius: 4px; + font-weight: 600; +} + +.section-status-badge { + font-size: 12px; + padding: 4px 10px; + border-radius: 4px; + font-weight: 500; +} + +.section-status-badge.completed { + background: transparent; + color: var(--text-muted, #888); +} + +.section-status-badge.running { + background: var(--accent-blue, #4c9ee9); + color: var(--bg-primary, #0a0a0f); +} + +.section-status-badge.pending { + background: var(--bg-tertiary, #1a1a24); + color: var(--text-muted, #666); +} + +.log-section-body { + display: none; + padding-left: 20px; + background: var(--bg-tertiary, #0d0d14); +} + +.log-section.expanded .log-section-body { + display: block; +} + +.log-children { + padding: 0; +} + +.log-child { + border-bottom: 1px solid var(--border-subtle, #151520); +} + +.log-child:last-child { + border-bottom: none; +} + +.log-child-header { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 20px 12px 32px; + cursor: pointer; + transition: background 0.2s; +} + +.log-child-header:hover { + background: var(--bg-hover, #131320); +} + +.child-indent { + width: 16px; + height: 1px; + background: var(--border-color, #1a1a24); + flex-shrink: 0; +} + +.child-name { + font-size: 13px; + color: var(--text-secondary, #aaa); +} + +.child-details-link { + font-size: 11px; + color: var(--text-muted, #555); + cursor: pointer; + transition: color 0.2s; +} + +.child-details-link:hover { + color: var(--accent-yellow, #d4e94c); +} + +.child-step-badge { + margin-left: auto; + font-size: 11px; + padding: 3px 8px; + background: var(--accent-yellow, #d4e94c); + color: var(--bg-primary, #0a0a0f); + border-radius: 4px; + font-weight: 600; +} + +.child-status-badge { + font-size: 11px; + padding: 3px 8px; + border-radius: 4px; +} + +.child-status-badge.completed { + color: var(--text-muted, #888); +} + +.log-child-body { + display: none; + padding-left: 48px; +} + +.log-child.expanded .log-child-body { + display: block; +} + +.log-items { + padding: 8px 0; +} + +.log-item { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 20px; + font-size: 12px; +} + +.item-dot { + width: 6px; + height: 6px; + border-radius: 50%; + flex-shrink: 0; +} + +.item-dot.completed { + background: var(--accent-yellow, #d4e94c); +} + +.item-dot.running { + background: var(--accent-blue, #4c9ee9); +} + +.item-dot.pending { + background: var(--text-muted, #444); +} + +.item-name { + color: var(--text-secondary, #999); + flex: 1; +} + +.item-info { + display: flex; + align-items: center; + gap: 8px; + margin-left: auto; +} + +.item-duration { + font-size: 11px; + color: var(--text-muted, #666); +} + +.item-check { + font-size: 14px; +} + +.item-check.completed { + color: var(--accent-green, #4ce97a); +} + +.item-check.running { + color: var(--accent-blue, #4c9ee9); +} + +.terminal-section { + background: var(--bg-terminal, #0a0a0f); + border-radius: 8px; + border: 1px solid var(--border-color, #1a1a24); + overflow: hidden; + flex-shrink: 0; + min-height: 150px; + max-height: 250px; + display: flex; + flex-direction: column; +} + +.terminal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 14px 20px; + border-bottom: 1px solid var(--border-color, #1a1a24); + background: var(--bg-secondary, #111118); + flex-shrink: 0; +} + +.terminal-label { + font-size: 11px; + font-weight: 600; + letter-spacing: 1.2px; + color: var(--text-muted, #666); + text-transform: uppercase; +} + +.terminal-stats { + display: flex; + align-items: center; + gap: 4px; + font-size: 12px; + color: var(--text-muted, #666); +} + +.terminal-stats strong { + color: var(--text-primary, #e0e0e0); + font-weight: 500; +} + +.stat-separator { + margin: 0 8px; + color: var(--text-muted, #444); +} + +.terminal-content { + padding: 16px 20px; + font-family: "JetBrains Mono", "Fira Code", "Monaco", monospace; + font-size: 13px; + line-height: 1.6; + flex: 1; + min-height: 0; + overflow-y: auto; + background: var(--bg-terminal, #0a0a0f); +} + +.terminal-line { + color: var(--text-terminal, #8b8b8b); + padding: 2px 0; +} + +.terminal-line.info { + color: var(--text-terminal, #8b8b8b); +} + +.terminal-line.success { + color: var(--accent-green, #4ce97a); +} + +.terminal-line.error { + color: var(--accent-red, #e94c4c); +} + +.terminal-line.warning { + color: var(--accent-yellow, #d4e94c); +} + +.terminal-line.progress { + color: var(--accent-blue, #4c9ee9); +} + +.progress-log-content::-webkit-scrollbar, +.terminal-content::-webkit-scrollbar { + width: 6px; +} + +.progress-log-content::-webkit-scrollbar-track, +.terminal-content::-webkit-scrollbar-track { + background: var(--bg-tertiary, #0d0d14); +} + +.progress-log-content::-webkit-scrollbar-thumb, +.terminal-content::-webkit-scrollbar-thumb { + background: var(--border-color, #1a1a24); + border-radius: 3px; +} + +.progress-log-content::-webkit-scrollbar-thumb:hover, +.terminal-content::-webkit-scrollbar-thumb:hover { + background: var(--text-muted, #444); +} diff --git a/ui/suite/tasks/progress-panel.html b/ui/suite/tasks/progress-panel.html new file mode 100644 index 0000000..b3e0aea --- /dev/null +++ b/ui/suite/tasks/progress-panel.html @@ -0,0 +1,103 @@ +$1");
+
+ // Code blocks (```code```)
+ text = text.replace(
+ /```([\s\S]*?)```/g,
+ '