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
This commit is contained in:
Digital Production Factory
2025-12-09 18:45:48 -03:00
commit 276ed71f31
884 changed files with 373737 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
/**
* admin-workdesk.js
* Admin Team workdesk - System settings and project management
*/
import BaseWorkdesk from './base-workdesk.js';
import '../components/admin/ds-admin-settings.js';
import '../components/admin/ds-project-list.js';
export default class AdminWorkdesk extends BaseWorkdesk {
constructor(shell) {
super(shell);
this.teamId = 'admin';
this.teamName = 'Admin';
this.tools = [
{
id: 'settings',
name: 'System Settings',
description: 'Configure DSS hostname, port, and setup type',
component: 'ds-admin-settings'
},
{
id: 'projects',
name: 'Projects',
description: 'Create and manage design system projects',
component: 'ds-project-list'
}
];
this.currentTab = 'settings';
}
/**
* Admin sidebar shows tab navigation for Settings and Projects
*/
renderSidebar() {
const sidebar = this.shell.sidebarContent;
if (!sidebar) return;
sidebar.innerHTML = `
<div style="padding: 12px; display: flex; flex-direction: column; gap: 8px;">
${this.tools.map(tool => `
<button class="tab-btn" data-tab="${tool.id}" style="
padding: 8px 12px;
border: 1px solid ${this.currentTab === tool.id ? 'var(--vscode-focusBorder)' : 'var(--vscode-border)'};
background: ${this.currentTab === tool.id ? 'var(--vscode-selection)' : 'var(--vscode-sidebar)'};
color: var(--vscode-foreground);
border-radius: 4px;
cursor: pointer;
font-weight: ${this.currentTab === tool.id ? '600' : '500'};
text-align: left;
transition: all 0.2s;
" title="${tool.description}">
${tool.name}
</button>
`).join('')}
</div>
`;
// Setup tab click handlers
sidebar.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', () => {
const tabId = btn.dataset.tab;
this.switchTab(tabId);
});
});
}
/**
* Admin stage shows tab content - either Settings or Projects
*/
renderStage() {
const stage = this.shell.stageContent;
if (!stage) return;
stage.innerHTML = `
<div style="height: 100%; overflow-y: auto;">
${this.renderTabContent()}
</div>
`;
// Trigger connectedCallback on custom elements
this.hydrateComponents();
}
/**
* Render content based on current tab
*/
renderTabContent() {
switch (this.currentTab) {
case 'settings':
return '<ds-admin-settings></ds-admin-settings>';
case 'projects':
return '<ds-project-list></ds-project-list>';
default:
return '<ds-admin-settings></ds-admin-settings>';
}
}
/**
* Switch to a different tab and re-render
*/
switchTab(tabId) {
this.currentTab = tabId;
this.renderSidebar();
this.renderStage();
}
/**
* Hydrate custom elements after rendering
*/
hydrateComponents() {
const stage = this.shell.stageContent;
if (!stage) return;
// Find and trigger connectedCallback on custom elements
stage.querySelectorAll('ds-admin-settings, ds-project-list').forEach(el => {
// Web components auto-trigger connectedCallback when inserted into DOM
// No manual trigger needed
});
}
/**
* Admin panel shows minimal system log
*/
renderPanel() {
const panel = this.shell.querySelector('ds-panel');
if (panel) {
// Configure panel with admin config
panel.configure('admin', false);
}
}
}

View File

