diff --git a/ccw/src/core/data-aggregator.js b/ccw/src/core/data-aggregator.js index 79c73ee9..09890b95 100644 --- a/ccw/src/core/data-aggregator.js +++ b/ccw/src/core/data-aggregator.js @@ -19,6 +19,7 @@ export async function aggregateData(sessions, workflowDir) { liteFix: [] }, reviewData: null, + projectOverview: null, statistics: { totalSessions: 0, activeSessions: 0, @@ -65,6 +66,13 @@ export async function aggregateData(sessions, workflowDir) { console.error('Error scanning lite tasks:', err.message); } + // Load project overview from project.json + try { + data.projectOverview = loadProjectOverview(workflowDir); + } catch (err) { + console.error('Error loading project overview:', err.message); + } + return data; } @@ -338,3 +346,64 @@ function sortTaskIds(a, b) { const [b1, b2] = parseId(b); return a1 - b1 || a2 - b2; } + +/** + * Load project overview from project.json + * @param {string} workflowDir - Path to .workflow directory + * @returns {Object|null} - Project overview data or null if not found + */ +function loadProjectOverview(workflowDir) { + const projectFile = join(workflowDir, 'project.json'); + + if (!existsSync(projectFile)) { + console.log(`Project file not found at: ${projectFile}`); + return null; + } + + try { + const fileContent = readFileSync(projectFile, 'utf8'); + const projectData = JSON.parse(fileContent); + + console.log(`Successfully loaded project overview: ${projectData.project_name || 'Unknown'}`); + + return { + projectName: projectData.project_name || 'Unknown', + description: projectData.overview?.description || '', + initializedAt: projectData.initialized_at || null, + technologyStack: projectData.overview?.technology_stack || { + languages: [], + frameworks: [], + build_tools: [], + test_frameworks: [] + }, + architecture: projectData.overview?.architecture || { + style: 'Unknown', + layers: [], + patterns: [] + }, + keyComponents: projectData.overview?.key_components || [], + features: projectData.features || [], + developmentIndex: projectData.development_index || { + feature: [], + enhancement: [], + bugfix: [], + refactor: [], + docs: [] + }, + statistics: projectData.statistics || { + total_features: 0, + total_sessions: 0, + last_updated: null + }, + metadata: projectData._metadata || { + initialized_by: 'unknown', + analysis_timestamp: null, + analysis_mode: 'unknown' + } + }; + } catch (err) { + console.error(`Failed to parse project.json at ${projectFile}:`, err.message); + console.error('Error stack:', err.stack); + return null; + } +} diff --git a/ccw/src/core/server.js b/ccw/src/core/server.js index 5008c90a..eaa5e33a 100644 --- a/ccw/src/core/server.js +++ b/ccw/src/core/server.js @@ -120,6 +120,7 @@ async function getWorkflowData(projectPath) { archivedSessions: [], liteTasks: { litePlan: [], liteFix: [] }, reviewData: { dimensions: {} }, + projectOverview: null, statistics: { totalSessions: 0, activeSessions: 0, @@ -332,6 +333,7 @@ function generateServerDashboard(initialPath) { archivedSessions: [], liteTasks: { litePlan: [], liteFix: [] }, reviewData: { dimensions: {} }, + projectOverview: null, statistics: { totalSessions: 0, activeSessions: 0, totalTasks: 0, completedTasks: 0, reviewFindings: 0, litePlanCount: 0, liteFixCount: 0 } }; diff --git a/ccw/src/templates/dashboard.css b/ccw/src/templates/dashboard.css index 3b1da3ee..87a91d9c 100644 --- a/ccw/src/templates/dashboard.css +++ b/ccw/src/templates/dashboard.css @@ -879,6 +879,31 @@ code { border-left-color: hsl(var(--muted-foreground)); } +/* Status-based background colors for task cards */ +.detail-task-item.status-completed { + background: hsl(var(--success) / 0.08); +} + +.detail-task-item.status-completed:hover { + background: hsl(var(--success) / 0.12); +} + +.detail-task-item.status-in_progress { + background: hsl(var(--warning) / 0.08); +} + +.detail-task-item.status-in_progress:hover { + background: hsl(var(--warning) / 0.12); +} + +.detail-task-item.status-pending { + background: hsl(var(--muted-foreground) / 0.05); +} + +.detail-task-item.status-pending:hover { + background: hsl(var(--muted-foreground) / 0.08); +} + .task-item-header { display: flex; align-items: center; @@ -1687,6 +1712,21 @@ code { line-height: 1.6; } +.markdown-content { + background: hsl(var(--muted)); + padding: 1rem; + border-radius: 0.5rem; + overflow-x: auto; + font-size: 0.8rem; + line-height: 1.6; + color: hsl(var(--foreground)); + max-height: 600px; + overflow-y: auto; + white-space: pre-wrap; + word-break: break-word; + margin: 0; +} + /* JSON Modal */ .json-modal-overlay { position: fixed; @@ -3794,3 +3834,116 @@ ol.step-commands code { .ref-files-list li { margin-bottom: 4px; } + +/* =================================== + Markdown Modal & View Button Styles + ================================== */ + +.btn-view-modal { + padding: 4px 12px; + background: hsl(var(--primary)); + color: hsl(var(--primary-foreground)); + border: none; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; + margin-left: auto; +} + +.btn-view-modal:hover { + background: hsl(var(--primary) / 0.9); + transform: translateY(-1px); +} + +.markdown-modal .markdown-preview { + color: hsl(var(--foreground)); + line-height: 1.6; +} + +.markdown-modal .markdown-preview h1, +.markdown-modal .markdown-preview h2, +.markdown-modal .markdown-preview h3, +.markdown-modal .markdown-preview h4 { + color: hsl(var(--foreground)); + font-weight: 600; + margin-top: 1.5em; + margin-bottom: 0.5em; +} + +.markdown-modal .markdown-preview h1 { font-size: 1.8em; } +.markdown-modal .markdown-preview h2 { font-size: 1.5em; } +.markdown-modal .markdown-preview h3 { font-size: 1.3em; } +.markdown-modal .markdown-preview h4 { font-size: 1.1em; } + +.markdown-modal .markdown-preview code { + background: hsl(var(--muted)); + padding: 2px 6px; + border-radius: 3px; + font-size: 0.9em; + font-family: 'Consolas', 'Monaco', monospace; +} + +.markdown-modal .markdown-preview pre { + background: hsl(var(--muted)); + padding: 12px; + border-radius: 6px; + overflow-x: auto; + margin: 1em 0; +} + +.markdown-modal .markdown-preview pre code { + background: transparent; + padding: 0; +} + +.markdown-modal .markdown-preview ul, +.markdown-modal .markdown-preview ol { + margin: 1em 0; + padding-left: 2em; +} + +.markdown-modal .markdown-preview li { + margin: 0.5em 0; +} + +.markdown-modal .markdown-preview blockquote { + border-left: 4px solid hsl(var(--primary)); + padding-left: 1em; + margin: 1em 0; + color: hsl(var(--muted-foreground)); +} + +.markdown-modal .markdown-preview table { + border-collapse: collapse; + width: 100%; + margin: 1em 0; +} + +.markdown-modal .markdown-preview th, +.markdown-modal .markdown-preview td { + border: 1px solid hsl(var(--border)); + padding: 8px 12px; + text-align: left; +} + +.markdown-modal .markdown-preview th { + background: hsl(var(--muted)); + font-weight: 600; +} + +.md-tab-btn { + cursor: pointer; + transition: all 0.2s; +} + +.md-tab-btn:not(.active) { + color: hsl(var(--muted-foreground)); +} + +.md-tab-btn.active { + background: hsl(var(--background)); + color: hsl(var(--foreground)); + font-weight: 500; +} diff --git a/ccw/src/templates/dashboard.html b/ccw/src/templates/dashboard.html index b17307e2..cf5a49b6 100644 --- a/ccw/src/templates/dashboard.html +++ b/ccw/src/templates/dashboard.html @@ -16,7 +16,8 @@ safelist: [ // Background colors 'bg-card', 'bg-background', 'bg-hover', 'bg-accent', 'bg-muted', 'bg-primary', 'bg-success', 'bg-warning', - 'bg-success-light', 'bg-warning-light', 'bg-sidebar-background', 'bg-destructive', + 'bg-success-light', 'bg-warning-light', 'bg-primary-light', 'bg-sidebar-background', 'bg-destructive', + 'bg-destructive/5', 'bg-destructive/10', 'bg-warning/5', // Text colors 'text-foreground', 'text-muted-foreground', 'text-primary', 'text-card-foreground', 'text-success', 'text-warning', 'text-primary-foreground', 'text-accent-foreground', 'text-sidebar-foreground', 'text-destructive', @@ -57,6 +58,7 @@ ring: 'hsl(var(--ring))', primary: 'hsl(var(--primary))', 'primary-foreground': 'hsl(var(--primary-foreground))', + 'primary-light': 'hsl(var(--primary-light))', secondary: 'hsl(var(--secondary))', 'secondary-foreground': 'hsl(var(--secondary-foreground))', accent: 'hsl(var(--accent))', @@ -99,6 +101,7 @@ --ring: 220 65% 50%; --primary: 220 65% 50%; --primary-foreground: 0 0% 100%; + --primary-light: 220 65% 95%; --secondary: 220 60% 65%; --secondary-foreground: 0 0% 100%; --accent: 220 40% 95%; @@ -127,6 +130,7 @@ --ring: 220 65% 55%; --primary: 220 65% 55%; --primary-foreground: 0 0% 100%; + --primary-light: 220 50% 25%; --secondary: 220 60% 60%; --secondary-foreground: 0 0% 100%; --accent: 220 30% 20%; @@ -202,30 +206,31 @@ - -