@llc3233149/core 0.9.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 (457) hide show
  1. package/dist/canvaskit.d.ts +9 -0
  2. package/dist/canvaskit.d.ts.map +1 -0
  3. package/dist/canvaskit.js +55 -0
  4. package/dist/canvaskit.js.map +1 -0
  5. package/dist/clipboard.d.ts +32 -0
  6. package/dist/clipboard.d.ts.map +1 -0
  7. package/dist/clipboard.js +345 -0
  8. package/dist/clipboard.js.map +1 -0
  9. package/dist/color.d.ts +28 -0
  10. package/dist/color.d.ts.map +1 -0
  11. package/dist/color.js +67 -0
  12. package/dist/color.js.map +1 -0
  13. package/dist/constants.d.ts +159 -0
  14. package/dist/constants.d.ts.map +1 -0
  15. package/dist/constants.js +224 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/copy.d.ts +20 -0
  18. package/dist/copy.d.ts.map +1 -0
  19. package/dist/copy.js +64 -0
  20. package/dist/copy.js.map +1 -0
  21. package/dist/fig-export-worker.d.ts +2 -0
  22. package/dist/fig-export-worker.d.ts.map +1 -0
  23. package/dist/fig-export-worker.js +17 -0
  24. package/dist/fig-export-worker.js.map +1 -0
  25. package/dist/fig-export.d.ts +13 -0
  26. package/dist/fig-export.d.ts.map +1 -0
  27. package/dist/fig-export.js +243 -0
  28. package/dist/fig-export.js.map +1 -0
  29. package/dist/figma-api.d.ts +268 -0
  30. package/dist/figma-api.d.ts.map +1 -0
  31. package/dist/figma-api.js +1074 -0
  32. package/dist/figma-api.js.map +1 -0
  33. package/dist/figma-rest-import.d.ts +122 -0
  34. package/dist/figma-rest-import.d.ts.map +1 -0
  35. package/dist/figma-rest-import.js +319 -0
  36. package/dist/figma-rest-import.js.map +1 -0
  37. package/dist/fonts.d.ts +24 -0
  38. package/dist/fonts.d.ts.map +1 -0
  39. package/dist/fonts.js +356 -0
  40. package/dist/fonts.js.map +1 -0
  41. package/dist/geometry.d.ts +14 -0
  42. package/dist/geometry.d.ts.map +1 -0
  43. package/dist/geometry.js +47 -0
  44. package/dist/geometry.js.map +1 -0
  45. package/dist/index.d.ts +38 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +32 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/kiwi/codec.d.ts +292 -0
  50. package/dist/kiwi/codec.d.ts.map +1 -0
  51. package/dist/kiwi/codec.js +340 -0
  52. package/dist/kiwi/codec.js.map +1 -0
  53. package/dist/kiwi/fig-file.d.ts +4 -0
  54. package/dist/kiwi/fig-file.d.ts.map +1 -0
  55. package/dist/kiwi/fig-file.js +236 -0
  56. package/dist/kiwi/fig-file.js.map +1 -0
  57. package/dist/kiwi/fig-import.d.ts +4 -0
  58. package/dist/kiwi/fig-import.d.ts.map +1 -0
  59. package/dist/kiwi/fig-import.js +248 -0
  60. package/dist/kiwi/fig-import.js.map +1 -0
  61. package/dist/kiwi/fig-parse-worker.d.ts +7 -0
  62. package/dist/kiwi/fig-parse-worker.d.ts.map +1 -0
  63. package/dist/kiwi/fig-parse-worker.js +184 -0
  64. package/dist/kiwi/fig-parse-worker.js.map +1 -0
  65. package/dist/kiwi/instance-overrides.d.ts +66 -0
  66. package/dist/kiwi/instance-overrides.d.ts.map +1 -0
  67. package/dist/kiwi/instance-overrides.js +644 -0
  68. package/dist/kiwi/instance-overrides.js.map +1 -0
  69. package/dist/kiwi/kiwi-convert.d.ts +19 -0
  70. package/dist/kiwi/kiwi-convert.d.ts.map +1 -0
  71. package/dist/kiwi/kiwi-convert.js +678 -0
  72. package/dist/kiwi/kiwi-convert.js.map +1 -0
  73. package/dist/kiwi/kiwi-schema/bb.d.ts +25 -0
  74. package/dist/kiwi/kiwi-schema/bb.d.ts.map +1 -0
  75. package/dist/kiwi/kiwi-schema/bb.js +225 -0
  76. package/dist/kiwi/kiwi-schema/bb.js.map +1 -0
  77. package/dist/kiwi/kiwi-schema/binary.d.ts +5 -0
  78. package/dist/kiwi/kiwi-schema/binary.d.ts.map +1 -0
  79. package/dist/kiwi/kiwi-schema/binary.js +86 -0
  80. package/dist/kiwi/kiwi-schema/binary.js.map +1 -0
  81. package/dist/kiwi/kiwi-schema/index.d.ts +6 -0
  82. package/dist/kiwi/kiwi-schema/index.d.ts.map +1 -0
  83. package/dist/kiwi/kiwi-schema/index.js +5 -0
  84. package/dist/kiwi/kiwi-schema/index.js.map +1 -0
  85. package/dist/kiwi/kiwi-schema/js.d.ts +4 -0
  86. package/dist/kiwi/kiwi-schema/js.d.ts.map +1 -0
  87. package/dist/kiwi/kiwi-schema/js.js +282 -0
  88. package/dist/kiwi/kiwi-schema/js.js.map +1 -0
  89. package/dist/kiwi/kiwi-schema/parser.d.ts +5 -0
  90. package/dist/kiwi/kiwi-schema/parser.d.ts.map +1 -0
  91. package/dist/kiwi/kiwi-schema/parser.js +228 -0
  92. package/dist/kiwi/kiwi-schema/parser.js.map +1 -0
  93. package/dist/kiwi/kiwi-schema/schema.d.ts +22 -0
  94. package/dist/kiwi/kiwi-schema/schema.d.ts.map +1 -0
  95. package/dist/kiwi/kiwi-schema/schema.js +2 -0
  96. package/dist/kiwi/kiwi-schema/schema.js.map +1 -0
  97. package/dist/kiwi/kiwi-schema/util.d.ts +3 -0
  98. package/dist/kiwi/kiwi-schema/util.d.ts.map +1 -0
  99. package/dist/kiwi/kiwi-schema/util.js +10 -0
  100. package/dist/kiwi/kiwi-schema/util.js.map +1 -0
  101. package/dist/kiwi/protocol.d.ts +172 -0
  102. package/dist/kiwi/protocol.d.ts.map +1 -0
  103. package/dist/kiwi/protocol.js +214 -0
  104. package/dist/kiwi/protocol.js.map +1 -0
  105. package/dist/kiwi/schema.d.ts +3 -0
  106. package/dist/kiwi/schema.d.ts.map +1 -0
  107. package/dist/kiwi/schema.js +2285 -0
  108. package/dist/kiwi/schema.js.map +1 -0
  109. package/dist/kiwi-serialize.d.ts +17 -0
  110. package/dist/kiwi-serialize.d.ts.map +1 -0
  111. package/dist/kiwi-serialize.js +419 -0
  112. package/dist/kiwi-serialize.js.map +1 -0
  113. package/dist/layout.d.ts +9 -0
  114. package/dist/layout.d.ts.map +1 -0
  115. package/dist/layout.js +533 -0
  116. package/dist/layout.js.map +1 -0
  117. package/dist/profiler/draw-call-counter.d.ts +9 -0
  118. package/dist/profiler/draw-call-counter.d.ts.map +1 -0
  119. package/dist/profiler/draw-call-counter.js +37 -0
  120. package/dist/profiler/draw-call-counter.js.map +1 -0
  121. package/dist/profiler/frame-capture.d.ts +34 -0
  122. package/dist/profiler/frame-capture.d.ts.map +1 -0
  123. package/dist/profiler/frame-capture.js +90 -0
  124. package/dist/profiler/frame-capture.js.map +1 -0
  125. package/dist/profiler/frame-stats.d.ts +33 -0
  126. package/dist/profiler/frame-stats.d.ts.map +1 -0
  127. package/dist/profiler/frame-stats.js +103 -0
  128. package/dist/profiler/frame-stats.js.map +1 -0
  129. package/dist/profiler/gpu-timer.d.ts +15 -0
  130. package/dist/profiler/gpu-timer.d.ts.map +1 -0
  131. package/dist/profiler/gpu-timer.js +77 -0
  132. package/dist/profiler/gpu-timer.js.map +1 -0
  133. package/dist/profiler/hud-renderer.d.ts +22 -0
  134. package/dist/profiler/hud-renderer.d.ts.map +1 -0
  135. package/dist/profiler/hud-renderer.js +200 -0
  136. package/dist/profiler/hud-renderer.js.map +1 -0
  137. package/dist/profiler/index.d.ts +9 -0
  138. package/dist/profiler/index.d.ts.map +1 -0
  139. package/dist/profiler/index.js +8 -0
  140. package/dist/profiler/index.js.map +1 -0
  141. package/dist/profiler/phase-timer.d.ts +9 -0
  142. package/dist/profiler/phase-timer.d.ts.map +1 -0
  143. package/dist/profiler/phase-timer.js +51 -0
  144. package/dist/profiler/phase-timer.js.map +1 -0
  145. package/dist/profiler/render-profiler.d.ts +41 -0
  146. package/dist/profiler/render-profiler.d.ts.map +1 -0
  147. package/dist/profiler/render-profiler.js +142 -0
  148. package/dist/profiler/render-profiler.js.map +1 -0
  149. package/dist/render/components.d.ts +25 -0
  150. package/dist/render/components.d.ts.map +1 -0
  151. package/dist/render/components.js +55 -0
  152. package/dist/render/components.js.map +1 -0
  153. package/dist/render/emoji-icons.d.ts +5 -0
  154. package/dist/render/emoji-icons.d.ts.map +1 -0
  155. package/dist/render/emoji-icons.js +55 -0
  156. package/dist/render/emoji-icons.js.map +1 -0
  157. package/dist/render/export-code.d.ts +9 -0
  158. package/dist/render/export-code.d.ts.map +1 -0
  159. package/dist/render/export-code.js +335 -0
  160. package/dist/render/export-code.js.map +1 -0
  161. package/dist/render/export-jsx.d.ts +5 -0
  162. package/dist/render/export-jsx.d.ts.map +1 -0
  163. package/dist/render/export-jsx.js +621 -0
  164. package/dist/render/export-jsx.js.map +1 -0
  165. package/dist/render/index.d.ts +8 -0
  166. package/dist/render/index.d.ts.map +1 -0
  167. package/dist/render/index.js +8 -0
  168. package/dist/render/index.js.map +1 -0
  169. package/dist/render/jsx-runtime.d.ts +31 -0
  170. package/dist/render/jsx-runtime.d.ts.map +1 -0
  171. package/dist/render/jsx-runtime.js +13 -0
  172. package/dist/render/jsx-runtime.js.map +1 -0
  173. package/dist/render/mini-react.d.ts +14 -0
  174. package/dist/render/mini-react.d.ts.map +1 -0
  175. package/dist/render/mini-react.js +16 -0
  176. package/dist/render/mini-react.js.map +1 -0
  177. package/dist/render/render-jsx.d.ts +24 -0
  178. package/dist/render/render-jsx.d.ts.map +1 -0
  179. package/dist/render/render-jsx.js +51 -0
  180. package/dist/render/render-jsx.js.map +1 -0
  181. package/dist/render/renderer.d.ts +16 -0
  182. package/dist/render/renderer.d.ts.map +1 -0
  183. package/dist/render/renderer.js +437 -0
  184. package/dist/render/renderer.js.map +1 -0
  185. package/dist/render/tailwind.d.ts +20 -0
  186. package/dist/render/tailwind.d.ts.map +1 -0
  187. package/dist/render/tailwind.js +115 -0
  188. package/dist/render/tailwind.js.map +1 -0
  189. package/dist/render/tree.d.ts +72 -0
  190. package/dist/render/tree.d.ts.map +1 -0
  191. package/dist/render/tree.js +63 -0
  192. package/dist/render/tree.js.map +1 -0
  193. package/dist/render-image.d.ts +19 -0
  194. package/dist/render-image.d.ts.map +1 -0
  195. package/dist/render-image.js +84 -0
  196. package/dist/render-image.js.map +1 -0
  197. package/dist/renderer/effects.d.ts +9 -0
  198. package/dist/renderer/effects.d.ts.map +1 -0
  199. package/dist/renderer/effects.js +44 -0
  200. package/dist/renderer/effects.js.map +1 -0
  201. package/dist/renderer/fills.d.ts +9 -0
  202. package/dist/renderer/fills.d.ts.map +1 -0
  203. package/dist/renderer/fills.js +189 -0
  204. package/dist/renderer/fills.js.map +1 -0
  205. package/dist/renderer/index.d.ts +2 -0
  206. package/dist/renderer/index.d.ts.map +1 -0
  207. package/dist/renderer/index.js +2 -0
  208. package/dist/renderer/index.js.map +1 -0
  209. package/dist/renderer/label-cache.d.ts +43 -0
  210. package/dist/renderer/label-cache.d.ts.map +1 -0
  211. package/dist/renderer/label-cache.js +85 -0
  212. package/dist/renderer/label-cache.js.map +1 -0
  213. package/dist/renderer/labels.d.ts +6 -0
  214. package/dist/renderer/labels.d.ts.map +1 -0
  215. package/dist/renderer/labels.js +126 -0
  216. package/dist/renderer/labels.js.map +1 -0
  217. package/dist/renderer/lru-cache.d.ts +20 -0
  218. package/dist/renderer/lru-cache.d.ts.map +1 -0
  219. package/dist/renderer/lru-cache.js +65 -0
  220. package/dist/renderer/lru-cache.js.map +1 -0
  221. package/dist/renderer/overlays.d.ts +25 -0
  222. package/dist/renderer/overlays.d.ts.map +1 -0
  223. package/dist/renderer/overlays.js +783 -0
  224. package/dist/renderer/overlays.js.map +1 -0
  225. package/dist/renderer/renderer.d.ts +229 -0
  226. package/dist/renderer/renderer.d.ts.map +1 -0
  227. package/dist/renderer/renderer.js +909 -0
  228. package/dist/renderer/renderer.js.map +1 -0
  229. package/dist/renderer/rulers.d.ts +8 -0
  230. package/dist/renderer/rulers.d.ts.map +1 -0
  231. package/dist/renderer/rulers.js +147 -0
  232. package/dist/renderer/rulers.js.map +1 -0
  233. package/dist/renderer/scene.d.ts +11 -0
  234. package/dist/renderer/scene.d.ts.map +1 -0
  235. package/dist/renderer/scene.js +392 -0
  236. package/dist/renderer/scene.js.map +1 -0
  237. package/dist/renderer/shapes.d.ts +13 -0
  238. package/dist/renderer/shapes.d.ts.map +1 -0
  239. package/dist/renderer/shapes.js +160 -0
  240. package/dist/renderer/shapes.js.map +1 -0
  241. package/dist/renderer/strokes.d.ts +9 -0
  242. package/dist/renderer/strokes.d.ts.map +1 -0
  243. package/dist/renderer/strokes.js +180 -0
  244. package/dist/renderer/strokes.js.map +1 -0
  245. package/dist/rpc/commands.d.ts +202 -0
  246. package/dist/rpc/commands.d.ts.map +1 -0
  247. package/dist/rpc/commands.js +429 -0
  248. package/dist/rpc/commands.js.map +1 -0
  249. package/dist/rpc/index.d.ts +3 -0
  250. package/dist/rpc/index.d.ts.map +1 -0
  251. package/dist/rpc/index.js +2 -0
  252. package/dist/rpc/index.js.map +1 -0
  253. package/dist/scene-graph.d.ts +318 -0
  254. package/dist/scene-graph.d.ts.map +1 -0
  255. package/dist/scene-graph.js +803 -0
  256. package/dist/scene-graph.js.map +1 -0
  257. package/dist/snap.d.ts +16 -0
  258. package/dist/snap.d.ts.map +1 -0
  259. package/dist/snap.js +97 -0
  260. package/dist/snap.js.map +1 -0
  261. package/dist/style-runs.d.ts +20 -0
  262. package/dist/style-runs.d.ts.map +1 -0
  263. package/dist/style-runs.js +168 -0
  264. package/dist/style-runs.js.map +1 -0
  265. package/dist/svg-export/defs.d.ts +19 -0
  266. package/dist/svg-export/defs.d.ts.map +1 -0
  267. package/dist/svg-export/defs.js +191 -0
  268. package/dist/svg-export/defs.js.map +1 -0
  269. package/dist/svg-export/index.d.ts +8 -0
  270. package/dist/svg-export/index.d.ts.map +1 -0
  271. package/dist/svg-export/index.js +318 -0
  272. package/dist/svg-export/index.js.map +1 -0
  273. package/dist/svg-export/paths.d.ts +9 -0
  274. package/dist/svg-export/paths.d.ts.map +1 -0
  275. package/dist/svg-export/paths.js +189 -0
  276. package/dist/svg-export/paths.js.map +1 -0
  277. package/dist/svg-node.d.ts +8 -0
  278. package/dist/svg-node.d.ts.map +1 -0
  279. package/dist/svg-node.js +48 -0
  280. package/dist/svg-node.js.map +1 -0
  281. package/dist/text-editor.d.ts +57 -0
  282. package/dist/text-editor.d.ts.map +1 -0
  283. package/dist/text-editor.js +337 -0
  284. package/dist/text-editor.js.map +1 -0
  285. package/dist/tools/ai-adapter.d.ts +22 -0
  286. package/dist/tools/ai-adapter.d.ts.map +1 -0
  287. package/dist/tools/ai-adapter.js +121 -0
  288. package/dist/tools/ai-adapter.js.map +1 -0
  289. package/dist/tools/analyze.d.ts +22 -0
  290. package/dist/tools/analyze.d.ts.map +1 -0
  291. package/dist/tools/analyze.js +677 -0
  292. package/dist/tools/analyze.js.map +1 -0
  293. package/dist/tools/create.d.ts +8 -0
  294. package/dist/tools/create.d.ts.map +1 -0
  295. package/dist/tools/create.js +204 -0
  296. package/dist/tools/create.js.map +1 -0
  297. package/dist/tools/describe.d.ts +9 -0
  298. package/dist/tools/describe.d.ts.map +1 -0
  299. package/dist/tools/describe.js +295 -0
  300. package/dist/tools/describe.js.map +1 -0
  301. package/dist/tools/export-code.d.ts +2 -0
  302. package/dist/tools/export-code.d.ts.map +1 -0
  303. package/dist/tools/export-code.js +34 -0
  304. package/dist/tools/export-code.js.map +1 -0
  305. package/dist/tools/export-tailwind-config.d.ts +27 -0
  306. package/dist/tools/export-tailwind-config.d.ts.map +1 -0
  307. package/dist/tools/export-tailwind-config.js +215 -0
  308. package/dist/tools/export-tailwind-config.js.map +1 -0
  309. package/dist/tools/icons.d.ts +5 -0
  310. package/dist/tools/icons.d.ts.map +1 -0
  311. package/dist/tools/icons.js +192 -0
  312. package/dist/tools/icons.js.map +1 -0
  313. package/dist/tools/index.d.ts +7 -0
  314. package/dist/tools/index.d.ts.map +1 -0
  315. package/dist/tools/index.js +6 -0
  316. package/dist/tools/index.js.map +1 -0
  317. package/dist/tools/modify.d.ts +22 -0
  318. package/dist/tools/modify.d.ts.map +1 -0
  319. package/dist/tools/modify.js +653 -0
  320. package/dist/tools/modify.js.map +1 -0
  321. package/dist/tools/patterns.d.ts +2 -0
  322. package/dist/tools/patterns.d.ts.map +1 -0
  323. package/dist/tools/patterns.js +173 -0
  324. package/dist/tools/patterns.js.map +1 -0
  325. package/dist/tools/read.d.ts +17 -0
  326. package/dist/tools/read.d.ts.map +1 -0
  327. package/dist/tools/read.js +379 -0
  328. package/dist/tools/read.js.map +1 -0
  329. package/dist/tools/registry.d.ts +3 -0
  330. package/dist/tools/registry.d.ts.map +1 -0
  331. package/dist/tools/registry.js +124 -0
  332. package/dist/tools/registry.js.map +1 -0
  333. package/dist/tools/schema.d.ts +46 -0
  334. package/dist/tools/schema.d.ts.map +1 -0
  335. package/dist/tools/schema.js +17 -0
  336. package/dist/tools/schema.js.map +1 -0
  337. package/dist/tools/structure.d.ts +18 -0
  338. package/dist/tools/structure.d.ts.map +1 -0
  339. package/dist/tools/structure.js +345 -0
  340. package/dist/tools/structure.js.map +1 -0
  341. package/dist/tools/variables.d.ts +12 -0
  342. package/dist/tools/variables.d.ts.map +1 -0
  343. package/dist/tools/variables.js +190 -0
  344. package/dist/tools/variables.js.map +1 -0
  345. package/dist/tools/vector.d.ts +15 -0
  346. package/dist/tools/vector.d.ts.map +1 -0
  347. package/dist/tools/vector.js +312 -0
  348. package/dist/tools/vector.js.map +1 -0
  349. package/dist/types.d.ts +29 -0
  350. package/dist/types.d.ts.map +1 -0
  351. package/dist/types.js +2 -0
  352. package/dist/types.js.map +1 -0
  353. package/dist/undo.d.ts +33 -0
  354. package/dist/undo.d.ts.map +1 -0
  355. package/dist/undo.js +98 -0
  356. package/dist/undo.js.map +1 -0
  357. package/dist/vector.d.ts +14 -0
  358. package/dist/vector.d.ts.map +1 -0
  359. package/dist/vector.js +334 -0
  360. package/dist/vector.js.map +1 -0
  361. package/dist/xpath.d.ts +8 -0
  362. package/dist/xpath.d.ts.map +1 -0
  363. package/dist/xpath.js +199 -0
  364. package/dist/xpath.js.map +1 -0
  365. package/package.json +54 -0
  366. package/src/canvaskit.ts +66 -0
  367. package/src/clipboard.ts +444 -0
  368. package/src/color.ts +83 -0
  369. package/src/constants.ts +277 -0
  370. package/src/copy.ts +75 -0
  371. package/src/fig-export-worker.ts +28 -0
  372. package/src/fig-export.ts +321 -0
  373. package/src/figma-api.ts +1297 -0
  374. package/src/figma-rest-import.ts +443 -0
  375. package/src/fonts.ts +369 -0
  376. package/src/geometry.ts +73 -0
  377. package/src/global.d.ts +20 -0
  378. package/src/index.ts +247 -0
  379. package/src/kiwi/codec.ts +614 -0
  380. package/src/kiwi/fig-file.ts +248 -0
  381. package/src/kiwi/fig-import.ts +300 -0
  382. package/src/kiwi/fig-parse-worker.ts +213 -0
  383. package/src/kiwi/instance-overrides.ts +682 -0
  384. package/src/kiwi/kiwi-convert.ts +771 -0
  385. package/src/kiwi/kiwi-schema/bb.ts +244 -0
  386. package/src/kiwi/kiwi-schema/binary.ts +102 -0
  387. package/src/kiwi/kiwi-schema/index.ts +5 -0
  388. package/src/kiwi/kiwi-schema/js.ts +339 -0
  389. package/src/kiwi/kiwi-schema/parser.ts +287 -0
  390. package/src/kiwi/kiwi-schema/schema.ts +24 -0
  391. package/src/kiwi/kiwi-schema/util.ts +10 -0
  392. package/src/kiwi/protocol.ts +237 -0
  393. package/src/kiwi/schema.ts +2286 -0
  394. package/src/kiwi-serialize.ts +467 -0
  395. package/src/layout.ts +608 -0
  396. package/src/profiler/draw-call-counter.ts +43 -0
  397. package/src/profiler/frame-capture.ts +139 -0
  398. package/src/profiler/frame-stats.ts +122 -0
  399. package/src/profiler/gpu-timer.ts +93 -0
  400. package/src/profiler/hud-renderer.ts +242 -0
  401. package/src/profiler/index.ts +8 -0
  402. package/src/profiler/phase-timer.ts +62 -0
  403. package/src/profiler/render-profiler.ts +163 -0
  404. package/src/render/components.ts +69 -0
  405. package/src/render/emoji-icons.ts +60 -0
  406. package/src/render/export-code.ts +366 -0
  407. package/src/render/export-jsx.ts +633 -0
  408. package/src/render/index.ts +38 -0
  409. package/src/render/jsx-runtime.ts +36 -0
  410. package/src/render/mini-react.ts +30 -0
  411. package/src/render/render-jsx.ts +74 -0
  412. package/src/render/renderer.ts +508 -0
  413. package/src/render/tailwind.ts +114 -0
  414. package/src/render/tree.ts +137 -0
  415. package/src/render-image.ts +139 -0
  416. package/src/renderer/effects.ts +64 -0
  417. package/src/renderer/fills.ts +248 -0
  418. package/src/renderer/index.ts +1 -0
  419. package/src/renderer/label-cache.ts +114 -0
  420. package/src/renderer/labels.ts +168 -0
  421. package/src/renderer/lru-cache.ts +71 -0
  422. package/src/renderer/overlays.ts +1089 -0
  423. package/src/renderer/renderer.ts +1226 -0
  424. package/src/renderer/rulers.ts +230 -0
  425. package/src/renderer/scene.ts +554 -0
  426. package/src/renderer/shapes.ts +193 -0
  427. package/src/renderer/strokes.ts +221 -0
  428. package/src/rpc/commands.ts +667 -0
  429. package/src/rpc/index.ts +25 -0
  430. package/src/scene-graph.ts +1254 -0
  431. package/src/snap.ts +122 -0
  432. package/src/style-runs.ts +234 -0
  433. package/src/svg-export/defs.ts +255 -0
  434. package/src/svg-export/index.ts +427 -0
  435. package/src/svg-export/paths.ts +212 -0
  436. package/src/svg-node.ts +62 -0
  437. package/src/text-editor.ts +349 -0
  438. package/src/tools/ai-adapter.ts +144 -0
  439. package/src/tools/analyze.ts +786 -0
  440. package/src/tools/create.ts +206 -0
  441. package/src/tools/describe.ts +280 -0
  442. package/src/tools/export-code.ts +41 -0
  443. package/src/tools/export-tailwind-config.ts +266 -0
  444. package/src/tools/icons.ts +213 -0
  445. package/src/tools/index.ts +6 -0
  446. package/src/tools/modify.ts +651 -0
  447. package/src/tools/patterns.ts +189 -0
  448. package/src/tools/read.ts +401 -0
  449. package/src/tools/registry.ts +155 -0
  450. package/src/tools/schema.ts +65 -0
  451. package/src/tools/structure.ts +348 -0
  452. package/src/tools/variables.ts +193 -0
  453. package/src/tools/vector.ts +311 -0
  454. package/src/types.ts +32 -0
  455. package/src/undo.ts +126 -0
  456. package/src/vector.ts +413 -0
  457. package/src/xpath.ts +259 -0
