Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
269 lines
10 KiB
JavaScript
269 lines
10 KiB
JavaScript
/**
|
|
* ds-project-analysis.js
|
|
* Project analysis results viewer showing token usage, component adoption, etc.
|
|
* UI Team Tool #4
|
|
*/
|
|
|
|
import { ComponentHelpers } from '../../utils/component-helpers.js';
|
|
import contextStore from '../../stores/context-store.js';
|
|
import toolBridge from '../../services/tool-bridge.js';
|
|
|
|
class DSProjectAnalysis extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.analysisResults = null;
|
|
this.isAnalyzing = false;
|
|
}
|
|
|
|
async connectedCallback() {
|
|
this.render();
|
|
this.setupEventListeners();
|
|
await this.loadCachedResults();
|
|
}
|
|
|
|
async loadCachedResults() {
|
|
try {
|
|
const context = contextStore.getMCPContext();
|
|
if (!context.project_id) return;
|
|
|
|
const cached = localStorage.getItem(`analysis_${context.project_id}`);
|
|
if (cached) {
|
|
this.analysisResults = JSON.parse(cached);
|
|
this.renderResults();
|
|
}
|
|
} catch (error) {
|
|
console.error('[DSProjectAnalysis] Failed to load cached results:', error);
|
|
}
|
|
}
|
|
|
|
setupEventListeners() {
|
|
const analyzeBtn = this.querySelector('#analyze-project-btn');
|
|
const pathInput = this.querySelector('#project-path-input');
|
|
|
|
if (analyzeBtn) {
|
|
analyzeBtn.addEventListener('click', () => this.analyzeProject());
|
|
}
|
|
|
|
if (pathInput) {
|
|
pathInput.addEventListener('keypress', (e) => {
|
|
if (e.key === 'Enter') {
|
|
this.analyzeProject();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
async analyzeProject() {
|
|
const pathInput = this.querySelector('#project-path-input');
|
|
const projectPath = pathInput?.value.trim() || '';
|
|
|
|
if (!projectPath) {
|
|
ComponentHelpers.showToast?.('Please enter a project path', 'error');
|
|
return;
|
|
}
|
|
|
|
this.isAnalyzing = true;
|
|
this.updateLoadingState();
|
|
|
|
try {
|
|
// Call dss_analyze_project MCP tool
|
|
const result = await toolBridge.executeTool('dss_analyze_project', {
|
|
path: projectPath
|
|
});
|
|
|
|
this.analysisResults = result;
|
|
|
|
// Cache results
|
|
const context = contextStore.getMCPContext();
|
|
if (context.project_id) {
|
|
localStorage.setItem(`analysis_${context.project_id}`, JSON.stringify(result));
|
|
}
|
|
|
|
this.renderResults();
|
|
ComponentHelpers.showToast?.('Project analysis complete', 'success');
|
|
} catch (error) {
|
|
console.error('[DSProjectAnalysis] Analysis failed:', error);
|
|
ComponentHelpers.showToast?.(`Analysis failed: ${error.message}`, 'error');
|
|
|
|
const resultsContainer = this.querySelector('#results-container');
|
|
if (resultsContainer) {
|
|
resultsContainer.innerHTML = ComponentHelpers.renderError('Project analysis failed', error);
|
|
}
|
|
} finally {
|
|
this.isAnalyzing = false;
|
|
this.updateLoadingState();
|
|
}
|
|
}
|
|
|
|
updateLoadingState() {
|
|
const analyzeBtn = this.querySelector('#analyze-project-btn');
|
|
const resultsContainer = this.querySelector('#results-container');
|
|
|
|
if (!analyzeBtn || !resultsContainer) return;
|
|
|
|
if (this.isAnalyzing) {
|
|
analyzeBtn.disabled = true;
|
|
analyzeBtn.textContent = '⏳ Analyzing...';
|
|
resultsContainer.innerHTML = ComponentHelpers.renderLoading('Analyzing project structure and token usage...');
|
|
} else {
|
|
analyzeBtn.disabled = false;
|
|
analyzeBtn.textContent = '🔍 Analyze Project';
|
|
}
|
|
}
|
|
|
|
renderResults() {
|
|
const resultsContainer = this.querySelector('#results-container');
|
|
if (!resultsContainer || !this.analysisResults) return;
|
|
|
|
const { patterns, components, tokens, dependencies } = this.analysisResults;
|
|
|
|
resultsContainer.innerHTML = `
|
|
<div style="padding: 16px; overflow: auto; height: 100%;">
|
|
<!-- Summary Cards -->
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px;">
|
|
${this.createStatCard('Components Found', components?.length || 0, '🧩')}
|
|
${this.createStatCard('Patterns Detected', patterns?.length || 0, '🎨')}
|
|
${this.createStatCard('Tokens Used', Object.keys(tokens || {}).length, '🎯')}
|
|
${this.createStatCard('Dependencies', dependencies?.length || 0, '📦')}
|
|
</div>
|
|
|
|
<!-- Patterns Section -->
|
|
${patterns && patterns.length > 0 ? `
|
|
<div style="background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px; margin-bottom: 16px;">
|
|
<h4 style="font-size: 12px; font-weight: 600; margin-bottom: 12px;">Design Patterns</h4>
|
|
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
${patterns.map(pattern => `
|
|
<div style="padding: 8px; background: var(--vscode-bg); border-radius: 2px; font-size: 11px;">
|
|
<div style="font-weight: 600; margin-bottom: 4px;">${ComponentHelpers.escapeHtml(pattern.name)}</div>
|
|
<div style="color: var(--vscode-text-dim);">
|
|
${ComponentHelpers.escapeHtml(pattern.description)} • Used ${pattern.count} times
|
|
</div>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
` : ''}
|
|
|
|
<!-- Components Section -->
|
|
${components && components.length > 0 ? `
|
|
<div style="background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px; margin-bottom: 16px;">
|
|
<h4 style="font-size: 12px; font-weight: 600; margin-bottom: 12px;">React Components</h4>
|
|
<div style="max-height: 300px; overflow-y: auto;">
|
|
<table style="width: 100%; font-size: 11px; border-collapse: collapse;">
|
|
<thead style="position: sticky; top: 0; background: var(--vscode-sidebar);">
|
|
<tr>
|
|
<th style="text-align: left; padding: 6px; border-bottom: 1px solid var(--vscode-border);">Component</th>
|
|
<th style="text-align: left; padding: 6px; border-bottom: 1px solid var(--vscode-border);">Path</th>
|
|
<th style="text-align: right; padding: 6px; border-bottom: 1px solid var(--vscode-border);">DS Adoption</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${components.slice(0, 20).map(comp => `
|
|
<tr style="border-bottom: 1px solid var(--vscode-border);">
|
|
<td style="padding: 6px; font-family: monospace;">${ComponentHelpers.escapeHtml(comp.name)}</td>
|
|
<td style="padding: 6px; color: var(--vscode-text-dim);">${ComponentHelpers.escapeHtml(comp.path)}</td>
|
|
<td style="padding: 6px; text-align: right;">
|
|
${this.renderAdoptionBadge(comp.dsAdoption || 0)}
|
|
</td>
|
|
</tr>
|
|
`).join('')}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
` : ''}
|
|
|
|
<!-- Token Usage Section -->
|
|
${tokens && Object.keys(tokens).length > 0 ? `
|
|
<div style="background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px;">
|
|
<h4 style="font-size: 12px; font-weight: 600; margin-bottom: 12px;">Token Usage</h4>
|
|
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
|
|
${Object.entries(tokens).slice(0, 30).map(([key, count]) => `
|
|
<div style="padding: 4px 8px; background: var(--vscode-bg); border-radius: 2px; font-size: 10px; font-family: monospace;">
|
|
${ComponentHelpers.escapeHtml(key)} <span style="color: var(--vscode-text-dim);">(${count})</span>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
createStatCard(label, value, icon) {
|
|
return `
|
|
<div style="background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px; text-align: center;">
|
|
<div style="font-size: 32px; margin-bottom: 8px;">${icon}</div>
|
|
<div style="font-size: 24px; font-weight: 600; margin-bottom: 4px;">${value}</div>
|
|
<div style="font-size: 11px; color: var(--vscode-text-dim);">${label}</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
renderAdoptionBadge(percentage) {
|
|
let color = '#f48771';
|
|
let label = 'Low';
|
|
|
|
if (percentage >= 80) {
|
|
color = '#89d185';
|
|
label = 'High';
|
|
} else if (percentage >= 50) {
|
|
color = '#ffbf00';
|
|
label = 'Medium';
|
|
}
|
|
|
|
return `<span style="padding: 2px 6px; background: ${color}; border-radius: 2px; font-size: 10px; font-weight: 600;">${label}</span>`;
|
|
}
|
|
|
|
render() {
|
|
this.innerHTML = `
|
|
<div style="display: flex; flex-direction: column; height: 100%;">
|
|
<!-- Header -->
|
|
<div style="padding: 16px; border-bottom: 1px solid var(--vscode-border); background: var(--vscode-sidebar);">
|
|
<h3 style="font-size: 12px; font-weight: 600; margin-bottom: 12px;">Project Analysis</h3>
|
|
|
|
<div style="display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: end;">
|
|
<div>
|
|
<label style="display: block; font-size: 11px; font-weight: 600; margin-bottom: 4px; color: var(--vscode-text-dim);">
|
|
Project Path
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="project-path-input"
|
|
placeholder="/path/to/your/project"
|
|
class="input"
|
|
style="width: 100%; font-size: 11px; font-family: monospace;"
|
|
/>
|
|
</div>
|
|
|
|
<button id="analyze-project-btn" class="button" style="font-size: 11px; padding: 6px 16px;">
|
|
🔍 Analyze Project
|
|
</button>
|
|
</div>
|
|
|
|
<div style="margin-top: 8px; font-size: 10px; color: var(--vscode-text-dim);">
|
|
💡 Analyzes components, patterns, token usage, and design system adoption
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Results Container -->
|
|
<div id="results-container" style="flex: 1; overflow: hidden;">
|
|
<div style="display: flex; align-items: center; justify-content: center; height: 100%; text-align: center; padding: 48px;">
|
|
<div>
|
|
<div style="font-size: 48px; margin-bottom: 16px;">🔍</div>
|
|
<h3 style="font-size: 14px; font-weight: 600; margin-bottom: 8px;">Ready to Analyze</h3>
|
|
<p style="font-size: 12px; color: var(--vscode-text-dim);">
|
|
Enter your project path above to analyze component usage and design system adoption
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
customElements.define('ds-project-analysis', DSProjectAnalysis);
|
|
|
|
export default DSProjectAnalysis;
|