tellfigma 0.2.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,QAiDhD"}
@@ -0,0 +1,104 @@
1
+ // ============================================================
2
+ // MCP Prompts & Resources
3
+ // System prompt resource + design prompts
4
+ // ============================================================
5
+ import { SYSTEM_PROMPT } from './prompt.js';
6
+ export function registerPrompts(server) {
7
+ // -- Resource: System Prompt --
8
+ server.resource('figma-system-prompt', 'tellfigma://prompt', async (uri) => ({
9
+ contents: [
10
+ {
11
+ uri: uri.href,
12
+ mimeType: 'text/plain',
13
+ text: SYSTEM_PROMPT,
14
+ },
15
+ ],
16
+ }));
17
+ // -- Prompt: Figma Design Instructions --
18
+ server.prompt('figma-design', 'Instructions for designing in Figma. Attach this to your conversation for best results.', () => ({
19
+ messages: [
20
+ {
21
+ role: 'user',
22
+ content: {
23
+ type: 'text',
24
+ text: SYSTEM_PROMPT,
25
+ },
26
+ },
27
+ ],
28
+ }));
29
+ // -- Prompt: Design From Project --
30
+ server.prompt('design-from-project', 'Design Figma screens that match your existing codebase. The AI reads your project files first to extract colors, fonts, spacing, and component patterns.', () => ({
31
+ messages: [
32
+ {
33
+ role: 'user',
34
+ content: {
35
+ type: 'text',
36
+ text: DESIGN_FROM_PROJECT_PROMPT,
37
+ },
38
+ },
39
+ ],
40
+ }));
41
+ }
42
+ // ---- Design From Project Prompt ----
43
+ const DESIGN_FROM_PROJECT_PROMPT = `## Design From Project — Code-Aware Figma Design
44
+
45
+ You have access to both the user's project files (through the editor) AND Figma (through tellfigma). Use both together.
46
+
47
+ ### Before Creating Anything in Figma
48
+
49
+ Read the user's project files to extract their design system. Look for these files (in order of priority):
50
+
51
+ **Tailwind / CSS Config:**
52
+ - \`tailwind.config.ts\` or \`tailwind.config.js\` — colors, spacing, fonts, borderRadius, screens
53
+ - \`src/index.css\` or \`src/globals.css\` or \`app/globals.css\` — CSS variables, @theme, @layer base
54
+ - \`postcss.config.js\` — plugins that affect styling
55
+
56
+ **Theme / Design Tokens:**
57
+ - \`src/lib/theme.ts\` or \`src/theme/*\` — custom theme definitions
58
+ - \`src/styles/*\` — shared style files
59
+ - \`tokens.json\` or \`*.tokens.json\` — design token files
60
+ - \`.storybook/preview.ts\` — global decorators, theme config
61
+
62
+ **Component Patterns:**
63
+ - \`src/components/ui/*\` — UI primitives (buttons, inputs, cards)
64
+ - \`src/components/layouts/*\` — layout components (sidebar, header, page wrapper)
65
+ - \`package.json\` — check for UI libraries (shadcn, radix, chakra, mantine, mui, antd)
66
+
67
+ **Framework-Specific:**
68
+ - \`next.config.*\` — Next.js (check for fonts config)
69
+ - \`nuxt.config.*\` — Nuxt
70
+ - \`vite.config.*\` — Vite setup
71
+ - \`app/layout.tsx\` or \`src/app.tsx\` — root layout, font imports, providers
72
+
73
+ ### What to Extract
74
+
75
+ From these files, build a mental model of:
76
+ 1. **Color palette** — primary, secondary, destructive, muted, accent colors (exact hex/HSL values)
77
+ 2. **Typography** — font families, size scale, weight scale, line heights
78
+ 3. **Spacing** — base unit, spacing scale
79
+ 4. **Border radius** — radius scale values
80
+ 5. **Shadows** — elevation/shadow definitions
81
+ 6. **Breakpoints** — responsive widths
82
+ 7. **Component patterns** — how buttons, cards, inputs, modals are structured (padding, gaps, border patterns)
83
+
84
+ ### Then Design in Figma
85
+
86
+ Use the exact values from the project. Don't use tellfigma's default design system — use THEIRS. Match:
87
+ - Their exact hex colors (not generic Tailwind colors unless that's what they use)
88
+ - Their font family (not Inter, unless that's what they use)
89
+ - Their spacing scale
90
+ - Their border radius values
91
+ - Their shadow definitions
92
+ - Their component structure
93
+
94
+ ### Example Workflow
95
+
96
+ 1. User: "Design a settings page for my app"
97
+ 2. You: Read tailwind.config.ts → extract colors, fonts, radius
98
+ 3. You: Read src/components/ui/button.tsx → understand button patterns
99
+ 4. You: Read src/components/layouts/sidebar.tsx → understand layout
100
+ 5. You: Create Figma design using THEIR exact design tokens
101
+ 6. You: Screenshot and verify it looks consistent with their codebase
102
+
103
+ This way the Figma design IS the spec — it matches the code perfectly.`;
104
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,0BAA0B;AAC1B,0CAA0C;AAC1C,+DAA+D;AAG/D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,gCAAgC;IAChC,MAAM,CAAC,QAAQ,CACb,qBAAqB,EACrB,oBAAoB,EACpB,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACd,QAAQ,EAAE;YACR;gBACE,GAAG,EAAE,GAAG,CAAC,IAAI;gBACb,QAAQ,EAAE,YAAY;gBACtB,IAAI,EAAE,aAAa;aACpB;SACF;KACF,CAAC,CACH,CAAC;IAEF,0CAA0C;IAC1C,MAAM,CAAC,MAAM,CACX,cAAc,EACd,yFAAyF,EACzF,GAAG,EAAE,CAAC,CAAC;QACL,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,aAAa;iBACpB;aACF;SACF;KACF,CAAC,CACH,CAAC;IAEF,oCAAoC;IACpC,MAAM,CAAC,MAAM,CACX,qBAAqB,EACrB,0JAA0J,EAC1J,GAAG,EAAE,CAAC,CAAC;QACL,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,0BAA0B;iBACjC;aACF;SACF;KACF,CAAC,CACH,CAAC;AACJ,CAAC;AAED,uCAAuC;AAEvC,MAAM,0BAA0B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uEA4DoC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerTools(server: McpServer): void;
3
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,QAmiB9C"}
package/dist/tools.js ADDED
@@ -0,0 +1,457 @@
1
+ // ============================================================
2
+ // MCP Tools — All 16 Figma control tools
3
+ // Each tool is a thin wrapper around the figma.ts helpers
4
+ // ============================================================
5
+ import { z } from 'zod';
6
+ import { ensureConnected, executeFigmaCode, takeScreenshot, getPageInfo } from './figma.js';
7
+ export function registerTools(server) {
8
+ // -- Tool: Execute Figma Code --
9
+ server.tool('execute_figma_code', `Execute JavaScript code in the Figma browser tab. The \`figma\` global object gives full access to the Figma Plugin API.
10
+
11
+ Key APIs:
12
+ - figma.createFrame(), figma.createText(), figma.createRectangle(), figma.createComponent()
13
+ - figma.currentPage.selection, figma.currentPage.findAll(), figma.currentPage.findOne()
14
+ - figma.viewport.scrollAndZoomIntoView([node])
15
+ - figma.loadFontAsync({ family, style }) — MUST call before setting text
16
+ - node.fills, node.strokes, node.effects, node.cornerRadius
17
+ - node.layoutMode, node.itemSpacing, node.paddingTop/Right/Bottom/Left
18
+ - child.layoutSizingHorizontal = 'FILL' — MUST set AFTER appendChild()
19
+
20
+ Always wrap async code: (async () => { ... })()
21
+ RGB values are 0-1, not 0-255.
22
+ Always use blendMode: 'NORMAL' on DROP_SHADOW effects.
23
+ Font "Semi Bold" has a space (not "SemiBold") for Inter.`, {
24
+ code: z
25
+ .string()
26
+ .describe('JavaScript code to execute in the Figma browser context. The `figma` global is available.'),
27
+ }, async ({ code }) => {
28
+ const result = await executeFigmaCode(code);
29
+ return {
30
+ content: [{ type: 'text', text: result }],
31
+ };
32
+ });
33
+ // -- Tool: Take Screenshot --
34
+ server.tool('take_screenshot', 'Capture a screenshot of the current Figma browser tab. Use this after making visual changes to verify your work. Returns the screenshot as an image.', {}, async () => {
35
+ const { base64, width, height } = await takeScreenshot();
36
+ return {
37
+ content: [
38
+ {
39
+ type: 'image',
40
+ data: base64,
41
+ mimeType: 'image/png',
42
+ },
43
+ {
44
+ type: 'text',
45
+ text: `Screenshot captured (${width}x${height}px)`,
46
+ },
47
+ ],
48
+ };
49
+ });
50
+ // -- Tool: Get Page Context --
51
+ server.tool('get_page_context', 'Get information about the current Figma page — selected nodes, top-level frames, page name, and whether the figma global is available.', {}, async () => {
52
+ const info = await getPageInfo();
53
+ return {
54
+ content: [{ type: 'text', text: info }],
55
+ };
56
+ });
57
+ // -- Tool: Navigate --
58
+ server.tool('navigate', 'Navigate the Chrome browser to a URL. Useful for opening a specific Figma file.', {
59
+ url: z.string().url().describe('The URL to navigate to'),
60
+ }, async ({ url }) => {
61
+ const client = await ensureConnected();
62
+ await client.Page.navigate({ url });
63
+ await new Promise((r) => setTimeout(r, 2000));
64
+ return {
65
+ content: [{ type: 'text', text: `Navigated to ${url}` }],
66
+ };
67
+ });
68
+ // -- Tool: Click --
69
+ server.tool('click', 'Click at a specific position on the page. Use with caution — prefer execute_figma_code for most operations.', {
70
+ x: z.number().describe('X coordinate to click'),
71
+ y: z.number().describe('Y coordinate to click'),
72
+ }, async ({ x, y }) => {
73
+ const client = await ensureConnected();
74
+ await client.Input.dispatchMouseEvent({ type: 'mousePressed', x, y, button: 'left', clickCount: 1 });
75
+ await client.Input.dispatchMouseEvent({ type: 'mouseReleased', x, y, button: 'left', clickCount: 1 });
76
+ return {
77
+ content: [{ type: 'text', text: `Clicked at (${x}, ${y})` }],
78
+ };
79
+ });
80
+ // -- Tool: Get Snapshot (DOM/Accessibility) --
81
+ server.tool('get_snapshot', 'Get the accessibility tree / DOM snapshot of the current page. Useful for understanding UI structure and finding elements to click.', {}, async () => {
82
+ const client = await ensureConnected();
83
+ const { nodes } = await client.Accessibility.getFullAXTree();
84
+ const simplified = nodes.slice(0, 200).map((n) => ({
85
+ role: n.role?.value,
86
+ name: n.name?.value,
87
+ description: n.description?.value,
88
+ })).filter((n) => n.name || n.role);
89
+ return {
90
+ content: [
91
+ {
92
+ type: 'text',
93
+ text: JSON.stringify(simplified, null, 2),
94
+ },
95
+ ],
96
+ };
97
+ });
98
+ // -- Tool: Undo --
99
+ server.tool('undo', 'Undo the last action in Figma. Use this to roll back mistakes. Can be called multiple times to undo multiple steps.', {
100
+ steps: z.number().optional().default(1).describe('Number of undo steps (default: 1)'),
101
+ }, async ({ steps }) => {
102
+ const count = Math.min(steps, 50);
103
+ const result = await executeFigmaCode(`
104
+ try {
105
+ for (let i = 0; i < ${count}; i++) {
106
+ figma.undo();
107
+ }
108
+ "Undid ${count} step(s)"
109
+ } catch(e) {
110
+ "Undo may not be available via API: " + e.message
111
+ }
112
+ `);
113
+ return { content: [{ type: 'text', text: result }] };
114
+ });
115
+ // -- Tool: Redo --
116
+ server.tool('redo', 'Redo the last undone action in Figma.', {
117
+ steps: z.number().optional().default(1).describe('Number of redo steps (default: 1)'),
118
+ }, async ({ steps }) => {
119
+ const count = Math.min(steps, 50);
120
+ const result = await executeFigmaCode(`
121
+ try {
122
+ for (let i = 0; i < ${count}; i++) {
123
+ figma.redo();
124
+ }
125
+ "Redid ${count} step(s)"
126
+ } catch(e) {
127
+ "Redo may not be available via API: " + e.message
128
+ }
129
+ `);
130
+ return { content: [{ type: 'text', text: result }] };
131
+ });
132
+ // -- Tool: Select Nodes --
133
+ server.tool('select_nodes', 'Find and select nodes in the current Figma page by name or type. Returns info about matched nodes and selects them on the canvas.', {
134
+ query: z.string().optional().describe('Search by node name (case-insensitive substring match)'),
135
+ type: z.string().optional().describe('Filter by node type: FRAME, TEXT, RECTANGLE, COMPONENT, INSTANCE, GROUP, ELLIPSE, etc.'),
136
+ select: z.boolean().optional().default(true).describe('Whether to select the matched nodes on the canvas (default: true)'),
137
+ }, async ({ query, type, select }) => {
138
+ const conditions = [];
139
+ if (query)
140
+ conditions.push(`n.name.toLowerCase().includes(${JSON.stringify(query.toLowerCase())})`);
141
+ if (type)
142
+ conditions.push(`n.type === ${JSON.stringify(type.toUpperCase())}`);
143
+ if (conditions.length === 0)
144
+ conditions.push('true');
145
+ const filter = conditions.join(' && ');
146
+ const result = await executeFigmaCode(`
147
+ const matches = figma.currentPage.findAll(n => ${filter});
148
+ ${select ? 'figma.currentPage.selection = matches;' : ''}
149
+ ${select ? 'if (matches.length > 0) figma.viewport.scrollAndZoomIntoView(matches);' : ''}
150
+ JSON.stringify({
151
+ count: matches.length,
152
+ nodes: matches.slice(0, 50).map(n => ({
153
+ id: n.id, name: n.name, type: n.type,
154
+ width: 'width' in n ? Math.round(n.width) : null,
155
+ height: 'height' in n ? Math.round(n.height) : null,
156
+ x: 'x' in n ? Math.round(n.x) : null,
157
+ y: 'y' in n ? Math.round(n.y) : null,
158
+ }))
159
+ })
160
+ `);
161
+ return { content: [{ type: 'text', text: result }] };
162
+ });
163
+ // -- Tool: List Components --
164
+ server.tool('list_components', 'List all components and component sets on the current page, or search by name. Useful for finding reusable design elements.', {
165
+ query: z.string().optional().describe('Optional name filter (case-insensitive substring match)'),
166
+ }, async ({ query }) => {
167
+ const filter = query
168
+ ? `n => (n.type === 'COMPONENT' || n.type === 'COMPONENT_SET') && n.name.toLowerCase().includes(${JSON.stringify(query.toLowerCase())})`
169
+ : `n => n.type === 'COMPONENT' || n.type === 'COMPONENT_SET'`;
170
+ const result = await executeFigmaCode(`
171
+ const components = figma.currentPage.findAll(${filter});
172
+ JSON.stringify({
173
+ count: components.length,
174
+ components: components.slice(0, 100).map(c => ({
175
+ id: c.id,
176
+ name: c.name,
177
+ type: c.type,
178
+ width: Math.round(c.width),
179
+ height: Math.round(c.height),
180
+ description: 'description' in c ? c.description : '',
181
+ variantProperties: c.type === 'COMPONENT_SET' && 'variantGroupProperties' in c
182
+ ? Object.keys(c.variantGroupProperties) : [],
183
+ }))
184
+ })
185
+ `);
186
+ return { content: [{ type: 'text', text: result }] };
187
+ });
188
+ // -- Tool: Export Node --
189
+ server.tool('export_node', 'Export a Figma node (frame, component, etc.) as PNG or SVG. Returns the image data. If no nodeId is given, exports the current selection.', {
190
+ nodeId: z.string().optional().describe('The node ID to export (e.g., "123:456"). If omitted, exports the first selected node.'),
191
+ format: z.enum(['PNG', 'SVG', 'JPG', 'PDF']).optional().default('PNG').describe('Export format (default: PNG)'),
192
+ scale: z.number().optional().default(2).describe('Export scale for raster formats (default: 2x)'),
193
+ }, async ({ nodeId, format, scale }) => {
194
+ const result = await executeFigmaCode(`
195
+ let node;
196
+ if (${nodeId ? `true` : 'false'}) {
197
+ node = figma.getNodeById(${JSON.stringify(nodeId || '')});
198
+ } else {
199
+ node = figma.currentPage.selection[0];
200
+ }
201
+ if (!node) throw new Error('No node found. Select a node or provide a nodeId.');
202
+ if (!('exportAsync' in node)) throw new Error('This node type cannot be exported.');
203
+
204
+ const bytes = await node.exportAsync({
205
+ format: ${JSON.stringify(format)},
206
+ ${format === 'PNG' || format === 'JPG' ? `constraint: { type: 'SCALE', value: ${scale} },` : ''}
207
+ });
208
+ const base64 = figma.base64Encode(bytes);
209
+ JSON.stringify({ name: node.name, format: ${JSON.stringify(format)}, base64, byteLength: bytes.length });
210
+ `);
211
+ try {
212
+ const parsed = JSON.parse(result);
213
+ if (parsed.base64) {
214
+ const mimeType = format === 'SVG' ? 'image/svg+xml'
215
+ : format === 'PDF' ? 'application/pdf'
216
+ : format === 'JPG' ? 'image/jpeg'
217
+ : 'image/png';
218
+ return {
219
+ content: [
220
+ { type: 'image', data: parsed.base64, mimeType },
221
+ { type: 'text', text: `Exported "${parsed.name}" as ${format} (${parsed.byteLength} bytes)` },
222
+ ],
223
+ };
224
+ }
225
+ }
226
+ catch { }
227
+ return { content: [{ type: 'text', text: result }] };
228
+ });
229
+ // -- Tool: Read Selection (Deep Inspect) --
230
+ server.tool('read_selection', 'Deep inspection of the currently selected nodes. Returns fills, strokes, effects, fonts, layout properties, constraints, and more. Much richer than get_page_context.', {}, async () => {
231
+ const result = await executeFigmaCode(`
232
+ const sel = figma.currentPage.selection;
233
+ if (sel.length === 0) return JSON.stringify({ error: 'Nothing selected. Select a node first.' });
234
+
235
+ function inspectNode(n, depth) {
236
+ if (depth > 3) return { id: n.id, name: n.name, type: n.type, note: '(depth limit)' };
237
+ const info = {
238
+ id: n.id, name: n.name, type: n.type,
239
+ x: 'x' in n ? Math.round(n.x) : undefined,
240
+ y: 'y' in n ? Math.round(n.y) : undefined,
241
+ width: 'width' in n ? Math.round(n.width) : undefined,
242
+ height: 'height' in n ? Math.round(n.height) : undefined,
243
+ };
244
+
245
+ // Fills
246
+ if ('fills' in n && n.fills !== figma.mixed && Array.isArray(n.fills)) {
247
+ info.fills = n.fills.map(f => f.type === 'SOLID' ? {
248
+ type: 'SOLID',
249
+ hex: '#' + [f.color.r, f.color.g, f.color.b].map(c => Math.round(c*255).toString(16).padStart(2,'0')).join(''),
250
+ opacity: f.opacity !== undefined ? f.opacity : 1,
251
+ } : { type: f.type });
252
+ }
253
+
254
+ // Strokes
255
+ if ('strokes' in n && Array.isArray(n.strokes) && n.strokes.length > 0) {
256
+ info.strokes = n.strokes.map(s => s.type === 'SOLID' ? {
257
+ type: 'SOLID',
258
+ hex: '#' + [s.color.r, s.color.g, s.color.b].map(c => Math.round(c*255).toString(16).padStart(2,'0')).join(''),
259
+ } : { type: s.type });
260
+ info.strokeWeight = n.strokeWeight;
261
+ info.strokeAlign = n.strokeAlign;
262
+ }
263
+
264
+ // Effects
265
+ if ('effects' in n && Array.isArray(n.effects) && n.effects.length > 0) {
266
+ info.effects = n.effects.map(e => ({
267
+ type: e.type, visible: e.visible,
268
+ radius: e.radius,
269
+ offset: e.offset,
270
+ spread: e.spread,
271
+ }));
272
+ }
273
+
274
+ // Corner radius
275
+ if ('cornerRadius' in n) {
276
+ info.cornerRadius = n.cornerRadius !== figma.mixed ? n.cornerRadius : {
277
+ topLeft: n.topLeftRadius, topRight: n.topRightRadius,
278
+ bottomLeft: n.bottomLeftRadius, bottomRight: n.bottomRightRadius,
279
+ };
280
+ }
281
+
282
+ // Layout
283
+ if ('layoutMode' in n && n.layoutMode !== 'NONE') {
284
+ info.layout = {
285
+ mode: n.layoutMode,
286
+ primaryAxisSizing: n.primaryAxisSizingMode,
287
+ counterAxisSizing: n.counterAxisSizingMode,
288
+ primaryAxisAlign: n.primaryAxisAlignItems,
289
+ counterAxisAlign: n.counterAxisAlignItems,
290
+ padding: { top: n.paddingTop, right: n.paddingRight, bottom: n.paddingBottom, left: n.paddingLeft },
291
+ itemSpacing: n.itemSpacing,
292
+ };
293
+ }
294
+
295
+ // Layout child properties
296
+ if ('layoutSizingHorizontal' in n) {
297
+ info.layoutSizing = { horizontal: n.layoutSizingHorizontal, vertical: n.layoutSizingVertical };
298
+ }
299
+
300
+ // Text properties
301
+ if (n.type === 'TEXT') {
302
+ info.text = {
303
+ characters: n.characters.slice(0, 200),
304
+ fontSize: n.fontSize !== figma.mixed ? n.fontSize : 'mixed',
305
+ fontName: n.fontName !== figma.mixed ? n.fontName : 'mixed',
306
+ textAlignH: n.textAlignHorizontal,
307
+ textAlignV: n.textAlignVertical,
308
+ textAutoResize: n.textAutoResize,
309
+ lineHeight: n.lineHeight !== figma.mixed ? n.lineHeight : 'mixed',
310
+ };
311
+ }
312
+
313
+ // Opacity
314
+ if ('opacity' in n && n.opacity !== 1) info.opacity = n.opacity;
315
+
316
+ // Children (limited depth)
317
+ if ('children' in n && n.children.length > 0) {
318
+ info.childCount = n.children.length;
319
+ info.children = n.children.slice(0, 20).map(c => inspectNode(c, depth + 1));
320
+ }
321
+
322
+ return info;
323
+ }
324
+
325
+ JSON.stringify(sel.map(n => inspectNode(n, 0)), null, 2);
326
+ `);
327
+ return { content: [{ type: 'text', text: result }] };
328
+ });
329
+ // -- Tool: Get Variables (Design Tokens) --
330
+ server.tool('get_variables', 'List Figma variables (design tokens) in the current file — colors, numbers, strings, booleans. Includes collection and mode information.', {
331
+ collectionName: z.string().optional().describe('Filter by collection name (case-insensitive substring match)'),
332
+ }, async ({ collectionName }) => {
333
+ const result = await executeFigmaCode(`
334
+ const collections = await figma.variables.getLocalVariableCollectionsAsync();
335
+ const variables = await figma.variables.getLocalVariablesAsync();
336
+
337
+ const filtered = ${collectionName ? `collections.filter(c => c.name.toLowerCase().includes(${JSON.stringify(collectionName.toLowerCase())}))` : 'collections'};
338
+ const collectionIds = new Set(filtered.map(c => c.id));
339
+
340
+ const output = filtered.map(col => ({
341
+ id: col.id,
342
+ name: col.name,
343
+ modes: col.modes.map(m => ({ id: m.modeId, name: m.name })),
344
+ variables: variables
345
+ .filter(v => v.variableCollectionId === col.id)
346
+ .slice(0, 100)
347
+ .map(v => {
348
+ const firstModeId = col.modes[0]?.modeId;
349
+ const value = firstModeId ? v.valuesByMode[firstModeId] : undefined;
350
+ let resolvedValue = value;
351
+ if (value && typeof value === 'object' && 'r' in value) {
352
+ resolvedValue = {
353
+ r: Math.round(value.r * 255),
354
+ g: Math.round(value.g * 255),
355
+ b: Math.round(value.b * 255),
356
+ a: value.a !== undefined ? value.a : 1,
357
+ hex: '#' + [value.r, value.g, value.b].map(c => Math.round(c*255).toString(16).padStart(2,'0')).join(''),
358
+ };
359
+ }
360
+ return {
361
+ id: v.id,
362
+ name: v.name,
363
+ type: v.resolvedType,
364
+ value: resolvedValue,
365
+ };
366
+ })
367
+ }));
368
+ JSON.stringify(output, null, 2);
369
+ `);
370
+ return { content: [{ type: 'text', text: result }] };
371
+ });
372
+ // -- Tool: Duplicate Node --
373
+ server.tool('duplicate_node', 'Duplicate/clone a Figma node. If no nodeId is given, duplicates the first selected node. Returns the new node info.', {
374
+ nodeId: z.string().optional().describe('The node ID to duplicate. Omit to duplicate the first selected node.'),
375
+ offsetX: z.number().optional().default(20).describe('Horizontal offset for the clone (default: 20px)'),
376
+ offsetY: z.number().optional().default(20).describe('Vertical offset for the clone (default: 20px)'),
377
+ count: z.number().optional().default(1).describe('Number of duplicates to create (default: 1)'),
378
+ }, async ({ nodeId, offsetX, offsetY, count }) => {
379
+ const cloneCount = Math.min(count, 50);
380
+ const result = await executeFigmaCode(`
381
+ let node;
382
+ if (${nodeId ? 'true' : 'false'}) {
383
+ node = figma.getNodeById(${JSON.stringify(nodeId || '')});
384
+ } else {
385
+ node = figma.currentPage.selection[0];
386
+ }
387
+ if (!node) throw new Error('No node found. Select a node or provide a nodeId.');
388
+ if (!('clone' in node)) throw new Error('This node type cannot be cloned.');
389
+
390
+ const clones = [];
391
+ for (let i = 0; i < ${cloneCount}; i++) {
392
+ const clone = node.clone();
393
+ if ('x' in clone && 'x' in node) {
394
+ clone.x = node.x + ${offsetX} * (i + 1);
395
+ clone.y = node.y + ${offsetY} * (i + 1);
396
+ }
397
+ clones.push({ id: clone.id, name: clone.name, type: clone.type });
398
+ }
399
+ figma.currentPage.selection = clones.map(c => figma.getNodeById(c.id)).filter(Boolean);
400
+ figma.viewport.scrollAndZoomIntoView(figma.currentPage.selection);
401
+ JSON.stringify({ duplicated: node.name, clones });
402
+ `);
403
+ return { content: [{ type: 'text', text: result }] };
404
+ });
405
+ // -- Tool: Get Styles --
406
+ server.tool('get_styles', 'List all local styles (colors, text styles, effects, grids) in the current file. Useful for understanding the design system.', {}, async () => {
407
+ const result = await executeFigmaCode(`
408
+ const paintStyles = figma.getLocalPaintStyles().map(s => ({
409
+ id: s.id, name: s.name, type: 'PAINT',
410
+ paints: s.paints.map(p => p.type === 'SOLID' ? { type: 'SOLID', r: Math.round(p.color.r*255), g: Math.round(p.color.g*255), b: Math.round(p.color.b*255), a: p.opacity } : { type: p.type })
411
+ }));
412
+ const textStyles = figma.getLocalTextStyles().map(s => ({
413
+ id: s.id, name: s.name, type: 'TEXT',
414
+ fontFamily: s.fontName.family, fontStyle: s.fontName.style, fontSize: s.fontSize,
415
+ lineHeight: s.lineHeight, letterSpacing: s.letterSpacing,
416
+ }));
417
+ const effectStyles = figma.getLocalEffectStyles().map(s => ({
418
+ id: s.id, name: s.name, type: 'EFFECT',
419
+ effects: s.effects.map(e => ({ type: e.type, visible: e.visible }))
420
+ }));
421
+ JSON.stringify({ paintStyles, textStyles, effectStyles, total: paintStyles.length + textStyles.length + effectStyles.length });
422
+ `);
423
+ return { content: [{ type: 'text', text: result }] };
424
+ });
425
+ // -- Tool: Zoom to Selection --
426
+ server.tool('zoom_to', 'Zoom the viewport to fit specific nodes, the current selection, or the entire page.', {
427
+ target: z.enum(['selection', 'all', 'nodeId']).optional().default('selection').describe('What to zoom to'),
428
+ nodeId: z.string().optional().describe('Node ID to zoom to (when target is "nodeId")'),
429
+ }, async ({ target, nodeId }) => {
430
+ let code = '';
431
+ if (target === 'selection') {
432
+ code = `
433
+ const sel = figma.currentPage.selection;
434
+ if (sel.length === 0) return "Nothing selected";
435
+ figma.viewport.scrollAndZoomIntoView(sel);
436
+ "Zoomed to " + sel.length + " selected node(s)";
437
+ `;
438
+ }
439
+ else if (target === 'all') {
440
+ code = `
441
+ figma.viewport.scrollAndZoomIntoView(figma.currentPage.children);
442
+ "Zoomed to fit all " + figma.currentPage.children.length + " top-level nodes";
443
+ `;
444
+ }
445
+ else {
446
+ code = `
447
+ const node = figma.getNodeById(${JSON.stringify(nodeId || '')});
448
+ if (!node) return "Node not found: ${nodeId}";
449
+ figma.viewport.scrollAndZoomIntoView([node]);
450
+ "Zoomed to " + node.name;
451
+ `;
452
+ }
453
+ const result = await executeFigmaCode(code);
454
+ return { content: [{ type: 'text', text: result }] };
455
+ });
456
+ }
457
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,yCAAyC;AACzC,0DAA0D;AAC1D,+DAA+D;AAG/D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE5F,MAAM,UAAU,aAAa,CAAC,MAAiB;IAE7C,iCAAiC;IACjC,MAAM,CAAC,IAAI,CACT,oBAAoB,EACpB;;;;;;;;;;;;;;yDAcqD,EACrD;QACE,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,CACP,2FAA2F,CAC5F;KACJ,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC5C,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SAC1C,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,8BAA8B;IAC9B,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,sJAAsJ,EACtJ,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,cAAc,EAAE,CAAC;QACzD,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,MAAM;oBACZ,QAAQ,EAAE,WAAW;iBACtB;gBACD;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,wBAAwB,KAAK,IAAI,MAAM,KAAK;iBACnD;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,+BAA+B;IAC/B,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,wIAAwI,EACxI,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,IAAI,GAAG,MAAM,WAAW,EAAE,CAAC;QACjC,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SACxC,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,uBAAuB;IACvB,MAAM,CAAC,IAAI,CACT,UAAU,EACV,iFAAiF,EACjF;QACE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;KACzD,EACD,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QAChB,MAAM,MAAM,GAAG,MAAM,eAAe,EAAE,CAAC;QACvC,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACpC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9C,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,EAAE,EAAE,CAAC;SACzD,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,oBAAoB;IACpB,MAAM,CAAC,IAAI,CACT,OAAO,EACP,6GAA6G,EAC7G;QACE,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;QAC/C,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;KAChD,EACD,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE;QACjB,MAAM,MAAM,GAAG,MAAM,eAAe,EAAE,CAAC;QACvC,MAAM,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;QACrG,MAAM,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;QACtG,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;SAC7D,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,+CAA+C;IAC/C,MAAM,CAAC,IAAI,CACT,cAAc,EACd,qIAAqI,EACrI,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,MAAM,GAAG,MAAM,eAAe,EAAE,CAAC;QACvC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,CAAC;QAE7D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YACtD,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK;YACnB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK;YACnB,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK;SAClC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAEzC,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;iBAC1C;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,mBAAmB;IACnB,MAAM,CAAC,IAAI,CACT,MAAM,EACN,qHAAqH,EACrH;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mCAAmC,CAAC;KACtF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;;gCAEZ,KAAK;;;mBAGlB,KAAK;;;;OAIjB,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC,CACF,CAAC;IAEF,mBAAmB;IACnB,MAAM,CAAC,IAAI,CACT,MAAM,EACN,uCAAuC,EACvC;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mCAAmC,CAAC;KACtF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;;gCAEZ,KAAK;;;mBAGlB,KAAK;;;;OAIjB,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC,CACF,CAAC;IAEF,2BAA2B;IAC3B,MAAM,CAAC,IAAI,CACT,cAAc,EACd,mIAAmI,EACnI;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;QAC/F,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wFAAwF,CAAC;QAC9H,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,mEAAmE,CAAC;KAC3H,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QAChC,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,IAAI,KAAK;YAAE,UAAU,CAAC,IAAI,CAAC,iCAAiC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;QACpG,IAAI,IAAI;YAAE,UAAU,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9E,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAErD,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;yDACa,MAAM;UACrD,MAAM,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,CAAC,EAAE;UACtD,MAAM,CAAC,CAAC,CAAC,wEAAwE,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;OAWzF,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC,CACF,CAAC;IAEF,8BAA8B;IAC9B,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,6HAA6H,EAC7H;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yDAAyD,CAAC;KACjG,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAClB,MAAM,MAAM,GAAG,KAAK;YAClB,CAAC,CAAC,gGAAgG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,GAAG;YACxI,CAAC,CAAC,2DAA2D,CAAC;QAEhE,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;uDACW,MAAM;;;;;;;;;;;;;;OActD,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC,CACF,CAAC;IAEF,0BAA0B;IAC1B,MAAM,CAAC,IAAI,CACT,aAAa,EACb,2IAA2I,EAC3I;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uFAAuF,CAAC;QAC/H,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,8BAA8B,CAAC;QAC/G,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,+CAA+C,CAAC;KAClG,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE;QAClC,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;;cAE9B,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;qCACF,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,EAAE,CAAC;;;;;;;;oBAQ7C,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;YAC9B,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,uCAAuC,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE;;;oDAGrD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;OACnE,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAClC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,MAAM,QAAQ,GAAG,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,eAAe;oBACjD,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,iBAAiB;wBACtC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,YAAY;4BACjC,CAAC,CAAC,WAAW,CAAC;gBAChB,OAAO;oBACL,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,OAAgB,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE;wBACzD,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,aAAa,MAAM,CAAC,IAAI,QAAQ,MAAM,KAAK,MAAM,CAAC,UAAU,SAAS,EAAE;qBACvG;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;QAEV,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC,CACF,CAAC;IAEF,4CAA4C;IAC5C,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,uKAAuK,EACvK,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+FrC,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC,CACF,CAAC;IAEF,4CAA4C;IAC5C,MAAM,CAAC,IAAI,CACT,eAAe,EACf,0IAA0I,EAC1I;QACE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8DAA8D,CAAC;KAC/G,EACD,KAAK,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;;;;2BAIjB,cAAc,CAAC,CAAC,CAAC,yDAAyD,IAAI,CAAC,SAAS,CAAC,cAAe,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgC/J,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC,CACF,CAAC;IAEF,6BAA6B;IAC7B,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,qHAAqH,EACrH;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sEAAsE,CAAC;QAC9G,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,iDAAiD,CAAC;QACtG,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,+CAA+C,CAAC;QACpG,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,6CAA6C,CAAC;KAChG,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;QAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;;cAE9B,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;qCACF,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,EAAE,CAAC;;;;;;;;8BAQnC,UAAU;;;iCAGP,OAAO;iCACP,OAAO;;;;;;;OAOjC,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC,CACF,CAAC;IAEF,yBAAyB;IACzB,MAAM,CAAC,IAAI,CACT,YAAY,EACZ,8HAA8H,EAC9H,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;;;;;;;;;;;;;;;OAerC,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC,CACF,CAAC;IAEF,gCAAgC;IAChC,MAAM,CAAC,IAAI,CACT,SAAS,EACT,qFAAqF,EACrF;QACE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAC1G,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;KACvF,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QAC3B,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,IAAI,GAAG;;;;;SAKN,CAAC;QACJ,CAAC;aAAM,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YAC5B,IAAI,GAAG;;;SAGN,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,IAAI,GAAG;2CAC4B,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,EAAE,CAAC;+CACxB,MAAM;;;SAG5C,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC5C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC,CACF,CAAC;AACJ,CAAC"}