@@ -0,0 +1,189 @@
1
+ import { defineTool } from './schema'
2
+
3
+ interface DesignPattern {
4
+ jsx: string
5
+ notes: string
6
+ }
7
+
8
+ const PATTERNS: Record<string, DesignPattern> = {
9
+ card: {
10
+ jsx: `<Frame name="Card" w={380} h="hug" flex="col" gap={16} p={20} bg="#FFFFFF" rounded={12} shadow="0 1 3 #00000015">
11
+ <Text size={18} weight="bold" color="#111827">Card Title</Text>
12
+ <Text size={14} color="#6B7280">Description text goes here with enough detail.</Text>
13
+ <Frame flex="row" gap={8} items="center">
14
+ <Frame w={80} h={36} flex="row" items="center" justify="center" bg="#3B82F6" rounded={8}>
15
+ <Text size={13} weight="bold" color="#FFFFFF">Action</Text>
16
+ </Frame>
17
+ <Text size={13} color="#9CA3AF">Secondary</Text>
18
+ </Frame>
19
+ </Frame>`,
20
+ notes: '380px is the sweet spot for cards. p={20} inner < p={24} outer. gap={16} groups content. Button hugs content at 80px.'
21
+ },
22
+
23
+ 'nav-bar': {
24
+ jsx: `<Frame name="NavBar" w={1440} h={56} flex="row" items="center" px={24} bg="#FFFFFF" shadow="0 1 2 #0000000A">
25
+ <Text size={18} weight="bold" color="#111827">Logo</Text>
26
+ <Frame grow={1} />
27
+ <Frame flex="row" gap={24} items="center">
28
+ <Text size={14} color="#374151">Features</Text>
29
+ <Text size={14} color="#374151">Pricing</Text>
30
+ <Frame w={80} h={36} flex="row" items="center" justify="center" bg="#3B82F6" rounded={8}>
31
+ <Text size={13} weight="bold" color="#FFFFFF">Sign Up</Text>
32
+ </Frame>
33
+ </Frame>
34
+ </Frame>`,
35
+ notes: 'h={56} standard nav. grow={1} spacer pushes nav items right. Single CTA button stands out with color.'
36
+ },
37
+
38
+ 'hero-section': {
39
+ jsx: `<Frame name="Hero" w={1440} h="hug" flex="col" items="center" gap={24} py={80} px={24} bg="#F9FAFB">
40
+ <Text size={40} weight="bold" color="#111827" textAlign="center">Main Headline Here</Text>
41
+ <Text size={18} color="#6B7280" textAlign="center">Supporting text that explains the value proposition in one or two lines.</Text>
42
+ <Frame flex="row" gap={12} items="center">
43
+ <Frame w={140} h={44} flex="row" items="center" justify="center" bg="#3B82F6" rounded={8}>
44
+ <Text size={15} weight="bold" color="#FFFFFF">Get Started</Text>
45
+ </Frame>
46
+ <Frame w={120} h={44} flex="row" items="center" justify="center" rounded={8} stroke="#D1D5DB" strokeWidth={1}>
47
+ <Text size={15} color="#374151">Learn More</Text>
48
+ </Frame>
49
+ </Frame>
50
+ </Frame>`,
51
+ notes: 'py={80} gives breathing room. size={40} hero heading. Two CTAs: primary filled, secondary outlined.'
52
+ },
53
+
54
+ form: {
55
+ jsx: `<Frame name="Form" w={400} h="hug" flex="col" gap={20} p={24} bg="#FFFFFF" rounded={12}>
56
+ <Text size={20} weight="bold" color="#111827">Form Title</Text>
57
+ <Frame flex="col" gap={6} w="fill">
58
+ <Text size={13} weight="medium" color="#374151">Label</Text>
59
+ <Frame w="fill" h={40} flex="row" items="center" px={12} rounded={8} stroke="#D1D5DB" strokeWidth={1}>
60
+ <Text size={14} color="#9CA3AF">Placeholder text</Text>
61
+ </Frame>
62
+ </Frame>
63
+ <Frame w="fill" h={44} flex="row" items="center" justify="center" bg="#3B82F6" rounded={8}>
64
+ <Text size={15} weight="bold" color="#FFFFFF">Submit</Text>
65
+ </Frame>
66
+ </Frame>`,
67
+ notes: 'w={400} form width. Input h={40}. Label size={13} above input. Submit button w="fill" spans full width.'
68
+ },
69
+
70
+ 'list-item': {
71
+ jsx: `<Frame name="ListItem" w={380} h={64} flex="row" items="center" gap={12} px={16} bg="#FFFFFF">
72
+ <Ellipse w={40} h={40} bg="#E5E7EB" />
73
+ <Frame flex="col" gap={2} grow={1}>
74
+ <Text size={14} weight="bold" color="#111827">Item Title</Text>
75
+ <Text size={12} color="#9CA3AF">Subtitle or metadata</Text>
76
+ </Frame>
77
+ <Text size={12} color="#9CA3AF">→</Text>
78
+ </Frame>`,
79
+ notes: 'h={64} standard list item. Avatar 40px. grow={1} on text column. Arrow as navigation hint.'
80
+ },
81
+
82
+ modal: {
83
+ jsx: `<Frame name="ModalBackdrop" w={1440} h={900} items="center" justify="center" flex="col" bg="#00000040">
84
+ <Frame name="Modal" w={480} h="hug" flex="col" gap={20} p={24} bg="#FFFFFF" rounded={16} shadow="0 4 24 #00000020">
85
+ <Frame flex="row" items="center" w="fill">
86
+ <Text size={18} weight="bold" color="#111827" grow={1}>Modal Title</Text>
87
+ <Text size={18} color="#9CA3AF">✕</Text>
88
+ </Frame>
89
+ <Text size={14} color="#6B7280">Modal body content goes here.</Text>
90
+ <Frame flex="row" gap={8} justify="end" w="fill">
91
+ <Frame w={80} h={40} flex="row" items="center" justify="center" rounded={8} stroke="#D1D5DB" strokeWidth={1}>
92
+ <Text size={14} color="#374151">Cancel</Text>
93
+ </Frame>
94
+ <Frame w={80} h={40} flex="row" items="center" justify="center" bg="#3B82F6" rounded={8}>
95
+ <Text size={14} weight="bold" color="#FFFFFF">Save</Text>
96
+ </Frame>
97
+ </Frame>
98
+ </Frame>
99
+ </Frame>`,
100
+ notes: 'w={480} modal. Backdrop with 25% black. Close button top-right. Actions right-aligned.'
101
+ },
102
+
103
+ sidebar: {
104
+ jsx: `<Frame name="Sidebar" w={260} h={900} flex="col" gap={4} py={16} bg="#F9FAFB" stroke="#E5E7EB" strokeWidth={1}>
105
+ <Frame flex="row" items="center" gap={8} px={16} pb={16}>
106
+ <Ellipse w={32} h={32} bg="#3B82F6" />
107
+ <Text size={14} weight="bold" color="#111827">App Name</Text>
108
+ </Frame>
109
+ {['☰ Dashboard', '○ Users', '◆ Settings'].map(item => (
110
+ <Frame w="fill" h={40} flex="row" items="center" px={16} rounded={8}>
111
+ <Text size={14} color="#374151">{item}</Text>
112
+ </Frame>
113
+ ))}
114
+ </Frame>`,
115
+ notes: 'w={260} sidebar. h={40} nav items. px={16} consistent indent. Unicode icons for nav items.'
116
+ },
117
+
118
+ 'dashboard-grid': {
119
+ jsx: `<Frame name="DashboardGrid" w={1080} h="hug" flex="row" gap={20} wrap>
120
+ {['Revenue', 'Users', 'Orders', 'Growth'].map(label => (
121
+ <Frame w={250} h="hug" flex="col" gap={8} p={20} bg="#FFFFFF" rounded={12} shadow="0 1 3 #00000010">
122
+ <Text size={12} color="#9CA3AF">{label}</Text>
123
+ <Text size={28} weight="bold" color="#111827">1,234</Text>
124
+ <Text size={12} color="#10B981">↑ 12%</Text>
125
+ </Frame>
126
+ ))}
127
+ </Frame>`,
128
+ notes: 'Stat cards at 250px in a wrapping row. gap={20} between cards. Metric hierarchy: label → value → trend.'
129
+ },
130
+
131
+ 'pricing-table': {
132
+ jsx: `<Frame name="PricingTable" w={1080} h="hug" flex="row" gap={24} items="start" justify="center">
133
+ {[{name:'Basic',price:'$9'},{name:'Pro',price:'$29'},{name:'Enterprise',price:'$99'}].map(plan => (
134
+ <Frame w={320} h="hug" flex="col" gap={16} p={24} bg="#FFFFFF" rounded={12} stroke="#E5E7EB" strokeWidth={1}>
135
+ <Text size={16} weight="bold" color="#111827">{plan.name}</Text>
136
+ <Text size={32} weight="bold" color="#111827">{plan.price}<Text size={14} color="#9CA3AF">/mo</Text></Text>
137
+ <Frame w="fill" h={1} bg="#E5E7EB" />
138
+ {['Feature one', 'Feature two', 'Feature three'].map(f => (
139
+ <Frame flex="row" gap={8} items="center"><Text size={14} color="#10B981">✓</Text><Text size={14} color="#374151">{f}</Text></Frame>
140
+ ))}
141
+ <Frame w="fill" h={44} flex="row" items="center" justify="center" bg="#3B82F6" rounded={8}>
142
+ <Text size={14} weight="bold" color="#FFFFFF">Choose Plan</Text>
143
+ </Frame>
144
+ </Frame>
145
+ ))}
146
+ </Frame>`,
147
+ notes: 'Three columns at 320px. Price hierarchy: large number + small suffix. Divider separates price from features.'
148
+ },
149
+
150
+ 'profile-header': {
151
+ jsx: `<Frame name="ProfileHeader" w={400} h="hug" flex="col" items="center" gap={16} p={24} bg="#FFFFFF" rounded={12}>
152
+ <Ellipse w={64} h={64} bg="#E5E7EB" />
153
+ <Frame flex="col" gap={4} items="center">
154
+ <Text size={18} weight="bold" color="#111827">User Name</Text>
155
+ <Text size={14} color="#9CA3AF">user@example.com</Text>
156
+ </Frame>
157
+ <Frame flex="row" gap={32}>
158
+ <Frame flex="col" items="center" gap={2}>
159
+ <Text size={18} weight="bold" color="#111827">128</Text>
160
+ <Text size={12} color="#9CA3AF">Posts</Text>
161
+ </Frame>
162
+ <Frame flex="col" items="center" gap={2}>
163
+ <Text size={18} weight="bold" color="#111827">1.2k</Text>
164
+ <Text size={12} color="#9CA3AF">Followers</Text>
165
+ </Frame>
166
+ </Frame>
167
+ </Frame>`,
168
+ notes: 'Centered layout for profile. Avatar 64px. Stats in a row with gap={32} for visual separation.'
169
+ },
170
+
171
+ }
172
+
173
+ export const getDesignPattern = defineTool({
174
+ name: 'get_design_pattern',
175
+ description: 'Get a reference JSX template for a common UI pattern. Call BEFORE rendering to see recommended structure, sizing, and spacing.',
176
+ params: {
177
+ pattern: {
178
+ type: 'string',
179
+ required: true,
180
+ description: 'Pattern name',
181
+ enum: ['card', 'nav-bar', 'hero-section', 'form', 'list-item', 'modal', 'sidebar', 'dashboard-grid', 'pricing-table', 'profile-header']
182
+ }
183
+ },
184
+ execute: (_figma, { pattern }) => {
185
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive: runtime key may not exist
186
+ const p: DesignPattern | undefined = PATTERNS[pattern]
187
+ return p ?? { error: `Unknown pattern "${pattern}". Available: ${Object.keys(PATTERNS).join(', ')}` }
188
+ }
189
+ })
@@ -0,0 +1,401 @@
1
+ import { createTwoFilesPatch } from 'diff'
2
+
3
+ import { sceneNodeToJSX } from '../render/export-jsx'
4
+ import { defineTool, nodeSummary, nodeToResult } from './schema'
5
+ import { queryByXPath } from '../xpath'
6
+
7
+ import type { FigmaNodeProxy } from '../figma-api'
8
+ import type { Fill } from '../scene-graph'
9
+
10
+ export const getSelection = defineTool({
11
+ name: 'get_selection',
12
+ description: 'Get details about currently selected nodes.',
13
+ params: {},
14
+ execute: (figma) => {
15
+ const sel = figma.currentPage.selection
16
+ return { selection: sel.map(nodeToResult) }
17
+ }
18
+ })
19
+
20
+ export const getPageTree = defineTool({
21
+ name: 'get_page_tree',
22
+ description:
23
+ 'Get the node tree of the current page. Returns all nodes with hierarchy, types, positions, and sizes.',
24
+ params: {},
25
+ execute: (figma) => {
26
+ const page = figma.currentPage
27
+ return {
28
+ page: page.name,
29
+ children: page.children.map(nodeToResult)
30
+ }
31
+ }
32
+ })
33
+
34
+ export const getNode = defineTool({
35
+ name: 'get_node',
36
+ description: 'Get detailed properties of a node by ID.',
37
+ params: {
38
+ id: { type: 'string', description: 'Node ID', required: true }
39
+ },
40
+ execute: (figma, { id }) => {
41
+ const node = figma.getNodeById(id)
42
+ if (!node) return { error: `Node "${id}" not found` }
43
+ return nodeToResult(node)
44
+ }
45
+ })
46
+
47
+ export const findNodes = defineTool({
48
+ name: 'find_nodes',
49
+ description: 'Find nodes by name pattern and/or type.',
50
+ params: {
51
+ name: { type: 'string', description: 'Name substring to match (case-insensitive)' },
52
+ type: {
53
+ type: 'string',
54
+ description: 'Node type filter',
55
+ enum: [
56
+ 'FRAME',
57
+ 'RECTANGLE',
58
+ 'ELLIPSE',
59
+ 'TEXT',
60
+ 'LINE',
61
+ 'STAR',
62
+ 'POLYGON',
63
+ 'SECTION',
64
+ 'GROUP',
65
+ 'COMPONENT',
66
+ 'INSTANCE',
67
+ 'VECTOR'
68
+ ]
69
+ }
70
+ },
71
+ execute: (figma, args) => {
72
+ const page = figma.currentPage
73
+ const matches = page.findAll((node) => {
74
+ if (args.type && node.type !== args.type) return false
75
+ if (args.name && !node.name.toLowerCase().includes(args.name.toLowerCase())) return false
76
+ return true
77
+ })
78
+ return { count: matches.length, nodes: matches.map(nodeSummary) }
79
+ }
80
+ })
81
+
82
+ export const getComponents = defineTool({
83
+ name: 'get_components',
84
+ description: 'List all components in the document, optionally filtered by name.',
85
+ params: {
86
+ name: { type: 'string', description: 'Filter by name (case-insensitive substring)' },
87
+ limit: { type: 'number', description: 'Max results (default: 50)' }
88
+ },
89
+ execute: (figma, args) => {
90
+ const limit = args.limit ?? 50
91
+ const nameFilter = args.name?.toLowerCase()
92
+ const components: { id: string; name: string; type: string; page: string }[] = []
93
+
94
+ for (const page of figma.root.children) {
95
+ if (components.length >= limit) break
96
+ page.findAll((node) => {
97
+ if (components.length >= limit) return false
98
+ if (node.type !== 'COMPONENT' && node.type !== 'COMPONENT_SET') return false
99
+ if (nameFilter && !node.name.toLowerCase().includes(nameFilter)) return false
100
+ components.push({ id: node.id, name: node.name, type: node.type, page: page.name })
101
+ return false
102
+ })
103
+ }
104
+
105
+ return { count: components.length, components }
106
+ }
107
+ })
108
+
109
+ export const listPages = defineTool({
110
+ name: 'list_pages',
111
+ description: 'List all pages in the document.',
112
+ params: {},
113
+ execute: (figma) => {
114
+ const pages = figma.root.children
115
+ return {
116
+ current: figma.currentPage.name,
117
+ pages: pages.map((p) => ({ id: p.id, name: p.name }))
118
+ }
119
+ }
120
+ })
121
+
122
+ export const switchPage = defineTool({
123
+ name: 'switch_page',
124
+ mutates: true,
125
+ description: 'Switch to a different page by name or ID.',
126
+ params: {
127
+ page: { type: 'string', description: 'Page name or ID', required: true }
128
+ },
129
+ execute: (figma, { page }) => {
130
+ const target = figma.root.children.find((p) => p.name === page) ?? figma.getNodeById(page)
131
+ if (!target) return { error: `Page "${page}" not found` }
132
+ figma.currentPage = target
133
+ return { page: target.name, id: target.id }
134
+ }
135
+ })
136
+
137
+ export const getCurrentPage = defineTool({
138
+ name: 'get_current_page',
139
+ description: 'Get the current page name and ID.',
140
+ params: {},
141
+ execute: (figma) => {
142
+ return { id: figma.currentPage.id, name: figma.currentPage.name }
143
+ }
144
+ })
145
+
146
+ export const pageBounds = defineTool({
147
+ name: 'page_bounds',
148
+ description: 'Get bounding box of all objects on the current page.',
149
+ params: {},
150
+ execute: (figma) => {
151
+ const page = figma.currentPage
152
+ let minX = Infinity
153
+ let minY = Infinity
154
+ let maxX = -Infinity
155
+ let maxY = -Infinity
156
+ for (const child of page.children) {
157
+ const bounds = child.absoluteBoundingBox
158
+ minX = Math.min(minX, bounds.x)
159
+ minY = Math.min(minY, bounds.y)
160
+ maxX = Math.max(maxX, bounds.x + bounds.width)
161
+ maxY = Math.max(maxY, bounds.y + bounds.height)
162
+ }
163
+ if (minX === Infinity) return { x: 0, y: 0, width: 0, height: 0 }
164
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }
165
+ }
166
+ })
167
+
168
+ export const selectNodes = defineTool({
169
+ name: 'select_nodes',
170
+ mutates: true,
171
+ description: 'Select one or more nodes by ID.',
172
+ params: {
173
+ ids: { type: 'string[]', description: 'Node IDs to select', required: true }
174
+ },
175
+ execute: (figma, { ids }) => {
176
+ figma.currentPage.selection = ids
177
+ .map((id) => figma.getNodeById(id))
178
+ .filter((n): n is FigmaNodeProxy => n !== null)
179
+ return { selected: ids }
180
+ }
181
+ })
182
+
183
+ export const listFonts = defineTool({
184
+ name: 'list_fonts',
185
+ description: 'List fonts used in the current page.',
186
+ params: {
187
+ family: { type: 'string', description: 'Filter by family name (substring)' }
188
+ },
189
+ execute: (figma, args) => {
190
+ const fonts = new Map<string, Set<number>>()
191
+ const page = figma.currentPage
192
+ page.findAll((node) => {
193
+ if (node.type === 'TEXT') {
194
+ const raw = figma.graph.getNode(node.id)
195
+ if (raw) {
196
+ const key = raw.fontFamily
197
+ if (!fonts.has(key)) fonts.set(key, new Set())
198
+ fonts.get(key)?.add(raw.fontWeight)
199
+ }
200
+ }
201
+ return false
202
+ })
203
+ let result = [...fonts.entries()].map(([family, weights]) => ({
204
+ family,
205
+ weights: [...weights].sort((a, b) => a - b)
206
+ }))
207
+ if (args.family) {
208
+ const q = args.family.toLowerCase()
209
+ result = result.filter((f) => f.family.toLowerCase().includes(q))
210
+ }
211
+ return { count: result.length, fonts: result }
212
+ }
213
+ })
214
+
215
+ export const queryNodes = defineTool({
216
+ name: 'query_nodes',
217
+ description: `Query nodes using XPath selectors. Node types are element names (FRAME, TEXT, RECTANGLE, ELLIPSE, etc.). Attributes: name, width, height, x, y, visible, opacity, cornerRadius, fontSize, fontFamily, fontWeight, layoutMode, itemSpacing, paddingTop/Right/Bottom/Left, strokeWeight, rotation, locked, blendMode, text, lineHeight, letterSpacing.
218
+
219
+ Examples:
220
+ //FRAME — all frames
221
+ //FRAME[@width < 300] — frames narrower than 300px
222
+ //COMPONENT[starts-with(@name, 'Button')] — components starting with "Button"
223
+ //SECTION/FRAME — direct frame children of sections
224
+ //SECTION//TEXT — all text nodes inside sections
225
+ //*[@cornerRadius > 0] — any node with corner radius
226
+ //TEXT[contains(@text, 'Hello')] — text nodes containing "Hello"`,
227
+ params: {
228
+ selector: { type: 'string', description: 'XPath selector', required: true },
229
+ page: { type: 'string', description: 'Page name (default: current page)' },
230
+ limit: { type: 'number', description: 'Max results (default: 1000)' }
231
+ },
232
+ execute: async (figma, args) => {
233
+ try {
234
+ const nodes = await queryByXPath(figma.graph, args.selector, {
235
+ page: args.page ?? figma.currentPage.name,
236
+ limit: args.limit
237
+ })
238
+ return {
239
+ count: nodes.length,
240
+ nodes: nodes.map((n) => ({ id: n.id, name: n.name, type: n.type }))
241
+ }
242
+ } catch (err) {
243
+ return { error: `XPath error: ${err instanceof Error ? err.message : String(err)}` }
244
+ }
245
+ }
246
+ })
247
+
248
+ const MAX_JSX_LENGTH = 12_000
249
+
250
+ export const getJsx = defineTool({
251
+ name: 'get_jsx',
252
+ description:
253
+ 'Get JSX representation of a node and its children. Compact round-trip format — same syntax as the render tool.',
254
+ params: {
255
+ id: { type: 'string', description: 'Node ID', required: true }
256
+ },
257
+ execute: (figma, { id }) => {
258
+ const node = figma.getNodeById(id)
259
+ if (!node) return { error: `Node "${id}" not found` }
260
+ const jsx = sceneNodeToJSX(id, figma.graph)
261
+ if (jsx.length > MAX_JSX_LENGTH) {
262
+ return {
263
+ id,
264
+ name: node.name,
265
+ jsx: jsx.slice(0, MAX_JSX_LENGTH),
266
+ truncated: true,
267
+ totalLength: jsx.length
268
+ }
269
+ }
270
+ return { id, name: node.name, jsx }
271
+ }
272
+ })
273
+
274
+ export const diffJsx = defineTool({
275
+ name: 'diff_jsx',
276
+ description:
277
+ 'Structural diff between two nodes in JSX format. Shows added/removed children, changed props.',
278
+ params: {
279
+ from: { type: 'string', description: 'Source node ID', required: true },
280
+ to: { type: 'string', description: 'Target node ID', required: true }
281
+ },
282
+ execute: (figma, { from, to }) => {
283
+ const fromNode = figma.getNodeById(from)
284
+ if (!fromNode) return { error: `Node "${from}" not found` }
285
+ const toNode = figma.getNodeById(to)
286
+ if (!toNode) return { error: `Node "${to}" not found` }
287
+
288
+ const fromJsx = sceneNodeToJSX(from, figma.graph)
289
+ const toJsx = sceneNodeToJSX(to, figma.graph)
290
+
291
+ if (fromJsx === toJsx) return { diff: null, message: 'No differences' }
292
+
293
+ const patch = createTwoFilesPatch(
294
+ fromNode.name,
295
+ toNode.name,
296
+ fromJsx,
297
+ toJsx,
298
+ 'source',
299
+ 'target',
300
+ { context: 3 }
301
+ )
302
+ return { diff: patch }
303
+ }
304
+ })
305
+
306
+ function fillToHex(fill: Fill): string | null {
307
+ if (fill.type !== 'SOLID' || !fill.visible) return null
308
+ const { r, g, b } = fill.color
309
+ return '#' + [r, g, b].map(c => Math.round(c * 255).toString(16).padStart(2, '0')).join('').toUpperCase()
310
+ }
311
+
312
+ function countDescendants(node: FigmaNodeProxy): number {
313
+ let count = 0
314
+ const walk = (n: FigmaNodeProxy) => { count++; for (const c of n.children) walk(c) }
315
+ walk(node)
316
+ return count - 1 // exclude self
317
+ }
318
+
319
+ export const designOverview = defineTool({
320
+ name: 'design_overview',
321
+ description: 'Compact overview of the entire design: pages, top-level frames with names/sizes, node counts by type, font list, color palette summary. Use this FIRST when analyzing a large imported design.',
322
+ params: {},
323
+ execute: (figma) => {
324
+ const pages = figma.root.children.map(p => ({
325
+ id: p.id, name: p.name, isCurrent: p.id === figma.currentPage.id
326
+ }))
327
+ const page = figma.currentPage
328
+ const screens = page.children.map(c => ({
329
+ id: c.id, name: c.name, type: c.type,
330
+ width: Math.round(c.width), height: Math.round(c.height),
331
+ childCount: countDescendants(c)
332
+ }))
333
+ const byType: Record<string, number> = {}
334
+ let totalNodes = 0
335
+ const fonts = new Set<string>()
336
+ const colors = new Map<string, number>()
337
+ page.findAll(node => {
338
+ totalNodes++
339
+ byType[node.type] = (byType[node.type] || 0) + 1
340
+ const raw = figma.graph.getNode(node.id)
341
+ if (!raw) return false
342
+ if (raw.type === 'TEXT' && raw.fontFamily) fonts.add(raw.fontFamily)
343
+ for (const fill of raw.fills) {
344
+ const hex = fillToHex(fill)
345
+ if (hex) colors.set(hex, (colors.get(hex) || 0) + 1)
346
+ }
347
+ return false
348
+ })
349
+ const topColors = [...colors.entries()]
350
+ .sort((a, b) => b[1] - a[1])
351
+ .slice(0, 10)
352
+ .map(([hex]) => hex)
353
+ return {
354
+ pages, screens,
355
+ stats: { totalNodes, byType },
356
+ fonts: [...fonts].sort(),
357
+ topColors
358
+ }
359
+ }
360
+ })
361
+
362
+ export const describeScreen = defineTool({
363
+ name: 'describe_screen',
364
+ description: 'Describe a single screen/frame: layout structure, key components, text content summary, and visual style. Use after design_overview to analyze each screen individually.',
365
+ params: {
366
+ id: { type: 'string', description: 'Node ID of the screen/frame to describe', required: true },
367
+ depth: { type: 'number', description: 'Max depth to traverse (default: 3)' }
368
+ },
369
+ execute: (figma, { id, depth = 3 }) => {
370
+ const node = figma.getNodeById(id)
371
+ if (!node) return { error: `Node "${id}" not found` }
372
+
373
+ const texts: string[] = []
374
+ type ChildSummary = { name: string; type: string; w: number; h: number; text?: string; children?: ChildSummary[] }
375
+
376
+ function walkNode(n: FigmaNodeProxy, d: number): ChildSummary {
377
+ const raw = figma.graph.getNode(n.id)
378
+ const entry: ChildSummary = {
379
+ name: n.name, type: n.type,
380
+ w: Math.round(n.width), h: Math.round(n.height)
381
+ }
382
+ if (n.type === 'TEXT' && raw?.text) {
383
+ const t = raw.text.length > 100 ? raw.text.slice(0, 100) + '...' : raw.text
384
+ entry.text = t
385
+ texts.push(t)
386
+ }
387
+ if (d > 0 && n.children.length > 0) {
388
+ entry.children = n.children.map(c => walkNode(c, d - 1))
389
+ }
390
+ return entry
391
+ }
392
+
393
+ const raw = figma.graph.getNode(id)
394
+ const structure = walkNode(node, depth)
395
+ const layout = raw?.layoutMode
396
+ ? { mode: raw.layoutMode, gap: raw.itemSpacing, padding: [raw.paddingTop, raw.paddingRight, raw.paddingBottom, raw.paddingLeft] }
397
+ : null
398
+
399
+ return { id, name: node.name, width: Math.round(node.width), height: Math.round(node.height), layout, structure, textContent: texts.slice(0, 20) }
400
+ }
401
+ })