@@ -0,0 +1,210 @@
/**
* base-workdesk.js
* Abstract base class for all team workdesks
* Refactored: A11y improvements, extracted styles, event delegation
*/
import '../components/tools/ds-metrics-panel.js';
export default class BaseWorkdesk {
constructor(shell) {
if (new.target === BaseWorkdesk) {
throw new Error('Cannot instantiate abstract class BaseWorkdesk');
}
this.shell = shell;
this.teamId = '';
this.teamName = '';
this.tools = [];
}
/**
* Render the workdesk - must be implemented by subclasses
*/
render() {
this.renderSidebar();
this.renderStage();
this.renderPanel();
}
/**
* Render sidebar content
* Improved: Uses semantic <button> elements, injected styles, and keyboard support
*/
renderSidebar() {
const sidebar = this.shell.sidebarContent;
if (!sidebar) return;
// We define styles here to keep BaseWorkdesk self-contained without external CSS dependencies
const css = `
.workdesk-sidebar-container {
padding: 16px;
}
.workdesk-header {
color: var(--vscode-descriptionForeground);
margin-bottom: 12px;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
}
.tools-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.tool-btn {
appearance: none;
background: transparent;
border: 1px solid transparent;
border-radius: 4px;
padding: 8px;
width: 100%;
text-align: left;
cursor: pointer;
color: var(--vscode-foreground);
transition: background-color 0.1s, border-color 0.1s;
}
.tool-btn:hover {
background-color: var(--vscode-list-hoverBackground);
}
.tool-btn:focus-visible {
outline: 2px solid var(--vscode-focusBorder);
outline-offset: -2px;
background-color: var(--vscode-list-activeSelectionBackground);
color: var(--vscode-list-activeSelectionForeground);
}
.tool-name {
font-size: 13px;
margin-bottom: 2px;
font-weight: 500;
}
.tool-desc {
font-size: 11px;
color: var(--vscode-descriptionForeground);
}
/* A11y focus support for description inside button */
.tool-btn:focus-visible .tool-desc {
color: var(--vscode-list-activeSelectionForeground);
opacity: 0.9;
}
`;
sidebar.innerHTML = `
<style>${css}</style>
<div class="workdesk-sidebar-container">
<div class="workdesk-header" id="tools-heading">
${this.teamName} Tools
</div>
<div class="tools-list" role="group" aria-labelledby="tools-heading">
${this.tools.map(tool => `
<button class="tool-btn" data-tool="${tool.id}" type="button">
<div class="tool-name">${tool.name}</div>
<div class="tool-desc">${tool.description}</div>
</button>
`).join('')}
</div>
</div>
`;
// Event delegation for better performance and lifecycle management
const toolsList = sidebar.querySelector('.tools-list');
if (toolsList) {
toolsList.addEventListener('click', (e) => {
const btn = e.target.closest('.tool-btn');
if (btn) {
this.onToolClick(btn.dataset.tool);
}
});
}
}
/**
* Render stage content - must be implemented by subclasses
*/
renderStage() {
throw new Error('renderStage() must be implemented by subclass');
}
/**
* Render panel content - DEPRECATED
* Panel is now configured by ds-shell.js via panel.configure() during team switch
* Subclasses should not override this method
*/
renderPanel() {
// Panel configuration is now handled by ds-shell.js
// This method is kept for backwards compatibility but does nothing
console.log(`[${this.teamId}] Panel configured by shell via panel-config.js`);
}
/**
* Handle tool click - can be overridden by subclasses
*/
onToolClick(toolId) {
console.log(`Tool clicked: ${toolId}`);
const tool = this.tools.find(t => t.id === toolId);
if (tool) {
this.loadTool(tool);
}
}
/**
* Load a tool into the stage - can be overridden by subclasses
* Improved: Loading spinner using CSS instead of Emoji
*/
loadTool(tool) {
const stage = this.shell.stageContent;
if (!stage) return;
// Use a clean CSS spinner with codicon if available
stage.innerHTML = `
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; padding: 48px;">
<div class="codicon codicon-loading codicon-modifier-spin" style="font-size: 24px; margin-bottom: 12px;"></div>
<div style="font-size: 12px; color: var(--vscode-descriptionForeground);">Loading ${tool.name}...</div>
</div>
`;
}
/**
* Clean up workdesk - can be overridden by subclasses
*/
destroy() {
if (this.shell.sidebarContent) {
this.shell.sidebarContent.innerHTML = '';
}
if (this.shell.stageContent) {
this.shell.stageContent.innerHTML = '';
}
}
/**
* Utility: Create a card element
* NOTE: Deprecated - use ds-metric-card web component instead
*/
createCard(title, content) {
return `
<div style="
background-color: var(--vscode-sidebar);
border: 1px solid var(--vscode-border);
border-radius: 4px;
padding: 16px;
">
<h3 style="margin-bottom: 12px; font-size: 14px;">${title}</h3>
<div style="font-size: 12px; color: var(--vscode-text-dim);">
${content}
</div>
</div>
`;
}
/**
* Utility: Create a button
*/
createButton(label, onClick) {
const button = document.createElement('button');
button.className = 'button';
button.textContent = label;
button.addEventListener('click', onClick);
return button;
}
}

View File

