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
250 lines
8.8 KiB
JavaScript
250 lines
8.8 KiB
JavaScript
/**
|
|
* ds-icon-list.js
|
|
* Icon gallery and management
|
|
* Browse and export icons from the design system
|
|
*/
|
|
|
|
export default class IconList extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.icons = [
|
|
{ id: 'check', name: 'Check', category: 'Status', svg: '✓', tags: ['status', 'success', 'validation'] },
|
|
{ id: 'x', name: 'Close', category: 'Status', svg: '✕', tags: ['status', 'error', 'dismiss'] },
|
|
{ id: 'info', name: 'Info', category: 'Status', svg: 'ⓘ', tags: ['status', 'information', 'help'] },
|
|
{ id: 'warning', name: 'Warning', category: 'Status', svg: '⚠', tags: ['status', 'warning', 'alert'] },
|
|
{ id: 'arrow-right', name: 'Arrow Right', category: 'Navigation', svg: '→', tags: ['navigation', 'direction', 'next'] },
|
|
{ id: 'arrow-left', name: 'Arrow Left', category: 'Navigation', svg: '←', tags: ['navigation', 'direction', 'back'] },
|
|
{ id: 'arrow-up', name: 'Arrow Up', category: 'Navigation', svg: '↑', tags: ['navigation', 'direction', 'up'] },
|
|
{ id: 'arrow-down', name: 'Arrow Down', category: 'Navigation', svg: '↓', tags: ['navigation', 'direction', 'down'] },
|
|
{ id: 'search', name: 'Search', category: 'Actions', svg: '🔍', tags: ['action', 'search', 'find'] },
|
|
{ id: 'settings', name: 'Settings', category: 'Actions', svg: '⚙', tags: ['action', 'settings', 'config'] },
|
|
{ id: 'download', name: 'Download', category: 'Actions', svg: '⬇', tags: ['action', 'download', 'save'] },
|
|
{ id: 'upload', name: 'Upload', category: 'Actions', svg: '⬆', tags: ['action', 'upload', 'import'] },
|
|
];
|
|
this.selectedCategory = 'All';
|
|
this.searchTerm = '';
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.render();
|
|
this.setupEventListeners();
|
|
}
|
|
|
|
render() {
|
|
this.innerHTML = `
|
|
<div style="padding: 24px; height: 100%; overflow-y: auto;">
|
|
<div style="margin-bottom: 24px;">
|
|
<h1 style="margin: 0 0 8px 0; font-size: 24px;">Icon Library</h1>
|
|
<p style="margin: 0; color: var(--vscode-text-dim);">
|
|
Browse and manage icon assets
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Search and Filter -->
|
|
<div style="margin-bottom: 24px; display: flex; gap: 12px;">
|
|
<input
|
|
id="icon-search"
|
|
type="text"
|
|
placeholder="Search icons..."
|
|
style="
|
|
flex: 1;
|
|
padding: 8px 12px;
|
|
border: 1px solid var(--vscode-input-border);
|
|
background: var(--vscode-input-background);
|
|
color: var(--vscode-foreground);
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
"
|
|
/>
|
|
<select id="icon-filter" style="
|
|
padding: 8px 12px;
|
|
border: 1px solid var(--vscode-input-border);
|
|
background: var(--vscode-input-background);
|
|
color: var(--vscode-foreground);
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
">
|
|
<option value="All">All Categories</option>
|
|
<option value="Status">Status</option>
|
|
<option value="Navigation">Navigation</option>
|
|
<option value="Actions">Actions</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Icon Grid -->
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 12px; margin-bottom: 24px;">
|
|
${this.renderIconCards()}
|
|
</div>
|
|
|
|
<!-- Export Section -->
|
|
<div style="background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px;">
|
|
<h3 style="margin: 0 0 12px 0; font-size: 14px;">Export Options</h3>
|
|
<div style="display: flex; gap: 8px;">
|
|
<button id="export-svg-btn" style="
|
|
padding: 8px 16px;
|
|
background: var(--vscode-button-secondaryBackground);
|
|
color: var(--vscode-button-secondaryForeground);
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-weight: 500;
|
|
font-size: 12px;
|
|
">📦 Export as SVG</button>
|
|
<button id="export-font-btn" style="
|
|
padding: 8px 16px;
|
|
background: var(--vscode-button-secondaryBackground);
|
|
color: var(--vscode-button-secondaryForeground);
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-weight: 500;
|
|
font-size: 12px;
|
|
">🔤 Export as Font</button>
|
|
<button id="export-json-btn" style="
|
|
padding: 8px 16px;
|
|
background: var(--vscode-button-secondaryBackground);
|
|
color: var(--vscode-button-secondaryForeground);
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-weight: 500;
|
|
font-size: 12px;
|
|
">📄 Export as JSON</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
renderIconCards() {
|
|
let filtered = this.icons;
|
|
|
|
if (this.selectedCategory !== 'All') {
|
|
filtered = filtered.filter(i => i.category === this.selectedCategory);
|
|
}
|
|
|
|
if (this.searchTerm) {
|
|
const term = this.searchTerm.toLowerCase();
|
|
filtered = filtered.filter(i =>
|
|
i.name.toLowerCase().includes(term) ||
|
|
i.id.toLowerCase().includes(term) ||
|
|
i.tags.some(t => t.includes(term))
|
|
);
|
|
}
|
|
|
|
return filtered.map(icon => `
|
|
<div class="icon-card" data-icon-id="${icon.id}" style="
|
|
background: var(--vscode-sidebar);
|
|
border: 1px solid var(--vscode-border);
|
|
border-radius: 4px;
|
|
padding: 12px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
">
|
|
<div style="
|
|
font-size: 32px;
|
|
margin-bottom: 8px;
|
|
width: 48px;
|
|
height: 48px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: var(--vscode-bg);
|
|
border-radius: 3px;
|
|
">${icon.svg}</div>
|
|
<div style="text-align: center; width: 100%;">
|
|
<div style="font-size: 11px; font-weight: 500; margin-bottom: 2px;">
|
|
${icon.name}
|
|
</div>
|
|
<div style="font-size: 10px; color: var(--vscode-text-dim); font-family: monospace;">
|
|
${icon.id}
|
|
</div>
|
|
<div style="font-size: 9px; color: var(--vscode-text-dim); margin-top: 4px;">
|
|
${icon.category}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Search input
|
|
const searchInput = this.querySelector('#icon-search');
|
|
if (searchInput) {
|
|
searchInput.addEventListener('input', (e) => {
|
|
this.searchTerm = e.target.value;
|
|
this.render();
|
|
this.setupEventListeners();
|
|
});
|
|
}
|
|
|
|
// Category filter
|
|
const filterSelect = this.querySelector('#icon-filter');
|
|
if (filterSelect) {
|
|
filterSelect.addEventListener('change', (e) => {
|
|
this.selectedCategory = e.target.value;
|
|
this.render();
|
|
this.setupEventListeners();
|
|
});
|
|
}
|
|
|
|
// Icon cards (copy on click)
|
|
this.querySelectorAll('.icon-card').forEach(card => {
|
|
card.addEventListener('click', () => {
|
|
const iconId = card.dataset.iconId;
|
|
navigator.clipboard.writeText(iconId).then(() => {
|
|
const originalBg = card.style.background;
|
|
card.style.background = 'var(--vscode-selection)';
|
|
setTimeout(() => {
|
|
card.style.background = originalBg;
|
|
}, 300);
|
|
});
|
|
});
|
|
});
|
|
|
|
// Export buttons
|
|
const exportSvgBtn = this.querySelector('#export-svg-btn');
|
|
if (exportSvgBtn) {
|
|
exportSvgBtn.addEventListener('click', () => {
|
|
this.downloadIcons('svg');
|
|
});
|
|
}
|
|
|
|
const exportJsonBtn = this.querySelector('#export-json-btn');
|
|
if (exportJsonBtn) {
|
|
exportJsonBtn.addEventListener('click', () => {
|
|
this.downloadIcons('json');
|
|
});
|
|
}
|
|
}
|
|
|
|
downloadIcons(format) {
|
|
const data = format === 'json'
|
|
? JSON.stringify(this.icons, null, 2)
|
|
: this.generateSVGSheet();
|
|
|
|
const blob = new Blob([data], { type: format === 'json' ? 'application/json' : 'image/svg+xml' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `icons.${format === 'json' ? 'json' : 'svg'}`;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
generateSVGSheet() {
|
|
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
|
|
${this.icons.map((icon, i) => `
|
|
<text x="${(i % 12) * 100 + 50}" y="${Math.floor(i / 12) * 100 + 50}" font-size="40" text-anchor="middle">
|
|
${icon.svg}
|
|
</text>
|
|
`).join('')}
|
|
</svg>`;
|
|
}
|
|
}
|
|
|
|
customElements.define('ds-icon-list', IconList);
|