conductor-figma 1.0.2 → 3.0.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 +20 -0
- package/README.md +59 -153
- package/bin/conductor.js +1 -75
- package/figma-plugin/code.js +755 -0
- package/figma-plugin/manifest.json +14 -0
- package/figma-plugin/ui.html +77 -0
- package/package.json +25 -16
- package/src/bridge.js +60 -0
- package/src/design/intelligence.js +273 -294
- package/src/server.js +82 -196
- package/src/tools/handlers.js +145 -463
- package/src/tools/registry.js +1144 -336
- package/src/blueprints.js +0 -775
- package/src/design/craftguide.js +0 -181
- package/src/design/exporter.js +0 -72
- package/src/index.js +0 -33
- package/src/orchestrator.js +0 -100
- package/src/relay.js +0 -176
package/src/tools/handlers.js
CHANGED
|
@@ -1,486 +1,168 @@
|
|
|
1
1
|
// ═══════════════════════════════════════════
|
|
2
|
-
// CONDUCTOR — Tool Handlers
|
|
2
|
+
// CONDUCTOR v3 — Tool Handlers
|
|
3
3
|
// ═══════════════════════════════════════════
|
|
4
|
-
// Executes tool calls using design intelligence.
|
|
5
|
-
// Each handler returns { content: [{ type: 'text', text: string }] }
|
|
6
4
|
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
import {
|
|
6
|
+
snap, typeScale, semanticColors, componentDefaults, suggestAutoLayout,
|
|
7
|
+
checkContrast, auditAccessibility, getDesignCraftGuide, resolveFontWeight,
|
|
8
|
+
hexToFigmaColor, linearGradient, radialGradient, SPACING, RADIUS, SHADOWS,
|
|
9
|
+
} from '../design/intelligence.js'
|
|
10
|
+
|
|
11
|
+
// ─── Icon SVG Library ───
|
|
12
|
+
const ICONS = {
|
|
13
|
+
'arrow-right': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M12 5l7 7-7 7"/></svg>',
|
|
14
|
+
'arrow-left': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>',
|
|
15
|
+
'arrow-up': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 19V5M5 12l7-7 7 7"/></svg>',
|
|
16
|
+
'arrow-down': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M19 12l-7 7-7-7"/></svg>',
|
|
17
|
+
'check': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>',
|
|
18
|
+
'x': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>',
|
|
19
|
+
'plus': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>',
|
|
20
|
+
'minus': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14"/></svg>',
|
|
21
|
+
'search': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>',
|
|
22
|
+
'menu': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 6h16M4 12h16M4 18h16"/></svg>',
|
|
23
|
+
'settings': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>',
|
|
24
|
+
'user': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
|
|
25
|
+
'heart': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78L12 21.23l8.84-8.84a5.5 5.5 0 000-7.78z"/></svg>',
|
|
26
|
+
'star': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26"/></svg>',
|
|
27
|
+
'home': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg>',
|
|
28
|
+
'mail': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M22 7l-10 7L2 7"/></svg>',
|
|
29
|
+
'chevron-right': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>',
|
|
30
|
+
'chevron-left': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>',
|
|
31
|
+
'chevron-down': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>',
|
|
32
|
+
'chevron-up': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 15l-6-6-6 6"/></svg>',
|
|
33
|
+
'external-link': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3"/></svg>',
|
|
34
|
+
'copy': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>',
|
|
35
|
+
'trash': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2"/></svg>',
|
|
36
|
+
'edit': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 013 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>',
|
|
37
|
+
'download': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>',
|
|
38
|
+
'upload': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/></svg>',
|
|
39
|
+
'eye': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>',
|
|
40
|
+
'lock': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>',
|
|
41
|
+
'bell': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 01-3.46 0"/></svg>',
|
|
42
|
+
'filter': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="22,3 2,3 10,12.46 10,19 14,21 14,12.46"/></svg>',
|
|
43
|
+
'grid': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>',
|
|
44
|
+
'list': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>',
|
|
45
|
+
'link': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71"/></svg>',
|
|
46
|
+
'share': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>',
|
|
47
|
+
'sort': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19,12 12,19 5,12"/></svg>',
|
|
48
|
+
'clock': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12,6 12,12 16,14"/></svg>',
|
|
49
|
+
'calendar': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>',
|
|
50
|
+
'phone': '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6A19.79 19.79 0 012.12 4.18 2 2 0 014.11 2h3a2 2 0 012 1.72 12.84 12.84 0 00.7 2.81 2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45 12.84 12.84 0 002.81.7A2 2 0 0122 16.92z"/></svg>',
|
|
17
51
|
}
|
|
18
52
|
|
|
19
|
-
// ─── Handler
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return handler(args, figmaState);
|
|
28
|
-
} catch (err) {
|
|
29
|
-
return text(`Error in ${toolName}: ${err.message}`);
|
|
53
|
+
// ─── Main Handler ───
|
|
54
|
+
export async function handleTool(name, args, bridge) {
|
|
55
|
+
// Tools that don't need Figma connection
|
|
56
|
+
switch (name) {
|
|
57
|
+
case 'get_design_craft_guide': return getDesignCraftGuide()
|
|
58
|
+
case 'check_contrast': return checkContrast(args.foreground, args.background)
|
|
59
|
+
case 'suggest_color_palette': return semanticColors(args.brandColor, args.mode || 'dark')
|
|
60
|
+
case 'suggest_type_scale': return typeScale(args.baseSize || 16, args.ratio || 'major2')
|
|
30
61
|
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const HANDLERS = {
|
|
34
|
-
// ═══ CREATE ═══
|
|
35
|
-
create_frame(args) {
|
|
36
|
-
const padding = design.snapToGrid(args.padding || 16);
|
|
37
|
-
const gap = design.snapToGrid(args.gap || 8);
|
|
38
|
-
return json({
|
|
39
|
-
action: 'create_frame',
|
|
40
|
-
name: args.name,
|
|
41
|
-
layoutMode: (args.direction || 'vertical').toUpperCase(),
|
|
42
|
-
primaryAxisAlignItems: 'MIN',
|
|
43
|
-
counterAxisAlignItems: 'MIN',
|
|
44
|
-
paddingLeft: padding, paddingRight: padding, paddingTop: padding, paddingBottom: padding,
|
|
45
|
-
itemSpacing: gap,
|
|
46
|
-
width: args.width || null,
|
|
47
|
-
height: args.height || null,
|
|
48
|
-
fills: args.fill ? [{ type: 'SOLID', color: hexToFigmaColor(args.fill) }] : [],
|
|
49
|
-
designNotes: `Padding: ${padding}px (grid-aligned), Gap: ${gap}px`,
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
create_page(args) {
|
|
54
|
-
const sections = args.sections || getDefaultSections(args.pageType);
|
|
55
|
-
const colors = args.brandColor ? design.generateSemanticColors(args.brandColor) : null;
|
|
56
|
-
const typeScale = design.generateTypeScale(16, 'major-third');
|
|
57
|
-
|
|
58
|
-
return json({
|
|
59
|
-
action: 'create_page',
|
|
60
|
-
pageType: args.pageType,
|
|
61
|
-
title: args.title || `${args.pageType} page`,
|
|
62
|
-
sections,
|
|
63
|
-
typeScale: typeScale.sizes.map(s => ({ label: s.label, size: s.size })),
|
|
64
|
-
spacingScale: design.generateSpacingScale(8, 8),
|
|
65
|
-
colors,
|
|
66
|
-
darkMode: args.darkMode ? design.generateDarkMode(colors || {}) : null,
|
|
67
|
-
designNotes: `Type scale: ${typeScale.scale} (${typeScale.ratio}). Grid: 8px. Sections: ${sections.join(', ')}`,
|
|
68
|
-
});
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
create_section(args) {
|
|
72
|
-
const patterns = {
|
|
73
|
-
hero: { columns: 1, minHeight: 480, padding: 64, elements: ['heading', 'subheading', 'cta-group'] },
|
|
74
|
-
features: { columns: args.columns || 3, padding: 48, elements: ['section-heading', 'feature-cards'] },
|
|
75
|
-
testimonials: { columns: args.columns || 2, padding: 48, elements: ['section-heading', 'testimonial-cards'] },
|
|
76
|
-
faq: { columns: 1, padding: 48, elements: ['section-heading', 'accordion-items'] },
|
|
77
|
-
cta: { columns: 1, padding: 64, elements: ['heading', 'subheading', 'cta-button'] },
|
|
78
|
-
pricing: { columns: args.columns || 3, padding: 48, elements: ['section-heading', 'pricing-cards'] },
|
|
79
|
-
stats: { columns: args.columns || 4, padding: 48, elements: ['stat-items'] },
|
|
80
|
-
team: { columns: args.columns || 3, padding: 48, elements: ['section-heading', 'team-cards'] },
|
|
81
|
-
footer: { columns: 4, padding: 48, elements: ['logo', 'nav-groups', 'legal'] },
|
|
82
|
-
header: { columns: 1, padding: 16, elements: ['logo', 'nav-links', 'cta-button'] },
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const pattern = patterns[args.sectionType] || patterns.hero;
|
|
86
|
-
return json({
|
|
87
|
-
action: 'create_section',
|
|
88
|
-
sectionType: args.sectionType,
|
|
89
|
-
...pattern,
|
|
90
|
-
heading: args.heading,
|
|
91
|
-
designNotes: `Section pattern: ${args.sectionType}. ${pattern.columns} columns, ${pattern.padding}px padding, grid-aligned.`,
|
|
92
|
-
});
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
create_card(args) {
|
|
96
|
-
const variant = args.variant || 'elevated';
|
|
97
|
-
const shadows = design.generateElevation();
|
|
98
|
-
const shadow = variant === 'elevated' ? shadows.find(s => s.step === 'md') : null;
|
|
99
|
-
const radii = design.generateRadiusScale();
|
|
100
|
-
const radius = radii.find(r => r.name === 'lg');
|
|
101
|
-
|
|
102
|
-
return json({
|
|
103
|
-
action: 'create_card',
|
|
104
|
-
variant,
|
|
105
|
-
width: args.width || 320,
|
|
106
|
-
padding: 24,
|
|
107
|
-
gap: 12,
|
|
108
|
-
cornerRadius: radius?.value || 12,
|
|
109
|
-
shadow: shadow?.css || 'none',
|
|
110
|
-
title: args.title,
|
|
111
|
-
description: args.description,
|
|
112
|
-
hasImage: args.hasImage || false,
|
|
113
|
-
hasAction: args.hasAction || false,
|
|
114
|
-
designNotes: `${variant} card. 24px padding (3×8), 12px gap, ${radius?.value}px radius.`,
|
|
115
|
-
});
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
create_form(args) {
|
|
119
|
-
const fields = args.fields.map(f => ({
|
|
120
|
-
...f,
|
|
121
|
-
inputHeight: 40,
|
|
122
|
-
padding: { x: 12, y: 10 },
|
|
123
|
-
fontSize: 14,
|
|
124
|
-
labelSize: 13,
|
|
125
|
-
gap: 4,
|
|
126
|
-
}));
|
|
127
|
-
|
|
128
|
-
return json({
|
|
129
|
-
action: 'create_form',
|
|
130
|
-
layout: args.layout || 'vertical',
|
|
131
|
-
fieldGap: 20,
|
|
132
|
-
submitLabel: args.submitLabel || 'Submit',
|
|
133
|
-
fields,
|
|
134
|
-
designNotes: `Form: ${fields.length} fields, ${args.layout || 'vertical'} layout. 40px input height, 14px text. All spacing grid-aligned.`,
|
|
135
|
-
});
|
|
136
|
-
},
|
|
137
|
-
|
|
138
|
-
create_table(args) {
|
|
139
|
-
return json({
|
|
140
|
-
action: 'create_table',
|
|
141
|
-
columns: args.columns,
|
|
142
|
-
rows: args.rows || 5,
|
|
143
|
-
headerHeight: 40,
|
|
144
|
-
rowHeight: 48,
|
|
145
|
-
cellPadding: { x: 16, y: 12 },
|
|
146
|
-
hasPagination: args.hasPagination || false,
|
|
147
|
-
hasSorting: args.hasSorting || false,
|
|
148
|
-
hasCheckbox: args.hasCheckbox || false,
|
|
149
|
-
designNotes: `Table: ${args.columns.length} cols × ${args.rows || 5} rows. Header: 40px, Row: 48px. 16px cell padding.`,
|
|
150
|
-
});
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
create_modal(args) {
|
|
154
|
-
const sizes = { sm: 400, md: 560, lg: 720, xl: 900 };
|
|
155
|
-
const width = sizes[args.size || 'md'] || 560;
|
|
156
62
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
padding: 24,
|
|
162
|
-
headerHeight: 56,
|
|
163
|
-
cornerRadius: 16,
|
|
164
|
-
hasCloseButton: args.hasCloseButton !== false,
|
|
165
|
-
actions: args.actions || ['Cancel', 'Confirm'],
|
|
166
|
-
overlay: { color: '#000000', opacity: 0.5 },
|
|
167
|
-
designNotes: `Modal: ${args.size || 'md'} (${width}px). 24px padding, 16px radius.`,
|
|
168
|
-
});
|
|
169
|
-
},
|
|
170
|
-
|
|
171
|
-
create_nav(args) {
|
|
172
|
-
return json({
|
|
173
|
-
action: 'create_nav',
|
|
174
|
-
navType: args.navType,
|
|
175
|
-
items: args.items,
|
|
176
|
-
logoText: args.logoText,
|
|
177
|
-
hasSearch: args.hasSearch || false,
|
|
178
|
-
hasAvatar: args.hasAvatar || false,
|
|
179
|
-
height: args.navType === 'topbar' ? 56 : undefined,
|
|
180
|
-
width: args.navType === 'sidebar' ? 240 : undefined,
|
|
181
|
-
padding: { x: 16, y: 12 },
|
|
182
|
-
itemGap: args.navType === 'tabs' ? 0 : 8,
|
|
183
|
-
designNotes: `Nav: ${args.navType} with ${args.items.length} items. All spacing grid-aligned.`,
|
|
184
|
-
});
|
|
185
|
-
},
|
|
186
|
-
|
|
187
|
-
// ═══ LAYOUT ═══
|
|
188
|
-
layout_auto(args) {
|
|
189
|
-
return json({
|
|
190
|
-
action: 'set_auto_layout',
|
|
191
|
-
nodeId: args.nodeId,
|
|
192
|
-
direction: args.direction || 'auto-detect',
|
|
193
|
-
gap: args.gap ? design.snapToGrid(args.gap) : 'auto-detect',
|
|
194
|
-
padding: args.padding ? design.snapToGrid(args.padding) : 'auto-detect',
|
|
195
|
-
designNotes: 'Converting to auto-layout. Direction and gap auto-detected from child positions if not specified.',
|
|
196
|
-
});
|
|
197
|
-
},
|
|
198
|
-
|
|
199
|
-
layout_grid(args) {
|
|
200
|
-
return json({
|
|
201
|
-
action: 'apply_grid',
|
|
202
|
-
nodeId: args.nodeId,
|
|
203
|
-
columns: args.columns || 12,
|
|
204
|
-
gutter: design.snapToGrid(args.gutter || 24),
|
|
205
|
-
margin: design.snapToGrid(args.margin || 24),
|
|
206
|
-
type: args.type || 'stretch',
|
|
207
|
-
});
|
|
208
|
-
},
|
|
209
|
-
|
|
210
|
-
layout_stack(args) {
|
|
211
|
-
return json({ action: 'stack', nodeId: args.nodeId, direction: args.direction, gap: design.snapToGrid(args.gap || 8), align: args.align || 'start' });
|
|
212
|
-
},
|
|
213
|
-
|
|
214
|
-
layout_wrap(args) {
|
|
215
|
-
return json({ action: 'wrap_layout', nodeId: args.nodeId, gap: design.snapToGrid(args.gap || 8), maxWidth: args.maxWidth });
|
|
216
|
-
},
|
|
217
|
-
|
|
218
|
-
layout_constrain(args) {
|
|
219
|
-
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 });
|
|
220
|
-
},
|
|
63
|
+
// Everything else needs Figma
|
|
64
|
+
if (!bridge || !bridge.isConnected()) {
|
|
65
|
+
throw new Error('Figma plugin not connected. Open Figma → Plugins → Development → Conductor, then try again.')
|
|
66
|
+
}
|
|
221
67
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
},
|
|
68
|
+
// Apply design intelligence to args before sending
|
|
69
|
+
const enhanced = enhanceWithIntelligence(name, args)
|
|
225
70
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
71
|
+
// Send to Figma plugin
|
|
72
|
+
return bridge.send(name, enhanced)
|
|
73
|
+
}
|
|
229
74
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
75
|
+
// ─── Design Intelligence Enhancement ───
|
|
76
|
+
function enhanceWithIntelligence(name, args) {
|
|
77
|
+
const a = { ...args }
|
|
78
|
+
|
|
79
|
+
switch (name) {
|
|
80
|
+
case 'create_frame': {
|
|
81
|
+
// Snap all spacing to grid
|
|
82
|
+
if (a.padding !== undefined) { const p = snap(a.padding); a.paddingTop = p; a.paddingRight = p; a.paddingBottom = p; a.paddingLeft = p; delete a.padding }
|
|
83
|
+
if (a.paddingTop !== undefined) a.paddingTop = snap(a.paddingTop)
|
|
84
|
+
if (a.paddingRight !== undefined) a.paddingRight = snap(a.paddingRight)
|
|
85
|
+
if (a.paddingBottom !== undefined) a.paddingBottom = snap(a.paddingBottom)
|
|
86
|
+
if (a.paddingLeft !== undefined) a.paddingLeft = snap(a.paddingLeft)
|
|
87
|
+
if (a.gap !== undefined) a.gap = snap(a.gap)
|
|
88
|
+
// Default auto-layout
|
|
89
|
+
if (!a.direction) a.direction = 'VERTICAL'
|
|
90
|
+
break
|
|
235
91
|
}
|
|
236
|
-
return json({ action: 'detect_type_scale', nodeId: args.nodeId, designNotes: 'Scanning all text nodes to detect current scale ratio.' });
|
|
237
|
-
},
|
|
238
|
-
|
|
239
|
-
type_hierarchy(args) {
|
|
240
|
-
const scale = design.generateTypeScale(args.baseSize || 16, 'major-third', { down: 1, up: args.levels || 4 });
|
|
241
|
-
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 })) });
|
|
242
|
-
},
|
|
243
|
-
|
|
244
|
-
type_pair(args) {
|
|
245
|
-
const pairs = [
|
|
246
|
-
{ heading: 'Instrument Serif', body: 'Sora', style: 'editorial' },
|
|
247
|
-
{ heading: 'Space Grotesk', body: 'IBM Plex Sans', style: 'technical' },
|
|
248
|
-
{ heading: 'Fraunces', body: 'Commissioner', style: 'classic' },
|
|
249
|
-
{ heading: 'Cabinet Grotesk', body: 'Satoshi', style: 'modern' },
|
|
250
|
-
{ heading: 'Gloock', body: 'DM Sans', style: 'playful' },
|
|
251
|
-
];
|
|
252
|
-
const match = args.style ? pairs.find(p => p.style === args.style) : pairs[0];
|
|
253
|
-
return json({ suggestions: pairs, recommended: match || pairs[0] });
|
|
254
|
-
},
|
|
255
|
-
|
|
256
|
-
type_measure(args) {
|
|
257
|
-
return json({ action: 'check_measure', nodeId: args.nodeId, optimalRange: '45-75 characters', designNotes: 'Measuring character count per line and checking line-height ratios.' });
|
|
258
|
-
},
|
|
259
|
-
|
|
260
|
-
type_apply(args) {
|
|
261
|
-
return json({ action: 'apply_text_styles', nodeId: args.nodeId, styles: args.styles });
|
|
262
|
-
},
|
|
263
|
-
|
|
264
|
-
type_audit(args) {
|
|
265
|
-
return json({ action: 'audit_typography', nodeId: args.nodeId, designNotes: 'Scanning all text nodes for unique styles. Will flag off-scale sizes and inconsistent weights.' });
|
|
266
|
-
},
|
|
267
|
-
|
|
268
|
-
// ═══ COLOR ═══
|
|
269
|
-
color_palette(args) {
|
|
270
|
-
const palette = design.generatePalette(args.baseColor, args.steps);
|
|
271
|
-
return json({ action: 'palette_generated', baseColor: args.baseColor, shades: palette });
|
|
272
|
-
},
|
|
273
|
-
|
|
274
|
-
color_semantic(args) {
|
|
275
|
-
const colors = design.generateSemanticColors(args.brandColor);
|
|
276
|
-
return json({ action: 'semantic_colors_generated', ...colors });
|
|
277
|
-
},
|
|
278
92
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
return json({ action: 'contrast_checked', ...design.checkContrast(args.foreground, args.background) });
|
|
93
|
+
case 'create_text': {
|
|
94
|
+
// Resolve font weight
|
|
95
|
+
if (a.fontWeight) a.fontWeight = resolveFontWeight(a.fontWeight)
|
|
96
|
+
if (!a.fontFamily) a.fontFamily = 'Inter'
|
|
97
|
+
// Build fontName for Figma
|
|
98
|
+
a.fontName = { family: a.fontFamily, style: a.fontWeight || 'Regular' }
|
|
99
|
+
break
|
|
287
100
|
}
|
|
288
|
-
return json({ action: 'audit_contrast', nodeId: args.nodeId, designNotes: 'Checking all text/background pairs in frame.' });
|
|
289
|
-
},
|
|
290
|
-
|
|
291
|
-
color_apply(args) {
|
|
292
|
-
return json({ action: 'apply_colors', nodeId: args.nodeId, colorMap: args.colorMap });
|
|
293
|
-
},
|
|
294
|
-
|
|
295
|
-
style_shadow(args) {
|
|
296
|
-
const shadows = design.generateElevation(args.levels);
|
|
297
|
-
return json({ action: 'elevation_generated', shadows });
|
|
298
|
-
},
|
|
299
|
-
|
|
300
|
-
style_radius(args) {
|
|
301
|
-
const scale = design.generateRadiusScale(args.base || 4);
|
|
302
|
-
return json({ action: 'radius_scale_generated', scale, nodeId: args.nodeId });
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
// ═══ COMPONENTS ═══
|
|
306
|
-
component_list(args) { return json({ action: 'list_components', source: args.source || 'file', filter: args.filter }); },
|
|
307
|
-
component_use(args) { return json({ action: 'instantiate_component', componentName: args.componentName, variants: args.variants, position: { x: args.x || 0, y: args.y || 0 } }); },
|
|
308
|
-
component_create(args) { return json({ action: 'create_component', nodeId: args.nodeId, name: args.name, variants: args.variants }); },
|
|
309
|
-
component_swap(args) { return json({ action: 'swap_component', nodeId: args.nodeId, from: args.fromComponent, to: args.toComponent }); },
|
|
310
|
-
component_detach(args) { return json({ action: 'detach_instance', nodeId: args.nodeId }); },
|
|
311
|
-
component_audit(args) { return json({ action: 'audit_components', nodeId: args.nodeId }); },
|
|
312
|
-
|
|
313
|
-
// ═══ SPACING ═══
|
|
314
|
-
spacing_scale(args) {
|
|
315
|
-
const scale = design.generateSpacingScale(args.base || 8, args.steps || 12);
|
|
316
|
-
return json({ action: 'spacing_scale_generated', base: args.base || 8, scale });
|
|
317
|
-
},
|
|
318
|
-
|
|
319
|
-
spacing_fix(args) {
|
|
320
|
-
return json({ action: 'fix_spacing', nodeId: args.nodeId, base: args.base || 8, designNotes: 'Snapping all padding, margin, and gap values to nearest grid multiple.' });
|
|
321
|
-
},
|
|
322
|
-
|
|
323
|
-
spacing_audit(args) {
|
|
324
|
-
return json({ action: 'audit_spacing', nodeId: args.nodeId, base: args.base || 8 });
|
|
325
|
-
},
|
|
326
|
-
|
|
327
|
-
spacing_rhythm(args) {
|
|
328
|
-
return json({ action: 'check_rhythm', nodeId: args.nodeId, designNotes: 'Measuring vertical gaps between sections and checking for proportional consistency.' });
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
grid_apply(args) {
|
|
332
|
-
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) });
|
|
333
|
-
},
|
|
334
|
-
|
|
335
|
-
grid_check(args) {
|
|
336
|
-
return json({ action: 'check_grid_alignment', nodeId: args.nodeId });
|
|
337
|
-
},
|
|
338
|
-
|
|
339
|
-
// ═══ AUDIT ═══
|
|
340
|
-
audit_full(args) { return json({ action: 'full_audit', nodeId: args.nodeId, categories: ['spacing', 'typography', 'color', 'components', 'accessibility', 'hierarchy'] }); },
|
|
341
|
-
audit_hierarchy(args) { return json({ action: 'audit_hierarchy', nodeId: args.nodeId }); },
|
|
342
|
-
audit_consistency(args) { return json({ action: 'audit_consistency', nodeId: args.nodeId }); },
|
|
343
|
-
audit_alignment(args) { return json({ action: 'audit_alignment', nodeId: args.nodeId, tolerance: args.tolerance || 2 }); },
|
|
344
|
-
audit_density(args) { return json({ action: 'audit_density', nodeId: args.nodeId }); },
|
|
345
|
-
|
|
346
|
-
audit_score(args) {
|
|
347
|
-
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%.' });
|
|
348
|
-
},
|
|
349
|
-
|
|
350
|
-
// ═══ ACCESSIBILITY ═══
|
|
351
|
-
a11y_contrast(args) {
|
|
352
|
-
if (!args.nodeId) return json({ action: 'check_contrast_global', level: args.level || 'aa' });
|
|
353
|
-
return json({ action: 'check_contrast', nodeId: args.nodeId, level: args.level || 'aa' });
|
|
354
|
-
},
|
|
355
|
-
|
|
356
|
-
a11y_touch(args) { return json({ action: 'check_touch_targets', nodeId: args.nodeId, minSize: args.minSize || 44 }); },
|
|
357
|
-
a11y_focus(args) { return json({ action: 'generate_focus_states', nodeId: args.nodeId, color: args.color || '#2563eb', offset: args.offset || 2 }); },
|
|
358
|
-
a11y_labels(args) { return json({ action: 'check_labels', nodeId: args.nodeId }); },
|
|
359
|
-
a11y_fix(args) { return json({ action: 'auto_fix_a11y', nodeId: args.nodeId, fixes: args.fixes || ['contrast', 'touch', 'focus'] }); },
|
|
360
101
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const widths = args.widths || [375, 768, 1024, 1440];
|
|
370
|
-
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` })) });
|
|
371
|
-
},
|
|
372
|
-
responsive_check(args) { return json({ action: 'check_responsive', nodeId: args.nodeId }); },
|
|
373
|
-
|
|
374
|
-
// ═══ EXPORT ═══
|
|
375
|
-
export_tokens_css(args) {
|
|
376
|
-
const tokens = buildTokenSet(args);
|
|
377
|
-
return text(exporter.exportCSS(tokens));
|
|
378
|
-
},
|
|
379
|
-
export_tokens_tailwind(args) {
|
|
380
|
-
const tokens = buildTokenSet(args);
|
|
381
|
-
return text(exporter.exportTailwind(tokens));
|
|
382
|
-
},
|
|
383
|
-
export_tokens_json(args) {
|
|
384
|
-
const tokens = buildTokenSet(args);
|
|
385
|
-
return text(exporter.exportW3CTokens(tokens));
|
|
386
|
-
},
|
|
387
|
-
export_tokens_scss(args) {
|
|
388
|
-
const tokens = buildTokenSet(args);
|
|
389
|
-
return text(exporter.exportSCSS(tokens));
|
|
390
|
-
},
|
|
391
|
-
export_spec(args) { return json({ action: 'generate_spec', nodeId: args.nodeId, format: args.format || 'markdown' }); },
|
|
392
|
-
export_changelog(args) { return json({ action: 'diff_frames', nodeIdA: args.nodeIdA, nodeIdB: args.nodeIdB, format: args.format || 'markdown' }); },
|
|
393
|
-
|
|
394
|
-
// ═══ DESIGN CRAFT ═══
|
|
395
|
-
get_design_craft_guide() {
|
|
396
|
-
return text(getDesignCraftGuide());
|
|
397
|
-
},
|
|
398
|
-
|
|
399
|
-
// ═══ CREATE (additional) ═══
|
|
400
|
-
create_ellipse(args) {
|
|
401
|
-
return json({
|
|
402
|
-
action: 'create_ellipse',
|
|
403
|
-
name: args.name || 'Ellipse',
|
|
404
|
-
width: args.width, height: args.height,
|
|
405
|
-
fill: args.fill || '#ffffff',
|
|
406
|
-
x: args.x, y: args.y,
|
|
407
|
-
parentId: args.parentId,
|
|
408
|
-
opacity: args.opacity,
|
|
409
|
-
});
|
|
410
|
-
},
|
|
102
|
+
case 'create_smart_component': {
|
|
103
|
+
const defaults = componentDefaults(a.type, a.variant)
|
|
104
|
+
if (!defaults) throw new Error(`Unknown component type: ${a.type}. Available: button, input, card, avatar, badge, chip, switch, checkbox, radio, toast, tooltip, modal, dropdown, tabs, table, progress, skeleton, divider`)
|
|
105
|
+
const colors = semanticColors(a.brandColor || '#6366f1', a.mode || 'dark')
|
|
106
|
+
a._defaults = defaults
|
|
107
|
+
a._colors = colors
|
|
108
|
+
break
|
|
109
|
+
}
|
|
411
110
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
fill: args.color || '#1a1a32',
|
|
420
|
-
parentId: args.parentId,
|
|
421
|
-
});
|
|
422
|
-
},
|
|
111
|
+
case 'set_effects': {
|
|
112
|
+
if (a.preset) {
|
|
113
|
+
const s = SHADOWS[a.preset]
|
|
114
|
+
if (s) a.shadow = s
|
|
115
|
+
}
|
|
116
|
+
break
|
|
117
|
+
}
|
|
423
118
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
119
|
+
case 'set_fill': {
|
|
120
|
+
if (a.type === 'LINEAR' && a.gradient) {
|
|
121
|
+
a._fill = linearGradient(a.gradient.angle || 180, a.gradient.stops)
|
|
122
|
+
} else if (a.type === 'RADIAL' && a.gradient) {
|
|
123
|
+
a._fill = radialGradient(a.gradient.stops)
|
|
124
|
+
} else if (a.color) {
|
|
125
|
+
a._fill = { type: 'SOLID', color: hexToFigmaColor(a.color), opacity: a.opacity !== undefined ? a.opacity : 1 }
|
|
126
|
+
}
|
|
127
|
+
break
|
|
128
|
+
}
|
|
433
129
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
},
|
|
130
|
+
case 'create_icon': {
|
|
131
|
+
const svg = ICONS[a.icon]
|
|
132
|
+
if (!svg) throw new Error(`Unknown icon: ${a.icon}. Available: ${Object.keys(ICONS).join(', ')}`)
|
|
133
|
+
a._svg = svg.replace('currentColor', a.color || '#ffffff')
|
|
134
|
+
a._size = a.size || 24
|
|
135
|
+
break
|
|
136
|
+
}
|
|
442
137
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
start: args.start,
|
|
449
|
-
end: args.end,
|
|
450
|
-
fontSize: args.fontSize,
|
|
451
|
-
fontWeight: args.fontWeight,
|
|
452
|
-
color: args.color,
|
|
453
|
-
});
|
|
454
|
-
},
|
|
455
|
-
};
|
|
138
|
+
case 'set_auto_layout': {
|
|
139
|
+
if (a.padding !== undefined) { const p = snap(a.padding); a.paddingTop = p; a.paddingRight = p; a.paddingBottom = p; a.paddingLeft = p; delete a.padding }
|
|
140
|
+
;['paddingTop','paddingRight','paddingBottom','paddingLeft','gap'].forEach(k => { if (a[k] !== undefined) a[k] = snap(a[k]) })
|
|
141
|
+
break
|
|
142
|
+
}
|
|
456
143
|
|
|
457
|
-
|
|
144
|
+
case 'create_design_tokens': {
|
|
145
|
+
const colors = semanticColors(a.brandColor, 'dark')
|
|
146
|
+
const colorsLight = semanticColors(a.brandColor, 'light')
|
|
147
|
+
const scale = typeScale(16, 'major2')
|
|
148
|
+
a._darkColors = colors
|
|
149
|
+
a._lightColors = colorsLight
|
|
150
|
+
a._typeScale = scale
|
|
151
|
+
a._spacing = SPACING
|
|
152
|
+
a._radius = RADIUS
|
|
153
|
+
break
|
|
154
|
+
}
|
|
458
155
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
156
|
+
case 'fix_spacing': {
|
|
157
|
+
a._grid = a.grid || 8
|
|
158
|
+
break
|
|
159
|
+
}
|
|
463
160
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
settings: ['sidebar', 'header', 'form'],
|
|
470
|
-
auth: ['hero', 'form'],
|
|
471
|
-
blog: ['header', 'hero', 'cards', 'footer'],
|
|
472
|
-
portfolio: ['header', 'hero', 'cards', 'cta', 'footer'],
|
|
473
|
-
docs: ['sidebar', 'header', 'content'],
|
|
474
|
-
};
|
|
475
|
-
return defaults[pageType] || defaults.landing;
|
|
476
|
-
}
|
|
161
|
+
case 'lint_design': {
|
|
162
|
+
a._rules = a.rules || ['spacing', 'naming', 'colors', 'fonts', 'contrast', 'alignment', 'touch-targets']
|
|
163
|
+
break
|
|
164
|
+
}
|
|
165
|
+
}
|
|
477
166
|
|
|
478
|
-
|
|
479
|
-
return {
|
|
480
|
-
colors: design.generateSemanticColors(args.brandColor || '#6366f1'),
|
|
481
|
-
spacing: design.generateSpacingScale(8, 8),
|
|
482
|
-
fontSizes: design.generateTypeScale(16, 'major-third').sizes,
|
|
483
|
-
radii: design.generateRadiusScale(4),
|
|
484
|
-
shadows: design.generateElevation(),
|
|
485
|
-
};
|
|
167
|
+
return a
|
|
486
168
|
}
|