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
211 lines
5.9 KiB
JavaScript
211 lines
5.9 KiB
JavaScript
/**
|
|
* 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;
|
|
}
|
|
}
|