@@ -0,0 +1,175 @@
/**
* qa-workdesk.js
* QA Team workdesk - Console logs, network monitoring, DOM inspection
* MVP2: Added metrics frontpage as default view
*/
import BaseWorkdesk from './base-workdesk.js';
import { hydrateComponent } from '../config/component-registry.js';
import '../components/tools/ds-console-viewer.js';
export default class QAWorkdesk extends BaseWorkdesk {
constructor(shell) {
super(shell);
this.teamId = 'qa';
this.teamName = 'QA Team';
this.tools = [
{
id: 'frontpage',
name: 'Dashboard',
description: 'Team metrics and quick actions',
component: 'ds-frontpage'
},
{
id: 'figma-live-compare',
name: 'Figma vs Live',
description: 'QA validation: Figma design vs live implementation',
component: 'ds-figma-live-compare'
},
{
id: 'esre-editor',
name: 'ESRE Editor',
description: 'Edit Explicit Style Requirements and Expectations',
component: 'ds-esre-editor'
},
{
id: 'console-viewer',
name: 'Console Viewer',
description: 'Monitor browser console logs',
mcpTool: 'browser_get_logs'
},
{
id: 'network-monitor',
name: 'Network Monitor',
description: 'Track network requests',
mcpTool: 'devtools_network_requests'
},
{
id: 'error-tracker',
name: 'Error Tracker',
description: 'Track uncaught exceptions',
mcpTool: 'browser_get_errors'
}
];
this.currentTool = 'frontpage';
}
async loadTool(tool) {
const stage = this.shell.stageContent;
if (!stage) return;
// For tools with components, load them dynamically
if (tool.component) {
// Show loading state
stage.innerHTML = `
<div style="display: flex; align-items: center; justify-content: center; height: 100%; padding: 48px;">
<div style="text-align: center;">
<div style="font-size: 24px; margin-bottom: 12px;">⏳</div>
<div style="font-size: 12px; color: var(--vscode-text-dim);">Loading ${tool.name}...</div>
</div>
</div>
`;
try {
// Clear the stage and hydrate the component
stage.innerHTML = '';
await hydrateComponent(tool.component, stage);
console.log(`[QAWorkdesk] Loaded component: ${tool.component}`);
} catch (error) {
console.error(`[QAWorkdesk] Failed to load tool ${tool.component}:`, error);
stage.innerHTML = `
<div style="padding: 24px;">
<h2 style="margin-bottom: 16px; color: var(--vscode-error);">Error Loading Tool</h2>
<p style="color: var(--vscode-text-dim);">${error.message}</p>
</div>
`;
}
} else {
// Fall back to base implementation for MCP tools
super.loadTool(tool);
}
}
renderStage() {
const stage = this.shell.stageContent;
if (!stage) return;
stage.innerHTML = `
<div style="padding: 24px;">
<h1 style="margin-bottom: 8px; font-size: 24px;">QA Team Workdesk</h1>
<p style="color: var(--vscode-text-dim); margin-bottom: 32px;">
QA validation, testing, and debugging tools
</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
${this.createCard('QA Validation', `
<p>Compare and validate implementations:</p>
<ul style="margin-top: 8px; padding-left: 20px;">
<li>Figma vs Live comparison</li>
<li>Screenshot capture</li>
<li>Visual validation</li>
<li>Style requirements (ESRE)</li>
</ul>
`)}
${this.createCard('Console Monitoring', `
<p>Real-time console log streaming:</p>
<ul style="margin-top: 8px; padding-left: 20px;">
<li>Log, warn, error levels</li>
<li>Stack trace analysis</li>
<li>Filter by severity</li>
<li>Export logs</li>
</ul>
`)}
${this.createCard('Network Debugging', `
<p>Track all HTTP requests:</p>
<ul style="margin-top: 8px; padding-left: 20px;">
<li>Request/response headers</li>
<li>Payload inspection</li>
<li>Timing analysis</li>
<li>Failed requests</li>
</ul>
`)}
</div>
<div style="margin-top: 32px;">
<h2 style="margin-bottom: 16px; font-size: 16px;">Quick Actions</h2>
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
<button class="button" id="compare-btn">Figma vs Live</button>
<button class="button" id="esre-btn">Edit ESRE</button>
<button class="button" id="console-btn">Open Console</button>
<button class="button" id="network-btn">Network Monitor</button>
</div>
</div>
</div>
`;
// Setup button handlers
stage.querySelector('#compare-btn')?.addEventListener('click', () => {
this.onToolClick('figma-live-compare');
});
stage.querySelector('#esre-btn')?.addEventListener('click', () => {
this.onToolClick('esre-editor');
});
stage.querySelector('#console-btn')?.addEventListener('click', () => {
this.onToolClick('console-viewer');
// Switch panel to console tab (component is already configured via panel-config.js)
const panel = this.shell.querySelector('ds-panel');
if (panel) {
panel.switchTab('console');
}
});
stage.querySelector('#network-btn')?.addEventListener('click', () => {
this.onToolClick('network-monitor');
// Switch panel to network tab (component is already configured via panel-config.js)
const panel = this.shell.querySelector('ds-panel');
if (panel) {
panel.switchTab('network');
}
});
}
}

