Files
dss/admin-ui/js/components/tools/ds-project-analysis.js
Digital Production Factory 276ed71f31 Initial commit: Clean DSS implementation
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
2025-12-09 18:45:48 -03:00

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;