@obra-studio/figma-console-mcp 1.32.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 (354) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +879 -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/core/accessibility-tools.d.ts +21 -0
  52. package/dist/core/accessibility-tools.d.ts.map +1 -0
  53. package/dist/core/accessibility-tools.js +307 -0
  54. package/dist/core/accessibility-tools.js.map +1 -0
  55. package/dist/core/annotation-tools.d.ts +14 -0
  56. package/dist/core/annotation-tools.d.ts.map +1 -0
  57. package/dist/core/annotation-tools.js +231 -0
  58. package/dist/core/annotation-tools.js.map +1 -0
  59. package/dist/core/autodocs-tools.d.ts +7 -0
  60. package/dist/core/autodocs-tools.d.ts.map +1 -0
  61. package/dist/core/autodocs-tools.js +195 -0
  62. package/dist/core/autodocs-tools.js.map +1 -0
  63. package/dist/core/comment-tools.d.ts +11 -0
  64. package/dist/core/comment-tools.d.ts.map +1 -0
  65. package/dist/core/comment-tools.js +293 -0
  66. package/dist/core/comment-tools.js.map +1 -0
  67. package/dist/core/config.d.ts +17 -0
  68. package/dist/core/config.d.ts.map +1 -0
  69. package/dist/core/config.js +154 -0
  70. package/dist/core/config.js.map +1 -0
  71. package/dist/core/console-monitor.d.ts +82 -0
  72. package/dist/core/console-monitor.d.ts.map +1 -0
  73. package/dist/core/console-monitor.js +428 -0
  74. package/dist/core/console-monitor.js.map +1 -0
  75. package/dist/core/deep-component-tools.d.ts +14 -0
  76. package/dist/core/deep-component-tools.d.ts.map +1 -0
  77. package/dist/core/deep-component-tools.js +129 -0
  78. package/dist/core/deep-component-tools.js.map +1 -0
  79. package/dist/core/design-code-tools.d.ts +116 -0
  80. package/dist/core/design-code-tools.d.ts.map +1 -0
  81. package/dist/core/design-code-tools.js +2751 -0
  82. package/dist/core/design-code-tools.js.map +1 -0
  83. package/dist/core/design-system-manifest.d.ts +272 -0
  84. package/dist/core/design-system-manifest.d.ts.map +1 -0
  85. package/dist/core/design-system-manifest.js +261 -0
  86. package/dist/core/design-system-manifest.js.map +1 -0
  87. package/dist/core/design-system-tools.d.ts +67 -0
  88. package/dist/core/design-system-tools.d.ts.map +1 -0
  89. package/dist/core/design-system-tools.js +874 -0
  90. package/dist/core/design-system-tools.js.map +1 -0
  91. package/dist/core/diagnose-tool.d.ts +33 -0
  92. package/dist/core/diagnose-tool.d.ts.map +1 -0
  93. package/dist/core/diagnose-tool.js +97 -0
  94. package/dist/core/diagnose-tool.js.map +1 -0
  95. package/dist/core/diff/changelog-formatter.d.ts +35 -0
  96. package/dist/core/diff/changelog-formatter.d.ts.map +1 -0
  97. package/dist/core/diff/changelog-formatter.js +276 -0
  98. package/dist/core/diff/changelog-formatter.js.map +1 -0
  99. package/dist/core/diff/diff-engine.d.ts +127 -0
  100. package/dist/core/diff/diff-engine.d.ts.map +1 -0
  101. package/dist/core/diff/diff-engine.js +335 -0
  102. package/dist/core/diff/diff-engine.js.map +1 -0
  103. package/dist/core/diff/property-compare.d.ts +19 -0
  104. package/dist/core/diff/property-compare.d.ts.map +1 -0
  105. package/dist/core/diff/property-compare.js +37 -0
  106. package/dist/core/diff/property-compare.js.map +1 -0
  107. package/dist/core/diff/version-cache.d.ts +40 -0
  108. package/dist/core/diff/version-cache.d.ts.map +1 -0
  109. package/dist/core/diff/version-cache.js +75 -0
  110. package/dist/core/diff/version-cache.js.map +1 -0
  111. package/dist/core/enrichment/enrichment-service.d.ts +52 -0
  112. package/dist/core/enrichment/enrichment-service.d.ts.map +1 -0
  113. package/dist/core/enrichment/enrichment-service.js +369 -0
  114. package/dist/core/enrichment/enrichment-service.js.map +1 -0
  115. package/dist/core/enrichment/index.d.ts +8 -0
  116. package/dist/core/enrichment/index.d.ts.map +1 -0
  117. package/dist/core/enrichment/index.js +8 -0
  118. package/dist/core/enrichment/index.js.map +1 -0
  119. package/dist/core/enrichment/relationship-mapper.d.ts +106 -0
  120. package/dist/core/enrichment/relationship-mapper.d.ts.map +1 -0
  121. package/dist/core/enrichment/relationship-mapper.js +352 -0
  122. package/dist/core/enrichment/relationship-mapper.js.map +1 -0
  123. package/dist/core/enrichment/style-resolver.d.ts +80 -0
  124. package/dist/core/enrichment/style-resolver.d.ts.map +1 -0
  125. package/dist/core/enrichment/style-resolver.js +327 -0
  126. package/dist/core/enrichment/style-resolver.js.map +1 -0
  127. package/dist/core/figjam-tools.d.ts +8 -0
  128. package/dist/core/figjam-tools.d.ts.map +1 -0
  129. package/dist/core/figjam-tools.js +548 -0
  130. package/dist/core/figjam-tools.js.map +1 -0
  131. package/dist/core/figma-api.d.ts +245 -0
  132. package/dist/core/figma-api.d.ts.map +1 -0
  133. package/dist/core/figma-api.js +446 -0
  134. package/dist/core/figma-api.js.map +1 -0
  135. package/dist/core/figma-connector.d.ts +180 -0
  136. package/dist/core/figma-connector.d.ts.map +1 -0
  137. package/dist/core/figma-connector.js +8 -0
  138. package/dist/core/figma-connector.js.map +1 -0
  139. package/dist/core/figma-desktop-connector.d.ts +312 -0
  140. package/dist/core/figma-desktop-connector.d.ts.map +1 -0
  141. package/dist/core/figma-desktop-connector.js +1298 -0
  142. package/dist/core/figma-desktop-connector.js.map +1 -0
  143. package/dist/core/figma-reconstruction-spec.d.ts +166 -0
  144. package/dist/core/figma-reconstruction-spec.d.ts.map +1 -0
  145. package/dist/core/figma-reconstruction-spec.js +403 -0
  146. package/dist/core/figma-reconstruction-spec.js.map +1 -0
  147. package/dist/core/figma-style-extractor.d.ts +76 -0
  148. package/dist/core/figma-style-extractor.d.ts.map +1 -0
  149. package/dist/core/figma-style-extractor.js +312 -0
  150. package/dist/core/figma-style-extractor.js.map +1 -0
  151. package/dist/core/figma-tools.d.ts +22 -0
  152. package/dist/core/figma-tools.d.ts.map +1 -0
  153. package/dist/core/figma-tools.js +3187 -0
  154. package/dist/core/figma-tools.js.map +1 -0
  155. package/dist/core/identity.d.ts +41 -0
  156. package/dist/core/identity.d.ts.map +1 -0
  157. package/dist/core/identity.js +97 -0
  158. package/dist/core/identity.js.map +1 -0
  159. package/dist/core/library-tools.d.ts +17 -0
  160. package/dist/core/library-tools.d.ts.map +1 -0
  161. package/dist/core/library-tools.js +581 -0
  162. package/dist/core/library-tools.js.map +1 -0
  163. package/dist/core/logger.d.ts +22 -0
  164. package/dist/core/logger.d.ts.map +1 -0
  165. package/dist/core/logger.js +54 -0
  166. package/dist/core/logger.js.map +1 -0
  167. package/dist/core/port-discovery.d.ts +171 -0
  168. package/dist/core/port-discovery.d.ts.map +1 -0
  169. package/dist/core/port-discovery.js +563 -0
  170. package/dist/core/port-discovery.js.map +1 -0
  171. package/dist/core/resolve-package-root.d.ts +2 -0
  172. package/dist/core/resolve-package-root.d.ts.map +1 -0
  173. package/dist/core/resolve-package-root.js +12 -0
  174. package/dist/core/resolve-package-root.js.map +1 -0
  175. package/dist/core/slides-tools.d.ts +8 -0
  176. package/dist/core/slides-tools.d.ts.map +1 -0
  177. package/dist/core/slides-tools.js +715 -0
  178. package/dist/core/slides-tools.js.map +1 -0
  179. package/dist/core/snippet-injector.d.ts +24 -0
  180. package/dist/core/snippet-injector.d.ts.map +1 -0
  181. package/dist/core/snippet-injector.js +97 -0
  182. package/dist/core/snippet-injector.js.map +1 -0
  183. package/dist/core/tokens/alias-resolver.d.ts +55 -0
  184. package/dist/core/tokens/alias-resolver.d.ts.map +1 -0
  185. package/dist/core/tokens/alias-resolver.js +136 -0
  186. package/dist/core/tokens/alias-resolver.js.map +1 -0
  187. package/dist/core/tokens/config.d.ts +87 -0
  188. package/dist/core/tokens/config.d.ts.map +1 -0
  189. package/dist/core/tokens/config.js +285 -0
  190. package/dist/core/tokens/config.js.map +1 -0
  191. package/dist/core/tokens/figma-converter.d.ts +81 -0
  192. package/dist/core/tokens/figma-converter.d.ts.map +1 -0
  193. package/dist/core/tokens/figma-converter.js +196 -0
  194. package/dist/core/tokens/figma-converter.js.map +1 -0
  195. package/dist/core/tokens/formatters/css-vars.d.ts +24 -0
  196. package/dist/core/tokens/formatters/css-vars.d.ts.map +1 -0
  197. package/dist/core/tokens/formatters/css-vars.js +330 -0
  198. package/dist/core/tokens/formatters/css-vars.js.map +1 -0
  199. package/dist/core/tokens/formatters/dtcg.d.ts +28 -0
  200. package/dist/core/tokens/formatters/dtcg.d.ts.map +1 -0
  201. package/dist/core/tokens/formatters/dtcg.js +301 -0
  202. package/dist/core/tokens/formatters/dtcg.js.map +1 -0
  203. package/dist/core/tokens/formatters/index.d.ts +30 -0
  204. package/dist/core/tokens/formatters/index.d.ts.map +1 -0
  205. package/dist/core/tokens/formatters/index.js +46 -0
  206. package/dist/core/tokens/formatters/index.js.map +1 -0
  207. package/dist/core/tokens/formatters/json.d.ts +37 -0
  208. package/dist/core/tokens/formatters/json.d.ts.map +1 -0
  209. package/dist/core/tokens/formatters/json.js +188 -0
  210. package/dist/core/tokens/formatters/json.js.map +1 -0
  211. package/dist/core/tokens/formatters/less.d.ts +4 -0
  212. package/dist/core/tokens/formatters/less.d.ts.map +1 -0
  213. package/dist/core/tokens/formatters/less.js +5 -0
  214. package/dist/core/tokens/formatters/less.js.map +1 -0
  215. package/dist/core/tokens/formatters/scss.d.ts +26 -0
  216. package/dist/core/tokens/formatters/scss.d.ts.map +1 -0
  217. package/dist/core/tokens/formatters/scss.js +253 -0
  218. package/dist/core/tokens/formatters/scss.js.map +1 -0
  219. package/dist/core/tokens/formatters/stubs.d.ts +9 -0
  220. package/dist/core/tokens/formatters/stubs.d.ts.map +1 -0
  221. package/dist/core/tokens/formatters/stubs.js +14 -0
  222. package/dist/core/tokens/formatters/stubs.js.map +1 -0
  223. package/dist/core/tokens/formatters/style-dictionary-v3.d.ts +45 -0
  224. package/dist/core/tokens/formatters/style-dictionary-v3.d.ts.map +1 -0
  225. package/dist/core/tokens/formatters/style-dictionary-v3.js +208 -0
  226. package/dist/core/tokens/formatters/style-dictionary-v3.js.map +1 -0
  227. package/dist/core/tokens/formatters/tailwind-v3.d.ts +37 -0
  228. package/dist/core/tokens/formatters/tailwind-v3.d.ts.map +1 -0
  229. package/dist/core/tokens/formatters/tailwind-v3.js +238 -0
  230. package/dist/core/tokens/formatters/tailwind-v3.js.map +1 -0
  231. package/dist/core/tokens/formatters/tailwind-v4.d.ts +41 -0
  232. package/dist/core/tokens/formatters/tailwind-v4.d.ts.map +1 -0
  233. package/dist/core/tokens/formatters/tailwind-v4.js +331 -0
  234. package/dist/core/tokens/formatters/tailwind-v4.js.map +1 -0
  235. package/dist/core/tokens/formatters/tokens-studio.d.ts +44 -0
  236. package/dist/core/tokens/formatters/tokens-studio.d.ts.map +1 -0
  237. package/dist/core/tokens/formatters/tokens-studio.js +251 -0
  238. package/dist/core/tokens/formatters/tokens-studio.js.map +1 -0
  239. package/dist/core/tokens/formatters/ts-module.d.ts +35 -0
  240. package/dist/core/tokens/formatters/ts-module.d.ts.map +1 -0
  241. package/dist/core/tokens/formatters/ts-module.js +199 -0
  242. package/dist/core/tokens/formatters/ts-module.js.map +1 -0
  243. package/dist/core/tokens/index.d.ts +17 -0
  244. package/dist/core/tokens/index.d.ts.map +1 -0
  245. package/dist/core/tokens/index.js +16 -0
  246. package/dist/core/tokens/index.js.map +1 -0
  247. package/dist/core/tokens/parsers/css-vars.d.ts +3 -0
  248. package/dist/core/tokens/parsers/css-vars.d.ts.map +1 -0
  249. package/dist/core/tokens/parsers/css-vars.js +5 -0
  250. package/dist/core/tokens/parsers/css-vars.js.map +1 -0
  251. package/dist/core/tokens/parsers/dtcg.d.ts +21 -0
  252. package/dist/core/tokens/parsers/dtcg.d.ts.map +1 -0
  253. package/dist/core/tokens/parsers/dtcg.js +254 -0
  254. package/dist/core/tokens/parsers/dtcg.js.map +1 -0
  255. package/dist/core/tokens/parsers/index.d.ts +37 -0
  256. package/dist/core/tokens/parsers/index.d.ts.map +1 -0
  257. package/dist/core/tokens/parsers/index.js +139 -0
  258. package/dist/core/tokens/parsers/index.js.map +1 -0
  259. package/dist/core/tokens/parsers/json.d.ts +4 -0
  260. package/dist/core/tokens/parsers/json.d.ts.map +1 -0
  261. package/dist/core/tokens/parsers/json.js +8 -0
  262. package/dist/core/tokens/parsers/json.js.map +1 -0
  263. package/dist/core/tokens/parsers/scss.d.ts +3 -0
  264. package/dist/core/tokens/parsers/scss.d.ts.map +1 -0
  265. package/dist/core/tokens/parsers/scss.js +5 -0
  266. package/dist/core/tokens/parsers/scss.js.map +1 -0
  267. package/dist/core/tokens/parsers/stubs.d.ts +15 -0
  268. package/dist/core/tokens/parsers/stubs.d.ts.map +1 -0
  269. package/dist/core/tokens/parsers/stubs.js +21 -0
  270. package/dist/core/tokens/parsers/stubs.js.map +1 -0
  271. package/dist/core/tokens/parsers/style-dictionary-v3.d.ts +3 -0
  272. package/dist/core/tokens/parsers/style-dictionary-v3.d.ts.map +1 -0
  273. package/dist/core/tokens/parsers/style-dictionary-v3.js +5 -0
  274. package/dist/core/tokens/parsers/style-dictionary-v3.js.map +1 -0
  275. package/dist/core/tokens/parsers/tailwind-v3.d.ts +3 -0
  276. package/dist/core/tokens/parsers/tailwind-v3.d.ts.map +1 -0
  277. package/dist/core/tokens/parsers/tailwind-v3.js +5 -0
  278. package/dist/core/tokens/parsers/tailwind-v3.js.map +1 -0
  279. package/dist/core/tokens/parsers/tailwind-v4.d.ts +3 -0
  280. package/dist/core/tokens/parsers/tailwind-v4.d.ts.map +1 -0
  281. package/dist/core/tokens/parsers/tailwind-v4.js +5 -0
  282. package/dist/core/tokens/parsers/tailwind-v4.js.map +1 -0
  283. package/dist/core/tokens/parsers/tokens-studio.d.ts +3 -0
  284. package/dist/core/tokens/parsers/tokens-studio.d.ts.map +1 -0
  285. package/dist/core/tokens/parsers/tokens-studio.js +5 -0
  286. package/dist/core/tokens/parsers/tokens-studio.js.map +1 -0
  287. package/dist/core/tokens/schemas.d.ts +31 -0
  288. package/dist/core/tokens/schemas.d.ts.map +1 -0
  289. package/dist/core/tokens/schemas.js +149 -0
  290. package/dist/core/tokens/schemas.js.map +1 -0
  291. package/dist/core/tokens/transforms/color.d.ts +9 -0
  292. package/dist/core/tokens/transforms/color.d.ts.map +1 -0
  293. package/dist/core/tokens/transforms/color.js +13 -0
  294. package/dist/core/tokens/transforms/color.js.map +1 -0
  295. package/dist/core/tokens/transforms/index.d.ts +36 -0
  296. package/dist/core/tokens/transforms/index.d.ts.map +1 -0
  297. package/dist/core/tokens/transforms/index.js +30 -0
  298. package/dist/core/tokens/transforms/index.js.map +1 -0
  299. package/dist/core/tokens/transforms/size.d.ts +7 -0
  300. package/dist/core/tokens/transforms/size.d.ts.map +1 -0
  301. package/dist/core/tokens/transforms/size.js +8 -0
  302. package/dist/core/tokens/transforms/size.js.map +1 -0
  303. package/dist/core/tokens/types.d.ts +228 -0
  304. package/dist/core/tokens/types.d.ts.map +1 -0
  305. package/dist/core/tokens/types.js +19 -0
  306. package/dist/core/tokens/types.js.map +1 -0
  307. package/dist/core/tokens-tools.d.ts +42 -0
  308. package/dist/core/tokens-tools.d.ts.map +1 -0
  309. package/dist/core/tokens-tools.js +860 -0
  310. package/dist/core/tokens-tools.js.map +1 -0
  311. package/dist/core/types/design-code.d.ts +271 -0
  312. package/dist/core/types/design-code.d.ts.map +1 -0
  313. package/dist/core/types/design-code.js +5 -0
  314. package/dist/core/types/design-code.js.map +1 -0
  315. package/dist/core/types/enriched.d.ts +213 -0
  316. package/dist/core/types/enriched.d.ts.map +1 -0
  317. package/dist/core/types/enriched.js +6 -0
  318. package/dist/core/types/enriched.js.map +1 -0
  319. package/dist/core/types/index.d.ts +104 -0
  320. package/dist/core/types/index.d.ts.map +1 -0
  321. package/dist/core/types/index.js +5 -0
  322. package/dist/core/types/index.js.map +1 -0
  323. package/dist/core/variable-resolver.d.ts +45 -0
  324. package/dist/core/variable-resolver.d.ts.map +1 -0
  325. package/dist/core/variable-resolver.js +86 -0
  326. package/dist/core/variable-resolver.js.map +1 -0
  327. package/dist/core/version-tools.d.ts +59 -0
  328. package/dist/core/version-tools.d.ts.map +1 -0
  329. package/dist/core/version-tools.js +1159 -0
  330. package/dist/core/version-tools.js.map +1 -0
  331. package/dist/core/websocket-connector.d.ts +187 -0
  332. package/dist/core/websocket-connector.d.ts.map +1 -0
  333. package/dist/core/websocket-connector.js +378 -0
  334. package/dist/core/websocket-connector.js.map +1 -0
  335. package/dist/core/websocket-server.js +866 -0
  336. package/dist/core/websocket-server.js.map +1 -0
  337. package/dist/core/write-tools.d.ts +7 -0
  338. package/dist/core/write-tools.d.ts.map +1 -0
  339. package/dist/core/write-tools.js +2172 -0
  340. package/dist/core/write-tools.js.map +1 -0
  341. package/dist/local.d.ts +95 -0
  342. package/dist/local.d.ts.map +1 -0
  343. package/dist/local.js +3036 -0
  344. package/dist/local.js.map +1 -0
  345. package/dist/vendor/obra-autodocs/autodocs-body.generated.d.ts +2 -0
  346. package/dist/vendor/obra-autodocs/autodocs-body.generated.d.ts.map +1 -0
  347. package/dist/vendor/obra-autodocs/autodocs-body.generated.js +6 -0
  348. package/dist/vendor/obra-autodocs/autodocs-body.generated.js.map +1 -0
  349. package/figma-desktop-bridge/README.md +365 -0
  350. package/figma-desktop-bridge/code.js +6504 -0
  351. package/figma-desktop-bridge/icon.png +0 -0
  352. package/figma-desktop-bridge/manifest.json +67 -0
  353. package/figma-desktop-bridge/ui.html +2441 -0
  354. package/package.json +98 -0
