From 1d3436d51b5e67e439951c925a02f3c6c59c9fbb Mon Sep 17 00:00:00 2001 From: catlog22 Date: Fri, 5 Dec 2025 15:51:37 +0800 Subject: [PATCH] feat(dashboard): simplify lite task list and add dedicated drawer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove META/CONTEXT/FLOW_CONTROL collapsible sections from task list - Add compact task item with action/scope/mods/steps badges - Create dedicated renderLiteTaskDrawerContent for plan.json parsing - Add Overview tab with description, scope, acceptance, dependencies - Add Implementation tab with steps and modification points - Add proper file list extraction from modification_points - Add CSS styles for lite task badges and drawer components 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- ccw/src/templates/dashboard.css | 176 ++++++++++++++++++++++ ccw/src/templates/dashboard.js | 257 ++++++++++++++++++++++++++------ 2 files changed, 388 insertions(+), 45 deletions(-) diff --git a/ccw/src/templates/dashboard.css b/ccw/src/templates/dashboard.css index 0978a66b..aaf799c5 100644 --- a/ccw/src/templates/dashboard.css +++ b/ccw/src/templates/dashboard.css @@ -3609,3 +3609,179 @@ ol.step-commands code { line-height: 1.6; color: var(--text-primary, #374151); } + + +/* Lite Task List Item Styles */ +.lite-task-item { + border: 1px solid var(--border-color, #e5e7eb); + border-radius: 8px; + padding: 12px 16px; + margin-bottom: 8px; + transition: all 0.2s ease; + background: var(--bg-primary, #fff); +} + +.lite-task-item:hover { + border-color: var(--primary, #3b82f6); + box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1); +} + +.task-item-header-lite { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 8px; +} + +.task-item-header-lite .task-title { + flex: 1; + font-weight: 500; + color: var(--text-primary, #111827); +} + +.task-item-meta-lite { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.meta-badge { + padding: 2px 8px; + border-radius: 4px; + font-size: 11px; + font-weight: 500; +} + +.meta-badge.action { + background: var(--bg-primary-light, #eff6ff); + color: var(--primary, #3b82f6); + border: 1px solid var(--primary, #3b82f6); +} + +.meta-badge.scope { + background: var(--bg-secondary, #f9fafb); + color: var(--text-secondary, #6b7280); +} + +.meta-badge.mods { + background: var(--bg-warning, #fffbeb); + color: var(--warning, #d97706); +} + +.meta-badge.impl { + background: var(--bg-success, #ecfdf5); + color: var(--success, #059669); +} + +.meta-badge.accept { + background: var(--bg-info, #eff6ff); + color: var(--info, #2563eb); +} + +/* Lite Task Drawer Styles */ +.action-badge { + padding: 4px 10px; + border-radius: 4px; + font-size: 12px; + font-weight: 600; + background: var(--primary, #3b82f6); + color: white; + text-transform: uppercase; +} + +.scope-path { + display: block; + padding: 8px 12px; + background: var(--bg-secondary, #f9fafb); + border-radius: 4px; + font-size: 13px; +} + +.impl-steps-list { + list-style: none; + padding: 0; + margin: 0; + counter-reset: step-counter; +} + +.impl-step-item { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 10px 0; + border-bottom: 1px solid var(--border-color, #e5e7eb); +} + +.impl-step-item:last-child { + border-bottom: none; +} + +.step-number { + flex-shrink: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + background: var(--primary, #3b82f6); + color: white; + border-radius: 50%; + font-size: 12px; + font-weight: 600; +} + +.step-text { + flex: 1; + font-size: 13px; + line-height: 1.5; + color: var(--text-primary, #374151); +} + +.mod-points-list { + display: flex; + flex-direction: column; + gap: 10px; +} + +.mod-point-card { + padding: 12px; + background: var(--bg-secondary, #f9fafb); + border-radius: 6px; + border-left: 3px solid var(--primary, #3b82f6); +} + +.mod-point-card .mod-file code { + font-size: 12px; + color: var(--primary, #3b82f6); + font-weight: 500; +} + +.mod-point-card .mod-target { + font-size: 12px; + color: var(--text-secondary, #6b7280); + margin-top: 4px; +} + +.mod-point-card .mod-change { + font-size: 13px; + color: var(--text-primary, #374151); + margin-top: 6px; + line-height: 1.4; +} + +.ref-item { + margin-bottom: 10px; +} + +.ref-item:last-child { + margin-bottom: 0; +} + +.ref-files-list { + margin: 4px 0 0 16px; + padding: 0; +} + +.ref-files-list li { + margin-bottom: 4px; +} diff --git a/ccw/src/templates/dashboard.js b/ccw/src/templates/dashboard.js index 7261691a..285d0efe 100644 --- a/ccw/src/templates/dashboard.js +++ b/ccw/src/templates/dashboard.js @@ -1803,49 +1803,26 @@ function renderLiteTaskDetailItem(sessionId, task) { const taskJsonId = `task-json-${sessionId}-${task.id}`.replace(/[^a-zA-Z0-9-]/g, '-'); taskJsonStore[taskJsonId] = rawTask; + // Get preview info for lite tasks + const action = rawTask.action || ''; + const scope = rawTask.scope || ''; + const modCount = rawTask.modification_points?.length || 0; + const implCount = rawTask.implementation?.length || 0; + const acceptCount = rawTask.acceptance?.length || 0; + return ` -
-
+
+
${escapeHtml(task.id)} ${escapeHtml(task.title || 'Untitled')}
- - -
-
- â–¶ - - ${escapeHtml(getMetaPreviewForLite(task, rawTask))} -
- -
- - -
-
- â–¶ - - ${escapeHtml(getContextPreview(task.context, rawTask))} -
- -
- - -
-
- â–¶ - - ${escapeHtml(getFlowControlPreview(task.flow_control, rawTask))} -
- +
+ ${action ? `${escapeHtml(action)}` : ''} + ${scope ? `${escapeHtml(scope)}` : ''} + ${modCount > 0 ? `${modCount} mods` : ''} + ${implCount > 0 ? `${implCount} steps` : ''} + ${acceptCount > 0 ? `${acceptCount} acceptance` : ''}
`; @@ -1866,18 +1843,15 @@ function openTaskDrawerForLite(sessionId, taskId) { const task = session.tasks?.find(t => t.id === taskId); if (!task) return; - // Set current drawer tasks + // Set current drawer tasks and session context currentDrawerTasks = session.tasks || []; + window._currentDrawerSession = session; document.getElementById('drawerTaskTitle').textContent = task.title || taskId; - document.getElementById('drawerContent').innerHTML = renderTaskDrawerContent(task); + // Use dedicated lite task drawer renderer + document.getElementById('drawerContent').innerHTML = renderLiteTaskDrawerContent(task, session); document.getElementById('taskDetailDrawer').classList.add('open'); document.getElementById('drawerOverlay').classList.add('active'); - - // Initialize flowchart after DOM is updated - setTimeout(() => { - renderFullFlowchart(task.flow_control || task._raw?.flow_control); - }, 100); } function renderLitePlanTab(session) { @@ -2816,6 +2790,199 @@ function renderTaskImplementationDetails(task) { } + +// Dedicated lite task drawer content renderer +function renderLiteTaskDrawerContent(task, session) { + const rawTask = task._raw || task; + + return ` + +
+ ${escapeHtml(task.task_id || task.id || 'N/A')} + ${rawTask.action ? `${escapeHtml(rawTask.action)}` : ''} +
+ + +
+ + + + +
+ + +
+ +
+ ${renderLiteTaskOverview(rawTask)} +
+ + +
+ ${renderLiteTaskImplementation(rawTask)} +
+ + +
+ ${renderLiteTaskFiles(rawTask)} +
+ + +
+
${escapeHtml(JSON.stringify(rawTask, null, 2))}
+
+
+ `; +} + +// Render lite task overview +function renderLiteTaskOverview(task) { + let sections = []; + + // Description + if (task.description) { + sections.push(` +
+

Description

+

${escapeHtml(task.description)}

+
+ `); + } + + // Scope + if (task.scope) { + sections.push(` +
+

Scope

+ ${escapeHtml(task.scope)} +
+ `); + } + + // Acceptance Criteria + if (task.acceptance && task.acceptance.length > 0) { + sections.push(` +
+

Acceptance Criteria

+
    + ${task.acceptance.map(a => `
  • ${escapeHtml(a)}
  • `).join('')} +
+
+ `); + } + + // Dependencies + if (task.depends_on && task.depends_on.length > 0) { + sections.push(` +
+

Dependencies

+
+ ${task.depends_on.map(dep => `${escapeHtml(dep)}`).join(' ')} +
+
+ `); + } + + // Reference + if (task.reference) { + sections.push(` +
+

Reference

+ ${task.reference.pattern ? `
Pattern: ${escapeHtml(task.reference.pattern)}
` : ''} + ${task.reference.files && task.reference.files.length > 0 ? ` +
+ Files: +
    + ${task.reference.files.map(f => `
  • ${escapeHtml(f)}
  • `).join('')} +
+
+ ` : ''} + ${task.reference.examples ? `
Examples: ${escapeHtml(task.reference.examples)}
` : ''} +
+ `); + } + + return sections.length > 0 ? sections.join('') : '
No overview data
'; +} + +// Render lite task implementation steps +function renderLiteTaskImplementation(task) { + let sections = []; + + // Implementation Steps + if (task.implementation && task.implementation.length > 0) { + sections.push(` +
+

Implementation Steps

+
    + ${task.implementation.map((step, idx) => ` +
  1. + ${idx + 1} + ${escapeHtml(typeof step === 'string' ? step : step.step || JSON.stringify(step))} +
  2. + `).join('')} +
+
+ `); + } + + // Modification Points + if (task.modification_points && task.modification_points.length > 0) { + sections.push(` +
+

Modification Points

+
+ ${task.modification_points.map(mp => ` +
+
${escapeHtml(mp.file || '')}
+ ${mp.target ? `
Target: ${escapeHtml(mp.target)}
` : ''} + ${mp.change ? `
${escapeHtml(mp.change)}
` : ''} +
+ `).join('')} +
+
+ `); + } + + return sections.length > 0 ? sections.join('') : '
No implementation data
'; +} + +// Render lite task files +function renderLiteTaskFiles(task) { + const files = []; + + // Collect from modification_points + if (task.modification_points) { + task.modification_points.forEach(mp => { + if (mp.file && !files.includes(mp.file)) files.push(mp.file); + }); + } + + // Collect from scope + if (task.scope && !files.includes(task.scope)) { + files.push(task.scope); + } + + if (files.length === 0) { + return '
No files specified
'; + } + + return ` +
+

Target Files

+
    + ${files.map(f => ` +
  • + 📄 + ${escapeHtml(f)} +
  • + `).join('')} +
+
+ `; +} + + function closeTaskDrawer() { document.getElementById('taskDetailDrawer').classList.remove('open'); document.getElementById('drawerOverlay').classList.remove('active');