mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +08:00
feat: Enhance navigation and cleanup for graph explorer view
- Added a cleanup function to reset the state when navigating away from the graph explorer. - Updated navigation logic to call the cleanup function before switching views. - Improved internationalization by adding new translations for graph-related terms. - Adjusted icon sizes for better UI consistency in the graph explorer. - Implemented impact analysis button functionality in the graph explorer. - Refactored CLI tool configuration to use updated model names. - Enhanced CLI executor to handle prompts correctly for codex commands. - Introduced code relationship storage for better visualization in the index tree. - Added support for parsing Markdown and plain text files in the symbol parser. - Updated tests to reflect changes in language detection logic.
This commit is contained in:
@@ -59,40 +59,22 @@ function renderStorageCard() {
|
||||
return date.toLocaleDateString();
|
||||
};
|
||||
|
||||
// Build project rows
|
||||
// Build project tree (hierarchical view)
|
||||
let projectRows = '';
|
||||
if (projects && projects.length > 0) {
|
||||
projects.slice(0, 5).forEach(p => {
|
||||
const historyBadge = p.historyRecords > 0
|
||||
? '<span class="text-xs px-1.5 py-0.5 bg-primary/10 text-primary rounded">' + p.historyRecords + '</span>'
|
||||
: '<span class="text-xs text-muted-foreground">-</span>';
|
||||
const tree = buildProjectTree(projects);
|
||||
projectRows = renderProjectTree(tree, 0, formatTimeAgo);
|
||||
|
||||
projectRows += '\
|
||||
<tr class="border-b border-border/50 hover:bg-muted/30">\
|
||||
<td class="py-2 px-2 font-mono text-xs text-muted-foreground">' + escapeHtml(p.id.substring(0, 8)) + '...</td>\
|
||||
<td class="py-2 px-2 text-sm text-right">' + escapeHtml(p.totalSizeFormatted) + '</td>\
|
||||
<td class="py-2 px-2 text-center">' + historyBadge + '</td>\
|
||||
<td class="py-2 px-2 text-xs text-muted-foreground text-right">' + formatTimeAgo(p.lastModified) + '</td>\
|
||||
<td class="py-2 px-1 text-right">\
|
||||
<button onclick="cleanProjectStorage(\'' + escapeHtml(p.id) + '\')" \
|
||||
class="text-xs px-2 py-1 text-destructive hover:bg-destructive/10 rounded transition-colors" \
|
||||
title="Clean this project storage">\
|
||||
<i data-lucide="trash-2" class="w-3 h-3"></i>\
|
||||
</button>\
|
||||
</td>\
|
||||
</tr>\
|
||||
';
|
||||
});
|
||||
|
||||
if (projects.length > 5) {
|
||||
projectRows += '\
|
||||
<tr>\
|
||||
<td colspan="5" class="py-2 px-2 text-xs text-muted-foreground text-center">\
|
||||
... and ' + (projects.length - 5) + ' more projects\
|
||||
</td>\
|
||||
</tr>\
|
||||
';
|
||||
}
|
||||
// Initially hide all child rows (level > 0)
|
||||
setTimeout(() => {
|
||||
const allRows = document.querySelectorAll('.project-row');
|
||||
allRows.forEach(row => {
|
||||
const level = parseInt(row.getAttribute('data-level'));
|
||||
if (level > 0) {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
} else {
|
||||
projectRows = '\
|
||||
<tr>\
|
||||
@@ -178,6 +160,162 @@ function getTotalRecords() {
|
||||
return storageData.projects.reduce((sum, p) => sum + (p.historyRecords || 0), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build project tree from flat list
|
||||
* Converts flat project list to hierarchical tree structure
|
||||
*/
|
||||
function buildProjectTree(projects) {
|
||||
const tree = [];
|
||||
const map = new Map();
|
||||
|
||||
// Sort by path depth (shallowest first)
|
||||
const sorted = projects.slice().sort((a, b) => {
|
||||
const depthA = (a.id.match(/\//g) || []).length;
|
||||
const depthB = (b.id.match(/\//g) || []).length;
|
||||
return depthA - depthB;
|
||||
});
|
||||
|
||||
for (const project of sorted) {
|
||||
const segments = project.id.split('/');
|
||||
|
||||
if (segments.length === 1) {
|
||||
// Root level project
|
||||
const node = {
|
||||
...project,
|
||||
children: [],
|
||||
isExpanded: false
|
||||
};
|
||||
tree.push(node);
|
||||
map.set(project.id, node);
|
||||
} else {
|
||||
// Sub-project
|
||||
const parentId = segments.slice(0, -1).join('/');
|
||||
const parent = map.get(parentId);
|
||||
|
||||
if (parent) {
|
||||
const node = {
|
||||
...project,
|
||||
children: [],
|
||||
isExpanded: false
|
||||
};
|
||||
parent.children.push(node);
|
||||
map.set(project.id, node);
|
||||
} else {
|
||||
// Orphaned project (parent not found) - add to root
|
||||
const node = {
|
||||
...project,
|
||||
children: [],
|
||||
isExpanded: false
|
||||
};
|
||||
tree.push(node);
|
||||
map.set(project.id, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render project tree recursively
|
||||
*/
|
||||
function renderProjectTree(tree, level = 0, formatTimeAgo) {
|
||||
if (!tree || tree.length === 0) return '';
|
||||
|
||||
let html = '';
|
||||
|
||||
for (const node of tree) {
|
||||
const hasChildren = node.children && node.children.length > 0;
|
||||
const indent = level * 20;
|
||||
const projectName = node.id.split('/').pop();
|
||||
|
||||
const historyBadge = node.historyRecords > 0
|
||||
? '<span class="text-xs px-1.5 py-0.5 bg-primary/10 text-primary rounded">' + node.historyRecords + '</span>'
|
||||
: '<span class="text-xs text-muted-foreground">-</span>';
|
||||
|
||||
const toggleIcon = hasChildren
|
||||
? '<i data-lucide="chevron-right" class="w-3 h-3 transition-transform duration-200 toggle-icon"></i>'
|
||||
: '<span class="w-3 h-3 inline-block"></span>';
|
||||
|
||||
html += '\
|
||||
<tr class="border-b border-border/50 hover:bg-muted/30 project-row" data-project-id="' + escapeHtml(node.id) + '" data-level="' + level + '">\
|
||||
<td class="py-2 px-2 font-mono text-xs text-muted-foreground">\
|
||||
<div class="flex items-center gap-1" style="padding-left: ' + indent + 'px">\
|
||||
' + (hasChildren ? '<button class="toggle-btn hover:bg-muted/50 rounded p-0.5" onclick="toggleProjectNode(\'' + escapeHtml(node.id) + '\')">' + toggleIcon + '</button>' : '<span class="w-3 h-3 inline-block"></span>') + '\
|
||||
<span class="truncate max-w-[150px]" title="' + escapeHtml(node.id) + '">' + escapeHtml(projectName) + '</span>\
|
||||
</div>\
|
||||
</td>\
|
||||
<td class="py-2 px-2 text-sm text-right">' + escapeHtml(node.totalSizeFormatted) + '</td>\
|
||||
<td class="py-2 px-2 text-center">' + historyBadge + '</td>\
|
||||
<td class="py-2 px-2 text-xs text-muted-foreground text-right">' + formatTimeAgo(node.lastModified) + '</td>\
|
||||
<td class="py-2 px-1 text-right">\
|
||||
<button onclick="cleanProjectStorage(\'' + escapeHtml(node.id) + '\')" \
|
||||
class="text-xs px-2 py-1 text-destructive hover:bg-destructive/10 rounded transition-colors" \
|
||||
title="Clean this project storage">\
|
||||
<i data-lucide="trash-2" class="w-3 h-3"></i>\
|
||||
</button>\
|
||||
</td>\
|
||||
</tr>\
|
||||
';
|
||||
|
||||
// Render children (initially hidden)
|
||||
if (hasChildren) {
|
||||
html += renderProjectTree(node.children, level + 1, formatTimeAgo);
|
||||
}
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle project node expansion
|
||||
*/
|
||||
function toggleProjectNode(projectId) {
|
||||
const row = document.querySelector('[data-project-id="' + projectId + '"]');
|
||||
if (!row) return;
|
||||
|
||||
const icon = row.querySelector('.toggle-icon');
|
||||
const level = parseInt(row.getAttribute('data-level'));
|
||||
|
||||
// Find all child rows
|
||||
let nextRow = row.nextElementSibling;
|
||||
const childRows = [];
|
||||
|
||||
while (nextRow && nextRow.classList.contains('project-row')) {
|
||||
const nextLevel = parseInt(nextRow.getAttribute('data-level'));
|
||||
if (nextLevel <= level) break;
|
||||
childRows.push(nextRow);
|
||||
nextRow = nextRow.nextElementSibling;
|
||||
}
|
||||
|
||||
// Toggle visibility
|
||||
const isExpanded = row.classList.contains('expanded');
|
||||
|
||||
if (isExpanded) {
|
||||
// Collapse
|
||||
row.classList.remove('expanded');
|
||||
if (icon) icon.style.transform = 'rotate(0deg)';
|
||||
childRows.forEach(child => {
|
||||
child.style.display = 'none';
|
||||
});
|
||||
} else {
|
||||
// Expand (only immediate children)
|
||||
row.classList.add('expanded');
|
||||
if (icon) icon.style.transform = 'rotate(90deg)';
|
||||
childRows.forEach(child => {
|
||||
const childLevel = parseInt(child.getAttribute('data-level'));
|
||||
if (childLevel === level + 1) {
|
||||
child.style.display = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Reinitialize Lucide icons
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render error state for storage card
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user