mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +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} targetPath - Directory path to update
|
||||||
* @param {string} tool - CLI tool to use (gemini, qwen, codex)
|
* @param {string} tool - CLI tool to use (gemini, qwen, codex)
|
||||||
* @param {string} strategy - Update strategy (single-layer, multi-layer)
|
* @param {string} strategy - Update strategy (single-layer, multi-layer)
|
||||||
* @returns {Promise<Object>}
|
* @returns {Promise<Object>}
|
||||||
*/
|
*/
|
||||||
async function triggerUpdateClaudeMd(targetPath, tool, strategy) {
|
async function triggerUpdateClaudeMd(targetPath, tool, strategy) {
|
||||||
const { execSync } = await import('child_process');
|
const { spawn } = await import('child_process');
|
||||||
|
|
||||||
try {
|
// Normalize path
|
||||||
// Normalize path
|
let normalizedPath = targetPath.replace(/\\/g, '/');
|
||||||
let normalizedPath = targetPath.replace(/\\/g, '/');
|
if (normalizedPath.match(/^\/[a-zA-Z]\//)) {
|
||||||
if (normalizedPath.match(/^\/[a-zA-Z]\//)) {
|
normalizedPath = normalizedPath.charAt(1).toUpperCase() + ':' + normalizedPath.slice(2);
|
||||||
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)) {
|
// Spawn the process
|
||||||
return { error: 'Directory not found' };
|
const child = spawn('ccw', ['tool', 'exec', 'update_module_claude', params], {
|
||||||
}
|
cwd: normalizedPath,
|
||||||
|
shell: isWindows,
|
||||||
if (!statSync(normalizedPath).isDirectory()) {
|
stdio: ['ignore', 'pipe', 'pipe']
|
||||||
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
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
let stdout = '';
|
||||||
success: true,
|
let stderr = '';
|
||||||
message: `CLAUDE.md updated successfully using ${tool} (${strategy})`,
|
|
||||||
output,
|
child.stdout.on('data', (data) => {
|
||||||
path: normalizedPath
|
stdout += data.toString();
|
||||||
};
|
});
|
||||||
} catch (error) {
|
|
||||||
console.error('Error updating CLAUDE.md:', error);
|
child.stderr.on('data', (data) => {
|
||||||
return {
|
stderr += data.toString();
|
||||||
success: false,
|
});
|
||||||
error: error.message,
|
|
||||||
output: error.stdout || error.stderr || ''
|
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));
|
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 {
|
.task-queue-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 4px;
|
||||||
padding: 10px 16px;
|
|
||||||
border-bottom: 1px solid hsl(var(--border));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.queue-action-btn {
|
.queue-action-btn {
|
||||||
|
|||||||
@@ -271,3 +271,13 @@ function getHookEventIcon(event) {
|
|||||||
};
|
};
|
||||||
return icons[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 updateTaskQueue = [];
|
||||||
let isTaskQueueVisible = false;
|
let isTaskQueueVisible = false;
|
||||||
let isTaskRunning = 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>
|
<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>
|
<button class="task-queue-close" onclick="toggleTaskQueue()">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="task-queue-actions">
|
<div class="task-queue-toolbar">
|
||||||
<button class="queue-action-btn" onclick="openAddTaskModal()" title="Add update task">
|
<div class="queue-cli-selector">
|
||||||
<i data-lucide="plus" class="w-4 h-4"></i> Add
|
<label>CLI:</label>
|
||||||
</button>
|
<select id="queueCliTool" onchange="updateDefaultCliTool(this.value)">
|
||||||
<button class="queue-action-btn queue-start-btn" onclick="startTaskQueue()" id="startQueueBtn" disabled>
|
<option value="gemini">Gemini</option>
|
||||||
<i data-lucide="play" class="w-4 h-4"></i> Start
|
<option value="qwen">Qwen</option>
|
||||||
</button>
|
<option value="codex">Codex</option>
|
||||||
<button class="queue-action-btn queue-clear-btn" onclick="clearCompletedTasks()" title="Clear completed">
|
</select>
|
||||||
<i data-lucide="trash-2" class="w-4 h-4"></i> Clear
|
</div>
|
||||||
</button>
|
<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>
|
||||||
<div class="task-queue-list" id="taskQueueList">
|
<div class="task-queue-list" id="taskQueueList">
|
||||||
<div class="task-queue-empty">
|
<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
|
* 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)
|
* Add task from folder context (right-click or button)
|
||||||
*/
|
*/
|
||||||
function addFolderToQueue(folderPath, strategy = 'single-layer') {
|
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
|
// Show task queue if not visible
|
||||||
if (!isTaskQueueVisible) {
|
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() {
|
async function startTaskQueue() {
|
||||||
if (isTaskRunning) return;
|
if (isTaskRunning) return;
|
||||||
@@ -751,55 +818,13 @@ async function startTaskQueue() {
|
|||||||
isTaskRunning = true;
|
isTaskRunning = true;
|
||||||
document.getElementById('startQueueBtn').disabled = 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;
|
// Execute all tasks in parallel
|
||||||
let failCount = 0;
|
const results = await Promise.all(pendingTasks.map(task => executeTask(task)));
|
||||||
|
|
||||||
for (const task of pendingTasks) {
|
const successCount = results.filter(r => r.success).length;
|
||||||
const folderName = task.path.split('/').pop() || task.path;
|
const failCount = results.filter(r => !r.success).length;
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
isTaskRunning = false;
|
isTaskRunning = false;
|
||||||
|
|
||||||
|
|||||||
@@ -73,11 +73,12 @@ function renderSessions() {
|
|||||||
if (sessions.length === 0) {
|
if (sessions.length === 0) {
|
||||||
container.innerHTML = `
|
container.innerHTML = `
|
||||||
<div class="empty-state" style="grid-column: 1/-1;">
|
<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-title">No Sessions Found</div>
|
||||||
<div class="empty-text">No workflow sessions match your current filter.</div>
|
<div class="empty-text">No workflow sessions match your current filter.</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,8 +121,8 @@ function renderSessionCard(session) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="session-body">
|
<div class="session-body">
|
||||||
<div class="session-meta">
|
<div class="session-meta">
|
||||||
<span class="session-meta-item">📅 ${formatDate(date)}</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">📋 ${taskCount} tasks</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>
|
</div>
|
||||||
${taskCount > 0 ? `
|
${taskCount > 0 ? `
|
||||||
<div class="progress-container">
|
<div class="progress-container">
|
||||||
@@ -171,16 +172,16 @@ function renderReviewSessionCard(session, sessionKey, typeBadge, isActive, date)
|
|||||||
</div>
|
</div>
|
||||||
<div class="session-body">
|
<div class="session-body">
|
||||||
<div class="session-meta">
|
<div class="session-meta">
|
||||||
<span class="session-meta-item">📅 ${formatDate(date)}</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">🔍 ${totalFindings} findings</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>
|
</div>
|
||||||
${totalFindings > 0 ? `
|
${totalFindings > 0 ? `
|
||||||
<div class="review-findings-summary">
|
<div class="review-findings-summary">
|
||||||
<div class="findings-severity-row">
|
<div class="findings-severity-row">
|
||||||
${criticalCount > 0 ? `<span class="finding-count critical">🔴 ${criticalCount}</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">🟠 ${highCount}</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">🟡 ${mediumCount}</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">🟢 ${lowCount}</span>` : ''}
|
${lowCount > 0 ? `<span class="finding-count low"><i data-lucide="check-circle" class="w-3 h-3 inline"></i> ${lowCount}</span>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="dimensions-info">
|
<div class="dimensions-info">
|
||||||
${dimensions.length} dimensions
|
${dimensions.length} dimensions
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ async function renderHookManager() {
|
|||||||
|
|
||||||
${projectHookCount === 0 ? `
|
${projectHookCount === 0 ? `
|
||||||
<div class="hook-empty-state bg-card border border-border rounded-lg p-6 text-center">
|
<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-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>
|
<p class="text-sm text-muted-foreground mt-1">Create a hook to automate actions on tool usage</p>
|
||||||
</div>
|
</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="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-start justify-between mb-3">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-xl">${getHookEventIcon(event)}</span>
|
${getHookEventIconLucide(event)}
|
||||||
<div>
|
<div>
|
||||||
<h4 class="font-semibold text-foreground">${event}</h4>
|
<h4 class="font-semibold text-foreground">${event}</h4>
|
||||||
<p class="text-xs text-muted-foreground">${getHookEventDescription(event)}</p>
|
<p class="text-xs text-muted-foreground">${getHookEventDescription(event)}</p>
|
||||||
@@ -173,7 +173,7 @@ function renderHooksByEvent(hooks, scope) {
|
|||||||
data-index="${index}"
|
data-index="${index}"
|
||||||
data-action="edit"
|
data-action="edit"
|
||||||
title="Edit hook">
|
title="Edit hook">
|
||||||
✏️
|
<i data-lucide="pencil" class="w-4 h-4"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 rounded transition-colors"
|
<button class="p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 rounded transition-colors"
|
||||||
data-scope="${scope}"
|
data-scope="${scope}"
|
||||||
@@ -181,7 +181,7 @@ function renderHooksByEvent(hooks, scope) {
|
|||||||
data-index="${index}"
|
data-index="${index}"
|
||||||
data-action="delete"
|
data-action="delete"
|
||||||
title="Delete hook">
|
title="Delete hook">
|
||||||
🗑️
|
<i data-lucide="trash-2" class="w-4 h-4"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</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="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-start justify-between mb-3">
|
||||||
<div class="flex items-center gap-2">
|
<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>
|
<div>
|
||||||
<h4 class="font-semibold text-foreground">${escapeHtml(title)}</h4>
|
<h4 class="font-semibold text-foreground">${escapeHtml(title)}</h4>
|
||||||
<p class="text-xs text-muted-foreground">${escapeHtml(description)}</p>
|
<p class="text-xs text-muted-foreground">${escapeHtml(description)}</p>
|
||||||
|
|||||||
@@ -14,11 +14,12 @@ function renderLiteTasks() {
|
|||||||
if (sessions.length === 0) {
|
if (sessions.length === 0) {
|
||||||
container.innerHTML = `
|
container.innerHTML = `
|
||||||
<div class="empty-state">
|
<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-title">No ${currentLiteType} Sessions</div>
|
||||||
<div class="empty-text">No sessions found in .workflow/.${currentLiteType}/</div>
|
<div class="empty-text">No sessions found in .workflow/.${currentLiteType}/</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,13 +52,13 @@ function renderLiteTaskCard(session) {
|
|||||||
<div class="session-header">
|
<div class="session-header">
|
||||||
<div class="session-title">${escapeHtml(session.id)}</div>
|
<div class="session-title">${escapeHtml(session.id)}</div>
|
||||||
<span class="session-status ${session.type}">
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="session-body">
|
<div class="session-body">
|
||||||
<div class="session-meta">
|
<div class="session-meta">
|
||||||
<span class="session-meta-item">📅 ${formatDate(session.createdAt)}</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">📋 ${tasks.length} tasks</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,7 +103,7 @@ function showLiteTaskDetailPage(sessionKey) {
|
|||||||
<span>Back to ${session.type === 'lite-plan' ? 'Lite Plan' : 'Lite Fix'}</span>
|
<span>Back to ${session.type === 'lite-plan' ? 'Lite Plan' : 'Lite Fix'}</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="detail-title-row">
|
<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">
|
<div class="detail-badges">
|
||||||
<span class="session-type-badge ${session.type}">${session.type}</span>
|
<span class="session-type-badge ${session.type}">${session.type}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -124,20 +125,20 @@ function showLiteTaskDetailPage(sessionKey) {
|
|||||||
<!-- Tab Navigation -->
|
<!-- Tab Navigation -->
|
||||||
<div class="detail-tabs">
|
<div class="detail-tabs">
|
||||||
<button class="detail-tab active" data-tab="tasks" onclick="switchLiteDetailTab('tasks')">
|
<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-text">Tasks</span>
|
||||||
<span class="tab-count">${tasks.length}</span>
|
<span class="tab-count">${tasks.length}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="detail-tab" data-tab="plan" onclick="switchLiteDetailTab('plan')">
|
<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>
|
<span class="tab-text">Plan</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="detail-tab" data-tab="context" onclick="switchLiteDetailTab('context')">
|
<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>
|
<span class="tab-text">Context</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="detail-tab" data-tab="summary" onclick="switchLiteDetailTab('summary')">
|
<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>
|
<span class="tab-text">Summary</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -209,7 +210,7 @@ function renderLiteTasksTab(session, tasks, completed, inProgress, pending) {
|
|||||||
if (tasks.length === 0) {
|
if (tasks.length === 0) {
|
||||||
return `
|
return `
|
||||||
<div class="tab-empty-state">
|
<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-title">No Tasks</div>
|
||||||
<div class="empty-text">This session has no tasks defined.</div>
|
<div class="empty-text">This session has no tasks defined.</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -287,7 +288,7 @@ function renderLitePlanTab(session) {
|
|||||||
if (!plan) {
|
if (!plan) {
|
||||||
return `
|
return `
|
||||||
<div class="tab-empty-state">
|
<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-title">No Plan Data</div>
|
||||||
<div class="empty-text">No plan.json found for this session.</div>
|
<div class="empty-text">No plan.json found for this session.</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -299,7 +300,7 @@ function renderLitePlanTab(session) {
|
|||||||
<!-- Summary -->
|
<!-- Summary -->
|
||||||
${plan.summary ? `
|
${plan.summary ? `
|
||||||
<div class="plan-section">
|
<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>
|
<p class="plan-summary-text">${escapeHtml(plan.summary)}</p>
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
@@ -307,7 +308,7 @@ function renderLitePlanTab(session) {
|
|||||||
<!-- Approach -->
|
<!-- Approach -->
|
||||||
${plan.approach ? `
|
${plan.approach ? `
|
||||||
<div class="plan-section">
|
<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>
|
<p class="plan-approach-text">${escapeHtml(plan.approach)}</p>
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
@@ -315,7 +316,7 @@ function renderLitePlanTab(session) {
|
|||||||
<!-- Focus Paths -->
|
<!-- Focus Paths -->
|
||||||
${plan.focus_paths?.length ? `
|
${plan.focus_paths?.length ? `
|
||||||
<div class="plan-section">
|
<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">
|
<div class="path-tags">
|
||||||
${plan.focus_paths.map(p => `<span class="path-tag">${escapeHtml(p)}</span>`).join('')}
|
${plan.focus_paths.map(p => `<span class="path-tag">${escapeHtml(p)}</span>`).join('')}
|
||||||
</div>
|
</div>
|
||||||
@@ -324,7 +325,7 @@ function renderLitePlanTab(session) {
|
|||||||
|
|
||||||
<!-- Metadata -->
|
<!-- Metadata -->
|
||||||
<div class="plan-section">
|
<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">
|
<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.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>` : ''}
|
${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
|
// Fallback
|
||||||
contentArea.innerHTML = `
|
contentArea.innerHTML = `
|
||||||
<div class="tab-empty-state">
|
<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-title">No Summaries</div>
|
||||||
<div class="empty-text">No summaries found in .summaries/</div>
|
<div class="empty-text">No summaries found in .summaries/</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
contentArea.innerHTML = `<div class="tab-error">Failed to load summaries: ${err.message}</div>`;
|
contentArea.innerHTML = `<div class="tab-error">Failed to load summaries: ${err.message}</div>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ async function renderMcpManager() {
|
|||||||
|
|
||||||
${currentProjectServerNames.length === 0 ? `
|
${currentProjectServerNames.length === 0 ? `
|
||||||
<div class="mcp-empty-state bg-card border border-border rounded-lg p-6 text-center">
|
<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-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>
|
<p class="text-sm text-muted-foreground mt-1">Add servers from the available list below</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,7 +72,7 @@ async function renderMcpManager() {
|
|||||||
<div class="mcp-section mb-6">
|
<div class="mcp-section mb-6">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<div class="flex items-center gap-2">
|
<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>
|
<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>
|
<span class="text-xs px-2 py-0.5 bg-warning/20 text-warning rounded-full">Managed</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -92,7 +92,7 @@ async function renderMcpManager() {
|
|||||||
<div class="mcp-section mb-6">
|
<div class="mcp-section mb-6">
|
||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<div class="flex items-center gap-2">
|
<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>
|
<h3 class="text-lg font-semibold text-foreground">User MCP Servers</h3>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm text-muted-foreground">${userServerEntries.length} servers from ~/.claude.json</span>
|
<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'}">
|
<tr class="border-b border-border last:border-b-0 ${isCurrentProject ? 'bg-primary/5' : 'hover:bg-hover/50'}">
|
||||||
<td class="px-4 py-3">
|
<td class="px-4 py-3">
|
||||||
<div class="flex items-center gap-2 min-w-0">
|
<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="min-w-0">
|
||||||
<div class="font-medium text-foreground truncate text-sm" title="${escapeHtml(path)}">
|
<div class="font-medium text-foreground truncate text-sm" title="${escapeHtml(path)}">
|
||||||
${escapeHtml(path.split('\\').pop() || 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="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-start justify-between mb-3">
|
||||||
<div class="flex items-center gap-2">
|
<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>
|
<h4 class="font-semibold text-foreground">${escapeHtml(serverName)}</h4>
|
||||||
</div>
|
</div>
|
||||||
<label class="mcp-toggle relative inline-flex items-center cursor-pointer">
|
<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="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-start justify-between mb-3">
|
||||||
<div class="flex items-center gap-2">
|
<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>
|
<h4 class="font-semibold text-foreground">${escapeHtml(serverName)}</h4>
|
||||||
</div>
|
</div>
|
||||||
<button class="px-3 py-1 text-xs bg-primary text-primary-foreground rounded hover:opacity-90 transition-opacity"
|
<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="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-start justify-between mb-3">
|
||||||
<div class="flex items-center gap-2">
|
<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>
|
<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>
|
<span class="text-xs px-2 py-0.5 bg-primary/10 text-primary rounded-full">User</span>
|
||||||
</div>
|
</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="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-start justify-between mb-3">
|
||||||
<div class="flex items-center gap-2">
|
<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>
|
<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 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>
|
</div>
|
||||||
<span class="px-3 py-1 text-xs bg-muted text-muted-foreground rounded cursor-not-allowed">
|
<span class="px-3 py-1 text-xs bg-muted text-muted-foreground rounded cursor-not-allowed">
|
||||||
Read-only
|
Read-only
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ function renderProjectOverview() {
|
|||||||
if (!project) {
|
if (!project) {
|
||||||
container.innerHTML = `
|
container.innerHTML = `
|
||||||
<div class="flex flex-col items-center justify-center py-16 text-center">
|
<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>
|
<h3 class="text-xl font-semibold text-foreground mb-2">No Project Overview</h3>
|
||||||
<p class="text-muted-foreground mb-4">
|
<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
|
Run <code class="px-2 py-1 bg-muted rounded text-sm font-mono">/workflow:init</code> to initialize project analysis
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ function renderProjectOverview() {
|
|||||||
<!-- Technology Stack -->
|
<!-- Technology Stack -->
|
||||||
<div class="bg-card border border-border rounded-lg p-6 mb-6">
|
<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">
|
<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>
|
</h3>
|
||||||
|
|
||||||
<!-- Languages -->
|
<!-- Languages -->
|
||||||
@@ -91,7 +92,7 @@ function renderProjectOverview() {
|
|||||||
<!-- Architecture -->
|
<!-- Architecture -->
|
||||||
<div class="bg-card border border-border rounded-lg p-6 mb-6">
|
<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">
|
<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>
|
</h3>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-5">
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-5">
|
||||||
@@ -128,7 +129,7 @@ function renderProjectOverview() {
|
|||||||
<!-- Key Components -->
|
<!-- Key Components -->
|
||||||
<div class="bg-card border border-border rounded-lg p-6 mb-6">
|
<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">
|
<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>
|
</h3>
|
||||||
|
|
||||||
${project.keyComponents.length > 0 ? `
|
${project.keyComponents.length > 0 ? `
|
||||||
@@ -162,7 +163,7 @@ function renderProjectOverview() {
|
|||||||
<!-- Development Index -->
|
<!-- Development Index -->
|
||||||
<div class="bg-card border border-border rounded-lg p-6 mb-6">
|
<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">
|
<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>
|
</h3>
|
||||||
|
|
||||||
${renderDevelopmentIndex(project.developmentIndex)}
|
${renderDevelopmentIndex(project.developmentIndex)}
|
||||||
@@ -171,7 +172,7 @@ function renderProjectOverview() {
|
|||||||
<!-- Statistics -->
|
<!-- Statistics -->
|
||||||
<div class="bg-card border border-border rounded-lg p-6">
|
<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">
|
<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>
|
</h3>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<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>';
|
if (!devIndex) return '<p class="text-muted-foreground text-sm">No development history available</p>';
|
||||||
|
|
||||||
const categories = [
|
const categories = [
|
||||||
{ key: 'feature', label: 'Features', icon: '✨', badgeClass: 'bg-primary-light text-primary' },
|
{ 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: '⚡', badgeClass: 'bg-success-light text-success' },
|
{ 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: '🐛', badgeClass: 'bg-destructive/10 text-destructive' },
|
{ 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: '🔧', badgeClass: 'bg-warning-light text-warning' },
|
{ 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: '📚', badgeClass: 'bg-muted text-muted-foreground' }
|
{ 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);
|
const totalEntries = categories.reduce((sum, cat) => sum + (devIndex[cat.key]?.length || 0), 0);
|
||||||
|
|||||||
@@ -81,26 +81,26 @@ function showSessionDetailPage(sessionKey) {
|
|||||||
<!-- Tab Navigation -->
|
<!-- Tab Navigation -->
|
||||||
<div class="detail-tabs">
|
<div class="detail-tabs">
|
||||||
<button class="detail-tab active" data-tab="tasks" onclick="switchDetailTab('tasks')">
|
<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-text">Tasks</span>
|
||||||
<span class="tab-count">${tasks.length}</span>
|
<span class="tab-count">${tasks.length}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="detail-tab" data-tab="context" onclick="switchDetailTab('context')">
|
<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>
|
<span class="tab-text">Context</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="detail-tab" data-tab="summary" onclick="switchDetailTab('summary')">
|
<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>
|
<span class="tab-text">Summary</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="detail-tab" data-tab="impl-plan" onclick="switchDetailTab('impl-plan')">
|
<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>
|
<span class="tab-text">IMPL Plan</span>
|
||||||
</button>
|
</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 ? `
|
${session.hasReview ? `
|
||||||
<button class="detail-tab" data-tab="review" onclick="switchDetailTab('review')">
|
<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>
|
<span class="tab-text">Review</span>
|
||||||
</button>
|
</button>
|
||||||
` : ''}
|
` : ''}
|
||||||
@@ -199,7 +199,7 @@ function renderTasksTab(session, tasks, completed, inProgress, pending) {
|
|||||||
<div class="tab-loading">Loading task details...</div>
|
<div class="tab-loading">Loading task details...</div>
|
||||||
` : (tasks.length === 0 ? `
|
` : (tasks.length === 0 ? `
|
||||||
<div class="tab-empty-state">
|
<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-title">No Tasks</div>
|
||||||
<div class="empty-text">This session has no tasks defined.</div>
|
<div class="empty-text">This session has no tasks defined.</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -227,11 +227,12 @@ async function loadFullTaskDetails() {
|
|||||||
} else {
|
} else {
|
||||||
tasksContainer.innerHTML = `
|
tasksContainer.innerHTML = `
|
||||||
<div class="tab-empty-state">
|
<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-title">No Task Files</div>
|
||||||
<div class="empty-text">No IMPL-*.json files found in .task/</div>
|
<div class="empty-text">No IMPL-*.json files found in .task/</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -429,11 +430,12 @@ async function loadAndRenderContextTab(session, contentArea) {
|
|||||||
// Fallback: show placeholder
|
// Fallback: show placeholder
|
||||||
contentArea.innerHTML = `
|
contentArea.innerHTML = `
|
||||||
<div class="tab-empty-state">
|
<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-title">Context Data</div>
|
||||||
<div class="empty-text">Context data will be loaded from context-package.json</div>
|
<div class="empty-text">Context data will be loaded from context-package.json</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
contentArea.innerHTML = `<div class="tab-error">Failed to load context: ${err.message}</div>`;
|
contentArea.innerHTML = `<div class="tab-error">Failed to load context: ${err.message}</div>`;
|
||||||
}
|
}
|
||||||
@@ -453,11 +455,12 @@ async function loadAndRenderSummaryTab(session, contentArea) {
|
|||||||
}
|
}
|
||||||
contentArea.innerHTML = `
|
contentArea.innerHTML = `
|
||||||
<div class="tab-empty-state">
|
<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-title">Summaries</div>
|
||||||
<div class="empty-text">Session summaries will be loaded from .summaries/</div>
|
<div class="empty-text">Session summaries will be loaded from .summaries/</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
contentArea.innerHTML = `<div class="tab-error">Failed to load summaries: ${err.message}</div>`;
|
contentArea.innerHTML = `<div class="tab-error">Failed to load summaries: ${err.message}</div>`;
|
||||||
}
|
}
|
||||||
@@ -477,11 +480,12 @@ async function loadAndRenderImplPlanTab(session, contentArea) {
|
|||||||
}
|
}
|
||||||
contentArea.innerHTML = `
|
contentArea.innerHTML = `
|
||||||
<div class="tab-empty-state">
|
<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-title">IMPL Plan</div>
|
||||||
<div class="empty-text">IMPL plan will be loaded from IMPL_PLAN.md</div>
|
<div class="empty-text">IMPL plan will be loaded from IMPL_PLAN.md</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
contentArea.innerHTML = `<div class="tab-error">Failed to load IMPL plan: ${err.message}</div>`;
|
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 = `
|
contentArea.innerHTML = `
|
||||||
<div class="tab-empty-state">
|
<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-title">Review Data</div>
|
||||||
<div class="empty-text">Review data will be loaded from review files</div>
|
<div class="empty-text">Review data will be loaded from review files</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
contentArea.innerHTML = `<div class="tab-error">Failed to load review: ${err.message}</div>`;
|
contentArea.innerHTML = `<div class="tab-error">Failed to load review: ${err.message}</div>`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user