@mp3wizard/figma-console-mcp 1.14.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.
Files changed (201) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +816 -0
  3. package/dist/apps/design-system-dashboard/scoring/accessibility.d.ts +14 -0
  4. package/dist/apps/design-system-dashboard/scoring/accessibility.d.ts.map +1 -0
  5. package/dist/apps/design-system-dashboard/scoring/accessibility.js +278 -0
  6. package/dist/apps/design-system-dashboard/scoring/accessibility.js.map +1 -0
  7. package/dist/apps/design-system-dashboard/scoring/component-metadata.d.ts +29 -0
  8. package/dist/apps/design-system-dashboard/scoring/component-metadata.d.ts.map +1 -0
  9. package/dist/apps/design-system-dashboard/scoring/component-metadata.js +358 -0
  10. package/dist/apps/design-system-dashboard/scoring/component-metadata.js.map +1 -0
  11. package/dist/apps/design-system-dashboard/scoring/consistency.d.ts +14 -0
  12. package/dist/apps/design-system-dashboard/scoring/consistency.d.ts.map +1 -0
  13. package/dist/apps/design-system-dashboard/scoring/consistency.js +342 -0
  14. package/dist/apps/design-system-dashboard/scoring/consistency.js.map +1 -0
  15. package/dist/apps/design-system-dashboard/scoring/coverage.d.ts +14 -0
  16. package/dist/apps/design-system-dashboard/scoring/coverage.d.ts.map +1 -0
  17. package/dist/apps/design-system-dashboard/scoring/coverage.js +231 -0
  18. package/dist/apps/design-system-dashboard/scoring/coverage.js.map +1 -0
  19. package/dist/apps/design-system-dashboard/scoring/engine.d.ts +27 -0
  20. package/dist/apps/design-system-dashboard/scoring/engine.d.ts.map +1 -0
  21. package/dist/apps/design-system-dashboard/scoring/engine.js +93 -0
  22. package/dist/apps/design-system-dashboard/scoring/engine.js.map +1 -0
  23. package/dist/apps/design-system-dashboard/scoring/naming-semantics.d.ts +14 -0
  24. package/dist/apps/design-system-dashboard/scoring/naming-semantics.d.ts.map +1 -0
  25. package/dist/apps/design-system-dashboard/scoring/naming-semantics.js +309 -0
  26. package/dist/apps/design-system-dashboard/scoring/naming-semantics.js.map +1 -0
  27. package/dist/apps/design-system-dashboard/scoring/token-architecture.d.ts +14 -0
  28. package/dist/apps/design-system-dashboard/scoring/token-architecture.d.ts.map +1 -0
  29. package/dist/apps/design-system-dashboard/scoring/token-architecture.js +350 -0
  30. package/dist/apps/design-system-dashboard/scoring/token-architecture.js.map +1 -0
  31. package/dist/apps/design-system-dashboard/scoring/types.d.ts +89 -0
  32. package/dist/apps/design-system-dashboard/scoring/types.d.ts.map +1 -0
  33. package/dist/apps/design-system-dashboard/scoring/types.js +41 -0
  34. package/dist/apps/design-system-dashboard/scoring/types.js.map +1 -0
  35. package/dist/apps/design-system-dashboard/server.d.ts +24 -0
  36. package/dist/apps/design-system-dashboard/server.d.ts.map +1 -0
  37. package/dist/apps/design-system-dashboard/server.js +160 -0
  38. package/dist/apps/design-system-dashboard/server.js.map +1 -0
  39. package/dist/apps/token-browser/server.d.ts +26 -0
  40. package/dist/apps/token-browser/server.d.ts.map +1 -0
  41. package/dist/apps/token-browser/server.js +137 -0
  42. package/dist/apps/token-browser/server.js.map +1 -0
  43. package/dist/browser/base.d.ts +58 -0
  44. package/dist/browser/base.d.ts.map +1 -0
  45. package/dist/browser/base.js +6 -0
  46. package/dist/browser/base.js.map +1 -0
  47. package/dist/browser/local.d.ts +87 -0
  48. package/dist/browser/local.d.ts.map +1 -0
  49. package/dist/browser/local.js +318 -0
  50. package/dist/browser/local.js.map +1 -0
  51. package/dist/cloudflare/apps/design-system-dashboard/scoring/accessibility.js +277 -0
  52. package/dist/cloudflare/apps/design-system-dashboard/scoring/component-metadata.js +357 -0
  53. package/dist/cloudflare/apps/design-system-dashboard/scoring/consistency.js +341 -0
  54. package/dist/cloudflare/apps/design-system-dashboard/scoring/coverage.js +230 -0
  55. package/dist/cloudflare/apps/design-system-dashboard/scoring/engine.js +92 -0
  56. package/dist/cloudflare/apps/design-system-dashboard/scoring/naming-semantics.js +308 -0
  57. package/dist/cloudflare/apps/design-system-dashboard/scoring/token-architecture.js +349 -0
  58. package/dist/cloudflare/apps/design-system-dashboard/scoring/types.js +40 -0
  59. package/dist/cloudflare/apps/design-system-dashboard/server.js +159 -0
  60. package/dist/cloudflare/apps/token-browser/server.js +136 -0
  61. package/dist/cloudflare/browser/base.js +5 -0
  62. package/dist/cloudflare/browser/cloudflare.js +156 -0
  63. package/dist/cloudflare/browser-manager.js +157 -0
  64. package/dist/cloudflare/core/cloud-websocket-connector.js +267 -0
  65. package/dist/cloudflare/core/cloud-websocket-relay.js +199 -0
  66. package/dist/cloudflare/core/comment-tools.js +292 -0
  67. package/dist/cloudflare/core/config.js +161 -0
  68. package/dist/cloudflare/core/console-monitor.js +427 -0
  69. package/dist/cloudflare/core/design-code-tools.js +2504 -0
  70. package/dist/cloudflare/core/design-system-manifest.js +260 -0
  71. package/dist/cloudflare/core/design-system-tools.js +863 -0
  72. package/dist/cloudflare/core/enrichment/enrichment-service.js +272 -0
  73. package/dist/cloudflare/core/enrichment/index.js +7 -0
  74. package/dist/cloudflare/core/enrichment/relationship-mapper.js +351 -0
  75. package/dist/cloudflare/core/enrichment/style-resolver.js +326 -0
  76. package/dist/cloudflare/core/figma-api.js +409 -0
  77. package/dist/cloudflare/core/figma-connector.js +7 -0
  78. package/dist/cloudflare/core/figma-desktop-connector.js +1184 -0
  79. package/dist/cloudflare/core/figma-reconstruction-spec.js +402 -0
  80. package/dist/cloudflare/core/figma-style-extractor.js +311 -0
  81. package/dist/cloudflare/core/figma-tools.js +2947 -0
  82. package/dist/cloudflare/core/logger.js +53 -0
  83. package/dist/cloudflare/core/port-discovery.js +282 -0
  84. package/dist/cloudflare/core/snippet-injector.js +96 -0
  85. package/dist/cloudflare/core/types/design-code.js +4 -0
  86. package/dist/cloudflare/core/types/enriched.js +5 -0
  87. package/dist/cloudflare/core/types/index.js +4 -0
  88. package/dist/cloudflare/core/websocket-connector.js +256 -0
  89. package/dist/cloudflare/core/websocket-server.js +646 -0
  90. package/dist/cloudflare/core/write-tools.js +2091 -0
  91. package/dist/cloudflare/index.js +2899 -0
  92. package/dist/cloudflare/test-browser.js +88 -0
  93. package/dist/core/comment-tools.d.ts +11 -0
  94. package/dist/core/comment-tools.d.ts.map +1 -0
  95. package/dist/core/comment-tools.js +293 -0
  96. package/dist/core/comment-tools.js.map +1 -0
  97. package/dist/core/config.d.ts +17 -0
  98. package/dist/core/config.d.ts.map +1 -0
  99. package/dist/core/config.js +162 -0
  100. package/dist/core/config.js.map +1 -0
  101. package/dist/core/console-monitor.d.ts +82 -0
  102. package/dist/core/console-monitor.d.ts.map +1 -0
  103. package/dist/core/console-monitor.js +428 -0
  104. package/dist/core/console-monitor.js.map +1 -0
  105. package/dist/core/design-code-tools.d.ts +127 -0
  106. package/dist/core/design-code-tools.d.ts.map +1 -0
  107. package/dist/core/design-code-tools.js +2505 -0
  108. package/dist/core/design-code-tools.js.map +1 -0
  109. package/dist/core/design-system-manifest.d.ts +272 -0
  110. package/dist/core/design-system-manifest.d.ts.map +1 -0
  111. package/dist/core/design-system-manifest.js +261 -0
  112. package/dist/core/design-system-manifest.js.map +1 -0
  113. package/dist/core/design-system-tools.d.ts +17 -0
  114. package/dist/core/design-system-tools.d.ts.map +1 -0
  115. package/dist/core/design-system-tools.js +864 -0
  116. package/dist/core/design-system-tools.js.map +1 -0
  117. package/dist/core/enrichment/enrichment-service.d.ts +52 -0
  118. package/dist/core/enrichment/enrichment-service.d.ts.map +1 -0
  119. package/dist/core/enrichment/enrichment-service.js +273 -0
  120. package/dist/core/enrichment/enrichment-service.js.map +1 -0
  121. package/dist/core/enrichment/index.d.ts +8 -0
  122. package/dist/core/enrichment/index.d.ts.map +1 -0
  123. package/dist/core/enrichment/index.js +8 -0
  124. package/dist/core/enrichment/index.js.map +1 -0
  125. package/dist/core/enrichment/relationship-mapper.d.ts +106 -0
  126. package/dist/core/enrichment/relationship-mapper.d.ts.map +1 -0
  127. package/dist/core/enrichment/relationship-mapper.js +352 -0
  128. package/dist/core/enrichment/relationship-mapper.js.map +1 -0
  129. package/dist/core/enrichment/style-resolver.d.ts +80 -0
  130. package/dist/core/enrichment/style-resolver.d.ts.map +1 -0
  131. package/dist/core/enrichment/style-resolver.js +327 -0
  132. package/dist/core/enrichment/style-resolver.js.map +1 -0
  133. package/dist/core/figma-api.d.ts +201 -0
  134. package/dist/core/figma-api.d.ts.map +1 -0
  135. package/dist/core/figma-api.js +410 -0
  136. package/dist/core/figma-api.js.map +1 -0
  137. package/dist/core/figma-connector.d.ts +48 -0
  138. package/dist/core/figma-connector.d.ts.map +1 -0
  139. package/dist/core/figma-connector.js +8 -0
  140. package/dist/core/figma-connector.js.map +1 -0
  141. package/dist/core/figma-desktop-connector.d.ts +265 -0
  142. package/dist/core/figma-desktop-connector.d.ts.map +1 -0
  143. package/dist/core/figma-desktop-connector.js +1184 -0
  144. package/dist/core/figma-desktop-connector.js.map +1 -0
  145. package/dist/core/figma-reconstruction-spec.d.ts +166 -0
  146. package/dist/core/figma-reconstruction-spec.d.ts.map +1 -0
  147. package/dist/core/figma-reconstruction-spec.js +403 -0
  148. package/dist/core/figma-reconstruction-spec.js.map +1 -0
  149. package/dist/core/figma-style-extractor.d.ts +76 -0
  150. package/dist/core/figma-style-extractor.d.ts.map +1 -0
  151. package/dist/core/figma-style-extractor.js +312 -0
  152. package/dist/core/figma-style-extractor.js.map +1 -0
  153. package/dist/core/figma-tools.d.ts +23 -0
  154. package/dist/core/figma-tools.d.ts.map +1 -0
  155. package/dist/core/figma-tools.js +2948 -0
  156. package/dist/core/figma-tools.js.map +1 -0
  157. package/dist/core/logger.d.ts +22 -0
  158. package/dist/core/logger.d.ts.map +1 -0
  159. package/dist/core/logger.js +54 -0
  160. package/dist/core/logger.js.map +1 -0
  161. package/dist/core/port-discovery.d.ts +110 -0
  162. package/dist/core/port-discovery.d.ts.map +1 -0
  163. package/dist/core/port-discovery.js +283 -0
  164. package/dist/core/port-discovery.js.map +1 -0
  165. package/dist/core/snippet-injector.d.ts +24 -0
  166. package/dist/core/snippet-injector.d.ts.map +1 -0
  167. package/dist/core/snippet-injector.js +97 -0
  168. package/dist/core/snippet-injector.js.map +1 -0
  169. package/dist/core/types/design-code.d.ts +262 -0
  170. package/dist/core/types/design-code.d.ts.map +1 -0
  171. package/dist/core/types/design-code.js +5 -0
  172. package/dist/core/types/design-code.js.map +1 -0
  173. package/dist/core/types/enriched.d.ts +213 -0
  174. package/dist/core/types/enriched.d.ts.map +1 -0
  175. package/dist/core/types/enriched.js +6 -0
  176. package/dist/core/types/enriched.js.map +1 -0
  177. package/dist/core/types/index.d.ts +112 -0
  178. package/dist/core/types/index.d.ts.map +1 -0
  179. package/dist/core/types/index.js +5 -0
  180. package/dist/core/types/index.js.map +1 -0
  181. package/dist/core/websocket-connector.d.ts +55 -0
  182. package/dist/core/websocket-connector.d.ts.map +1 -0
  183. package/dist/core/websocket-connector.js +257 -0
  184. package/dist/core/websocket-connector.js.map +1 -0
  185. package/dist/core/websocket-server.d.ts +191 -0
  186. package/dist/core/websocket-server.d.ts.map +1 -0
  187. package/dist/core/websocket-server.js +647 -0
  188. package/dist/core/websocket-server.js.map +1 -0
  189. package/dist/core/write-tools.d.ts +7 -0
  190. package/dist/core/write-tools.d.ts.map +1 -0
  191. package/dist/core/write-tools.js +2092 -0
  192. package/dist/core/write-tools.js.map +1 -0
  193. package/dist/local.d.ts +84 -0
  194. package/dist/local.d.ts.map +1 -0
  195. package/dist/local.js +5039 -0
  196. package/dist/local.js.map +1 -0
  197. package/figma-desktop-bridge/README.md +313 -0
  198. package/figma-desktop-bridge/code.js +2818 -0
  199. package/figma-desktop-bridge/manifest.json +67 -0
  200. package/figma-desktop-bridge/ui.html +1236 -0
  201. package/package.json +87 -0
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Figma Style Extractor
3
+ *
4
+ * Extracts style information (colors, typography, spacing) from Figma files
5
+ * using the REST API /files endpoint. This provides an alternative to the
6
+ * Enterprise-only Variables API by parsing style data directly from nodes.
7
+ *
8
+ * Based on the approach used by Figma-Context-MCP
9
+ */
10
+ import { logger } from './logger';
11
+ export class FigmaStyleExtractor {
12
+ constructor() {
13
+ this.extractedVariables = new Map();
14
+ this.colorIndex = 0;
15
+ this.typographyIndex = 0;
16
+ this.spacingIndex = 0;
17
+ this.radiusIndex = 0;
18
+ }
19
+ /**
20
+ * Extract style "variables" from Figma file data
21
+ * This mimics what users would see as variables in Figma
22
+ */
23
+ async extractStylesFromFile(fileData) {
24
+ try {
25
+ logger.info('Extracting styles from Figma file data');
26
+ this.extractedVariables.clear();
27
+ this.colorIndex = 0;
28
+ this.typographyIndex = 0;
29
+ this.spacingIndex = 0;
30
+ this.radiusIndex = 0;
31
+ // Process the document tree
32
+ if (fileData.document) {
33
+ this.processNode(fileData.document);
34
+ }
35
+ // Also process components for more style data
36
+ if (fileData.components) {
37
+ Object.values(fileData.components).forEach((component) => {
38
+ if (component.node) {
39
+ this.processNode(component.node);
40
+ }
41
+ });
42
+ }
43
+ // Process styles if available
44
+ if (fileData.styles) {
45
+ this.processStyles(fileData.styles);
46
+ }
47
+ const variables = Array.from(this.extractedVariables.values());
48
+ logger.info({
49
+ colorCount: variables.filter(v => v.type === 'COLOR').length,
50
+ typographyCount: variables.filter(v => v.type === 'TYPOGRAPHY').length,
51
+ spacingCount: variables.filter(v => v.type === 'SPACING').length,
52
+ radiusCount: variables.filter(v => v.type === 'RADIUS').length,
53
+ totalCount: variables.length
54
+ }, 'Extracted style variables from file');
55
+ return variables;
56
+ }
57
+ catch (error) {
58
+ logger.error({ error }, 'Failed to extract styles from file');
59
+ throw error;
60
+ }
61
+ }
62
+ /**
63
+ * Process a single node and extract style information
64
+ */
65
+ processNode(node, depth = 0) {
66
+ if (!node || depth > 10)
67
+ return; // Limit depth to prevent infinite recursion
68
+ // Extract colors from fills
69
+ if (node.fills && Array.isArray(node.fills)) {
70
+ node.fills.forEach(fill => {
71
+ if (fill.type === 'SOLID' && fill.color && fill.visible !== false) {
72
+ this.extractColor(fill.color, fill.opacity, node);
73
+ }
74
+ });
75
+ }
76
+ // Extract colors from strokes
77
+ if (node.strokes && Array.isArray(node.strokes)) {
78
+ node.strokes.forEach(stroke => {
79
+ if (stroke.type === 'SOLID' && stroke.color && stroke.visible !== false) {
80
+ this.extractColor(stroke.color, stroke.opacity, node, 'stroke');
81
+ }
82
+ });
83
+ }
84
+ // Extract typography styles
85
+ if (node.type === 'TEXT' && node.style) {
86
+ this.extractTypography(node.style, node);
87
+ }
88
+ // Extract spacing from auto-layout
89
+ if (node.layoutMode) {
90
+ if (node.itemSpacing !== undefined && node.itemSpacing > 0) {
91
+ this.extractSpacing('spacing', node.itemSpacing, node);
92
+ }
93
+ if (node.paddingLeft !== undefined && node.paddingLeft > 0) {
94
+ this.extractSpacing('padding', node.paddingLeft, node);
95
+ }
96
+ }
97
+ // Extract corner radius
98
+ if (node.cornerRadius !== undefined && node.cornerRadius > 0) {
99
+ this.extractRadius(node.cornerRadius, node);
100
+ }
101
+ else if (node.rectangleCornerRadii && node.rectangleCornerRadii.length > 0) {
102
+ const uniqueRadii = [...new Set(node.rectangleCornerRadii)];
103
+ uniqueRadii.forEach(radius => {
104
+ if (radius > 0) {
105
+ this.extractRadius(radius, node);
106
+ }
107
+ });
108
+ }
109
+ // Process children recursively
110
+ if (node.children && Array.isArray(node.children)) {
111
+ node.children.forEach(child => {
112
+ this.processNode(child, depth + 1);
113
+ });
114
+ }
115
+ }
116
+ /**
117
+ * Extract color variable
118
+ */
119
+ extractColor(color, opacity = 1, node, type = 'fill') {
120
+ const r = Math.round(color.r * 255);
121
+ const g = Math.round(color.g * 255);
122
+ const b = Math.round(color.b * 255);
123
+ const a = opacity * (color.a || 1);
124
+ const hex = '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
125
+ const rgba = a < 1 ? `rgba(${r}, ${g}, ${b}, ${a})` : hex;
126
+ // Create a unique key based on the color value
127
+ const key = `color_${hex}_${a}`;
128
+ if (!this.extractedVariables.has(key)) {
129
+ // Generate a meaningful name based on the node
130
+ const category = this.inferColorCategory(node.name);
131
+ const name = this.generateColorName(category, type, this.colorIndex++);
132
+ this.extractedVariables.set(key, {
133
+ id: key,
134
+ name,
135
+ value: rgba,
136
+ type: 'COLOR',
137
+ category,
138
+ description: `Extracted from ${node.name || 'unnamed node'}`,
139
+ nodeId: node.id
140
+ });
141
+ }
142
+ }
143
+ /**
144
+ * Extract typography variable
145
+ */
146
+ extractTypography(style, node) {
147
+ const key = `typography_${style.fontFamily}_${style.fontSize}_${style.fontWeight}`;
148
+ if (!this.extractedVariables.has(key)) {
149
+ const name = this.generateTypographyName(node.name, this.typographyIndex++);
150
+ const value = [
151
+ `font-family: "${style.fontFamily || 'Inter'}"`,
152
+ style.fontSize ? `font-size: ${style.fontSize}px` : '',
153
+ style.fontWeight ? `font-weight: ${style.fontWeight}` : '',
154
+ style.lineHeightPx ? `line-height: ${style.lineHeightPx}px` : '',
155
+ style.letterSpacing ? `letter-spacing: ${style.letterSpacing}px` : ''
156
+ ].filter(Boolean).join(', ');
157
+ this.extractedVariables.set(key, {
158
+ id: key,
159
+ name,
160
+ value,
161
+ type: 'TYPOGRAPHY',
162
+ category: 'text',
163
+ description: `Extracted from ${node.name || 'unnamed text'}`,
164
+ nodeId: node.id
165
+ });
166
+ }
167
+ }
168
+ /**
169
+ * Extract spacing variable
170
+ */
171
+ extractSpacing(type, value, node) {
172
+ const key = `spacing_${type}_${value}`;
173
+ if (!this.extractedVariables.has(key)) {
174
+ const name = `${type}/${Math.round(value / 4) * 4 || value}`; // Round to nearest 4px
175
+ this.extractedVariables.set(key, {
176
+ id: key,
177
+ name,
178
+ value: `${value}px`,
179
+ type: 'SPACING',
180
+ category: type,
181
+ description: `Extracted from ${node.name || 'unnamed node'}`,
182
+ nodeId: node.id
183
+ });
184
+ }
185
+ }
186
+ /**
187
+ * Extract radius variable
188
+ */
189
+ extractRadius(value, node) {
190
+ const key = `radius_${value}`;
191
+ if (!this.extractedVariables.has(key)) {
192
+ const name = `radius/${this.categorizeRadius(value)}`;
193
+ this.extractedVariables.set(key, {
194
+ id: key,
195
+ name,
196
+ value: `${value}px`,
197
+ type: 'RADIUS',
198
+ category: 'border',
199
+ description: `Extracted from ${node.name || 'unnamed node'}`,
200
+ nodeId: node.id
201
+ });
202
+ }
203
+ }
204
+ /**
205
+ * Process Figma styles object
206
+ */
207
+ processStyles(styles) {
208
+ Object.entries(styles).forEach(([styleId, styleData]) => {
209
+ const { name, description, styleType } = styleData;
210
+ if (styleType === 'FILL' || styleType === 'TEXT') {
211
+ // These are named styles that could be considered variables
212
+ const variable = {
213
+ id: styleId,
214
+ name: name || styleId,
215
+ value: styleId, // We don't have the actual value here
216
+ type: styleType === 'FILL' ? 'COLOR' : 'TYPOGRAPHY',
217
+ description: description || `Style: ${name}`,
218
+ category: 'style'
219
+ };
220
+ this.extractedVariables.set(`style_${styleId}`, variable);
221
+ }
222
+ });
223
+ }
224
+ /**
225
+ * Helper to infer color category from node name
226
+ */
227
+ inferColorCategory(nodeName) {
228
+ if (!nodeName)
229
+ return 'color';
230
+ const name = nodeName.toLowerCase();
231
+ if (name.includes('background') || name.includes('bg'))
232
+ return 'background';
233
+ if (name.includes('text') || name.includes('label') || name.includes('title'))
234
+ return 'text';
235
+ if (name.includes('border') || name.includes('stroke'))
236
+ return 'border';
237
+ if (name.includes('primary') || name.includes('secondary') || name.includes('accent'))
238
+ return 'theme';
239
+ if (name.includes('success') || name.includes('error') || name.includes('warning'))
240
+ return 'semantic';
241
+ return 'color';
242
+ }
243
+ /**
244
+ * Generate a meaningful color name
245
+ */
246
+ generateColorName(category, type, index) {
247
+ const tier = index < 5 ? 'primary' : index < 10 ? 'secondary' : 'tertiary';
248
+ return `${category}/${tier}-${type}`;
249
+ }
250
+ /**
251
+ * Generate a meaningful typography name
252
+ */
253
+ generateTypographyName(nodeName, index) {
254
+ if (nodeName) {
255
+ const name = nodeName.toLowerCase();
256
+ if (name.includes('heading') || name.includes('h1') || name.includes('h2')) {
257
+ return `heading/${name.replace(/[^a-z0-9]/g, '-')}`;
258
+ }
259
+ if (name.includes('body') || name.includes('paragraph')) {
260
+ return `body/${name.replace(/[^a-z0-9]/g, '-')}`;
261
+ }
262
+ if (name.includes('caption') || name.includes('label')) {
263
+ return `caption/${name.replace(/[^a-z0-9]/g, '-')}`;
264
+ }
265
+ }
266
+ return `text/style-${index}`;
267
+ }
268
+ /**
269
+ * Categorize radius values
270
+ */
271
+ categorizeRadius(value) {
272
+ if (value === 0)
273
+ return 'none';
274
+ if (value <= 2)
275
+ return 'xs';
276
+ if (value <= 4)
277
+ return 'sm';
278
+ if (value <= 8)
279
+ return 'md';
280
+ if (value <= 16)
281
+ return 'lg';
282
+ if (value <= 24)
283
+ return 'xl';
284
+ return 'xxl';
285
+ }
286
+ /**
287
+ * Format the extracted variables for output
288
+ */
289
+ formatVariablesAsOutput(variables) {
290
+ // Group variables by type and category
291
+ const grouped = {};
292
+ variables.forEach(variable => {
293
+ const key = variable.name;
294
+ grouped[key] = variable.value;
295
+ });
296
+ // Add metadata about extraction method
297
+ grouped['_metadata'] = {
298
+ extractionMethod: 'REST_API_STYLES',
299
+ note: 'These are extracted style properties, not true Figma Variables (which require Enterprise)',
300
+ timestamp: new Date().toISOString(),
301
+ counts: {
302
+ colors: variables.filter(v => v.type === 'COLOR').length,
303
+ typography: variables.filter(v => v.type === 'TYPOGRAPHY').length,
304
+ spacing: variables.filter(v => v.type === 'SPACING').length,
305
+ radius: variables.filter(v => v.type === 'RADIUS').length,
306
+ total: variables.length
307
+ }
308
+ };
309
+ return grouped;
310
+ }
311
+ }