View File

@@ -0,0 +1,115 @@
/**
* ui-workdesk.js
* UI Team workdesk - Token management, Figma sync, code generation
* Refactored: Simplified renderStage to act as controller delegation
*/
import BaseWorkdesk from './base-workdesk.js';
import { hydrateComponent } from '../config/component-registry.js';
export default class UIWorkdesk extends BaseWorkdesk {
constructor(shell) {
super(shell);
this.teamId = 'ui';
this.teamName = 'UI Team';
this.tools = [
{
id: 'frontpage',
name: 'Dashboard',
description: 'Team metrics and quick actions',
component: 'ds-frontpage'
},
{
id: 'storybook-figma-compare',
name: 'Storybook vs Figma',
description: 'Compare Storybook and Figma side by side',
component: 'ds-storybook-figma-compare'
},
{
id: 'storybook-live-compare',
name: 'Storybook vs Live',
description: 'Compare Storybook and live app for drift detection',
component: 'ds-storybook-live-compare'
},
{
id: 'figma-extraction',
name: 'Figma Token Extraction',
description: 'Extract design tokens from Figma',
component: 'ds-figma-extraction'
},
{
id: 'project-analysis',
name: 'Project Analysis',
description: 'Analyze design system adoption',
component: 'ds-project-analysis'
},
{
id: 'quick-wins',
name: 'Quick Wins',
description: 'Find low-effort improvements',
component: 'ds-quick-wins'
},
{
id: 'regression-testing',
name: 'Regression Testing',
description: 'Visual regression testing',
component: 'ds-regression-testing'
}
];
this.currentTool = 'frontpage';
}
async loadTool(tool) {
const stage = this.shell.stageContent;
if (!stage) return;
// Show loading state with codicon spinner
stage.innerHTML = `
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%;">
<div class="codicon codicon-loading codicon-modifier-spin" style="font-size: 24px; margin-bottom: 12px;"></div>
<div style="color: var(--vscode-descriptionForeground);">Loading ${tool.name}...</div>
</div>
`;
try {
stage.innerHTML = '';
await hydrateComponent(tool.component, stage);
console.log(`[UIWorkdesk] Loaded component: ${tool.component}`);
// Update internal state tracking
this.currentTool = tool.id;
} catch (error) {
console.error(`[UIWorkdesk] Failed to load tool ${tool.component}:`, error);
stage.innerHTML = `
<div style="padding: 24px; max-width: 600px; margin: 0 auto;">
<h2 style="margin-bottom: 16px; color: var(--vscode-errorForeground);">Error Loading Tool</h2>
<p style="color: var(--vscode-descriptionForeground); margin-bottom: 16px;">${error.message}</p>
<button id="retry-btn" class="button" type="button"
style="padding: 8px 16px; background: var(--vscode-button-background); color: var(--vscode-button-foreground); border: none; border-radius: 4px; cursor: pointer;">
Retry
</button>
</div>
`;
// Add event listener without inline handler
const retryBtn = stage.querySelector('#retry-btn');
if (retryBtn) {
retryBtn.addEventListener('click', () => location.reload());
}
}
}
/**
* Render stage content
* REFACTORED: Now strictly delegates to the default tool (Dashboard)
* instead of maintaining a hardcoded duplicate view.
*/
renderStage() {
const defaultTool = this.tools.find(t => t.id === this.currentTool || t.id === 'frontpage');
if (defaultTool) {
this.loadTool(defaultTool);
} else {
console.error('Default tool "frontpage" not found in configuration');
}
}
}

View File

