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:
266
admin-ui/js/components/tools/ds-figma-plugin.js
Normal file
266
admin-ui/js/components/tools/ds-figma-plugin.js
Normal file
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* ds-figma-plugin.js
|
||||
* Interface for Figma plugin export and token management
|
||||
* UX Team Tool #1
|
||||
*/
|
||||
|
||||
import { createFormView, setupFormHandlers } 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 DSFigmaPlugin extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.exportHistory = [];
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
this.render();
|
||||
this.setupEventListeners();
|
||||
await this.loadExportHistory();
|
||||
}
|
||||
|
||||
async loadExportHistory() {
|
||||
try {
|
||||
const context = contextStore.getMCPContext();
|
||||
if (!context.project_id) return;
|
||||
|
||||
const cached = localStorage.getItem(`figma_exports_${context.project_id}`);
|
||||
if (cached) {
|
||||
this.exportHistory = JSON.parse(cached);
|
||||
this.renderHistory();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[DSFigmaPlugin] Failed to load history:', error);
|
||||
}
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
const exportBtn = this.querySelector('#export-figma-btn');
|
||||
const fileKeyInput = this.querySelector('#figma-file-key');
|
||||
const exportTypeSelect = this.querySelector('#export-type-select');
|
||||
|
||||
if (exportBtn) {
|
||||
exportBtn.addEventListener('click', () => this.exportFromFigma());
|
||||
}
|
||||
}
|
||||
|
||||
async exportFromFigma() {
|
||||
const fileKeyInput = this.querySelector('#figma-file-key');
|
||||
const exportTypeSelect = this.querySelector('#export-type-select');
|
||||
const formatSelect = this.querySelector('#export-format-select');
|
||||
|
||||
const fileKey = fileKeyInput?.value.trim() || '';
|
||||
const exportType = exportTypeSelect?.value || 'tokens';
|
||||
const format = formatSelect?.value || 'json';
|
||||
|
||||
if (!fileKey) {
|
||||
ComponentHelpers.showToast?.('Please enter a Figma file key', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const exportBtn = this.querySelector('#export-figma-btn');
|
||||
if (exportBtn) {
|
||||
exportBtn.disabled = true;
|
||||
exportBtn.textContent = '⏳ Exporting...';
|
||||
}
|
||||
|
||||
try {
|
||||
let result;
|
||||
|
||||
if (exportType === 'tokens') {
|
||||
// Export design tokens
|
||||
result = await toolBridge.executeTool('dss_sync_figma', {
|
||||
file_key: fileKey
|
||||
});
|
||||
} else if (exportType === 'assets') {
|
||||
// Export assets (icons, images)
|
||||
const response = await fetch('/api/figma/export-assets', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
projectId: contextStore.get('projectId'),
|
||||
fileKey,
|
||||
format
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Asset export failed: ${response.statusText}`);
|
||||
}
|
||||
|
||||
result = await response.json();
|
||||
} else if (exportType === 'components') {
|
||||
// Export component definitions
|
||||
const response = await fetch('/api/figma/export-components', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
projectId: contextStore.get('projectId'),
|
||||
fileKey,
|
||||
format
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Component export failed: ${response.statusText}`);
|
||||
}
|
||||
|
||||
result = await response.json();
|
||||
}
|
||||
|
||||
// Add to history
|
||||
const exportEntry = {
|
||||
timestamp: new Date().toISOString(),
|
||||
fileKey,
|
||||
type: exportType,
|
||||
format,
|
||||
itemCount: result.count || Object.keys(result.tokens || result.assets || result.components || {}).length
|
||||
};
|
||||
|
||||
this.exportHistory.unshift(exportEntry);
|
||||
this.exportHistory = this.exportHistory.slice(0, 10); // Keep last 10
|
||||
|
||||
// Cache history
|
||||
const context = contextStore.getMCPContext();
|
||||
if (context.project_id) {
|
||||
localStorage.setItem(`figma_exports_${context.project_id}`, JSON.stringify(this.exportHistory));
|
||||
}
|
||||
|
||||
this.renderHistory();
|
||||
ComponentHelpers.showToast?.(`Exported ${exportEntry.itemCount} ${exportType}`, 'success');
|
||||
} catch (error) {
|
||||
console.error('[DSFigmaPlugin] Export failed:', error);
|
||||
ComponentHelpers.showToast?.(`Export failed: ${error.message}`, 'error');
|
||||
} finally {
|
||||
if (exportBtn) {
|
||||
exportBtn.disabled = false;
|
||||
exportBtn.textContent = '📤 Export from Figma';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderHistory() {
|
||||
const historyContainer = this.querySelector('#export-history');
|
||||
if (!historyContainer) return;
|
||||
|
||||
if (this.exportHistory.length === 0) {
|
||||
historyContainer.innerHTML = ComponentHelpers.renderEmpty('No export history', '📋');
|
||||
return;
|
||||
}
|
||||
|
||||
historyContainer.innerHTML = `
|
||||
<div style="display: flex; flex-direction: column; gap: 8px;">
|
||||
${this.exportHistory.map((entry, idx) => `
|
||||
<div style="background: var(--vscode-bg); border: 1px solid var(--vscode-border); border-radius: 2px; padding: 12px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 6px;">
|
||||
<div style="flex: 1;">
|
||||
<div style="font-size: 11px; font-weight: 600; margin-bottom: 4px;">
|
||||
${ComponentHelpers.escapeHtml(entry.type)} Export
|
||||
</div>
|
||||
<div style="font-size: 10px; color: var(--vscode-text-dim); font-family: monospace;">
|
||||
${ComponentHelpers.escapeHtml(entry.fileKey)}
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<div style="font-size: 10px; color: var(--vscode-text-dim);">
|
||||
${ComponentHelpers.formatRelativeTime(new Date(entry.timestamp))}
|
||||
</div>
|
||||
<div style="font-size: 11px; font-weight: 600; margin-top: 2px;">
|
||||
${entry.itemCount} items
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 6px;">
|
||||
<span style="padding: 2px 6px; background: var(--vscode-sidebar); border-radius: 2px; font-size: 9px;">
|
||||
${entry.format.toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
this.innerHTML = `
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; height: 100%;">
|
||||
<!-- Export Panel -->
|
||||
<div style="display: flex; flex-direction: column; height: 100%; border-right: 1px solid var(--vscode-border);">
|
||||
<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: 4px;">Figma Export</h3>
|
||||
<p style="font-size: 10px; color: var(--vscode-text-dim);">
|
||||
Export tokens, assets, or components from Figma files
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="flex: 1; overflow: auto; padding: 16px;">
|
||||
<div style="display: flex; flex-direction: column; gap: 16px;">
|
||||
<div>
|
||||
<label style="display: block; font-size: 11px; font-weight: 600; margin-bottom: 6px;">
|
||||
Figma File Key
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="figma-file-key"
|
||||
placeholder="abc123def456..."
|
||||
class="input"
|
||||
style="width: 100%; font-size: 11px; font-family: monospace;"
|
||||
/>
|
||||
<div style="font-size: 10px; color: var(--vscode-text-dim); margin-top: 4px;">
|
||||
Find this in your Figma file URL
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style="display: block; font-size: 11px; font-weight: 600; margin-bottom: 6px;">
|
||||
Export Type
|
||||
</label>
|
||||
<select id="export-type-select" class="input" style="width: 100%; font-size: 11px;">
|
||||
<option value="tokens">Design Tokens</option>
|
||||
<option value="assets">Assets (Icons, Images)</option>
|
||||
<option value="components">Component Definitions</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label style="display: block; font-size: 11px; font-weight: 600; margin-bottom: 6px;">
|
||||
Export Format
|
||||
</label>
|
||||
<select id="export-format-select" class="input" style="width: 100%; font-size: 11px;">
|
||||
<option value="json">JSON</option>
|
||||
<option value="css">CSS</option>
|
||||
<option value="scss">SCSS</option>
|
||||
<option value="js">JavaScript</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button id="export-figma-btn" class="button" style="font-size: 12px; padding: 8px;">
|
||||
📤 Export from Figma
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- History Panel -->
|
||||
<div style="display: flex; flex-direction: column; height: 100%;">
|
||||
<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: 4px;">Export History</h3>
|
||||
<p style="font-size: 10px; color: var(--vscode-text-dim);">
|
||||
Recent Figma exports for this project
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="export-history" style="flex: 1; overflow: auto; padding: 16px;">
|
||||
${ComponentHelpers.renderLoading('Loading history...')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('ds-figma-plugin', DSFigmaPlugin);
|
||||
|
||||
export default DSFigmaPlugin;
|
||||
Reference in New Issue
Block a user