@muloka/figma-console-mcp 0.1.0

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