@@ -0,0 +1,158 @@
/**
* ux-workdesk.js
* UX Team workdesk - Visual diff, accessibility audits, component gallery
* MVP2: Added metrics frontpage as default view
*/
import BaseWorkdesk from './base-workdesk.js';
import { hydrateComponent } from '../config/component-registry.js';
export default class UXWorkdesk extends BaseWorkdesk {
constructor(shell) {
super(shell);
this.teamId = 'ux';
this.teamName = 'UX Team';
this.tools = [
{
id: 'frontpage',
name: 'Dashboard',
description: 'Team metrics and quick actions',
component: 'ds-frontpage'
},
{
id: 'figma-plugin',
name: 'Figma Plugin',
description: 'Export tokens/assets/components from Figma',
component: 'ds-figma-plugin'
},
{
id: 'token-list',
name: 'Token List',
description: 'View all design tokens',
component: 'ds-token-list'
},
{
id: 'asset-list',
name: 'Asset List',
description: 'Gallery of design assets',
component: 'ds-asset-list'
},
{
id: 'component-list',
name: 'Component List',
description: 'Design system components',
component: 'ds-component-list'
},
{
id: 'navigation-demos',
name: 'Navigation Demos',
description: 'Generate navigation flow demos',
component: 'ds-navigation-demos'
}
];
this.currentTool = 'frontpage';
}
async loadTool(tool) {
const stage = this.shell.stageContent;
if (!stage) return;
// Show loading state
stage.innerHTML = `
<div style="display: flex; align-items: center; justify-content: center; height: 100%; padding: 48px;">
<div style="text-align: center;">
<div style="font-size: 24px; margin-bottom: 12px;">⏳</div>
<div style="font-size: 12px; color: var(--vscode-text-dim);">Loading ${tool.name}...</div>
</div>
</div>
`;
try {
// Clear the stage and hydrate the component
stage.innerHTML = '';
await hydrateComponent(tool.component, stage);
console.log(`[UXWorkdesk] Loaded component: ${tool.component}`);
} catch (error) {
console.error(`[UXWorkdesk] Failed to load tool ${tool.component}:`, error);
stage.innerHTML = `
<div style="padding: 24px;">
<h2 style="margin-bottom: 16px; color: var(--vscode-error);">Error Loading Tool</h2>
<p style="color: var(--vscode-text-dim);">${error.message}</p>
</div>
`;
}
}
renderStage() {
const stage = this.shell.stageContent;
if (!stage) return;
stage.innerHTML = `
<div style="padding: 24px;">
<h1 style="margin-bottom: 8px; font-size: 24px;">UX Team Workdesk</h1>
<p style="color: var(--vscode-text-dim); margin-bottom: 32px;">
Design system assets, tokens, and component management
</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
${this.createCard('Figma Integration', `
<p>Export from Figma:</p>
<ul style="margin-top: 8px; padding-left: 20px;">
<li>Design tokens</li>
<li>Asset export</li>
<li>Component definitions</li>
<li>Multiple format support</li>
</ul>
`)}
${this.createCard('Asset Management', `
<p>Manage design assets:</p>
<ul style="margin-top: 8px; padding-left: 20px;">
<li>Token library</li>
<li>Asset gallery</li>
<li>Component catalog</li>
<li>Visual preview</li>
</ul>
`)}
${this.createCard('Navigation', `
<p>Navigation flow demos:</p>
<ul style="margin-top: 8px; padding-left: 20px;">
<li>Generate flow demos</li>
<li>Interactive HTML</li>
<li>Flow library</li>
<li>Quick preview</li>
</ul>
`)}
</div>
<div style="margin-top: 32px;">
<h2 style="margin-bottom: 16px; font-size: 16px;">Quick Actions</h2>
<div style="display: flex; gap: 12px; flex-wrap: wrap;">
<button class="button" id="figma-btn">Figma Export</button>
<button class="button" id="tokens-btn">View Tokens</button>
<button class="button" id="assets-btn">Asset Gallery</button>
<button class="button" id="components-btn">Components</button>
</div>
</div>
</div>
`;
// Setup button handlers
stage.querySelector('#figma-btn')?.addEventListener('click', () => {
this.onToolClick('figma-plugin');
});
stage.querySelector('#tokens-btn')?.addEventListener('click', () => {
this.onToolClick('token-list');
});
stage.querySelector('#assets-btn')?.addEventListener('click', () => {
this.onToolClick('asset-list');
});
stage.querySelector('#components-btn')?.addEventListener('click', () => {
this.onToolClick('component-list');
});
}
}