conductor-figma 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +193 -0
- package/bin/conductor.js +58 -0
- package/package.json +37 -0
- package/src/design/exporter.js +72 -0
- package/src/design/intelligence.js +341 -0
- package/src/index.js +29 -0
- package/src/server.js +115 -0
- package/src/tools/handlers.js +423 -0
- package/src/tools/registry.js +292 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// CONDUCTOR — Tool Handlers
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
// Executes tool calls using design intelligence.
|
|
5
|
+
// Each handler returns { content: [{ type: 'text', text: string }] }
|
|
6
|
+
|
|
7
|
+
import * as design from '../design/intelligence.js';
|
|
8
|
+
import * as exporter from '../design/exporter.js';
|
|
9
|
+
|
|
10
|
+
function text(str) {
|
|
11
|
+
return { content: [{ type: 'text', text: str }] };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function json(obj) {
|
|
15
|
+
return text(JSON.stringify(obj, null, 2));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ─── Handler Map ───
|
|
19
|
+
// Returns a response for each tool. Tools that need Figma connection
|
|
20
|
+
// return instructions; tools that are pure design logic execute immediately.
|
|
21
|
+
|
|
22
|
+
export function handleTool(toolName, args, figmaState) {
|
|
23
|
+
const handler = HANDLERS[toolName];
|
|
24
|
+
if (!handler) return text(`Unknown tool: ${toolName}`);
|
|
25
|
+
try {
|
|
26
|
+
return handler(args, figmaState);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
return text(`Error in ${toolName}: ${err.message}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const HANDLERS = {
|
|
33
|
+
// ═══ CREATE ═══
|
|
34
|
+
create_frame(args) {
|
|
35
|
+
const padding = design.snapToGrid(args.padding || 16);
|
|
36
|
+
const gap = design.snapToGrid(args.gap || 8);
|
|
37
|
+
return json({
|
|
38
|
+
action: 'create_frame',
|
|
39
|
+
name: args.name,
|
|
40
|
+
layoutMode: (args.direction || 'vertical').toUpperCase(),
|
|
41
|
+
primaryAxisAlignItems: 'MIN',
|
|
42
|
+
counterAxisAlignItems: 'MIN',
|
|
43
|
+
paddingLeft: padding, paddingRight: padding, paddingTop: padding, paddingBottom: padding,
|
|
44
|
+
itemSpacing: gap,
|
|
45
|
+
width: args.width || null,
|
|
46
|
+
height: args.height || null,
|
|
47
|
+
fills: args.fill ? [{ type: 'SOLID', color: hexToFigmaColor(args.fill) }] : [],
|
|
48
|
+
designNotes: `Padding: ${padding}px (grid-aligned), Gap: ${gap}px`,
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
create_page(args) {
|
|
53
|
+
const sections = args.sections || getDefaultSections(args.pageType);
|
|
54
|
+
const colors = args.brandColor ? design.generateSemanticColors(args.brandColor) : null;
|
|
55
|
+
const typeScale = design.generateTypeScale(16, 'major-third');
|
|
56
|
+
|
|
57
|
+
return json({
|
|
58
|
+
action: 'create_page',
|
|
59
|
+
pageType: args.pageType,
|
|
60
|
+
title: args.title || `${args.pageType} page`,
|
|
61
|
+
sections,
|
|
62
|
+
typeScale: typeScale.sizes.map(s => ({ label: s.label, size: s.size })),
|
|
63
|
+
spacingScale: design.generateSpacingScale(8, 8),
|
|
64
|
+
colors,
|
|
65
|
+
darkMode: args.darkMode ? design.generateDarkMode(colors || {}) : null,
|
|
66
|
+
designNotes: `Type scale: ${typeScale.scale} (${typeScale.ratio}). Grid: 8px. Sections: ${sections.join(', ')}`,
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
create_section(args) {
|
|
71
|
+
const patterns = {
|
|
72
|
+
hero: { columns: 1, minHeight: 480, padding: 64, elements: ['heading', 'subheading', 'cta-group'] },
|
|
73
|
+
features: { columns: args.columns || 3, padding: 48, elements: ['section-heading', 'feature-cards'] },
|
|
74
|
+
testimonials: { columns: args.columns || 2, padding: 48, elements: ['section-heading', 'testimonial-cards'] },
|
|
75
|
+
faq: { columns: 1, padding: 48, elements: ['section-heading', 'accordion-items'] },
|
|
76
|
+
cta: { columns: 1, padding: 64, elements: ['heading', 'subheading', 'cta-button'] },
|
|
77
|
+
pricing: { columns: args.columns || 3, padding: 48, elements: ['section-heading', 'pricing-cards'] },
|
|
78
|
+
stats: { columns: args.columns || 4, padding: 48, elements: ['stat-items'] },
|
|
79
|
+
team: { columns: args.columns || 3, padding: 48, elements: ['section-heading', 'team-cards'] },
|
|
80
|
+
footer: { columns: 4, padding: 48, elements: ['logo', 'nav-groups', 'legal'] },
|
|
81
|
+
header: { columns: 1, padding: 16, elements: ['logo', 'nav-links', 'cta-button'] },
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const pattern = patterns[args.sectionType] || patterns.hero;
|
|
85
|
+
return json({
|
|
86
|
+
action: 'create_section',
|
|
87
|
+
sectionType: args.sectionType,
|
|
88
|
+
...pattern,
|
|
89
|
+
heading: args.heading,
|
|
90
|
+
designNotes: `Section pattern: ${args.sectionType}. ${pattern.columns} columns, ${pattern.padding}px padding, grid-aligned.`,
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
create_card(args) {
|
|
95
|
+
const variant = args.variant || 'elevated';
|
|
96
|
+
const shadows = design.generateElevation();
|
|
97
|
+
const shadow = variant === 'elevated' ? shadows.find(s => s.step === 'md') : null;
|
|
98
|
+
const radii = design.generateRadiusScale();
|
|
99
|
+
const radius = radii.find(r => r.name === 'lg');
|
|
100
|
+
|
|
101
|
+
return json({
|
|
102
|
+
action: 'create_card',
|
|
103
|
+
variant,
|
|
104
|
+
width: args.width || 320,
|
|
105
|
+
padding: 24,
|
|
106
|
+
gap: 12,
|
|
107
|
+
cornerRadius: radius?.value || 12,
|
|
108
|
+
shadow: shadow?.css || 'none',
|
|
109
|
+
title: args.title,
|
|
110
|
+
description: args.description,
|
|
111
|
+
hasImage: args.hasImage || false,
|
|
112
|
+
hasAction: args.hasAction || false,
|
|
113
|
+
designNotes: `${variant} card. 24px padding (3×8), 12px gap, ${radius?.value}px radius.`,
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
create_form(args) {
|
|
118
|
+
const fields = args.fields.map(f => ({
|
|
119
|
+
...f,
|
|
120
|
+
inputHeight: 40,
|
|
121
|
+
padding: { x: 12, y: 10 },
|
|
122
|
+
fontSize: 14,
|
|
123
|
+
labelSize: 13,
|
|
124
|
+
gap: 4,
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
return json({
|
|
128
|
+
action: 'create_form',
|
|
129
|
+
layout: args.layout || 'vertical',
|
|
130
|
+
fieldGap: 20,
|
|
131
|
+
submitLabel: args.submitLabel || 'Submit',
|
|
132
|
+
fields,
|
|
133
|
+
designNotes: `Form: ${fields.length} fields, ${args.layout || 'vertical'} layout. 40px input height, 14px text. All spacing grid-aligned.`,
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
create_table(args) {
|
|
138
|
+
return json({
|
|
139
|
+
action: 'create_table',
|
|
140
|
+
columns: args.columns,
|
|
141
|
+
rows: args.rows || 5,
|
|
142
|
+
headerHeight: 40,
|
|
143
|
+
rowHeight: 48,
|
|
144
|
+
cellPadding: { x: 16, y: 12 },
|
|
145
|
+
hasPagination: args.hasPagination || false,
|
|
146
|
+
hasSorting: args.hasSorting || false,
|
|
147
|
+
hasCheckbox: args.hasCheckbox || false,
|
|
148
|
+
designNotes: `Table: ${args.columns.length} cols × ${args.rows || 5} rows. Header: 40px, Row: 48px. 16px cell padding.`,
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
create_modal(args) {
|
|
153
|
+
const sizes = { sm: 400, md: 560, lg: 720, xl: 900 };
|
|
154
|
+
const width = sizes[args.size || 'md'] || 560;
|
|
155
|
+
|
|
156
|
+
return json({
|
|
157
|
+
action: 'create_modal',
|
|
158
|
+
title: args.title,
|
|
159
|
+
width,
|
|
160
|
+
padding: 24,
|
|
161
|
+
headerHeight: 56,
|
|
162
|
+
cornerRadius: 16,
|
|
163
|
+
hasCloseButton: args.hasCloseButton !== false,
|
|
164
|
+
actions: args.actions || ['Cancel', 'Confirm'],
|
|
165
|
+
overlay: { color: '#000000', opacity: 0.5 },
|
|
166
|
+
designNotes: `Modal: ${args.size || 'md'} (${width}px). 24px padding, 16px radius.`,
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
create_nav(args) {
|
|
171
|
+
return json({
|
|
172
|
+
action: 'create_nav',
|
|
173
|
+
navType: args.navType,
|
|
174
|
+
items: args.items,
|
|
175
|
+
logoText: args.logoText,
|
|
176
|
+
hasSearch: args.hasSearch || false,
|
|
177
|
+
hasAvatar: args.hasAvatar || false,
|
|
178
|
+
height: args.navType === 'topbar' ? 56 : undefined,
|
|
179
|
+
width: args.navType === 'sidebar' ? 240 : undefined,
|
|
180
|
+
padding: { x: 16, y: 12 },
|
|
181
|
+
itemGap: args.navType === 'tabs' ? 0 : 8,
|
|
182
|
+
designNotes: `Nav: ${args.navType} with ${args.items.length} items. All spacing grid-aligned.`,
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
// ═══ LAYOUT ═══
|
|
187
|
+
layout_auto(args) {
|
|
188
|
+
return json({
|
|
189
|
+
action: 'set_auto_layout',
|
|
190
|
+
nodeId: args.nodeId,
|
|
191
|
+
direction: args.direction || 'auto-detect',
|
|
192
|
+
gap: args.gap ? design.snapToGrid(args.gap) : 'auto-detect',
|
|
193
|
+
padding: args.padding ? design.snapToGrid(args.padding) : 'auto-detect',
|
|
194
|
+
designNotes: 'Converting to auto-layout. Direction and gap auto-detected from child positions if not specified.',
|
|
195
|
+
});
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
layout_grid(args) {
|
|
199
|
+
return json({
|
|
200
|
+
action: 'apply_grid',
|
|
201
|
+
nodeId: args.nodeId,
|
|
202
|
+
columns: args.columns || 12,
|
|
203
|
+
gutter: design.snapToGrid(args.gutter || 24),
|
|
204
|
+
margin: design.snapToGrid(args.margin || 24),
|
|
205
|
+
type: args.type || 'stretch',
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
layout_stack(args) {
|
|
210
|
+
return json({ action: 'stack', nodeId: args.nodeId, direction: args.direction, gap: design.snapToGrid(args.gap || 8), align: args.align || 'start' });
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
layout_wrap(args) {
|
|
214
|
+
return json({ action: 'wrap_layout', nodeId: args.nodeId, gap: design.snapToGrid(args.gap || 8), maxWidth: args.maxWidth });
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
layout_constrain(args) {
|
|
218
|
+
return json({ action: 'set_constraints', nodeId: args.nodeId, horizontal: args.horizontal, vertical: args.vertical, minWidth: args.minWidth, maxWidth: args.maxWidth, minHeight: args.minHeight, maxHeight: args.maxHeight });
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
layout_align(args) {
|
|
222
|
+
return json({ action: 'align', nodeIds: args.nodeIds, alignment: args.alignment });
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
layout_nest(args) {
|
|
226
|
+
return json({ action: 'restructure_to_autolayout', nodeId: args.nodeId, maxDepth: args.depth || 3, designNotes: 'Analyzing spatial relationships to build auto-layout tree. Groups detected by proximity and alignment.' });
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
// ═══ TYPOGRAPHY ═══
|
|
230
|
+
type_scale(args) {
|
|
231
|
+
if (args.action === 'generate') {
|
|
232
|
+
const result = design.generateTypeScale(args.baseSize || 16, args.scaleRatio || 'major-third');
|
|
233
|
+
return json({ action: 'type_scale_generated', ...result });
|
|
234
|
+
}
|
|
235
|
+
return json({ action: 'detect_type_scale', nodeId: args.nodeId, designNotes: 'Scanning all text nodes to detect current scale ratio.' });
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
type_hierarchy(args) {
|
|
239
|
+
const scale = design.generateTypeScale(args.baseSize || 16, 'major-third', { down: 1, up: args.levels || 4 });
|
|
240
|
+
return json({ action: 'apply_hierarchy', nodeId: args.nodeId, levels: scale.sizes.map(s => ({ label: s.label, size: s.size, lineHeight: design.getLineHeight(s.size), weight: s.step >= 1 ? 700 : s.step === 0 ? 400 : 400 })) });
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
type_pair(args) {
|
|
244
|
+
const pairs = [
|
|
245
|
+
{ heading: 'Instrument Serif', body: 'Sora', style: 'editorial' },
|
|
246
|
+
{ heading: 'Space Grotesk', body: 'IBM Plex Sans', style: 'technical' },
|
|
247
|
+
{ heading: 'Fraunces', body: 'Commissioner', style: 'classic' },
|
|
248
|
+
{ heading: 'Cabinet Grotesk', body: 'Satoshi', style: 'modern' },
|
|
249
|
+
{ heading: 'Gloock', body: 'DM Sans', style: 'playful' },
|
|
250
|
+
];
|
|
251
|
+
const match = args.style ? pairs.find(p => p.style === args.style) : pairs[0];
|
|
252
|
+
return json({ suggestions: pairs, recommended: match || pairs[0] });
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
type_measure(args) {
|
|
256
|
+
return json({ action: 'check_measure', nodeId: args.nodeId, optimalRange: '45-75 characters', designNotes: 'Measuring character count per line and checking line-height ratios.' });
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
type_apply(args) {
|
|
260
|
+
return json({ action: 'apply_text_styles', nodeId: args.nodeId, styles: args.styles });
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
type_audit(args) {
|
|
264
|
+
return json({ action: 'audit_typography', nodeId: args.nodeId, designNotes: 'Scanning all text nodes for unique styles. Will flag off-scale sizes and inconsistent weights.' });
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
// ═══ COLOR ═══
|
|
268
|
+
color_palette(args) {
|
|
269
|
+
const palette = design.generatePalette(args.baseColor, args.steps);
|
|
270
|
+
return json({ action: 'palette_generated', baseColor: args.baseColor, shades: palette });
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
color_semantic(args) {
|
|
274
|
+
const colors = design.generateSemanticColors(args.brandColor);
|
|
275
|
+
return json({ action: 'semantic_colors_generated', ...colors });
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
color_darkmode(args) {
|
|
279
|
+
if (args.colors) return json({ action: 'dark_mode_generated', colors: design.generateDarkMode(args.colors) });
|
|
280
|
+
return json({ action: 'generate_dark_mode', nodeId: args.nodeId, designNotes: 'Reading frame colors and generating dark mode with preserved contrast.' });
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
color_contrast(args) {
|
|
284
|
+
if (args.foreground && args.background) {
|
|
285
|
+
return json({ action: 'contrast_checked', ...design.checkContrast(args.foreground, args.background) });
|
|
286
|
+
}
|
|
287
|
+
return json({ action: 'audit_contrast', nodeId: args.nodeId, designNotes: 'Checking all text/background pairs in frame.' });
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
color_apply(args) {
|
|
291
|
+
return json({ action: 'apply_colors', nodeId: args.nodeId, colorMap: args.colorMap });
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
style_shadow(args) {
|
|
295
|
+
const shadows = design.generateElevation(args.levels);
|
|
296
|
+
return json({ action: 'elevation_generated', shadows });
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
style_radius(args) {
|
|
300
|
+
const scale = design.generateRadiusScale(args.base || 4);
|
|
301
|
+
return json({ action: 'radius_scale_generated', scale, nodeId: args.nodeId });
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
// ═══ COMPONENTS ═══
|
|
305
|
+
component_list(args) { return json({ action: 'list_components', source: args.source || 'file', filter: args.filter }); },
|
|
306
|
+
component_use(args) { return json({ action: 'instantiate_component', componentName: args.componentName, variants: args.variants, position: { x: args.x || 0, y: args.y || 0 } }); },
|
|
307
|
+
component_create(args) { return json({ action: 'create_component', nodeId: args.nodeId, name: args.name, variants: args.variants }); },
|
|
308
|
+
component_swap(args) { return json({ action: 'swap_component', nodeId: args.nodeId, from: args.fromComponent, to: args.toComponent }); },
|
|
309
|
+
component_detach(args) { return json({ action: 'detach_instance', nodeId: args.nodeId }); },
|
|
310
|
+
component_audit(args) { return json({ action: 'audit_components', nodeId: args.nodeId }); },
|
|
311
|
+
|
|
312
|
+
// ═══ SPACING ═══
|
|
313
|
+
spacing_scale(args) {
|
|
314
|
+
const scale = design.generateSpacingScale(args.base || 8, args.steps || 12);
|
|
315
|
+
return json({ action: 'spacing_scale_generated', base: args.base || 8, scale });
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
spacing_fix(args) {
|
|
319
|
+
return json({ action: 'fix_spacing', nodeId: args.nodeId, base: args.base || 8, designNotes: 'Snapping all padding, margin, and gap values to nearest grid multiple.' });
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
spacing_audit(args) {
|
|
323
|
+
return json({ action: 'audit_spacing', nodeId: args.nodeId, base: args.base || 8 });
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
spacing_rhythm(args) {
|
|
327
|
+
return json({ action: 'check_rhythm', nodeId: args.nodeId, designNotes: 'Measuring vertical gaps between sections and checking for proportional consistency.' });
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
grid_apply(args) {
|
|
331
|
+
return json({ action: 'apply_grid', nodeId: args.nodeId, type: args.type, count: args.count || 12, gutter: design.snapToGrid(args.gutter || 24), margin: design.snapToGrid(args.margin || 24) });
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
grid_check(args) {
|
|
335
|
+
return json({ action: 'check_grid_alignment', nodeId: args.nodeId });
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
// ═══ AUDIT ═══
|
|
339
|
+
audit_full(args) { return json({ action: 'full_audit', nodeId: args.nodeId, categories: ['spacing', 'typography', 'color', 'components', 'accessibility', 'hierarchy'] }); },
|
|
340
|
+
audit_hierarchy(args) { return json({ action: 'audit_hierarchy', nodeId: args.nodeId }); },
|
|
341
|
+
audit_consistency(args) { return json({ action: 'audit_consistency', nodeId: args.nodeId }); },
|
|
342
|
+
audit_alignment(args) { return json({ action: 'audit_alignment', nodeId: args.nodeId, tolerance: args.tolerance || 2 }); },
|
|
343
|
+
audit_density(args) { return json({ action: 'audit_density', nodeId: args.nodeId }); },
|
|
344
|
+
|
|
345
|
+
audit_score(args) {
|
|
346
|
+
return json({ action: 'compute_score', nodeId: args.nodeId, designNotes: 'Computing 0-100 score weighted: spacing 25%, typography 20%, color 15%, components 15%, accessibility 15%, hierarchy 10%.' });
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
// ═══ ACCESSIBILITY ═══
|
|
350
|
+
a11y_contrast(args) {
|
|
351
|
+
if (!args.nodeId) return json({ action: 'check_contrast_global', level: args.level || 'aa' });
|
|
352
|
+
return json({ action: 'check_contrast', nodeId: args.nodeId, level: args.level || 'aa' });
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
a11y_touch(args) { return json({ action: 'check_touch_targets', nodeId: args.nodeId, minSize: args.minSize || 44 }); },
|
|
356
|
+
a11y_focus(args) { return json({ action: 'generate_focus_states', nodeId: args.nodeId, color: args.color || '#2563eb', offset: args.offset || 2 }); },
|
|
357
|
+
a11y_labels(args) { return json({ action: 'check_labels', nodeId: args.nodeId }); },
|
|
358
|
+
a11y_fix(args) { return json({ action: 'auto_fix_a11y', nodeId: args.nodeId, fixes: args.fixes || ['contrast', 'touch', 'focus'] }); },
|
|
359
|
+
|
|
360
|
+
// ═══ RESPONSIVE ═══
|
|
361
|
+
responsive_variant(args) {
|
|
362
|
+
const breakpoints = args.breakpoints || [375, 768, 1440];
|
|
363
|
+
return json({ action: 'generate_variants', nodeId: args.nodeId, breakpoints: breakpoints.map(w => ({ width: w, name: w <= 375 ? 'mobile' : w <= 768 ? 'tablet' : 'desktop' })) });
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
responsive_reflow(args) { return json({ action: 'reflow', nodeId: args.nodeId, targetWidth: args.targetWidth }); },
|
|
367
|
+
responsive_breakpoints(args) {
|
|
368
|
+
const widths = args.widths || [375, 768, 1024, 1440];
|
|
369
|
+
return json({ action: 'setup_breakpoints', frames: widths.map(w => ({ width: w, height: args.height || 900, name: design.BREAKPOINTS[w <= 375 ? 'mobile' : w <= 768 ? 'tablet' : 'desktop']?.name || `${w}px` })) });
|
|
370
|
+
},
|
|
371
|
+
responsive_check(args) { return json({ action: 'check_responsive', nodeId: args.nodeId }); },
|
|
372
|
+
|
|
373
|
+
// ═══ EXPORT ═══
|
|
374
|
+
export_tokens_css(args) {
|
|
375
|
+
const tokens = buildTokenSet(args);
|
|
376
|
+
return text(exporter.exportCSS(tokens));
|
|
377
|
+
},
|
|
378
|
+
export_tokens_tailwind(args) {
|
|
379
|
+
const tokens = buildTokenSet(args);
|
|
380
|
+
return text(exporter.exportTailwind(tokens));
|
|
381
|
+
},
|
|
382
|
+
export_tokens_json(args) {
|
|
383
|
+
const tokens = buildTokenSet(args);
|
|
384
|
+
return text(exporter.exportW3CTokens(tokens));
|
|
385
|
+
},
|
|
386
|
+
export_tokens_scss(args) {
|
|
387
|
+
const tokens = buildTokenSet(args);
|
|
388
|
+
return text(exporter.exportSCSS(tokens));
|
|
389
|
+
},
|
|
390
|
+
export_spec(args) { return json({ action: 'generate_spec', nodeId: args.nodeId, format: args.format || 'markdown' }); },
|
|
391
|
+
export_changelog(args) { return json({ action: 'diff_frames', nodeIdA: args.nodeIdA, nodeIdB: args.nodeIdB, format: args.format || 'markdown' }); },
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// ─── Helpers ───
|
|
395
|
+
|
|
396
|
+
function hexToFigmaColor(hex) {
|
|
397
|
+
const { r, g, b } = design.hexToRgb(hex);
|
|
398
|
+
return { r: r / 255, g: g / 255, b: b / 255 };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function getDefaultSections(pageType) {
|
|
402
|
+
const defaults = {
|
|
403
|
+
landing: ['header', 'hero', 'features', 'testimonials', 'cta', 'footer'],
|
|
404
|
+
pricing: ['header', 'hero', 'pricing', 'faq', 'cta', 'footer'],
|
|
405
|
+
dashboard: ['sidebar', 'header', 'stats', 'table'],
|
|
406
|
+
settings: ['sidebar', 'header', 'form'],
|
|
407
|
+
auth: ['hero', 'form'],
|
|
408
|
+
blog: ['header', 'hero', 'cards', 'footer'],
|
|
409
|
+
portfolio: ['header', 'hero', 'cards', 'cta', 'footer'],
|
|
410
|
+
docs: ['sidebar', 'header', 'content'],
|
|
411
|
+
};
|
|
412
|
+
return defaults[pageType] || defaults.landing;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function buildTokenSet(args) {
|
|
416
|
+
return {
|
|
417
|
+
colors: design.generateSemanticColors(args.brandColor || '#6366f1'),
|
|
418
|
+
spacing: design.generateSpacingScale(8, 8),
|
|
419
|
+
fontSizes: design.generateTypeScale(16, 'major-third').sizes,
|
|
420
|
+
radii: design.generateRadiusScale(4),
|
|
421
|
+
shadows: design.generateElevation(),
|
|
422
|
+
};
|
|
423
|
+
}
|