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:
201
admin-ui/js/components/tools/ds-token-list.js
Normal file
201
admin-ui/js/components/tools/ds-token-list.js
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* ds-token-list.js
|
||||
* List view of all design tokens in the project
|
||||
* UX Team Tool #2
|
||||
*/
|
||||
|
||||
import { createListView, setupListHandlers } from '../../utils/tool-templates.js';
|
||||
import { ComponentHelpers } from '../../utils/component-helpers.js';
|
||||
import contextStore from '../../stores/context-store.js';
|
||||
import toolBridge from '../../services/tool-bridge.js';
|
||||
|
||||
class DSTokenList extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.tokens = [];
|
||||
this.filteredTokens = [];
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
this.render();
|
||||
await this.loadTokens();
|
||||
}
|
||||
|
||||
async loadTokens() {
|
||||
this.isLoading = true;
|
||||
const container = this.querySelector('#token-list-container');
|
||||
if (container) {
|
||||
container.innerHTML = ComponentHelpers.renderLoading('Loading design tokens...');
|
||||
}
|
||||
|
||||
try {
|
||||
const context = contextStore.getMCPContext();
|
||||
if (!context.project_id) {
|
||||
throw new Error('No project selected');
|
||||
}
|
||||
|
||||
// Try to get resolved context which includes all tokens
|
||||
const result = await toolBridge.executeTool('dss_get_resolved_context', {
|
||||
manifest_path: `/projects/${context.project_id}/ds.config.json`
|
||||
});
|
||||
|
||||
// Extract tokens from result
|
||||
this.tokens = this.extractTokensFromContext(result);
|
||||
this.filteredTokens = [...this.tokens];
|
||||
|
||||
this.renderTokenList();
|
||||
} catch (error) {
|
||||
console.error('[DSTokenList] Failed to load tokens:', error);
|
||||
if (container) {
|
||||
container.innerHTML = ComponentHelpers.renderError('Failed to load tokens', error);
|
||||
}
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
extractTokensFromContext(context) {
|
||||
const tokens = [];
|
||||
|
||||
// Extract from colors, typography, spacing, etc.
|
||||
const categories = ['colors', 'typography', 'spacing', 'shadows', 'borders', 'radii'];
|
||||
|
||||
for (const category of categories) {
|
||||
if (context[category]) {
|
||||
for (const [key, value] of Object.entries(context[category])) {
|
||||
tokens.push({
|
||||
category,
|
||||
name: key,
|
||||
value: typeof value === 'object' ? JSON.stringify(value) : String(value),
|
||||
type: this.inferTokenType(category, key, value)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
inferTokenType(category, key, value) {
|
||||
if (category === 'colors') return 'color';
|
||||
if (category === 'typography') return 'font';
|
||||
if (category === 'spacing') return 'size';
|
||||
if (category === 'shadows') return 'shadow';
|
||||
if (category === 'borders') return 'border';
|
||||
if (category === 'radii') return 'radius';
|
||||
return 'other';
|
||||
}
|
||||
|
||||
renderTokenList() {
|
||||
const container = this.querySelector('#token-list-container');
|
||||
if (!container) return;
|
||||
|
||||
const config = {
|
||||
title: 'Design Tokens',
|
||||
items: this.filteredTokens,
|
||||
columns: [
|
||||
{
|
||||
key: 'name',
|
||||
label: 'Token Name',
|
||||
render: (token) => `<span style="font-family: monospace; font-size: 11px;">${ComponentHelpers.escapeHtml(token.name)}</span>`
|
||||
},
|
||||
{
|
||||
key: 'category',
|
||||
label: 'Category',
|
||||
render: (token) => ComponentHelpers.createBadge(token.category, 'info')
|
||||
},
|
||||
{
|
||||
key: 'value',
|
||||
label: 'Value',
|
||||
render: (token) => {
|
||||
if (token.type === 'color') {
|
||||
return `
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<div style="width: 20px; height: 20px; background: ${ComponentHelpers.escapeHtml(token.value)}; border: 1px solid var(--vscode-border); border-radius: 2px;"></div>
|
||||
<span style="font-family: monospace; font-size: 10px;">${ComponentHelpers.escapeHtml(token.value)}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return `<span style="font-family: monospace; font-size: 10px;">${ComponentHelpers.escapeHtml(token.value)}</span>`;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
label: 'Type',
|
||||
render: (token) => `<span style="font-size: 10px; color: var(--vscode-text-dim);">${ComponentHelpers.escapeHtml(token.type)}</span>`
|
||||
}
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
label: 'Export All',
|
||||
icon: '📥',
|
||||
onClick: () => this.exportTokens()
|
||||
},
|
||||
{
|
||||
label: 'Refresh',
|
||||
icon: '🔄',
|
||||
onClick: () => this.loadTokens()
|
||||
}
|
||||
],
|
||||
onSearch: (query) => this.handleSearch(query),
|
||||
onFilter: (filterValue) => this.handleFilter(filterValue)
|
||||
};
|
||||
|
||||
container.innerHTML = createListView(config);
|
||||
setupListHandlers(container, config);
|
||||
|
||||
// Update filter dropdown with categories
|
||||
const filterSelect = container.querySelector('#filter-select');
|
||||
if (filterSelect) {
|
||||
const categories = [...new Set(this.tokens.map(t => t.category))];
|
||||
filterSelect.innerHTML = `
|
||||
<option value="">All Categories</option>
|
||||
${categories.map(cat => `<option value="${cat}">${cat}</option>`).join('')}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
handleSearch(query) {
|
||||
const lowerQuery = query.toLowerCase();
|
||||
this.filteredTokens = this.tokens.filter(token =>
|
||||
token.name.toLowerCase().includes(lowerQuery) ||
|
||||
token.value.toLowerCase().includes(lowerQuery) ||
|
||||
token.category.toLowerCase().includes(lowerQuery)
|
||||
);
|
||||
this.renderTokenList();
|
||||
}
|
||||
|
||||
handleFilter(filterValue) {
|
||||
if (!filterValue) {
|
||||
this.filteredTokens = [...this.tokens];
|
||||
} else {
|
||||
this.filteredTokens = this.tokens.filter(token => token.category === filterValue);
|
||||
}
|
||||
this.renderTokenList();
|
||||
}
|
||||
|
||||
exportTokens() {
|
||||
const data = JSON.stringify(this.tokens, null, 2);
|
||||
const blob = new Blob([data], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'design-tokens.json';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
ComponentHelpers.showToast?.('Tokens exported', 'success');
|
||||
}
|
||||
|
||||
render() {
|
||||
this.innerHTML = `
|
||||
<div id="token-list-container" style="height: 100%; overflow: hidden;">
|
||||
${ComponentHelpers.renderLoading('Loading tokens...')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('ds-token-list', DSTokenList);
|
||||
|
||||
export default DSTokenList;
|
||||
Reference in New Issue
Block a user