Files
dss/admin-ui/js/services/figma-service.js
Digital Production Factory 276ed71f31 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
2025-12-09 18:45:48 -03:00

230 lines
6.4 KiB
JavaScript

/**
* Design System Server (DSS) - Figma Service
*
* Client-side interface to Figma API tools.
* No mocks - requires backend connection.
*/
import notificationService from './notification-service.js';
class FigmaService {
constructor() {
this.apiBase = '/api/figma';
this.connected = null; // null = unknown, true/false = checked
this.listeners = new Set();
}
// === Connection ===
async checkConnection() {
try {
const response = await fetch('/health', { method: 'GET' });
this.connected = response.ok;
} catch {
this.connected = false;
}
return this.connected;
}
_requireConnection() {
if (this.connected === false) {
throw new Error('API unavailable. Start DSS server first.');
}
}
// === Event System ===
on(event, callback) {
this.listeners.add({ event, callback });
return () => this.listeners.delete({ event, callback });
}
emit(event, data) {
this.listeners.forEach(l => {
if (l.event === event) l.callback(data);
});
}
// === API Methods ===
async extractVariables(fileKey, format = 'css') {
this._requireConnection();
this.emit('loading', { operation: 'extractVariables' });
try {
const response = await fetch(`${this.apiBase}/extract-variables`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file_key: fileKey, format })
});
if (!response.ok) {
const err = await response.json().catch(() => ({}));
throw new Error(err.detail || 'Failed to extract variables');
}
const result = await response.json();
this.emit('complete', { operation: 'extractVariables', result });
notificationService.create({
title: 'Variables Extracted',
message: `Successfully extracted ${result.variable_count || 'variables'} from Figma.`,
type: 'success',
source: 'Figma',
actions: [
{ label: 'View Tokens', event: 'navigate', payload: { page: 'tokens' } }
]
});
return result;
} catch (error) {
notificationService.create({
title: 'Extraction Failed',
message: error.message,
type: 'error',
source: 'Figma'
});
throw error;
}
}
async extractComponents(fileKey) {
this._requireConnection();
this.emit('loading', { operation: 'extractComponents' });
const response = await fetch(`${this.apiBase}/extract-components`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file_key: fileKey })
});
if (!response.ok) {
const err = await response.json().catch(() => ({}));
throw new Error(err.detail || 'Failed to extract components');
}
const result = await response.json();
this.emit('complete', { operation: 'extractComponents', result });
return result;
}
async extractStyles(fileKey) {
this._requireConnection();
this.emit('loading', { operation: 'extractStyles' });
const response = await fetch(`${this.apiBase}/extract-styles`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file_key: fileKey })
});
if (!response.ok) {
const err = await response.json().catch(() => ({}));
throw new Error(err.detail || 'Failed to extract styles');
}
return response.json();
}
async syncTokens(fileKey, targetPath, format = 'css') {
this._requireConnection();
this.emit('loading', { operation: 'syncTokens' });
try {
const response = await fetch(`${this.apiBase}/sync-tokens`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file_key: fileKey, target_path: targetPath, format })
});
if (!response.ok) {
const err = await response.json().catch(() => ({}));
throw new Error(err.detail || 'Sync failed');
}
const result = await response.json();
this.emit('complete', { operation: 'syncTokens', result });
notificationService.create({
title: 'Tokens Synced',
message: `Tokens successfully written to ${result.path || targetPath}.`,
type: 'success',
source: 'Figma',
actions: [
{ label: 'View Tokens', event: 'navigate', payload: { page: 'tokens' } }
]
});
return result;
} catch (error) {
notificationService.create({
title: 'Token Sync Failed',
message: error.message,
type: 'error',
source: 'Figma',
actions: [
{ label: 'Retry', event: 'figma:sync', payload: { fileKey } }
]
});
throw error;
}
}
async visualDiff(fileKey, baselineVersion = 'latest') {
this._requireConnection();
this.emit('loading', { operation: 'visualDiff' });
const response = await fetch(`${this.apiBase}/visual-diff`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file_key: fileKey, baseline_version: baselineVersion })
});
if (!response.ok) {
const err = await response.json().catch(() => ({}));
throw new Error(err.detail || 'Visual diff failed');
}
return response.json();
}
async validateComponents(fileKey) {
this._requireConnection();
this.emit('loading', { operation: 'validateComponents' });
const response = await fetch(`${this.apiBase}/validate-components`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file_key: fileKey })
});
if (!response.ok) {
const err = await response.json().catch(() => ({}));
throw new Error(err.detail || 'Validation failed');
}
return response.json();
}
async generateCode(fileKey, componentName, framework = 'webcomponent') {
this._requireConnection();
this.emit('loading', { operation: 'generateCode' });
const response = await fetch(`${this.apiBase}/generate-code`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file_key: fileKey, component_name: componentName, framework })
});
if (!response.ok) {
const err = await response.json().catch(() => ({}));
throw new Error(err.detail || 'Code generation failed');
}
return response.json();
}
}
const figmaService = new FigmaService();
export { FigmaService };
export default figmaService;