diff --git a/ccw/src/templates/dashboard-css/04-lite-tasks.css b/ccw/src/templates/dashboard-css/04-lite-tasks.css index e2631dc1..87032d6f 100644 --- a/ccw/src/templates/dashboard-css/04-lite-tasks.css +++ b/ccw/src/templates/dashboard-css/04-lite-tasks.css @@ -1190,12 +1190,27 @@ border-radius: 0.5rem; padding: 1rem; cursor: pointer; - transition: all 0.2s ease; + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; +} + +.multi-cli-card::before { + content: ""; + position: absolute; + inset: 0; + border-radius: 0.5rem; + border: 2px solid transparent; + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); } .multi-cli-card:hover { border-color: hsl(var(--purple, 280 60% 50%) / 0.5); - box-shadow: 0 4px 12px hsl(var(--purple, 280 60% 50%) / 0.1); + box-shadow: 0 4px 16px hsl(var(--purple, 280 60% 50%) / 0.15); + transform: translateY(-2px); +} + +.multi-cli-card:hover::before { + border-color: hsl(var(--purple, 280 60% 50%) / 0.3); } .multi-cli-card-header { @@ -1246,7 +1261,8 @@ font-weight: 500; } -.multi-cli-status.converged { +.multi-cli-status.converged, +.multi-cli-status.decided { background: hsl(var(--success-light, 142 70% 95%)); color: hsl(var(--success, 142 70% 45%)); } @@ -1256,16 +1272,25 @@ color: hsl(var(--warning, 45 90% 40%)); } -.multi-cli-status.blocked { +.multi-cli-status.blocked, +.multi-cli-status.conflict { background: hsl(var(--destructive) / 0.1); color: hsl(var(--destructive)); } -.multi-cli-status.pending { - background: hsl(var(--muted)); +.multi-cli-status.pending, +.multi-cli-status.exploring, +.multi-cli-status.initialized { +) background: hsl(var(--muted)); color: hsl(var(--muted-foreground)); } +.multi-cli-status.plan_generated, +.multi-cli-status.completed { + background: hsl(var(--success-light, 142 70% 95%)); + color: hsl(var(--success, 142 70% 45%)); +} + /* Multi-CLI Detail Page */ .multi-cli-detail-page { display: flex; @@ -1286,6 +1311,25 @@ flex: 1; } +.multi-cli-detail-meta { + display: flex; + align-items: center; + gap: 1rem; + flex-wrap: wrap; + font-size: 0.85rem; + color: hsl(var(--muted-foreground)); +} + +.multi-cli-detail-meta .info-label { + font-weight: 500; + color: hsl(var(--muted-foreground)); +} + +.multi-cli-detail-meta .info-value { + color: hsl(var(--foreground)); + font-weight: 500; +} + .multi-cli-detail-title { font-size: 1.25rem; font-weight: 600; @@ -1305,43 +1349,70 @@ /* Multi-CLI Tabs */ .multi-cli-tabs { display: flex; - gap: 0.25rem; + gap: 0; border-bottom: 1px solid hsl(var(--border)); - margin-bottom: 1rem; + margin-bottom: 1.5rem; overflow-x: auto; + background: hsl(var(--muted) / 0.2); + padding: 0 0.5rem; + border-radius: 0.5rem 0.5rem 0 0; } .multi-cli-tab { - padding: 0.75rem 1rem; + padding: 0.75rem 1.25rem; font-size: 0.875rem; font-weight: 500; color: hsl(var(--muted-foreground)); background: transparent; border: none; - border-bottom: 2px solid transparent; + border-bottom: 3px solid transparent; cursor: pointer; - transition: all 0.2s ease; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); white-space: nowrap; + border-radius: 0.375rem 0.375rem 0 0; + display: flex; + align-items: center; + gap: 0.5rem; } .multi-cli-tab:hover { color: hsl(var(--foreground)); background: hsl(var(--hover)); + border-bottom-color: hsl(var(--purple, 280 60% 50%) / 0.5); } .multi-cli-tab.active { color: hsl(var(--purple, 280 60% 50%)); border-bottom-color: hsl(var(--purple, 280 60% 50%)); + background: hsl(var(--purple, 280 60% 50%) / 0.05); + font-weight: 600; +} + +.multi-cli-tab .tab-icon { + width: 1rem; + height: 1rem; } .multi-cli-tab-content { display: none; + animation: fadeIn 0.2s ease; } .multi-cli-tab-content.active { display: block; } +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + /* Multi-CLI Topic Tab */ .multi-cli-topic-section { background: hsl(var(--muted) / 0.3); @@ -1349,6 +1420,12 @@ border-radius: 0.5rem; padding: 1rem; margin-bottom: 1rem; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.multi-cli-topic-section:hover { + border-color: hsl(var(--purple, 280 60% 50%) / 0.3); + box-shadow: 0 2px 8px hsl(var(--purple, 280 60% 50%) / 0.05); } .multi-cli-topic-title { @@ -1359,6 +1436,7 @@ display: flex; align-items: center; gap: 0.5rem; + letter-spacing: -0.01em; } .multi-cli-topic-description { @@ -1379,13 +1457,20 @@ /* Tag Badge */ .tag-badge { - display: inline-block; + display: inline-flex; + align-items: center; + gap: 0.25rem; padding: 0.25rem 0.5rem; background: hsl(var(--primary) / 0.1); color: hsl(var(--primary)); border-radius: 0.25rem; font-size: 0.75rem; font-weight: 500; + transition: background 0.2s ease, color 0.2s ease; +} + +.tag-badge:hover { + background: hsl(var(--primary) / 0.15); } /* Additional Multi-CLI Status Variants */ @@ -1613,23 +1698,32 @@ gap: 0.75rem; } +/* Unified Solution Card - used in both Decision and Rounds tabs */ .solution-card { background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: 0.5rem; - padding: 1rem; - transition: all 0.2s ease; + overflow: hidden; + transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease; +} + +.solution-card:hover { + border-color: hsl(var(--primary) / 0.3); + box-shadow: 0 4px 12px hsla(var(--primary), 0.1); + transform: translateY(-1px); } .solution-card.selected { border-color: hsl(var(--success, 142 70% 45%)); background: hsl(var(--success, 142 70% 45%) / 0.05); + box-shadow: 0 0 0 6px hsl(var(--success, 142 70% 45%) / 0.15); } .solution-card.rejected { border-color: hsl(var(--destructive) / 0.5); background: hsl(var(--destructive) / 0.03); - opacity: 0.8; + opacity: 0.7; + text-decoration: line-through; } .solution-header { @@ -1638,6 +1732,9 @@ justify-content: space-between; gap: 1rem; margin-bottom: 0.75rem; + padding: 0.75rem 1rem; + background: hsl(var(--muted) / 0.2); + border-bottom: 1px solid hsl(var(--border)); } .solution-title { @@ -1740,8 +1837,9 @@ .timeline-event { display: flex; - gap: 1rem; + gap: 1.25rem; position: relative; + margin-bottom: 0.25rem; } .timeline-marker { @@ -1749,7 +1847,7 @@ flex-direction: column; align-items: center; flex-shrink: 0; - width: 2rem; + width: 2.25rem; } .timeline-dot { @@ -1761,6 +1859,13 @@ justify-content: center; z-index: 1; flex-shrink: 0; + box-shadow: 0 0 0 3px currentColor; + transition: all 0.2s ease; +} + +.timeline-dot:hover { + transform: scale(1.2); + box-shadow: 0 0 0 8px currentColor; } .timeline-dot.proposal { @@ -1806,12 +1911,15 @@ background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: 0.5rem; - padding: 0.75rem; + padding: 1rem; margin-bottom: 0.75rem; + transition: all 0.2s ease; } .timeline-content:hover { - border-color: hsl(var(--primary) / 0.3); + border-color: hsl(var(--primary) / 0.4); + box-shadow: 0 2px 8px hsl(var(--primary) / 0.05); + transform: translateX(4px); } .timeline-event-header { @@ -1989,16 +2097,20 @@ flex-direction: column; align-items: center; justify-content: center; - padding: 3rem; + padding: 4rem 2rem; text-align: center; color: hsl(var(--muted-foreground)); + background: hsl(var(--muted) / 0.2); + border: 1px dashed hsl(var(--border)); + border-radius: 0.75rem; } .multi-cli-empty-icon { - width: 3rem; - height: 3rem; - margin-bottom: 1rem; - color: hsl(var(--muted-foreground) / 0.5); + width: 3.5rem; + height: 3.5rem; + margin-bottom: 1.25rem; + color: hsl(var(--purple, 280 60% 50%) / 0.4); + animation: float 3s ease-in-out infinite; } .multi-cli-empty-title { @@ -2010,6 +2122,17 @@ .multi-cli-empty-description { font-size: 0.875rem; + color: hsl(var(--muted-foreground)); + line-height: 1.6; +} + +@keyframes float { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-8px); + } } /* =================================== @@ -2020,26 +2143,38 @@ .scope-list { display: flex; flex-direction: column; - gap: 0.375rem; + gap: 0.5rem; margin: 0; - padding-left: 1rem; + padding-left: 0; font-size: 0.85rem; } .scope-list li { - padding: 0.375rem 0.5rem; - background: hsl(var(--muted) / 0.3); - border-radius: 0.25rem; + padding: 0.5rem 0.75rem; + background: hsl(var(--card)); + border: 1px solid hsl(var(--border)); + border-radius: 0.375rem; font-family: var(--font-mono); color: hsl(var(--foreground)); - line-height: 1.4; + line-height: 1.5; + transition: all 0.2s ease; +} + +.scope-list li:hover { + border-color: hsl(var(--primary) / 0.5); + transform: translateX(4px); } .scope-list.excluded li { - background: hsl(var(--destructive) / 0.08); + background: hsl(var(--destructive) / 0.05); + border-color: hsl(var(--destructive) / 0.2); color: hsl(var(--muted-foreground)); text-decoration: line-through; - opacity: 0.8; + opacity: 0.7; +} + +.scope-list.excluded li:hover { + border-color: hsl(var(--destructive) / 0.4); } /* Acceptance List */ @@ -2195,7 +2330,7 @@ .cons-list { display: flex; flex-direction: column; - gap: 0.375rem; + gap: 0.5rem; margin: 0; padding: 0; list-style: none; @@ -2205,17 +2340,24 @@ .con-item { display: flex; align-items: flex-start; - gap: 0.5rem; - padding: 0.5rem 0.75rem; - border-radius: 0.375rem; + gap: 0.75rem; + padding: 0.75rem 1rem; + border-radius: 0.5rem; font-size: 0.85rem; - line-height: 1.5; + line-height: 1.6; color: hsl(var(--foreground)); + transition: all 0.2s ease; + position: relative; } .pro-item { background: hsl(142 71% 45% / 0.08); - border-left: 3px solid #10b981; + border-left: 4px solid #10b981; +} + +.pro-item:hover { + background: hsl(142 71% 45% / 0.12); + transform: translateX(4px); } .pro-item::before { @@ -2223,11 +2365,26 @@ font-weight: 700; color: #10b981; flex-shrink: 0; + width: 1.25rem; + height: 1.25rem; + display: flex; + align-items: center; + justify-content: center; + background: #10b981; + color: white; + border-radius: 50%; + font-size: 1rem; + line-height: 1; } .con-item { background: hsl(0 84% 60% / 0.08); - border-left: 3px solid #ef4444; + border-left: 4px solid #ef4444; +} + +.con-item:hover { + background: hsl(0 84% 60% / 0.12); + transform: translateX(4px); } .con-item::before { @@ -2235,40 +2392,64 @@ font-weight: 700; color: #ef4444; flex-shrink: 0; + width: 1.25rem; + height: 1.25rem; + display: flex; + align-items: center; + justify-content: center; + background: #ef4444; + color: white; + border-radius: 50%; + font-size: 1.25rem; + line-height: 1; } /* Rounds Navigation */ .rounds-nav { display: flex; - gap: 0.25rem; + gap: 0.5rem; flex-wrap: wrap; - padding-bottom: 0.75rem; + padding-bottom: 1rem; border-bottom: 1px solid hsl(var(--border)); margin-bottom: 1rem; + background: hsl(var(--muted) / 0.2); + padding: 0.5rem; + border-radius: 0.5rem; } .round-item { - padding: 0.5rem 0.875rem; - background: hsl(var(--muted)); + padding: 0.5rem 1rem; + background: hsl(var(--card)); border: 1px solid hsl(var(--border)); - border-radius: 0.375rem; + border-radius: 0.5rem; font-size: 0.8rem; font-weight: 500; color: hsl(var(--muted-foreground)); cursor: pointer; - transition: all 0.2s ease; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + display: flex; + align-items: center; + gap: 0.375rem; } .round-item:hover { background: hsl(var(--hover)); - border-color: hsl(var(--purple, 280 60% 50%) / 0.4); + border-color: hsl(var(--purple, 280 60% 50%) / 0.5); color: hsl(var(--foreground)); + transform: translateY(-2px); + box-shadow: 0 2px 8px hsl(var(--purple, 280 60% 50%) / 0.1); } .round-item.active { background: hsl(var(--purple, 280 60% 50%)); border-color: hsl(var(--purple, 280 60% 50%)); color: white; + box-shadow: 0 2px 8px hsl(var(--purple, 280 60% 50%) / 0.25); +} + +.round-item i { + width: 0.875rem; + height: 0.875rem; } /* Timeline Event Parts */ @@ -2350,6 +2531,112 @@ color: hsl(var(--primary)); } +/* File Tree List */ +.file-tree-list { + display: flex; + flex-direction: column; + gap: 0.25rem; + padding: 0.5rem; + background: hsl(var(--card)); + border: 1px solid hsl(var(--border)); + border-radius: 0.375rem; +} + +/* Impact List */ +.impact-list { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 0.5rem; + background: hsl(var(--card)); + border: 1px solid hsl(var(--border)); + border-radius: 0.375rem; +} + +.impact-item { + display: flex; + flex-direction: column; + gap: 0.375rem; + padding: 0.75rem; + background: hsl(var(--muted) / 0.3); + border-radius: 0.375rem; + border-left: 3px solid hsl(var(--warning)); +} + +.impact-item.impact-high { + border-left-color: hsl(var(--destructive)); +} + +.impact-item.impact-medium { + border-left-color: hsl(var(--warning)); +} + +.impact-item.impact-low { + border-left-color: hsl(var(--success)); +} + +.impact-header { + display: flex; + align-items: center; + gap: 0.5rem; + flex-wrap: wrap; +} + +.impact-file { + font-family: var(--font-mono); + font-size: 0.85rem; + color: hsl(var(--foreground)); + background: hsl(var(--muted)); + padding: 0.125rem 0.375rem; + border-radius: 0.25rem; +} + +.impact-line { + font-size: 0.8rem; + color: hsl(var(--muted-foreground)); +} + +.impact-score { + font-size: 0.75rem; + font-weight: 500; + padding: 0.125rem 0.5rem; + border-radius: 0.25rem; + text-transform: uppercase; +} + +.impact-score.high { + background: hsl(var(--destructive) / 0.15); + color: hsl(var(--destructive)); +} + +.impact-score.medium { + background: hsl(var(--warning) / 0.15); + color: hsl(var(--warning)); +} + +.impact-score.low { + background: hsl(var(--success) / 0.15); + color: hsl(var(--success)); +} + +.impact-reason { + font-size: 0.85rem; + color: hsl(var(--muted-foreground)); + line-height: 1.5; + padding-left: 0.25rem; +} + +/* Dependencies List */ +.deps-list { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 0.5rem; + background: hsl(var(--card)); + border: 1px solid hsl(var(--border)); + border-radius: 0.375rem; +} + /* Timeline Connecting Line */ .timeline-event::before { content: ""; @@ -2366,3 +2653,227 @@ display: none; } +/* Round Solutions */ +.round-solutions { + margin-top: 1.5rem; +} + +.round-solutions > strong { + display: block; + margin-bottom: 0.75rem; + font-size: 0.95rem; + color: hsl(var(--foreground)); +} + +.solutions-list { + display: flex; + flex-direction: column; + gap: 1rem; +} + +/* Solution Card Styles (extensions to unified .solution-card above) */ +.solution-number { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 1.5rem; + height: 1.5rem; + background: hsl(var(--primary)); + color: hsl(var(--primary-foreground)); + border-radius: 0.25rem; + font-size: 0.75rem; + font-weight: 600; + margin-right: 0.5rem; +} + +.solution-name { + font-weight: 600; + font-size: 0.95rem; + color: hsl(var(--foreground)); + flex: 1; +} + +.solution-meta { + display: flex; + flex-direction: column; + gap: 0.5rem; + font-size: 0.85rem; +} + +.source-clis { + display: flex; + gap: 0.375rem; + flex-wrap: wrap; +} + +.cli-badge { + display: inline-block; + padding: 0.125rem 0.5rem; + background: hsl(var(--purple, 280 60% 50%) / 0.15); + color: hsl(var(--purple, 280 60% 50%)); + border: 1px solid hsl(var(--purple, 280 60% 50%) / 0.3); + border-radius: 0.25rem; + font-size: 0.75rem; + font-weight: 500; +} + +.solution-scores { + display: flex; + gap: 0.375rem; + flex-wrap: wrap; +} + +.score-badge { + display: inline-block; + padding: 0.125rem 0.5rem; + border-radius: 0.25rem; + font-size: 0.75rem; + font-weight: 500; +} + +.score-badge.feasibility { + background: hsl(var(--success) / 0.15); + color: hsl(var(--success)); + border: 1px solid hsl(var(--success) / 0.3); +} + +.score-badge.effort-low { + background: hsl(var(--success) / 0.15); + color: hsl(var(--success)); + border: 1px solid hsl(var(--success) / 0.3); +} + +.score-badge.effort-medium { + background: hsl(var(--warning) / 0.15); + color: hsl(var(--warning)); + border: 1px solid hsl(var(--warning) / 0.3); +} + +.score-badge.effort-high { + background: hsl(var(--destructive) / 0.15); + color: hsl(var(--destructive)); + border: 1px solid hsl(var(--destructive) / 0.3); +} + +.score-badge.risk-low { + background: hsl(var(--success) / 0.15); + color: hsl(var(--success)); + border: 1px solid hsl(var(--success) / 0.3); +} + +.score-badge.risk-medium { + background: hsl(var(--warning) / 0.15); + color: hsl(var(--warning)); + border: 1px solid hsl(var(--warning) / 0.3); +} + +.score-badge.risk-high { + background: hsl(var(--destructive) / 0.15); + color: hsl(var(--destructive)); + border: 1px solid hsl(var(--destructive) / 0.3); +} + +.solution-summary { + padding: 1rem; + font-size: 0.875rem; + color: hsl(var(--foreground)); + line-height: 1.5; + border-bottom: 1px solid hsl(var(--border)); +} + +.solution-approach, +.solution-dependencies, +.solution-concerns { + border-bottom: 1px solid hsl(var(--border)); +} + +.solution-approach:last-child, +.solution-dependencies:last-child, +.solution-concerns:last-child { + border-bottom: none; +} + +.solution-tasks, +.solution-milestones, +.internal-deps, +.external-deps { + margin-top: 0.75rem; +} + +.solution-tasks > strong, +.solution-milestones > strong, +.internal-deps > strong, +.external-deps > strong { + display: block; + margin-bottom: 0.5rem; + font-size: 0.85rem; + color: hsl(var(--foreground)); +} + +.task-list, +.milestone-list, +.dep-list, +.concern-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +.task-item, +.milestone-item, +.dep-item, +.concern-item { + padding: 0.375rem 0.5rem; + background: hsl(var(--muted) / 0.2); + border-radius: 0.25rem; + font-size: 0.85rem; + color: hsl(var(--foreground)); + line-height: 1.4; +} + +.task-id { + display: inline-block; + min-width: 2rem; + font-weight: 600; + color: hsl(var(--primary)); + margin-right: 0.5rem; + font-size: 0.75rem; +} + +.task-name { + flex: 1; +} + +.task-key-point { + display: block; + margin-top: 0.25rem; + font-size: 0.75rem; + color: hsl(var(--muted-foreground)); + font-style: italic; +} + +.execution-flow { + margin-top: 0.75rem; +} + +.execution-flow > strong { + display: block; + margin-bottom: 0.5rem; + font-size: 0.85rem; + color: hsl(var(--foreground)); +} + +.flow-code { + display: block; + padding: 0.5rem; + background: hsl(var(--muted)); + border-radius: 0.25rem; + font-size: 0.8rem; + font-family: var(--font-mono); + color: hsl(var(--foreground)); + overflow-x: auto; +} + diff --git a/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js b/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js index d155ca50..8561a1a5 100644 --- a/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +++ b/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js @@ -3,6 +3,12 @@ * Real-time streaming output viewer for CLI executions */ +// ===== Lifecycle Management ===== +let cliStreamViewerDestroy = null; +let streamKeyboardHandler = null; +let streamScrollHandler = null; // Track scroll listener +let streamStatusTimers = []; // Track status update timers + // ===== State Management ===== let cliStreamExecutions = {}; // { executionId: { tool, mode, output, status, startTime, endTime } } let activeStreamTab = null; @@ -91,7 +97,7 @@ async function syncActiveExecutions() { // ===== Initialization ===== function initCliStreamViewer() { // Initialize keyboard shortcuts - document.addEventListener('keydown', function(e) { + streamKeyboardHandler = function(e) { if (e.key === 'Escape' && isCliStreamViewerOpen) { if (searchFilter) { clearSearch(); @@ -108,12 +114,14 @@ function initCliStreamViewer() { searchInput.select(); } } - }); + }; + document.addEventListener('keydown', streamKeyboardHandler); // Initialize scroll detection for auto-scroll const content = document.getElementById('cliStreamContent'); if (content) { - content.addEventListener('scroll', handleStreamContentScroll); + streamScrollHandler = handleStreamContentScroll; + content.addEventListener('scroll', streamScrollHandler); } // Sync active executions from server (recover state for mid-execution joins) @@ -592,11 +600,12 @@ function renderStreamStatus(executionId) { // Update duration periodically for running executions if (exec.status === 'running') { - setTimeout(() => { + const timerId = setTimeout(() => { if (activeStreamTab === executionId && cliStreamExecutions[executionId]?.status === 'running') { renderStreamStatus(executionId); } }, 1000); + streamStatusTimers.push(timerId); } } @@ -760,6 +769,31 @@ if (document.readyState === 'loading') { initCliStreamViewer(); } +// ===== Lifecycle Functions ===== +function destroyCliStreamViewer() { + // Remove keyboard event listener if exists + if (streamKeyboardHandler) { + document.removeEventListener('keydown', streamKeyboardHandler); + streamKeyboardHandler = null; + } + + // Remove scroll event listener if exists + if (streamScrollHandler) { + const content = document.getElementById('cliStreamContent'); + if (content) { + content.removeEventListener('scroll', streamScrollHandler); + } + streamScrollHandler = null; + } + + // Clear all pending status update timers + streamStatusTimers.forEach(timerId => clearTimeout(timerId)); + streamStatusTimers = []; +} + +// Export lifecycle functions +window.destroyCliStreamViewer = destroyCliStreamViewer; + // ===== Global Exposure ===== window.toggleCliStreamViewer = toggleCliStreamViewer; window.handleCliStreamStarted = handleCliStreamStarted; diff --git a/ccw/src/templates/dashboard-js/components/navigation.js b/ccw/src/templates/dashboard-js/components/navigation.js index 61db3fab..83fe734f 100644 --- a/ccw/src/templates/dashboard-js/components/navigation.js +++ b/ccw/src/templates/dashboard-js/components/navigation.js @@ -1,6 +1,9 @@ // Navigation and Routing // Manages navigation events, active state, content title updates, search, and path selector +// View lifecycle management +var currentViewDestroy = null; + // Path Selector function initPathSelector() { const btn = document.getElementById('pathButton'); @@ -54,6 +57,11 @@ function initPathSelector() { // Cleanup function for view transitions function cleanupPreviousView() { + // Call current view's destroy function if exists + if (currentViewDestroy) { + currentViewDestroy(); + currentViewDestroy = null; + } // Cleanup graph explorer if (currentView === 'graph-explorer' && typeof window.cleanupGraphExplorer === 'function') { window.cleanupGraphExplorer(); @@ -118,9 +126,13 @@ function initNavigation() { } else if (currentView === 'explorer') { renderExplorer(); } else if (currentView === 'cli-manager') { - renderCliManager(); + renderClaudeManager(); } else if (currentView === 'cli-history') { renderCliHistoryView(); + // Register destroy function for cli-history view + if (typeof window.destroyCliHistoryView === 'function') { + currentViewDestroy = window.destroyCliHistoryView; + } } else if (currentView === 'hook-manager') { renderHookManager(); } else if (currentView === 'memory') { @@ -133,6 +145,10 @@ function initNavigation() { renderRulesManager(); } else if (currentView === 'claude-manager') { renderClaudeManager(); + // Register destroy function for claude-manager view + if (typeof window.initClaudeManager === 'function') { + window.initClaudeManager(); + } } else if (currentView === 'graph-explorer') { renderGraphExplorer(); } else if (currentView === 'help') { diff --git a/ccw/src/templates/dashboard-js/views/claude-manager.js b/ccw/src/templates/dashboard-js/views/claude-manager.js index 4fb548ad..39762ff5 100644 --- a/ccw/src/templates/dashboard-js/views/claude-manager.js +++ b/ccw/src/templates/dashboard-js/views/claude-manager.js @@ -1,6 +1,10 @@ // CLAUDE.md Manager View // Three-column layout: File Tree | Viewer/Editor | Metadata & Actions +// ========== Lifecycle Management ========== +var claudeManagerDestroy = null; +var createDialogOverlay = null; // Track create dialog overlay for cleanup + // ========== State Management ========== var claudeFilesData = { user: { main: null }, @@ -19,9 +23,15 @@ var fileTreeExpanded = { var searchQuery = ''; var freshnessData = {}; // { [filePath]: FreshnessResult } var freshnessSummary = null; +var searchKeyboardHandlerAdded = false; // ========== Main Render Function ========== async function renderClaudeManager() { + // Initialize lifecycle + if (typeof initClaudeManager === 'function') { + initClaudeManager(); + } + var container = document.getElementById('mainContent'); if (!container) return; @@ -749,9 +759,11 @@ function filterFileTree(query) { renderFileTree(); // Add keyboard shortcut handler - if (query && !window.claudeSearchKeyboardHandlerAdded) { - document.addEventListener('keydown', handleSearchKeyboard); - window.claudeSearchKeyboardHandlerAdded = true; + if (query && !searchKeyboardHandlerAdded) { + (function() { + document.addEventListener('keydown', handleSearchKeyboard); + searchKeyboardHandlerAdded = true; + })(); } } @@ -796,6 +808,7 @@ function showCreateFileDialog() { ''; document.body.insertAdjacentHTML('beforeend', dialog); + createDialogOverlay = document.querySelector('.modal-overlay'); if (window.lucide) lucide.createIcons(); } @@ -918,3 +931,31 @@ function updateClaudeBadge() { badge.textContent = total; } } + +// ========== Lifecycle Functions ========== +function destroyClaudeManager() { + // Remove search keyboard event listener if added + if (searchKeyboardHandlerAdded) { + document.removeEventListener('keydown', handleSearchKeyboard); + searchKeyboardHandlerAdded = false; + } + + // Remove create dialog overlay if exists + if (createDialogOverlay) { + createDialogOverlay.remove(); + createDialogOverlay = null; + } + + // Reset view-specific state + selectedFile = null; + isEditMode = false; + isDirty = false; +} + +// Expose init/destroy functions for lifecycle management +window.initClaudeManager = function() { + claudeManagerDestroy = destroyClaudeManager; +}; + +// Make destroyClaudeManager accessible globally as well +window.destroyClaudeManager = destroyClaudeManager; diff --git a/ccw/src/templates/dashboard-js/views/history.js b/ccw/src/templates/dashboard-js/views/history.js index 328e244d..75cf9b74 100644 --- a/ccw/src/templates/dashboard-js/views/history.js +++ b/ccw/src/templates/dashboard-js/views/history.js @@ -7,6 +7,9 @@ var isMultiSelectMode = false; // ========== Rendering ========== async function renderCliHistoryView() { + // Reset view state to prevent stale data persistence + resetHistoryViewState(); + var container = document.getElementById('mainContent'); if (!container) return; @@ -183,6 +186,9 @@ async function renderCliHistoryView() { if (window.lucide) lucide.createIcons(); } +// Export destroy function for lifecycle management +window.destroyCliHistoryView = destroyCliHistoryView; + // ========== Actions ========== async function copyExecutionId(executionId) { try { @@ -218,16 +224,28 @@ async function refreshCliHistoryView() { } // ========== Multi-Select Functions ========== +var deleteDropdownListenerActive = false; + function toggleDeleteDropdown(event) { event.stopPropagation(); var menu = document.getElementById('deleteDropdownMenu'); if (menu) { - menu.classList.toggle('show'); - // Close on outside click if (menu.classList.contains('show')) { - setTimeout(function() { - document.addEventListener('click', closeDeleteDropdown); - }, 0); + // Closing: remove listener if active + menu.classList.remove('show'); + if (deleteDropdownListenerActive) { + document.removeEventListener('click', closeDeleteDropdown); + deleteDropdownListenerActive = false; + } + } else { + // Opening: add listener if not already active + menu.classList.add('show'); + if (!deleteDropdownListenerActive) { + setTimeout(function() { + document.addEventListener('click', closeDeleteDropdown); + deleteDropdownListenerActive = true; + }, 0); + } } } } @@ -235,7 +253,10 @@ function toggleDeleteDropdown(event) { function closeDeleteDropdown() { var menu = document.getElementById('deleteDropdownMenu'); if (menu) menu.classList.remove('show'); - document.removeEventListener('click', closeDeleteDropdown); + if (deleteDropdownListenerActive) { + document.removeEventListener('click', closeDeleteDropdown); + deleteDropdownListenerActive = false; + } } function enterMultiSelectMode() { @@ -279,6 +300,23 @@ function clearExecutionSelection() { renderCliHistoryView(); } +function resetHistoryViewState() { + selectedExecutions.clear(); + isMultiSelectMode = false; +} + +// ========== View Lifecycle ========== +function destroyCliHistoryView() { + // Clean up dropdown listener if active + if (deleteDropdownListenerActive) { + document.removeEventListener('click', closeDeleteDropdown); + deleteDropdownListenerActive = false; + } + // Ensure dropdown menu is closed + var menu = document.getElementById('deleteDropdownMenu'); + if (menu) menu.classList.remove('show'); +} + // ========== Batch Delete Functions ========== function confirmBatchDelete() { var count = selectedExecutions.size; diff --git a/ccw/src/templates/dashboard-js/views/lite-tasks.js b/ccw/src/templates/dashboard-js/views/lite-tasks.js index 0aa02413..5470d865 100644 --- a/ccw/src/templates/dashboard-js/views/lite-tasks.js +++ b/ccw/src/templates/dashboard-js/views/lite-tasks.js @@ -1117,6 +1117,143 @@ function renderRoundContent(round) { `); } + // Round solutions + if (round.solutions && round.solutions.length > 0) { + sections.push(` +
+ ${t('multiCli.solutions') || 'Solutions'} (${round.solutions.length}): +
+ ${round.solutions.map((solution, idx) => ` +
+
+
+ ${idx + 1} + ${escapeHtml(solution.name || `Solution ${idx + 1}`)} +
+
+ ${solution.source_cli?.length ? ` +
+ ${solution.source_cli.map(cli => `${escapeHtml(cli)}`).join('')} +
+ ` : ''} +
+ + ${Math.round((solution.feasibility || 0) * 100)}% + + + ${escapeHtml(solution.effort || 'medium')} + + + ${escapeHtml(solution.risk || 'medium')} + +
+
+
+ + ${solution.summary ? ` +
+ ${escapeHtml(getI18nText(solution.summary))} +
+ ` : ''} + + ${solution.implementation_plan?.approach ? ` +
+
+ + +
+ +
+ ` : ''} + + ${(solution.dependencies?.internal?.length || solution.dependencies?.external?.length) ? ` +
+
+ + +
+ +
+ ` : ''} + + ${solution.technical_concerns?.length ? ` +
+
+ + +
+ +
+ ` : ''} +
+ `).join('')} +
+
+ `); + } + return sections.join(''); }