@@ -0,0 +1,874 @@
1
+ /**
2
+ * Design System Kit Tool
3
+ * MCP tool that orchestrates existing Figma API tools to produce a structured
4
+ * design system specification — tokens, components, styles — in a single call.
5
+ *
6
+ * This enables AI code generation tools (Figma Make, v0, Cursor, Claude, etc.)
7
+ * to generate code with structural fidelity to the real design system.
8
+ */
9
+ import { z } from "zod";
10
+ import { extractFileKey } from "./figma-api.js";
11
+ import { resolveFormattedVariables } from "./variable-resolver.js";
12
+ import { createChildLogger } from "./logger.js";
13
+ const logger = createChildLogger({ component: "design-system-tools" });
14
+ // ============================================================================
15
+ // Helpers
16
+ // ============================================================================
17
+ /**
18
+ * Calculate JSON size in KB for response management
19
+ */
20
+ function calculateSizeKB(data) {
21
+ return JSON.stringify(data).length / 1024;
22
+ }
23
+ /**
24
+ * Wrap a promise with a timeout to prevent indefinite hangs
25
+ */
26
+ function withTimeout(promise, ms, label) {
27
+ return Promise.race([
28
+ promise,
29
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms)),
30
+ ]);
31
+ }
32
+ /**
33
+ * Convert Figma RGBA (0-1 range) to hex string
34
+ */
35
+ function rgbaToHex(color) {
36
+ const r = Math.round(color.r * 255);
37
+ const g = Math.round(color.g * 255);
38
+ const b = Math.round(color.b * 255);
39
+ const hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
40
+ return hex.toUpperCase();
41
+ }
42
+ /**
43
+ * Extract a compact visual specification from a Figma node.
44
+ * Captures the essential CSS-equivalent properties an AI needs to reproduce the component.
45
+ */
46
+ export function extractVisualSpec(node) {
47
+ if (!node)
48
+ return undefined;
49
+ const spec = {};
50
+ let hasData = false;
51
+ // Fills → background colors/gradients
52
+ if (node.fills && Array.isArray(node.fills) && node.fills.length > 0) {
53
+ spec.fills = node.fills
54
+ .filter((f) => f.visible !== false)
55
+ .map((f) => {
56
+ const fill = { type: f.type };
57
+ if (f.color)
58
+ fill.color = rgbaToHex(f.color);
59
+ if (f.opacity !== undefined)
60
+ fill.opacity = f.opacity;
61
+ return fill;
62
+ });
63
+ if (spec.fills.length > 0)
64
+ hasData = true;
65
+ }
66
+ // Strokes → borders
67
+ if (node.strokes && Array.isArray(node.strokes) && node.strokes.length > 0) {
68
+ spec.strokes = node.strokes
69
+ .filter((s) => s.visible !== false)
70
+ .map((s) => {
71
+ const stroke = { type: s.type };
72
+ if (s.color)
73
+ stroke.color = rgbaToHex(s.color);
74
+ return stroke;
75
+ });
76
+ if (node.strokeWeight !== undefined)
77
+ spec.strokes.forEach((s) => s.weight = node.strokeWeight);
78
+ if (node.strokeAlign)
79
+ spec.strokes.forEach((s) => s.align = node.strokeAlign);
80
+ if (spec.strokes.length > 0)
81
+ hasData = true;
82
+ }
83
+ // Effects → shadows, blurs
84
+ if (node.effects && Array.isArray(node.effects) && node.effects.length > 0) {
85
+ spec.effects = node.effects
86
+ .filter((e) => e.visible !== false)
87
+ .map((e) => {
88
+ const effect = { type: e.type };
89
+ if (e.color)
90
+ effect.color = rgbaToHex(e.color);
91
+ if (e.offset)
92
+ effect.offset = e.offset;
93
+ if (e.radius !== undefined)
94
+ effect.radius = e.radius;
95
+ if (e.spread !== undefined)
96
+ effect.spread = e.spread;
97
+ return effect;
98
+ });
99
+ if (spec.effects.length > 0)
100
+ hasData = true;
101
+ }
102
+ // Corner radius
103
+ if (node.cornerRadius !== undefined && node.cornerRadius > 0) {
104
+ spec.cornerRadius = node.cornerRadius;
105
+ hasData = true;
106
+ }
107
+ if (node.rectangleCornerRadii) {
108
+ spec.rectangleCornerRadii = node.rectangleCornerRadii;
109
+ hasData = true;
110
+ }
111
+ // Opacity
112
+ if (node.opacity !== undefined && node.opacity < 1) {
113
+ spec.opacity = node.opacity;
114
+ hasData = true;
115
+ }
116
+ // Auto-layout → CSS flex equivalent
117
+ if (node.layoutMode && node.layoutMode !== "NONE") {
118
+ spec.layout = {
119
+ mode: node.layoutMode,
120
+ };
121
+ if (node.paddingTop !== undefined)
122
+ spec.layout.paddingTop = node.paddingTop;
123
+ if (node.paddingRight !== undefined)
124
+ spec.layout.paddingRight = node.paddingRight;
125
+ if (node.paddingBottom !== undefined)
126
+ spec.layout.paddingBottom = node.paddingBottom;
127
+ if (node.paddingLeft !== undefined)
128
+ spec.layout.paddingLeft = node.paddingLeft;
129
+ if (node.itemSpacing !== undefined)
130
+ spec.layout.itemSpacing = node.itemSpacing;
131
+ if (node.primaryAxisAlignItems)
132
+ spec.layout.primaryAxisAlign = node.primaryAxisAlignItems;
133
+ if (node.counterAxisAlignItems)
134
+ spec.layout.counterAxisAlign = node.counterAxisAlignItems;
135
+ hasData = true;
136
+ }
137
+ // Typography (for TEXT nodes)
138
+ if (node.type === "TEXT" && node.style) {
139
+ spec.typography = {};
140
+ const s = node.style;
141
+ if (s.fontFamily)
142
+ spec.typography.fontFamily = s.fontFamily;
143
+ if (s.fontSize)
144
+ spec.typography.fontSize = s.fontSize;
145
+ if (s.fontWeight)
146
+ spec.typography.fontWeight = s.fontWeight;
147
+ if (s.lineHeightPx)
148
+ spec.typography.lineHeight = s.lineHeightPx;
149
+ if (s.letterSpacing)
150
+ spec.typography.letterSpacing = s.letterSpacing;
151
+ if (s.textAlignHorizontal)
152
+ spec.typography.textAlignHorizontal = s.textAlignHorizontal;
153
+ hasData = true;
154
+ }
155
+ return hasData ? spec : undefined;
156
+ }
157
+ /**
158
+ * Extract visual specs from a component node and its first-level children.
159
+ * Returns a compact representation of the component's visual appearance.
160
+ */
161
+ function extractComponentVisualData(node) {
162
+ if (!node)
163
+ return {};
164
+ const result = {};
165
+ const rootSpec = extractVisualSpec(node);
166
+ if (rootSpec)
167
+ result.visualSpec = rootSpec;
168
+ // Extract first-level children specs (the structural elements)
169
+ if (node.children && Array.isArray(node.children)) {
170
+ const childSpecs = [];
171
+ for (const child of node.children) {
172
+ const childInfo = {
173
+ name: child.name,
174
+ type: child.type,
175
+ };
176
+ const childVisual = extractVisualSpec(child);
177
+ if (childVisual)
178
+ childInfo.visualSpec = childVisual;
179
+ if (child.characters)
180
+ childInfo.characters = child.characters;
181
+ childSpecs.push(childInfo);
182
+ }
183
+ if (childSpecs.length > 0)
184
+ result.childSpecs = childSpecs;
185
+ }
186
+ return result;
187
+ }
188
+ /**
189
+ * Resolve style node IDs to their actual visual values.
190
+ * Styles only contain metadata from the styles endpoint — we need getNodes to get actual colors/fonts/effects.
191
+ */
192
+ async function resolveStyleValues(api, fileKey, styles) {
193
+ const resolved = new Map();
194
+ const nodeIds = styles.filter((s) => s.nodeId).map((s) => s.nodeId);
195
+ if (nodeIds.length === 0)
196
+ return resolved;
197
+ try {
198
+ const batchSize = 50;
199
+ for (let i = 0; i < nodeIds.length; i += batchSize) {
200
+ const batch = nodeIds.slice(i, i + batchSize);
201
+ const nodeResponse = await withTimeout(api.getNodes(fileKey, batch), 30000, `getStyleNodes(batch ${Math.floor(i / batchSize) + 1})`);
202
+ if (nodeResponse?.nodes) {
203
+ for (const [nodeId, nodeData] of Object.entries(nodeResponse.nodes)) {
204
+ const doc = nodeData?.document;
205
+ if (!doc)
206
+ continue;
207
+ const value = {};
208
+ // FILL styles → extract colors
209
+ if (doc.fills && Array.isArray(doc.fills)) {
210
+ value.fills = doc.fills
211
+ .filter((f) => f.visible !== false)
212
+ .map((f) => ({
213
+ type: f.type,
214
+ color: f.color ? rgbaToHex(f.color) : undefined,
215
+ opacity: f.opacity,
216
+ }));
217
+ }
218
+ // TEXT styles → extract typography
219
+ if (doc.type === "TEXT" && doc.style) {
220
+ value.typography = {
221
+ fontFamily: doc.style.fontFamily,
222
+ fontSize: doc.style.fontSize,
223
+ fontWeight: doc.style.fontWeight,
224
+ lineHeight: doc.style.lineHeightPx,
225
+ letterSpacing: doc.style.letterSpacing,
226
+ };
227
+ }
228
+ // EFFECT styles → extract shadows/blurs
229
+ if (doc.effects && Array.isArray(doc.effects)) {
230
+ value.effects = doc.effects
231
+ .filter((e) => e.visible !== false)
232
+ .map((e) => ({
233
+ type: e.type,
234
+ color: e.color ? rgbaToHex(e.color) : undefined,
235
+ offset: e.offset,
236
+ radius: e.radius,
237
+ spread: e.spread,
238
+ }));
239
+ }
240
+ resolved.set(nodeId, value);
241
+ }
242
+ }
243
+ }
244
+ }
245
+ catch (err) {
246
+ logger.warn({ error: err }, "Failed to resolve style values");
247
+ }
248
+ return resolved;
249
+ }
250
+ /**
251
+ * Group variables by collection for a clean hierarchical output
252
+ */
253
+ function groupVariablesByCollection(formatted) {
254
+ return formatted.collections.map((collection) => {
255
+ const collectionVars = formatted.variables
256
+ .filter((v) => v.variableCollectionId === collection.id)
257
+ .map((v) => ({
258
+ id: v.id,
259
+ name: v.name,
260
+ type: v.resolvedType,
261
+ description: v.description || undefined,
262
+ valuesByMode: v.valuesByMode,
263
+ scopes: v.scopes,
264
+ }));
265
+ return {
266
+ id: collection.id,
267
+ name: collection.name,
268
+ modes: collection.modes,
269
+ variables: collectionVars,
270
+ };
271
+ });
272
+ }
273
+ /**
274
+ * Deduplicate components — filter out individual variants when their
275
+ * parent component set is already present.
276
+ */
277
+ function deduplicateComponents(components, componentSets) {
278
+ const setNodeIds = new Set(componentSets.map((s) => s.node_id));
279
+ // Filter out variants that belong to a known component set
280
+ const standalone = components.filter((c) => {
281
+ if (c.containing_frame?.containingComponentSet) {
282
+ // This is a variant — check if parent set is already included
283
+ // Check both direct frame nodeId and the containingComponentSet.nodeId
284
+ // (some designs nest variants inside intermediate frames)
285
+ const frameNodeId = c.containing_frame?.nodeId;
286
+ const setNodeId = c.containing_frame?.containingComponentSet?.nodeId;
287
+ if ((frameNodeId && setNodeIds.has(frameNodeId)) ||
288
+ (setNodeId && setNodeIds.has(setNodeId))) {
289
+ return false; // Skip, parent set covers it
290
+ }
291
+ }
292
+ return true;
293
+ });
294
+ return { components: standalone, componentSets };
295
+ }
296
+ /**
297
+ * Compress the kit for large responses
298
+ */
299
+ function compressKit(kit, level) {
300
+ const compressed = { ...kit };
301
+ if (compressed.tokens) {
302
+ if (level === "compact") {
303
+ // Compact: only summary counts, drop all collections/variables
304
+ compressed.tokens = {
305
+ collections: [],
306
+ summary: compressed.tokens.summary,
307
+ };
308
+ }
309
+ else if (level === "inventory") {
310
+ // Only keep variable names and types, drop values
311
+ compressed.tokens = {
312
+ ...compressed.tokens,
313
+ collections: compressed.tokens.collections.map((c) => ({
314
+ ...c,
315
+ variables: c.variables.map((v) => ({
316
+ id: v.id,
317
+ name: v.name,
318
+ type: v.type,
319
+ description: v.description,
320
+ valuesByMode: {},
321
+ scopes: v.scopes,
322
+ })),
323
+ })),
324
+ };
325
+ }
326
+ }
327
+ if (compressed.components) {
328
+ if (level === "compact") {
329
+ // Compact: drastically reduce for large systems
330
+ // Separate component sets (design building blocks) from standalone components
331
+ const sets = compressed.components.items.filter((c) => c.variants && c.variants.length > 0);
332
+ const standalone = compressed.components.items.filter((c) => !c.variants || c.variants.length === 0);
333
+ // Keep all sets (they're the main building blocks), limit standalone to 100
334
+ const limitedStandalone = standalone.slice(0, 100);
335
+ const trimmedItems = [...sets, ...limitedStandalone];
336
+ compressed.components = {
337
+ ...compressed.components,
338
+ items: trimmedItems.map((c) => ({
339
+ id: c.id,
340
+ name: c.name,
341
+ // Compact: variant count only (not individual names) for very large sets
342
+ variants: c.variants
343
+ ? c.variants.length > 10
344
+ ? [{ name: `${c.variants.length} variants`, id: "" }]
345
+ : c.variants.map((v) => ({ name: v.name, id: v.id }))
346
+ : undefined,
347
+ properties: c.properties
348
+ ? Object.fromEntries(Object.entries(c.properties).map(([k, v]) => [
349
+ k,
350
+ { type: v.type, defaultValue: v.defaultValue },
351
+ ]))
352
+ : undefined,
353
+ })),
354
+ summary: {
355
+ ...compressed.components.summary,
356
+ totalComponents: trimmedItems.length,
357
+ ...(standalone.length > 100 ? { omittedStandaloneComponents: standalone.length - 100 } : {}),
358
+ },
359
+ };
360
+ }
361
+ else if (level === "inventory") {
362
+ // Only keep names and property keys — strip visual specs and variants
363
+ compressed.components = {
364
+ ...compressed.components,
365
+ items: compressed.components.items.map((c) => ({
366
+ id: c.id,
367
+ name: c.name,
368
+ description: c.description,
369
+ properties: c.properties
370
+ ? Object.fromEntries(Object.entries(c.properties).map(([k, v]) => [
371
+ k,
372
+ { type: v.type, defaultValue: v.defaultValue },
373
+ ]))
374
+ : undefined,
375
+ })),
376
+ };
377
+ }
378
+ else if (level === "summary") {
379
+ // Keep visual specs but strip variant-level specs to save space
380
+ compressed.components = {
381
+ ...compressed.components,
382
+ items: compressed.components.items.map((c) => ({
383
+ ...c,
384
+ variants: c.variants?.map((v) => ({ name: v.name, id: v.id })),
385
+ })),
386
+ };
387
+ }
388
+ // Drop image URLs at any compression level to save tokens
389
+ compressed.components.items = compressed.components.items.map((c) => {
390
+ const { imageUrl, ...rest } = c;
391
+ return rest;
392
+ });
393
+ }
394
+ if (compressed.styles) {
395
+ if (level === "compact") {
396
+ // Compact: only style names and types grouped by type, no resolved values
397
+ compressed.styles = {
398
+ ...compressed.styles,
399
+ items: compressed.styles.items.map((s) => ({
400
+ key: s.key,
401
+ name: s.name,
402
+ styleType: s.styleType,
403
+ })),
404
+ };
405
+ }
406
+ else if (level === "inventory") {
407
+ // Strip resolved values in inventory mode
408
+ compressed.styles = {
409
+ ...compressed.styles,
410
+ items: compressed.styles.items.map((s) => ({
411
+ key: s.key,
412
+ name: s.name,
413
+ styleType: s.styleType,
414
+ description: s.description,
415
+ })),
416
+ };
417
+ }
418
+ }
419
+ return compressed;
420
+ }
421
+ // ============================================================================
422
+ // Tool Registration
423
+ // ============================================================================
424
+ export function registerDesignSystemTools(server, getFigmaAPI, getCurrentUrl, variablesCache, options, getDesktopConnector) {
425
+ server.tool("figma_get_design_system_kit", "PREFERRED TOOL for design system extraction — replaces separate figma_get_styles, figma_get_variables, and figma_get_component calls. " +
426
+ "Returns tokens, components, and styles in a single optimized response with adaptive compression for large systems. " +
427
+ "Includes component visual specs (exact colors, padding, typography, layout), rendered screenshots, " +
428
+ "token values per mode (light/dark), and resolved style values. " +
429
+ "Use this instead of calling individual tools to avoid context window overflow. " +
430
+ "Ideal for AI code generation — use visualSpec for pixel-accurate reproduction. " +
431
+ "Tokens/variables are read through the connected Desktop Bridge or cloud relay and work on ANY Figma plan — no Enterprise required. " +
432
+ "If a tokens fetch ever reports the Variables REST API is plan-limited (403), the bridge/relay is the plan-independent path: ensure it's connected and retry rather than abandoning variables.", {
433
+ fileKey: z
434
+ .string()
435
+ .optional()
436
+ .describe("Figma file key. If omitted, extracted from the current browser URL."),
437
+ include: z
438
+ .array(z.enum(["tokens", "components", "styles"]))
439
+ .optional()
440
+ .default(["tokens", "components", "styles"])
441
+ .describe("Which sections to include. Defaults to all."),
442
+ componentIds: z
443
+ .array(z.string())
444
+ .optional()
445
+ .describe("Optional list of specific component node IDs to include. If omitted, all published components are returned."),
446
+ includeImages: z
447
+ .boolean()
448
+ .optional()
449
+ .default(false)
450
+ .describe("Include image URLs for components (adds latency). Default false."),
451
+ format: z
452
+ .enum(["full", "summary", "compact"])
453
+ .optional()
454
+ .default("full")
455
+ .describe("'full' returns complete data with visual specs and resolved values. " +
456
+ "'summary' strips variant-level visual specs (medium payload). " +
457
+ "'compact' returns only names, types, and property definitions (smallest payload, best for large design systems). " +
458
+ "Auto-compresses if response exceeds safe size regardless of format setting."),
459
+ }, async ({ fileKey, include, componentIds, includeImages, format }) => {
460
+ try {
461
+ const api = await getFigmaAPI();
462
+ // Resolve file key
463
+ let resolvedFileKey = fileKey;
464
+ if (!resolvedFileKey) {
465
+ const currentUrl = getCurrentUrl();
466
+ if (currentUrl) {
467
+ resolvedFileKey = extractFileKey(currentUrl) || undefined;
468
+ }
469
+ }
470
+ if (!resolvedFileKey) {
471
+ throw new Error("No file key provided and no Figma file currently open. " +
472
+ "Provide a fileKey parameter or navigate to a Figma file first.");
473
+ }
474
+ const errors = [];
475
+ const kit = {
476
+ fileKey: resolvedFileKey,
477
+ generatedAt: new Date().toISOString(),
478
+ format,
479
+ ai_instruction: "",
480
+ };
481
+ // ----------------------------------------------------------------
482
+ // Fetch tokens (variables)
483
+ // ----------------------------------------------------------------
484
+ if (include.includes("tokens")) {
485
+ try {
486
+ logger.info({ fileKey: resolvedFileKey }, "Fetching design tokens");
487
+ // Check cache first
488
+ const cacheKey = `vars:${resolvedFileKey}`;
489
+ let formatted = null;
490
+ if (variablesCache) {
491
+ const cached = variablesCache.get(cacheKey);
492
+ if (cached && Date.now() - cached.timestamp < 5 * 60 * 1000) {
493
+ formatted = cached.data;
494
+ logger.info("Using cached variables data");
495
+ }
496
+ }
497
+ if (!formatted) {
498
+ // Bridge-first: the Desktop Bridge / cloud relay reads variables on
499
+ // ANY plan via the Plugin API. The Enterprise-only REST Variables API
500
+ // is the fallback, used only when no bridge is connected — so most
501
+ // users (non-Enterprise) no longer dead-end on a 403 here.
502
+ formatted = await resolveFormattedVariables({
503
+ getDesktopConnector,
504
+ getFigmaAPI,
505
+ fileKey: resolvedFileKey,
506
+ });
507
+ if (variablesCache) {
508
+ variablesCache.set(cacheKey, {
509
+ data: formatted,
510
+ timestamp: Date.now(),
511
+ });
512
+ }
513
+ }
514
+ const collections = groupVariablesByCollection(formatted);
515
+ kit.tokens = {
516
+ collections,
517
+ summary: formatted.summary,
518
+ };
519
+ }
520
+ catch (err) {
521
+ const msg = err instanceof Error ? err.message : String(err);
522
+ logger.warn({ error: msg }, "Failed to fetch tokens");
523
+ errors.push({ section: "tokens", message: msg });
524
+ }
525
+ }
526
+ // ----------------------------------------------------------------
527
+ // Fetch components
528
+ // ----------------------------------------------------------------
529
+ if (include.includes("components")) {
530
+ try {
531
+ logger.info({ fileKey: resolvedFileKey }, "Fetching components");
532
+ const [componentsResponse, componentSetsResponse] = await Promise.all([
533
+ withTimeout(api.getComponents(resolvedFileKey), 30000, "getComponents"),
534
+ withTimeout(api.getComponentSets(resolvedFileKey), 30000, "getComponentSets"),
535
+ ]);
536
+ const allComponents = componentsResponse?.meta?.components || [];
537
+ const allComponentSets = componentSetsResponse?.meta?.component_sets || [];
538
+ const { components: standaloneComponents, componentSets } = deduplicateComponents(allComponents, allComponentSets);
539
+ // Filter by component IDs if provided
540
+ let targetComponents = standaloneComponents;
541
+ let targetSets = componentSets;
542
+ if (componentIds && componentIds.length > 0) {
543
+ const idSet = new Set(componentIds);
544
+ targetComponents = standaloneComponents.filter((c) => idSet.has(c.node_id));
545
+ targetSets = componentSets.filter((s) => idSet.has(s.node_id));
546
+ }
547
+ // Build component specs
548
+ const componentSpecs = [];
549
+ // Collect all node IDs we need to fetch details for (batched, not N+1)
550
+ const allNodeIds = [
551
+ ...targetSets.map((s) => s.node_id),
552
+ ...targetComponents.map((c) => c.node_id),
553
+ ];
554
+ // Batch fetch ALL node details in one call (max 50 per batch)
555
+ const nodeDetailsMap = {};
556
+ if (allNodeIds.length > 0) {
557
+ try {
558
+ const batchSize = 50;
559
+ for (let i = 0; i < allNodeIds.length; i += batchSize) {
560
+ const batch = allNodeIds.slice(i, i + batchSize);
561
+ const nodeResponse = await withTimeout(api.getNodes(resolvedFileKey, batch, { depth: 2 }), 30000, `getNodes(batch ${Math.floor(i / batchSize) + 1})`);
562
+ if (nodeResponse?.nodes) {
563
+ for (const [nodeId, nodeData] of Object.entries(nodeResponse.nodes)) {
564
+ nodeDetailsMap[nodeId] = nodeData?.document;
565
+ }
566
+ }
567
+ }
568
+ }
569
+ catch (err) {
570
+ logger.warn({ error: err }, "Failed to batch-fetch component node details");
571
+ }
572
+ }
573
+ // Process component sets (multi-variant components)
574
+ for (const set of targetSets) {
575
+ const spec = {
576
+ id: set.node_id,
577
+ name: set.name,
578
+ description: set.description || undefined,
579
+ };
580
+ // Use pre-fetched node details
581
+ const setNode = nodeDetailsMap[set.node_id];
582
+ // Get variant info from the child components
583
+ // Match by component_set_id, containing_frame.nodeId, OR containingComponentSet.nodeId
584
+ // (some designs nest variants inside intermediate frames)
585
+ const variants = allComponents
586
+ .filter((c) => c.component_set_id === set.node_id ||
587
+ c.containing_frame?.nodeId === set.node_id ||
588
+ c.containing_frame?.containingComponentSet?.nodeId === set.node_id)
589
+ .map((c) => {
590
+ const entry = { name: c.name, id: c.node_id };
591
+ // Attach visual spec from depth-2 children of the set node
592
+ if (setNode?.children) {
593
+ const variantNode = setNode.children.find((ch) => ch.id === c.node_id);
594
+ if (variantNode) {
595
+ const vs = extractVisualSpec(variantNode);
596
+ if (vs)
597
+ entry.visualSpec = vs;
598
+ }
599
+ }
600
+ return entry;
601
+ });
602
+ if (variants.length > 0) {
603
+ spec.variants = variants;
604
+ }
605
+ if (setNode?.componentPropertyDefinitions) {
606
+ spec.properties = setNode.componentPropertyDefinitions;
607
+ }
608
+ if (setNode?.absoluteBoundingBox) {
609
+ spec.bounds = {
610
+ width: setNode.absoluteBoundingBox.width,
611
+ height: setNode.absoluteBoundingBox.height,
612
+ };
613
+ }
614
+ // Extract visual spec from the set node (root + children)
615
+ if (setNode) {
616
+ const visualData = extractComponentVisualData(setNode);
617
+ if (visualData.visualSpec) {
618
+ spec.visualSpec = visualData.visualSpec;
619
+ }
620
+ }
621
+ componentSpecs.push(spec);
622
+ }
623
+ // Process standalone components (not part of a set)
624
+ for (const comp of targetComponents) {
625
+ const spec = {
626
+ id: comp.node_id,
627
+ name: comp.name,
628
+ description: comp.description || undefined,
629
+ };
630
+ // Use pre-fetched node details
631
+ const node = nodeDetailsMap[comp.node_id];
632
+ if (node?.componentPropertyDefinitions) {
633
+ spec.properties = node.componentPropertyDefinitions;
634
+ }
635
+ if (node?.absoluteBoundingBox) {
636
+ spec.bounds = {
637
+ width: node.absoluteBoundingBox.width,
638
+ height: node.absoluteBoundingBox.height,
639
+ };
640
+ }
641
+ // Extract visual spec from the component node (root + children)
642
+ if (node) {
643
+ const visualData = extractComponentVisualData(node);
644
+ if (visualData.visualSpec) {
645
+ spec.visualSpec = visualData.visualSpec;
646
+ }
647
+ }
648
+ componentSpecs.push(spec);
649
+ }
650
+ // Optionally fetch component images
651
+ if (includeImages && componentSpecs.length > 0) {
652
+ try {
653
+ const nodeIds = componentSpecs.map((c) => c.id);
654
+ // Batch in groups of 50 to stay within API limits
655
+ const batchSize = 50;
656
+ for (let i = 0; i < nodeIds.length; i += batchSize) {
657
+ const batch = nodeIds.slice(i, i + batchSize);
658
+ const imagesResult = await withTimeout(api.getImages(resolvedFileKey, batch, { scale: 2, format: "png" }), 30000, "getImages");
659
+ if (imagesResult?.images) {
660
+ for (const spec of componentSpecs) {
661
+ const url = imagesResult.images[spec.id];
662
+ if (url) {
663
+ spec.imageUrl = url;
664
+ }
665
+ }
666
+ }
667
+ }
668
+ }
669
+ catch (err) {
670
+ const msg = err instanceof Error ? err.message : String(err);
671
+ logger.warn({ error: msg }, "Failed to fetch component images");
672
+ errors.push({ section: "component_images", message: msg });
673
+ }
674
+ }
675
+ kit.components = {
676
+ items: componentSpecs,
677
+ summary: {
678
+ totalComponents: componentSpecs.length,
679
+ totalComponentSets: targetSets.length,
680
+ },
681
+ };
682
+ }
683
+ catch (err) {
684
+ const msg = err instanceof Error ? err.message : String(err);
685
+ logger.warn({ error: msg }, "Failed to fetch components");
686
+ errors.push({ section: "components", message: msg });
687
+ }
688
+ }
689
+ // ----------------------------------------------------------------
690
+ // Fetch styles
691
+ // ----------------------------------------------------------------
692
+ if (include.includes("styles")) {
693
+ try {
694
+ logger.info({ fileKey: resolvedFileKey }, "Fetching styles");
695
+ const stylesResponse = await withTimeout(api.getStyles(resolvedFileKey), 30000, "getStyles");
696
+ const allStyles = stylesResponse?.meta?.styles || [];
697
+ const styleSpecs = allStyles.map((s) => ({
698
+ key: s.key,
699
+ name: s.name,
700
+ styleType: s.style_type,
701
+ description: s.description || undefined,
702
+ nodeId: s.node_id,
703
+ }));
704
+ // Resolve actual values for styles (colors, typography, effects)
705
+ if (styleSpecs.length > 0) {
706
+ try {
707
+ const resolvedValues = await resolveStyleValues(api, resolvedFileKey, styleSpecs);
708
+ for (const style of styleSpecs) {
709
+ if (style.nodeId && resolvedValues.has(style.nodeId)) {
710
+ style.resolvedValue = resolvedValues.get(style.nodeId);
711
+ }
712
+ }
713
+ }
714
+ catch (err) {
715
+ logger.warn({ error: err }, "Failed to resolve style values");
716
+ }
717
+ }
718
+ const stylesByType = {};
719
+ for (const s of styleSpecs) {
720
+ stylesByType[s.styleType] = (stylesByType[s.styleType] || 0) + 1;
721
+ }
722
+ kit.styles = {
723
+ items: styleSpecs,
724
+ summary: {
725
+ totalStyles: styleSpecs.length,
726
+ stylesByType,
727
+ },
728
+ };
729
+ }
730
+ catch (err) {
731
+ const msg = err instanceof Error ? err.message : String(err);
732
+ logger.warn({ error: msg }, "Failed to fetch styles");
733
+ errors.push({ section: "styles", message: msg });
734
+ }
735
+ }
736
+ // ----------------------------------------------------------------
737
+ // Build AI instruction
738
+ // ----------------------------------------------------------------
739
+ if (errors.length > 0) {
740
+ kit.errors = errors;
741
+ }
742
+ const sections = [];
743
+ if (kit.tokens)
744
+ sections.push(`${kit.tokens.summary.totalVariables} tokens in ${kit.tokens.summary.totalCollections} collections`);
745
+ if (kit.components)
746
+ sections.push(`${kit.components.summary.totalComponents} components (${kit.components.summary.totalComponentSets} sets)`);
747
+ if (kit.styles)
748
+ sections.push(`${kit.styles.summary.totalStyles} styles`);
749
+ kit.ai_instruction =
750
+ "DESIGN SYSTEM SPECIFICATION — STRICT VISUAL FIDELITY REQUIRED\n\n" +
751
+ `Contains: ${sections.join(", ")}.\n\n` +
752
+ "RULES:\n" +
753
+ "1. ONLY use colors, spacing, and typography values from this data. " +
754
+ "Do NOT invent, guess, or add any visual properties not explicitly present.\n" +
755
+ "2. Map 'visualSpec' directly to CSS:\n" +
756
+ " - fills[].color → background-color (e.g. #181818)\n" +
757
+ " - strokes[].color/weight → border (e.g. 1px solid #9747FF)\n" +
758
+ " - effects[] → box-shadow (type DROP_SHADOW: offset.x offset.y radius spread color)\n" +
759
+ " - cornerRadius → border-radius\n" +
760
+ " - layout.mode HORIZONTAL → flex-direction:row, VERTICAL → flex-direction:column\n" +
761
+ " - layout.paddingTop/Right/Bottom/Left → padding\n" +
762
+ " - layout.itemSpacing → gap\n" +
763
+ " - layout.primaryAxisAlign → justify-content, counterAxisAlign → align-items\n" +
764
+ " - typography → font-family, font-size, font-weight, line-height, letter-spacing\n" +
765
+ "3. Do NOT add decorative elements (colored borders, accents, dividers, gradients) " +
766
+ "unless they appear in the visualSpec data.\n" +
767
+ "4. Use 'imageUrl' screenshots as the visual ground truth. If the screenshot " +
768
+ "shows a simple dark card, do not add colored side borders or other embellishments.\n" +
769
+ "5. Style 'resolvedValue' contains the exact design system colors and typography — " +
770
+ "match these values precisely, do not substitute similar colors.\n" +
771
+ "6. Component 'properties' define the component API (props). " +
772
+ "VARIANT type properties define the visual variants (e.g. Info, Danger, Success). " +
773
+ "BOOLEAN properties toggle features. TEXT properties accept string content.\n" +
774
+ "7. When applying to an existing component library (e.g. shadcn, MUI, Chakra), " +
775
+ "override the library's default theme values with the exact colors, spacing, and " +
776
+ "typography from this specification. Do not blend with library defaults.";
777
+ // ----------------------------------------------------------------
778
+ // Adaptive compression for large responses
779
+ // Thresholds tuned for consumer AI context windows (~128K tokens ≈ ~400KB text)
780
+ // ----------------------------------------------------------------
781
+ const sizeKB = calculateSizeKB(kit);
782
+ logger.info({ sizeKB: sizeKB.toFixed(0), format }, "Kit assembled, checking compression");
783
+ // Determine compression level from format + size
784
+ let compressionLevel = null;
785
+ if (format === "compact") {
786
+ compressionLevel = "compact";
787
+ }
788
+ else if (format === "summary") {
789
+ compressionLevel = "summary";
790
+ }
791
+ // Auto-compress based on size regardless of format setting
792
+ // Lower thresholds to stay within consumer context windows
793
+ if (sizeKB > 500) {
794
+ compressionLevel = "compact"; // >500KB → just names and types
795
+ }
796
+ else if (sizeKB > 200) {
797
+ // Upgrade to at least inventory if not already more aggressive
798
+ if (!compressionLevel || compressionLevel === "summary") {
799
+ compressionLevel = "inventory";
800
+ }
801
+ }
802
+ else if (sizeKB > 100) {
803
+ // Upgrade to at least summary
804
+ if (!compressionLevel) {
805
+ compressionLevel = "summary";
806
+ }
807
+ }
808
+ if (compressionLevel) {
809
+ const compressed = compressKit(kit, compressionLevel);
810
+ const compressedSizeKB = calculateSizeKB(compressed);
811
+ if (sizeKB > 100) {
812
+ compressed.ai_instruction =
813
+ `Response auto-compressed (${compressionLevel}) from ${sizeKB.toFixed(0)}KB to ${compressedSizeKB.toFixed(0)}KB. ` +
814
+ compressed.ai_instruction +
815
+ " For full visual specs of specific components, re-call with specific componentIds and format='full'.";
816
+ }
817
+ logger.info({ originalKB: sizeKB.toFixed(0), compressedKB: compressedSizeKB.toFixed(0), level: compressionLevel }, "Kit compressed");
818
+ return {
819
+ content: [
820
+ {
821
+ type: "text",
822
+ text: JSON.stringify(compressed),
823
+ },
824
+ ],
825
+ };
826
+ }
827
+ return {
828
+ content: [
829
+ {
830
+ type: "text",
831
+ text: JSON.stringify(kit),
832
+ },
833
+ ],
834
+ };
835
+ }
836
+ catch (error) {
837
+ logger.error({ error }, "Failed to generate design system kit");
838
+ const errorMessage = error instanceof Error ? error.message : String(error);
839
+ // Check if it's an auth error
840
+ let parsedError = null;
841
+ try {
842
+ parsedError = JSON.parse(errorMessage);
843
+ }
844
+ catch {
845
+ // Not a JSON error
846
+ }
847
+ if (parsedError?.error === "authentication_required" || parsedError?.error === "oauth_error") {
848
+ return {
849
+ content: [
850
+ {
851
+ type: "text",
852
+ text: JSON.stringify(parsedError),
853
+ },
854
+ ],
855
+ isError: true,
856
+ };
857
+ }
858
+ return {
859
+ content: [
860
+ {
861
+ type: "text",
862
+ text: JSON.stringify({
863
+ error: errorMessage,
864
+ message: "Failed to generate design system kit",
865
+ hint: "Ensure you have a valid Figma file key and the file contains published components/variables.",
866
+ }),
867
+ },
868
+ ],
869
+ isError: true,
870
+ };
871
+ }
872
+ });
873
+ }
874
+ //# sourceMappingURL=design-system-tools.js.map