mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
feat(dashboard): complete icon unification across all views
- Update home.js: inbox, calendar, list-checks icons - Update project-overview.js: code-2, blocks, component, git-branch, sparkles, bug, wrench, book-open icons - Update session-detail.js: list-checks, package, file-text, ruler, scale, search icons for tabs - Update lite-tasks.js: zap, file-edit, wrench, calendar, list-checks, ruler, package, file-text icons - Update mcp-manager.js: plug, building-2, user, map-pin, check-circle, x-circle, circle-dashed, lock icons - Update hook-manager.js: webhook, pencil, trash-2, clock, check-circle, bell, octagon-x icons - Add getHookEventIconLucide() helper function - Initialize Lucide icons after dynamic content rendering All emoji icons replaced with consistent Lucide SVG icons
This commit is contained in:
@@ -1810,54 +1810,109 @@ async function getFileContent(filePath) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger update-module-claude tool
|
||||
* Trigger update-module-claude tool (async execution)
|
||||
* @param {string} targetPath - Directory path to update
|
||||
* @param {string} tool - CLI tool to use (gemini, qwen, codex)
|
||||
* @param {string} strategy - Update strategy (single-layer, multi-layer)
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
async function triggerUpdateClaudeMd(targetPath, tool, strategy) {
|
||||
const { execSync } = await import('child_process');
|
||||
const { spawn } = await import('child_process');
|
||||
|
||||
try {
|
||||
// Normalize path
|
||||
let normalizedPath = targetPath.replace(/\\/g, '/');
|
||||
if (normalizedPath.match(/^\/[a-zA-Z]\//)) {
|
||||
normalizedPath = normalizedPath.charAt(1).toUpperCase() + ':' + normalizedPath.slice(2);
|
||||
}
|
||||
// Normalize path
|
||||
let normalizedPath = targetPath.replace(/\\/g, '/');
|
||||
if (normalizedPath.match(/^\/[a-zA-Z]\//)) {
|
||||
normalizedPath = normalizedPath.charAt(1).toUpperCase() + ':' + normalizedPath.slice(2);
|
||||
}
|
||||
|
||||
if (!existsSync(normalizedPath)) {
|
||||
return { error: 'Directory not found' };
|
||||
}
|
||||
|
||||
if (!statSync(normalizedPath).isDirectory()) {
|
||||
return { error: 'Not a directory' };
|
||||
}
|
||||
|
||||
// Build ccw tool command with JSON parameters
|
||||
const params = JSON.stringify({
|
||||
strategy,
|
||||
path: normalizedPath,
|
||||
tool
|
||||
});
|
||||
|
||||
console.log(`[Explorer] Running async: ccw tool exec update_module_claude with ${tool} (${strategy})`);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
if (!existsSync(normalizedPath)) {
|
||||
return { error: 'Directory not found' };
|
||||
}
|
||||
|
||||
if (!statSync(normalizedPath).isDirectory()) {
|
||||
return { error: 'Not a directory' };
|
||||
}
|
||||
|
||||
// Build ccw tool command
|
||||
const ccwBin = join(import.meta.dirname, '../../bin/ccw.js');
|
||||
const command = `node "${ccwBin}" tool update_module_claude --strategy="${strategy}" --path="${normalizedPath}" --tool="${tool}"`;
|
||||
|
||||
console.log(`[Explorer] Running: ${command}`);
|
||||
|
||||
const output = execSync(command, {
|
||||
encoding: 'utf8',
|
||||
timeout: 300000, // 5 minutes
|
||||
cwd: normalizedPath
|
||||
// Spawn the process
|
||||
const child = spawn('ccw', ['tool', 'exec', 'update_module_claude', params], {
|
||||
cwd: normalizedPath,
|
||||
shell: isWindows,
|
||||
stdio: ['ignore', 'pipe', 'pipe']
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `CLAUDE.md updated successfully using ${tool} (${strategy})`,
|
||||
output,
|
||||
path: normalizedPath
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error updating CLAUDE.md:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
output: error.stdout || error.stderr || ''
|
||||
};
|
||||
}
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
child.stdout.on('data', (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
child.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
child.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
// Parse the JSON output from the tool
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(stdout);
|
||||
} catch {
|
||||
result = { output: stdout };
|
||||
}
|
||||
|
||||
if (result.success === false || result.error) {
|
||||
resolve({
|
||||
success: false,
|
||||
error: result.error || result.message || 'Update failed',
|
||||
output: stdout
|
||||
});
|
||||
} else {
|
||||
resolve({
|
||||
success: true,
|
||||
message: result.message || `CLAUDE.md updated successfully using ${tool} (${strategy})`,
|
||||
output: stdout,
|
||||
path: normalizedPath
|
||||
});
|
||||
}
|
||||
} else {
|
||||
resolve({
|
||||
success: false,
|
||||
error: stderr || `Process exited with code ${code}`,
|
||||
output: stdout + stderr
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
child.on('error', (error) => {
|
||||
console.error('Error spawning process:', error);
|
||||
resolve({
|
||||
success: false,
|
||||
error: error.message,
|
||||
output: ''
|
||||
});
|
||||
});
|
||||
|
||||
// Timeout after 5 minutes
|
||||
setTimeout(() => {
|
||||
child.kill();
|
||||
resolve({
|
||||
success: false,
|
||||
error: 'Timeout: Process took longer than 5 minutes',
|
||||
output: stdout
|
||||
});
|
||||
}, 300000);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -847,11 +847,55 @@
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
/* Task Queue Toolbar */
|
||||
.task-queue-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* CLI Selector */
|
||||
.queue-cli-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.queue-cli-selector label {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
|
||||
.queue-cli-selector select {
|
||||
padding: 5px 8px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
background: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.queue-cli-selector select:hover {
|
||||
border-color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
.queue-cli-selector select:focus {
|
||||
outline: none;
|
||||
border-color: hsl(var(--primary));
|
||||
box-shadow: 0 0 0 2px hsl(var(--primary) / 0.2);
|
||||
}
|
||||
|
||||
.task-queue-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.queue-action-btn {
|
||||
|
||||
@@ -271,3 +271,13 @@ function getHookEventIcon(event) {
|
||||
};
|
||||
return icons[event] || '🪝';
|
||||
}
|
||||
|
||||
function getHookEventIconLucide(event) {
|
||||
const icons = {
|
||||
'PreToolUse': '<i data-lucide="clock" class="w-5 h-5"></i>',
|
||||
'PostToolUse': '<i data-lucide="check-circle" class="w-5 h-5"></i>',
|
||||
'Notification': '<i data-lucide="bell" class="w-5 h-5"></i>',
|
||||
'Stop': '<i data-lucide="octagon-x" class="w-5 h-5"></i>'
|
||||
};
|
||||
return icons[event] || '<i data-lucide="webhook" class="w-5 h-5"></i>';
|
||||
}
|
||||
@@ -13,6 +13,7 @@ let explorerExpandedDirs = new Set();
|
||||
let updateTaskQueue = [];
|
||||
let isTaskQueueVisible = false;
|
||||
let isTaskRunning = false;
|
||||
let defaultCliTool = 'gemini'; // Default CLI tool for updates
|
||||
|
||||
|
||||
/**
|
||||
@@ -75,16 +76,26 @@ async function renderExplorer() {
|
||||
<span class="task-queue-title"><i data-lucide="clipboard-list" class="w-4 h-4 inline-block mr-1"></i> Update Tasks</span>
|
||||
<button class="task-queue-close" onclick="toggleTaskQueue()">×</button>
|
||||
</div>
|
||||
<div class="task-queue-actions">
|
||||
<button class="queue-action-btn" onclick="openAddTaskModal()" title="Add update task">
|
||||
<i data-lucide="plus" class="w-4 h-4"></i> Add
|
||||
</button>
|
||||
<button class="queue-action-btn queue-start-btn" onclick="startTaskQueue()" id="startQueueBtn" disabled>
|
||||
<i data-lucide="play" class="w-4 h-4"></i> Start
|
||||
</button>
|
||||
<button class="queue-action-btn queue-clear-btn" onclick="clearCompletedTasks()" title="Clear completed">
|
||||
<i data-lucide="trash-2" class="w-4 h-4"></i> Clear
|
||||
</button>
|
||||
<div class="task-queue-toolbar">
|
||||
<div class="queue-cli-selector">
|
||||
<label>CLI:</label>
|
||||
<select id="queueCliTool" onchange="updateDefaultCliTool(this.value)">
|
||||
<option value="gemini">Gemini</option>
|
||||
<option value="qwen">Qwen</option>
|
||||
<option value="codex">Codex</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="task-queue-actions">
|
||||
<button class="queue-action-btn" onclick="openAddTaskModal()" title="Add update task">
|
||||
<i data-lucide="plus" class="w-4 h-4"></i>
|
||||
</button>
|
||||
<button class="queue-action-btn queue-start-btn" onclick="startTaskQueue()" id="startQueueBtn" disabled title="Start all tasks">
|
||||
<i data-lucide="play" class="w-4 h-4"></i>
|
||||
</button>
|
||||
<button class="queue-action-btn queue-clear-btn" onclick="clearCompletedTasks()" title="Clear completed">
|
||||
<i data-lucide="trash-2" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-queue-list" id="taskQueueList">
|
||||
<div class="task-queue-empty">
|
||||
@@ -576,6 +587,13 @@ function toggleTaskQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update default CLI tool
|
||||
*/
|
||||
function updateDefaultCliTool(tool) {
|
||||
defaultCliTool = tool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the FAB badge count
|
||||
*/
|
||||
@@ -659,7 +677,8 @@ function addUpdateTask(path, tool = 'gemini', strategy = 'single-layer') {
|
||||
* Add task from folder context (right-click or button)
|
||||
*/
|
||||
function addFolderToQueue(folderPath, strategy = 'single-layer') {
|
||||
addUpdateTask(folderPath, 'gemini', strategy);
|
||||
// Use the selected CLI tool from the queue panel
|
||||
addUpdateTask(folderPath, defaultCliTool, strategy);
|
||||
|
||||
// Show task queue if not visible
|
||||
if (!isTaskQueueVisible) {
|
||||
@@ -740,7 +759,55 @@ function clearCompletedTasks() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Start processing task queue
|
||||
* Execute a single task asynchronously
|
||||
*/
|
||||
async function executeTask(task) {
|
||||
const folderName = task.path.split('/').pop() || task.path;
|
||||
|
||||
// Update status to running
|
||||
task.status = 'running';
|
||||
task.message = 'Processing...';
|
||||
renderTaskQueue();
|
||||
|
||||
addGlobalNotification('info', `Processing: ${folderName}`, `Strategy: ${task.strategy}, Tool: ${task.tool}`, 'Explorer');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/update-claude-md', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
path: task.path,
|
||||
tool: task.tool,
|
||||
strategy: task.strategy
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
task.status = 'completed';
|
||||
task.message = 'Updated successfully';
|
||||
addGlobalNotification('success', `Completed: ${folderName}`, result.message, 'Explorer');
|
||||
return { success: true };
|
||||
} else {
|
||||
task.status = 'failed';
|
||||
task.message = result.error || 'Update failed';
|
||||
addGlobalNotification('error', `Failed: ${folderName}`, result.error || 'Unknown error', 'Explorer');
|
||||
return { success: false };
|
||||
}
|
||||
} catch (error) {
|
||||
task.status = 'failed';
|
||||
task.message = error.message;
|
||||
addGlobalNotification('error', `Error: ${folderName}`, error.message, 'Explorer');
|
||||
return { success: false };
|
||||
} finally {
|
||||
renderTaskQueue();
|
||||
updateFabBadge();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start processing task queue - executes tasks asynchronously in parallel
|
||||
*/
|
||||
async function startTaskQueue() {
|
||||
if (isTaskRunning) return;
|
||||
@@ -751,55 +818,13 @@ async function startTaskQueue() {
|
||||
isTaskRunning = true;
|
||||
document.getElementById('startQueueBtn').disabled = true;
|
||||
|
||||
addGlobalNotification('info', `Starting ${pendingTasks.length} task(s)...`, null, 'Explorer');
|
||||
addGlobalNotification('info', `Starting ${pendingTasks.length} task(s) in parallel...`, null, 'Explorer');
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
// Execute all tasks in parallel
|
||||
const results = await Promise.all(pendingTasks.map(task => executeTask(task)));
|
||||
|
||||
for (const task of pendingTasks) {
|
||||
const folderName = task.path.split('/').pop() || task.path;
|
||||
|
||||
// Update status to running
|
||||
task.status = 'running';
|
||||
task.message = 'Processing...';
|
||||
renderTaskQueue();
|
||||
|
||||
addGlobalNotification('info', `Processing: ${folderName}`, `Strategy: ${task.strategy}, Tool: ${task.tool}`, 'Explorer');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/update-claude-md', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
path: task.path,
|
||||
tool: task.tool,
|
||||
strategy: task.strategy
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
task.status = 'completed';
|
||||
task.message = 'Updated successfully';
|
||||
successCount++;
|
||||
addGlobalNotification('success', `Completed: ${folderName}`, result.message, 'Explorer');
|
||||
} else {
|
||||
task.status = 'failed';
|
||||
task.message = result.error || 'Update failed';
|
||||
failCount++;
|
||||
addGlobalNotification('error', `Failed: ${folderName}`, result.error || 'Unknown error', 'Explorer');
|
||||
}
|
||||
} catch (error) {
|
||||
task.status = 'failed';
|
||||
task.message = error.message;
|
||||
failCount++;
|
||||
addGlobalNotification('error', `Error: ${folderName}`, error.message, 'Explorer');
|
||||
}
|
||||
|
||||
renderTaskQueue();
|
||||
updateFabBadge();
|
||||
}
|
||||
const successCount = results.filter(r => r.success).length;
|
||||
const failCount = results.filter(r => !r.success).length;
|
||||
|
||||
isTaskRunning = false;
|
||||
|
||||
|
||||
@@ -73,11 +73,12 @@ function renderSessions() {
|
||||
if (sessions.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="empty-state" style="grid-column: 1/-1;">
|
||||
<div class="empty-icon">📭</div>
|
||||
<div class="empty-icon"><i data-lucide="inbox" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">No Sessions Found</div>
|
||||
<div class="empty-text">No workflow sessions match your current filter.</div>
|
||||
</div>
|
||||
`;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -120,8 +121,8 @@ function renderSessionCard(session) {
|
||||
</div>
|
||||
<div class="session-body">
|
||||
<div class="session-meta">
|
||||
<span class="session-meta-item">📅 ${formatDate(date)}</span>
|
||||
<span class="session-meta-item">📋 ${taskCount} tasks</span>
|
||||
<span class="session-meta-item"><i data-lucide="calendar" class="w-3.5 h-3.5 inline mr-1"></i>${formatDate(date)}</span>
|
||||
<span class="session-meta-item"><i data-lucide="list-checks" class="w-3.5 h-3.5 inline mr-1"></i>${taskCount} tasks</span>
|
||||
</div>
|
||||
${taskCount > 0 ? `
|
||||
<div class="progress-container">
|
||||
@@ -171,16 +172,16 @@ function renderReviewSessionCard(session, sessionKey, typeBadge, isActive, date)
|
||||
</div>
|
||||
<div class="session-body">
|
||||
<div class="session-meta">
|
||||
<span class="session-meta-item">📅 ${formatDate(date)}</span>
|
||||
<span class="session-meta-item">🔍 ${totalFindings} findings</span>
|
||||
<span class="session-meta-item"><i data-lucide="calendar" class="w-3.5 h-3.5 inline mr-1"></i>${formatDate(date)}</span>
|
||||
<span class="session-meta-item"><i data-lucide="search" class="w-3.5 h-3.5 inline mr-1"></i>${totalFindings} findings</span>
|
||||
</div>
|
||||
${totalFindings > 0 ? `
|
||||
<div class="review-findings-summary">
|
||||
<div class="findings-severity-row">
|
||||
${criticalCount > 0 ? `<span class="finding-count critical">🔴 ${criticalCount}</span>` : ''}
|
||||
${highCount > 0 ? `<span class="finding-count high">🟠 ${highCount}</span>` : ''}
|
||||
${mediumCount > 0 ? `<span class="finding-count medium">🟡 ${mediumCount}</span>` : ''}
|
||||
${lowCount > 0 ? `<span class="finding-count low">🟢 ${lowCount}</span>` : ''}
|
||||
${criticalCount > 0 ? `<span class="finding-count critical"><i data-lucide="alert-circle" class="w-3 h-3 inline"></i> ${criticalCount}</span>` : ''}
|
||||
${highCount > 0 ? `<span class="finding-count high"><i data-lucide="alert-triangle" class="w-3 h-3 inline"></i> ${highCount}</span>` : ''}
|
||||
${mediumCount > 0 ? `<span class="finding-count medium"><i data-lucide="info" class="w-3 h-3 inline"></i> ${mediumCount}</span>` : ''}
|
||||
${lowCount > 0 ? `<span class="finding-count low"><i data-lucide="check-circle" class="w-3 h-3 inline"></i> ${lowCount}</span>` : ''}
|
||||
</div>
|
||||
<div class="dimensions-info">
|
||||
${dimensions.length} dimensions
|
||||
|
||||
@@ -41,7 +41,7 @@ async function renderHookManager() {
|
||||
|
||||
${projectHookCount === 0 ? `
|
||||
<div class="hook-empty-state bg-card border border-border rounded-lg p-6 text-center">
|
||||
<div class="text-3xl mb-3">🪝</div>
|
||||
<div class="text-muted-foreground mb-3"><i data-lucide="webhook" class="w-10 h-10 mx-auto"></i></div>
|
||||
<p class="text-muted-foreground">No hooks configured for this project</p>
|
||||
<p class="text-sm text-muted-foreground mt-1">Create a hook to automate actions on tool usage</p>
|
||||
</div>
|
||||
@@ -160,7 +160,7 @@ function renderHooksByEvent(hooks, scope) {
|
||||
<div class="hook-card bg-card border border-border rounded-lg p-4 hover:shadow-md transition-all">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xl">${getHookEventIcon(event)}</span>
|
||||
${getHookEventIconLucide(event)}
|
||||
<div>
|
||||
<h4 class="font-semibold text-foreground">${event}</h4>
|
||||
<p class="text-xs text-muted-foreground">${getHookEventDescription(event)}</p>
|
||||
@@ -173,7 +173,7 @@ function renderHooksByEvent(hooks, scope) {
|
||||
data-index="${index}"
|
||||
data-action="edit"
|
||||
title="Edit hook">
|
||||
✏️
|
||||
<i data-lucide="pencil" class="w-4 h-4"></i>
|
||||
</button>
|
||||
<button class="p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 rounded transition-colors"
|
||||
data-scope="${scope}"
|
||||
@@ -181,7 +181,7 @@ function renderHooksByEvent(hooks, scope) {
|
||||
data-index="${index}"
|
||||
data-action="delete"
|
||||
title="Delete hook">
|
||||
🗑️
|
||||
<i data-lucide="trash-2" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -215,7 +215,7 @@ function renderQuickInstallCard(templateId, title, description, event, matcher)
|
||||
<div class="hook-template-card bg-card border border-border rounded-lg p-4 hover:shadow-md transition-all ${isInstalled ? 'border-success bg-success-light/30' : ''}">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xl">${isInstalled ? '✅' : '🪝'}</span>
|
||||
${isInstalled ? '<i data-lucide="check-circle" class="w-5 h-5 text-success"></i>' : '<i data-lucide="webhook" class="w-5 h-5"></i>'}
|
||||
<div>
|
||||
<h4 class="font-semibold text-foreground">${escapeHtml(title)}</h4>
|
||||
<p class="text-xs text-muted-foreground">${escapeHtml(description)}</p>
|
||||
|
||||
@@ -14,11 +14,12 @@ function renderLiteTasks() {
|
||||
if (sessions.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">⚡</div>
|
||||
<div class="empty-icon"><i data-lucide="zap" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">No ${currentLiteType} Sessions</div>
|
||||
<div class="empty-text">No sessions found in .workflow/.${currentLiteType}/</div>
|
||||
</div>
|
||||
`;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,13 +52,13 @@ function renderLiteTaskCard(session) {
|
||||
<div class="session-header">
|
||||
<div class="session-title">${escapeHtml(session.id)}</div>
|
||||
<span class="session-status ${session.type}">
|
||||
${session.type === 'lite-plan' ? '📝 PLAN' : '🔧 FIX'}
|
||||
${session.type === 'lite-plan' ? '<i data-lucide="file-edit" class="w-3 h-3 inline"></i> PLAN' : '<i data-lucide="wrench" class="w-3 h-3 inline"></i> FIX'}
|
||||
</span>
|
||||
</div>
|
||||
<div class="session-body">
|
||||
<div class="session-meta">
|
||||
<span class="session-meta-item">📅 ${formatDate(session.createdAt)}</span>
|
||||
<span class="session-meta-item">📋 ${tasks.length} tasks</span>
|
||||
<span class="session-meta-item"><i data-lucide="calendar" class="w-3.5 h-3.5 inline mr-1"></i>${formatDate(session.createdAt)}</span>
|
||||
<span class="session-meta-item"><i data-lucide="list-checks" class="w-3.5 h-3.5 inline mr-1"></i>${tasks.length} tasks</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,7 +103,7 @@ function showLiteTaskDetailPage(sessionKey) {
|
||||
<span>Back to ${session.type === 'lite-plan' ? 'Lite Plan' : 'Lite Fix'}</span>
|
||||
</button>
|
||||
<div class="detail-title-row">
|
||||
<h2 class="detail-session-id">${session.type === 'lite-plan' ? '📝' : '🔧'} ${escapeHtml(session.id)}</h2>
|
||||
<h2 class="detail-session-id">${session.type === 'lite-plan' ? '<i data-lucide="file-edit" class="w-5 h-5 inline mr-2"></i>' : '<i data-lucide="wrench" class="w-5 h-5 inline mr-2"></i>'} ${escapeHtml(session.id)}</h2>
|
||||
<div class="detail-badges">
|
||||
<span class="session-type-badge ${session.type}">${session.type}</span>
|
||||
</div>
|
||||
@@ -124,20 +125,20 @@ function showLiteTaskDetailPage(sessionKey) {
|
||||
<!-- Tab Navigation -->
|
||||
<div class="detail-tabs">
|
||||
<button class="detail-tab active" data-tab="tasks" onclick="switchLiteDetailTab('tasks')">
|
||||
<span class="tab-icon">📋</span>
|
||||
<span class="tab-icon"><i data-lucide="list-checks" class="w-4 h-4"></i></span>
|
||||
<span class="tab-text">Tasks</span>
|
||||
<span class="tab-count">${tasks.length}</span>
|
||||
</button>
|
||||
<button class="detail-tab" data-tab="plan" onclick="switchLiteDetailTab('plan')">
|
||||
<span class="tab-icon">📐</span>
|
||||
<span class="tab-icon"><i data-lucide="ruler" class="w-4 h-4"></i></span>
|
||||
<span class="tab-text">Plan</span>
|
||||
</button>
|
||||
<button class="detail-tab" data-tab="context" onclick="switchLiteDetailTab('context')">
|
||||
<span class="tab-icon">📦</span>
|
||||
<span class="tab-icon"><i data-lucide="package" class="w-4 h-4"></i></span>
|
||||
<span class="tab-text">Context</span>
|
||||
</button>
|
||||
<button class="detail-tab" data-tab="summary" onclick="switchLiteDetailTab('summary')">
|
||||
<span class="tab-icon">📝</span>
|
||||
<span class="tab-icon"><i data-lucide="file-text" class="w-4 h-4"></i></span>
|
||||
<span class="tab-text">Summary</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -209,7 +210,7 @@ function renderLiteTasksTab(session, tasks, completed, inProgress, pending) {
|
||||
if (tasks.length === 0) {
|
||||
return `
|
||||
<div class="tab-empty-state">
|
||||
<div class="empty-icon">📋</div>
|
||||
<div class="empty-icon"><i data-lucide="clipboard-list" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">No Tasks</div>
|
||||
<div class="empty-text">This session has no tasks defined.</div>
|
||||
</div>
|
||||
@@ -287,7 +288,7 @@ function renderLitePlanTab(session) {
|
||||
if (!plan) {
|
||||
return `
|
||||
<div class="tab-empty-state">
|
||||
<div class="empty-icon">📐</div>
|
||||
<div class="empty-icon"><i data-lucide="ruler" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">No Plan Data</div>
|
||||
<div class="empty-text">No plan.json found for this session.</div>
|
||||
</div>
|
||||
@@ -299,7 +300,7 @@ function renderLitePlanTab(session) {
|
||||
<!-- Summary -->
|
||||
${plan.summary ? `
|
||||
<div class="plan-section">
|
||||
<h4 class="plan-section-title">📋 Summary</h4>
|
||||
<h4 class="plan-section-title"><i data-lucide="clipboard-list" class="w-4 h-4 inline mr-1"></i> Summary</h4>
|
||||
<p class="plan-summary-text">${escapeHtml(plan.summary)}</p>
|
||||
</div>
|
||||
` : ''}
|
||||
@@ -307,7 +308,7 @@ function renderLitePlanTab(session) {
|
||||
<!-- Approach -->
|
||||
${plan.approach ? `
|
||||
<div class="plan-section">
|
||||
<h4 class="plan-section-title">🎯 Approach</h4>
|
||||
<h4 class="plan-section-title"><i data-lucide="target" class="w-4 h-4 inline mr-1"></i> Approach</h4>
|
||||
<p class="plan-approach-text">${escapeHtml(plan.approach)}</p>
|
||||
</div>
|
||||
` : ''}
|
||||
@@ -315,7 +316,7 @@ function renderLitePlanTab(session) {
|
||||
<!-- Focus Paths -->
|
||||
${plan.focus_paths?.length ? `
|
||||
<div class="plan-section">
|
||||
<h4 class="plan-section-title">📁 Focus Paths</h4>
|
||||
<h4 class="plan-section-title"><i data-lucide="folder" class="w-4 h-4 inline mr-1"></i> Focus Paths</h4>
|
||||
<div class="path-tags">
|
||||
${plan.focus_paths.map(p => `<span class="path-tag">${escapeHtml(p)}</span>`).join('')}
|
||||
</div>
|
||||
@@ -324,7 +325,7 @@ function renderLitePlanTab(session) {
|
||||
|
||||
<!-- Metadata -->
|
||||
<div class="plan-section">
|
||||
<h4 class="plan-section-title">ℹ️ Metadata</h4>
|
||||
<h4 class="plan-section-title"><i data-lucide="info" class="w-4 h-4 inline mr-1"></i> Metadata</h4>
|
||||
<div class="plan-meta-grid">
|
||||
${plan.estimated_time ? `<div class="meta-item"><span class="meta-label">Estimated Time:</span> ${escapeHtml(plan.estimated_time)}</div>` : ''}
|
||||
${plan.complexity ? `<div class="meta-item"><span class="meta-label">Complexity:</span> ${escapeHtml(plan.complexity)}</div>` : ''}
|
||||
@@ -379,11 +380,12 @@ async function loadAndRenderLiteSummaryTab(session, contentArea) {
|
||||
// Fallback
|
||||
contentArea.innerHTML = `
|
||||
<div class="tab-empty-state">
|
||||
<div class="empty-icon">📝</div>
|
||||
<div class="empty-icon"><i data-lucide="file-text" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">No Summaries</div>
|
||||
<div class="empty-text">No summaries found in .summaries/</div>
|
||||
</div>
|
||||
`;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
} catch (err) {
|
||||
contentArea.innerHTML = `<div class="tab-error">Failed to load summaries: ${err.message}</div>`;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ async function renderMcpManager() {
|
||||
|
||||
${currentProjectServerNames.length === 0 ? `
|
||||
<div class="mcp-empty-state bg-card border border-border rounded-lg p-6 text-center">
|
||||
<div class="text-3xl mb-3">🔌</div>
|
||||
<div class="text-muted-foreground mb-3"><i data-lucide="plug" class="w-10 h-10 mx-auto"></i></div>
|
||||
<p class="text-muted-foreground">No MCP servers configured for this project</p>
|
||||
<p class="text-sm text-muted-foreground mt-1">Add servers from the available list below</p>
|
||||
</div>
|
||||
@@ -72,7 +72,7 @@ async function renderMcpManager() {
|
||||
<div class="mcp-section mb-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-lg">🏢</span>
|
||||
<i data-lucide="building-2" class="w-5 h-5"></i>
|
||||
<h3 class="text-lg font-semibold text-foreground">Enterprise MCP Servers</h3>
|
||||
<span class="text-xs px-2 py-0.5 bg-warning/20 text-warning rounded-full">Managed</span>
|
||||
</div>
|
||||
@@ -92,7 +92,7 @@ async function renderMcpManager() {
|
||||
<div class="mcp-section mb-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-lg">👤</span>
|
||||
<i data-lucide="user" class="w-5 h-5"></i>
|
||||
<h3 class="text-lg font-semibold text-foreground">User MCP Servers</h3>
|
||||
</div>
|
||||
<span class="text-sm text-muted-foreground">${userServerEntries.length} servers from ~/.claude.json</span>
|
||||
@@ -154,7 +154,7 @@ async function renderMcpManager() {
|
||||
<tr class="border-b border-border last:border-b-0 ${isCurrentProject ? 'bg-primary/5' : 'hover:bg-hover/50'}">
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<span class="text-base shrink-0">${isCurrentProject ? '📍' : '📁'}</span>
|
||||
<span class="shrink-0">${isCurrentProject ? '<i data-lucide="map-pin" class="w-4 h-4 text-primary"></i>' : '<i data-lucide="folder" class="w-4 h-4"></i>'}</span>
|
||||
<div class="min-w-0">
|
||||
<div class="font-medium text-foreground truncate text-sm" title="${escapeHtml(path)}">
|
||||
${escapeHtml(path.split('\\').pop() || path)}
|
||||
@@ -208,7 +208,7 @@ function renderMcpServerCard(serverName, serverConfig, isEnabled, isInCurrentPro
|
||||
<div class="mcp-server-card bg-card border border-border rounded-lg p-4 hover:shadow-md transition-all ${isEnabled ? '' : 'opacity-60'}">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xl">${isEnabled ? '🟢' : '🔴'}</span>
|
||||
<span>${isEnabled ? '<i data-lucide="check-circle" class="w-5 h-5 text-success"></i>' : '<i data-lucide="x-circle" class="w-5 h-5 text-destructive"></i>'}</span>
|
||||
<h4 class="font-semibold text-foreground">${escapeHtml(serverName)}</h4>
|
||||
</div>
|
||||
<label class="mcp-toggle relative inline-flex items-center cursor-pointer">
|
||||
@@ -261,7 +261,7 @@ function renderAvailableServerCard(serverName, serverInfo) {
|
||||
<div class="mcp-server-card mcp-server-available bg-card border border-border border-dashed rounded-lg p-4 hover:shadow-md hover:border-solid transition-all">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xl">⚪</span>
|
||||
<span><i data-lucide="circle-dashed" class="w-5 h-5 text-muted-foreground"></i></span>
|
||||
<h4 class="font-semibold text-foreground">${escapeHtml(serverName)}</h4>
|
||||
</div>
|
||||
<button class="px-3 py-1 text-xs bg-primary text-primary-foreground rounded hover:opacity-90 transition-opacity"
|
||||
@@ -295,7 +295,7 @@ function renderGlobalServerCard(serverName, serverConfig, source = 'user') {
|
||||
<div class="mcp-server-card mcp-server-global bg-card border border-primary/30 rounded-lg p-4 hover:shadow-md transition-all">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xl">👤</span>
|
||||
<i data-lucide="user" class="w-5 h-5"></i>
|
||||
<h4 class="font-semibold text-foreground">${escapeHtml(serverName)}</h4>
|
||||
<span class="text-xs px-2 py-0.5 bg-primary/10 text-primary rounded-full">User</span>
|
||||
</div>
|
||||
@@ -342,10 +342,10 @@ function renderEnterpriseServerCard(serverName, serverConfig) {
|
||||
<div class="mcp-server-card mcp-server-enterprise bg-card border border-warning/30 rounded-lg p-4 hover:shadow-md transition-all">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xl">🏢</span>
|
||||
<i data-lucide="building-2" class="w-5 h-5"></i>
|
||||
<h4 class="font-semibold text-foreground">${escapeHtml(serverName)}</h4>
|
||||
<span class="text-xs px-2 py-0.5 bg-warning/20 text-warning rounded-full">Enterprise</span>
|
||||
<span class="text-xs text-muted-foreground">🔒</span>
|
||||
<i data-lucide="lock" class="w-3 h-3 text-muted-foreground"></i>
|
||||
</div>
|
||||
<span class="px-3 py-1 text-xs bg-muted text-muted-foreground rounded cursor-not-allowed">
|
||||
Read-only
|
||||
|
||||
@@ -12,13 +12,14 @@ function renderProjectOverview() {
|
||||
if (!project) {
|
||||
container.innerHTML = `
|
||||
<div class="flex flex-col items-center justify-center py-16 text-center">
|
||||
<div class="text-6xl mb-4">📋</div>
|
||||
<div class="text-muted-foreground mb-4"><i data-lucide="clipboard-list" class="w-16 h-16"></i></div>
|
||||
<h3 class="text-xl font-semibold text-foreground mb-2">No Project Overview</h3>
|
||||
<p class="text-muted-foreground mb-4">
|
||||
Run <code class="px-2 py-1 bg-muted rounded text-sm font-mono">/workflow:init</code> to initialize project analysis
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,7 +41,7 @@ function renderProjectOverview() {
|
||||
<!-- Technology Stack -->
|
||||
<div class="bg-card border border-border rounded-lg p-6 mb-6">
|
||||
<h3 class="text-lg font-semibold text-foreground mb-4 flex items-center gap-2">
|
||||
<span>💻</span> Technology Stack
|
||||
<i data-lucide="code-2" class="w-5 h-5"></i> Technology Stack
|
||||
</h3>
|
||||
|
||||
<!-- Languages -->
|
||||
@@ -91,7 +92,7 @@ function renderProjectOverview() {
|
||||
<!-- Architecture -->
|
||||
<div class="bg-card border border-border rounded-lg p-6 mb-6">
|
||||
<h3 class="text-lg font-semibold text-foreground mb-4 flex items-center gap-2">
|
||||
<span>🏗️</span> Architecture
|
||||
<i data-lucide="blocks" class="w-5 h-5"></i> Architecture
|
||||
</h3>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-5">
|
||||
@@ -128,7 +129,7 @@ function renderProjectOverview() {
|
||||
<!-- Key Components -->
|
||||
<div class="bg-card border border-border rounded-lg p-6 mb-6">
|
||||
<h3 class="text-lg font-semibold text-foreground mb-4 flex items-center gap-2">
|
||||
<span>⚙️</span> Key Components
|
||||
<i data-lucide="component" class="w-5 h-5"></i> Key Components
|
||||
</h3>
|
||||
|
||||
${project.keyComponents.length > 0 ? `
|
||||
@@ -162,7 +163,7 @@ function renderProjectOverview() {
|
||||
<!-- Development Index -->
|
||||
<div class="bg-card border border-border rounded-lg p-6 mb-6">
|
||||
<h3 class="text-lg font-semibold text-foreground mb-4 flex items-center gap-2">
|
||||
<span>📝</span> Development History
|
||||
<i data-lucide="git-branch" class="w-5 h-5"></i> Development History
|
||||
</h3>
|
||||
|
||||
${renderDevelopmentIndex(project.developmentIndex)}
|
||||
@@ -171,7 +172,7 @@ function renderProjectOverview() {
|
||||
<!-- Statistics -->
|
||||
<div class="bg-card border border-border rounded-lg p-6">
|
||||
<h3 class="text-lg font-semibold text-foreground mb-4 flex items-center gap-2">
|
||||
<span>📊</span> Statistics
|
||||
<i data-lucide="bar-chart-3" class="w-5 h-5"></i> Statistics
|
||||
</h3>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
@@ -196,11 +197,11 @@ function renderDevelopmentIndex(devIndex) {
|
||||
if (!devIndex) return '<p class="text-muted-foreground text-sm">No development history available</p>';
|
||||
|
||||
const categories = [
|
||||
{ key: 'feature', label: 'Features', icon: '✨', badgeClass: 'bg-primary-light text-primary' },
|
||||
{ key: 'enhancement', label: 'Enhancements', icon: '⚡', badgeClass: 'bg-success-light text-success' },
|
||||
{ key: 'bugfix', label: 'Bug Fixes', icon: '🐛', badgeClass: 'bg-destructive/10 text-destructive' },
|
||||
{ key: 'refactor', label: 'Refactorings', icon: '🔧', badgeClass: 'bg-warning-light text-warning' },
|
||||
{ key: 'docs', label: 'Documentation', icon: '📚', badgeClass: 'bg-muted text-muted-foreground' }
|
||||
{ key: 'feature', label: 'Features', icon: '<i data-lucide="sparkles" class="w-4 h-4 inline"></i>', badgeClass: 'bg-primary-light text-primary' },
|
||||
{ key: 'enhancement', label: 'Enhancements', icon: '<i data-lucide="zap" class="w-4 h-4 inline"></i>', badgeClass: 'bg-success-light text-success' },
|
||||
{ key: 'bugfix', label: 'Bug Fixes', icon: '<i data-lucide="bug" class="w-4 h-4 inline"></i>', badgeClass: 'bg-destructive/10 text-destructive' },
|
||||
{ key: 'refactor', label: 'Refactorings', icon: '<i data-lucide="wrench" class="w-4 h-4 inline"></i>', badgeClass: 'bg-warning-light text-warning' },
|
||||
{ key: 'docs', label: 'Documentation', icon: '<i data-lucide="book-open" class="w-4 h-4 inline"></i>', badgeClass: 'bg-muted text-muted-foreground' }
|
||||
];
|
||||
|
||||
const totalEntries = categories.reduce((sum, cat) => sum + (devIndex[cat.key]?.length || 0), 0);
|
||||
|
||||
@@ -81,26 +81,26 @@ function showSessionDetailPage(sessionKey) {
|
||||
<!-- Tab Navigation -->
|
||||
<div class="detail-tabs">
|
||||
<button class="detail-tab active" data-tab="tasks" onclick="switchDetailTab('tasks')">
|
||||
<span class="tab-icon">📋</span>
|
||||
<span class="tab-icon"><i data-lucide="list-checks" class="w-4 h-4"></i></span>
|
||||
<span class="tab-text">Tasks</span>
|
||||
<span class="tab-count">${tasks.length}</span>
|
||||
</button>
|
||||
<button class="detail-tab" data-tab="context" onclick="switchDetailTab('context')">
|
||||
<span class="tab-icon">📦</span>
|
||||
<span class="tab-icon"><i data-lucide="package" class="w-4 h-4"></i></span>
|
||||
<span class="tab-text">Context</span>
|
||||
</button>
|
||||
<button class="detail-tab" data-tab="summary" onclick="switchDetailTab('summary')">
|
||||
<span class="tab-icon">📝</span>
|
||||
<span class="tab-icon"><i data-lucide="file-text" class="w-4 h-4"></i></span>
|
||||
<span class="tab-text">Summary</span>
|
||||
</button>
|
||||
<button class="detail-tab" data-tab="impl-plan" onclick="switchDetailTab('impl-plan')">
|
||||
<span class="tab-icon">📐</span>
|
||||
<span class="tab-icon"><i data-lucide="ruler" class="w-4 h-4"></i></span>
|
||||
<span class="tab-text">IMPL Plan</span>
|
||||
</button>
|
||||
<button class="detail-tab" data-tab="conflict" onclick="switchDetailTab('conflict')"> <span class="tab-icon">⚖️</span> <span class="tab-text">Conflict</span> </button>
|
||||
<button class="detail-tab" data-tab="conflict" onclick="switchDetailTab('conflict')"> <span class="tab-icon"><i data-lucide="scale" class="w-4 h-4"></i></span> <span class="tab-text">Conflict</span> </button>
|
||||
${session.hasReview ? `
|
||||
<button class="detail-tab" data-tab="review" onclick="switchDetailTab('review')">
|
||||
<span class="tab-icon">🔍</span>
|
||||
<span class="tab-icon"><i data-lucide="search" class="w-4 h-4"></i></span>
|
||||
<span class="tab-text">Review</span>
|
||||
</button>
|
||||
` : ''}
|
||||
@@ -199,7 +199,7 @@ function renderTasksTab(session, tasks, completed, inProgress, pending) {
|
||||
<div class="tab-loading">Loading task details...</div>
|
||||
` : (tasks.length === 0 ? `
|
||||
<div class="tab-empty-state">
|
||||
<div class="empty-icon">📋</div>
|
||||
<div class="empty-icon"><i data-lucide="clipboard-list" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">No Tasks</div>
|
||||
<div class="empty-text">This session has no tasks defined.</div>
|
||||
</div>
|
||||
@@ -227,11 +227,12 @@ async function loadFullTaskDetails() {
|
||||
} else {
|
||||
tasksContainer.innerHTML = `
|
||||
<div class="tab-empty-state">
|
||||
<div class="empty-icon">📋</div>
|
||||
<div class="empty-icon"><i data-lucide="clipboard-list" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">No Task Files</div>
|
||||
<div class="empty-text">No IMPL-*.json files found in .task/</div>
|
||||
</div>
|
||||
`;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -429,11 +430,12 @@ async function loadAndRenderContextTab(session, contentArea) {
|
||||
// Fallback: show placeholder
|
||||
contentArea.innerHTML = `
|
||||
<div class="tab-empty-state">
|
||||
<div class="empty-icon">📦</div>
|
||||
<div class="empty-icon"><i data-lucide="package" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">Context Data</div>
|
||||
<div class="empty-text">Context data will be loaded from context-package.json</div>
|
||||
</div>
|
||||
`;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
} catch (err) {
|
||||
contentArea.innerHTML = `<div class="tab-error">Failed to load context: ${err.message}</div>`;
|
||||
}
|
||||
@@ -453,11 +455,12 @@ async function loadAndRenderSummaryTab(session, contentArea) {
|
||||
}
|
||||
contentArea.innerHTML = `
|
||||
<div class="tab-empty-state">
|
||||
<div class="empty-icon">📝</div>
|
||||
<div class="empty-icon"><i data-lucide="file-text" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">Summaries</div>
|
||||
<div class="empty-text">Session summaries will be loaded from .summaries/</div>
|
||||
</div>
|
||||
`;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
} catch (err) {
|
||||
contentArea.innerHTML = `<div class="tab-error">Failed to load summaries: ${err.message}</div>`;
|
||||
}
|
||||
@@ -477,11 +480,12 @@ async function loadAndRenderImplPlanTab(session, contentArea) {
|
||||
}
|
||||
contentArea.innerHTML = `
|
||||
<div class="tab-empty-state">
|
||||
<div class="empty-icon">📐</div>
|
||||
<div class="empty-icon"><i data-lucide="ruler" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">IMPL Plan</div>
|
||||
<div class="empty-text">IMPL plan will be loaded from IMPL_PLAN.md</div>
|
||||
</div>
|
||||
`;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
} catch (err) {
|
||||
contentArea.innerHTML = `<div class="tab-error">Failed to load IMPL plan: ${err.message}</div>`;
|
||||
}
|
||||
@@ -501,11 +505,12 @@ async function loadAndRenderReviewTab(session, contentArea) {
|
||||
}
|
||||
contentArea.innerHTML = `
|
||||
<div class="tab-empty-state">
|
||||
<div class="empty-icon">🔍</div>
|
||||
<div class="empty-icon"><i data-lucide="search" class="w-12 h-12"></i></div>
|
||||
<div class="empty-title">Review Data</div>
|
||||
<div class="empty-text">Review data will be loaded from review files</div>
|
||||
</div>
|
||||
`;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
} catch (err) {
|
||||
contentArea.innerHTML = `<div class="tab-error">Failed to load review: ${err.message}</div>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user