decantr 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 (382) hide show
  1. package/AGENTS.md +868 -0
  2. package/CHANGELOG.md +255 -0
  3. package/CLAUDE.md +178 -0
  4. package/LICENSE +21 -0
  5. package/README.md +229 -0
  6. package/cli/art.js +127 -0
  7. package/cli/commands/a11y.js +61 -0
  8. package/cli/commands/audit.js +225 -0
  9. package/cli/commands/build.js +38 -0
  10. package/cli/commands/dev.js +18 -0
  11. package/cli/commands/doctor.js +197 -0
  12. package/cli/commands/figma-sync.js +48 -0
  13. package/cli/commands/figma-tokens.js +55 -0
  14. package/cli/commands/generate.js +26 -0
  15. package/cli/commands/init.js +116 -0
  16. package/cli/commands/lint.js +209 -0
  17. package/cli/commands/mcp.js +530 -0
  18. package/cli/commands/migrate.js +175 -0
  19. package/cli/commands/test.js +38 -0
  20. package/cli/commands/validate.js +354 -0
  21. package/cli/index.js +113 -0
  22. package/package.json +95 -0
  23. package/reference/atoms.md +517 -0
  24. package/reference/behaviors.md +384 -0
  25. package/reference/build-tooling.md +275 -0
  26. package/reference/color-guidelines.md +965 -0
  27. package/reference/component-lifecycle.md +137 -0
  28. package/reference/compound-spacing.md +95 -0
  29. package/reference/decantation-process.md +499 -0
  30. package/reference/dev-server-routes.md +93 -0
  31. package/reference/form-system.md +253 -0
  32. package/reference/i18n.md +336 -0
  33. package/reference/icons.md +576 -0
  34. package/reference/llm-primer.md +953 -0
  35. package/reference/plugins.md +252 -0
  36. package/reference/registry-consumption.md +76 -0
  37. package/reference/router.md +217 -0
  38. package/reference/shells.md +116 -0
  39. package/reference/spatial-guidelines.md +541 -0
  40. package/reference/ssr.md +234 -0
  41. package/reference/state-data.md +215 -0
  42. package/reference/state-patterns.md +166 -0
  43. package/reference/state.md +194 -0
  44. package/reference/style-system.md +110 -0
  45. package/reference/tokens.md +460 -0
  46. package/src/app.js +19 -0
  47. package/src/chart/_animate.js +266 -0
  48. package/src/chart/_base.js +109 -0
  49. package/src/chart/_data.js +209 -0
  50. package/src/chart/_format.js +106 -0
  51. package/src/chart/_interact.js +364 -0
  52. package/src/chart/_palette.js +105 -0
  53. package/src/chart/_renderer.js +52 -0
  54. package/src/chart/_scene.js +262 -0
  55. package/src/chart/_shared.js +371 -0
  56. package/src/chart/index.js +637 -0
  57. package/src/chart/layouts/_layout-base.js +328 -0
  58. package/src/chart/layouts/cartesian.js +148 -0
  59. package/src/chart/layouts/hierarchy.js +562 -0
  60. package/src/chart/layouts/polar.js +101 -0
  61. package/src/chart/renderers/canvas.js +179 -0
  62. package/src/chart/renderers/svg.js +256 -0
  63. package/src/chart/renderers/webgpu.js +715 -0
  64. package/src/chart/types/_type-base.js +26 -0
  65. package/src/chart/types/area.js +134 -0
  66. package/src/chart/types/bar.js +173 -0
  67. package/src/chart/types/box-plot.js +125 -0
  68. package/src/chart/types/bubble.js +63 -0
  69. package/src/chart/types/candlestick.js +115 -0
  70. package/src/chart/types/chord.js +85 -0
  71. package/src/chart/types/combination.js +108 -0
  72. package/src/chart/types/funnel.js +68 -0
  73. package/src/chart/types/gauge.js +163 -0
  74. package/src/chart/types/heatmap.js +98 -0
  75. package/src/chart/types/histogram.js +71 -0
  76. package/src/chart/types/line.js +111 -0
  77. package/src/chart/types/org-chart.js +93 -0
  78. package/src/chart/types/pie.js +81 -0
  79. package/src/chart/types/radar.js +96 -0
  80. package/src/chart/types/radial.js +68 -0
  81. package/src/chart/types/range-area.js +55 -0
  82. package/src/chart/types/range-bar.js +61 -0
  83. package/src/chart/types/sankey.js +73 -0
  84. package/src/chart/types/scatter.js +66 -0
  85. package/src/chart/types/sparkline.js +81 -0
  86. package/src/chart/types/sunburst.js +69 -0
  87. package/src/chart/types/swimlane.js +88 -0
  88. package/src/chart/types/treemap.js +62 -0
  89. package/src/chart/types/waterfall.js +100 -0
  90. package/src/components/_base.js +1658 -0
  91. package/src/components/_behaviors.js +1140 -0
  92. package/src/components/_primitives.js +534 -0
  93. package/src/components/_qr-encoder.js +539 -0
  94. package/src/components/accordion.js +207 -0
  95. package/src/components/affix.js +62 -0
  96. package/src/components/alert-dialog.js +75 -0
  97. package/src/components/alert.js +47 -0
  98. package/src/components/aspect-ratio.js +24 -0
  99. package/src/components/avatar-group.js +55 -0
  100. package/src/components/avatar.js +38 -0
  101. package/src/components/back-top.js +75 -0
  102. package/src/components/badge.js +74 -0
  103. package/src/components/banner.js +68 -0
  104. package/src/components/breadcrumb.js +162 -0
  105. package/src/components/button.js +115 -0
  106. package/src/components/calendar.js +131 -0
  107. package/src/components/card.js +192 -0
  108. package/src/components/carousel.js +98 -0
  109. package/src/components/cascader.js +261 -0
  110. package/src/components/checkbox.js +80 -0
  111. package/src/components/chip.js +81 -0
  112. package/src/components/code-block.js +82 -0
  113. package/src/components/collapsible.js +50 -0
  114. package/src/components/color-palette.js +438 -0
  115. package/src/components/color-picker.js +314 -0
  116. package/src/components/combobox.js +181 -0
  117. package/src/components/command.js +174 -0
  118. package/src/components/comment.js +206 -0
  119. package/src/components/context-menu.js +76 -0
  120. package/src/components/data-table.js +724 -0
  121. package/src/components/date-picker.js +217 -0
  122. package/src/components/date-range-picker.js +244 -0
  123. package/src/components/datetime-picker.js +271 -0
  124. package/src/components/descriptions.js +68 -0
  125. package/src/components/drawer.js +179 -0
  126. package/src/components/dropdown.js +88 -0
  127. package/src/components/empty.js +41 -0
  128. package/src/components/float-button.js +90 -0
  129. package/src/components/form.js +106 -0
  130. package/src/components/hover-card.js +49 -0
  131. package/src/components/icon.js +87 -0
  132. package/src/components/image.js +97 -0
  133. package/src/components/index.js +117 -0
  134. package/src/components/input-group.js +75 -0
  135. package/src/components/input-number.js +155 -0
  136. package/src/components/input-otp.js +178 -0
  137. package/src/components/input.js +91 -0
  138. package/src/components/kbd.js +36 -0
  139. package/src/components/label.js +25 -0
  140. package/src/components/list.js +118 -0
  141. package/src/components/masked-input.js +236 -0
  142. package/src/components/mentions.js +165 -0
  143. package/src/components/menu.js +259 -0
  144. package/src/components/message.js +80 -0
  145. package/src/components/modal.js +147 -0
  146. package/src/components/navigation-menu.js +166 -0
  147. package/src/components/notification.js +84 -0
  148. package/src/components/pagination.js +104 -0
  149. package/src/components/placeholder.js +132 -0
  150. package/src/components/popconfirm.js +70 -0
  151. package/src/components/popover.js +58 -0
  152. package/src/components/progress.js +61 -0
  153. package/src/components/qrcode.js +251 -0
  154. package/src/components/radiogroup.js +120 -0
  155. package/src/components/range-slider.js +176 -0
  156. package/src/components/rate.js +186 -0
  157. package/src/components/resizable.js +83 -0
  158. package/src/components/result.js +57 -0
  159. package/src/components/scroll-area.js +43 -0
  160. package/src/components/segmented.js +97 -0
  161. package/src/components/select.js +165 -0
  162. package/src/components/separator.js +31 -0
  163. package/src/components/shell.js +407 -0
  164. package/src/components/skeleton.js +39 -0
  165. package/src/components/slider.js +141 -0
  166. package/src/components/sortable-list.js +176 -0
  167. package/src/components/space.js +42 -0
  168. package/src/components/spinner.js +112 -0
  169. package/src/components/splitter.js +147 -0
  170. package/src/components/statistic.js +136 -0
  171. package/src/components/steps.js +99 -0
  172. package/src/components/switch.js +95 -0
  173. package/src/components/table.js +44 -0
  174. package/src/components/tabs.js +216 -0
  175. package/src/components/tag.js +115 -0
  176. package/src/components/textarea.js +82 -0
  177. package/src/components/time-picker.js +153 -0
  178. package/src/components/time-range-picker.js +170 -0
  179. package/src/components/timeline.js +226 -0
  180. package/src/components/toast.js +71 -0
  181. package/src/components/toggle.js +213 -0
  182. package/src/components/tooltip.js +57 -0
  183. package/src/components/tour.js +159 -0
  184. package/src/components/transfer.js +163 -0
  185. package/src/components/tree-select.js +274 -0
  186. package/src/components/tree.js +141 -0
  187. package/src/components/typography.js +136 -0
  188. package/src/components/upload.js +118 -0
  189. package/src/components/visually-hidden.js +20 -0
  190. package/src/components/watermark.js +124 -0
  191. package/src/core/index.js +539 -0
  192. package/src/core/lifecycle.js +69 -0
  193. package/src/css/atoms.js +651 -0
  194. package/src/css/components.js +940 -0
  195. package/src/css/derive.js +1296 -0
  196. package/src/css/index.js +265 -0
  197. package/src/css/runtime.js +268 -0
  198. package/src/css/styles/addons/bioluminescent.js +93 -0
  199. package/src/css/styles/addons/clay.js +70 -0
  200. package/src/css/styles/addons/clean.js +57 -0
  201. package/src/css/styles/addons/command-center.js +143 -0
  202. package/src/css/styles/addons/dopamine.js +83 -0
  203. package/src/css/styles/addons/editorial.js +80 -0
  204. package/src/css/styles/addons/glassmorphism.js +99 -0
  205. package/src/css/styles/addons/liquid-glass.js +105 -0
  206. package/src/css/styles/addons/prismatic.js +100 -0
  207. package/src/css/styles/addons/retro.js +63 -0
  208. package/src/css/styles/auradecantism.js +96 -0
  209. package/src/css/theme-registry.js +444 -0
  210. package/src/data/entity.js +281 -0
  211. package/src/data/index.js +13 -0
  212. package/src/data/persist.js +225 -0
  213. package/src/data/query.js +839 -0
  214. package/src/data/realtime.js +299 -0
  215. package/src/data/url.js +177 -0
  216. package/src/data/worker.js +134 -0
  217. package/src/explorer/archetypes.js +243 -0
  218. package/src/explorer/atoms.js +228 -0
  219. package/src/explorer/charts.js +497 -0
  220. package/src/explorer/components.js +129 -0
  221. package/src/explorer/foundations.js +949 -0
  222. package/src/explorer/icons.js +178 -0
  223. package/src/explorer/patterns.js +247 -0
  224. package/src/explorer/recipes.js +194 -0
  225. package/src/explorer/shared/pattern-examples.js +1337 -0
  226. package/src/explorer/shared/showcase-renderer.js +958 -0
  227. package/src/explorer/shared/spec-table.js +41 -0
  228. package/src/explorer/shared/usage-links.js +87 -0
  229. package/src/explorer/shell-config.js +10 -0
  230. package/src/explorer/shells.js +551 -0
  231. package/src/explorer/styles.js +161 -0
  232. package/src/explorer/tokens.js +262 -0
  233. package/src/explorer/tools.js +525 -0
  234. package/src/form/index.js +804 -0
  235. package/src/i18n/index.js +251 -0
  236. package/src/icons/essential.js +479 -0
  237. package/src/icons/index.js +53 -0
  238. package/src/plugins/index.js +282 -0
  239. package/src/registry/archetypes/content-site.json +71 -0
  240. package/src/registry/archetypes/docs-explorer.json +23 -0
  241. package/src/registry/archetypes/ecommerce.json +104 -0
  242. package/src/registry/archetypes/financial-dashboard.json +77 -0
  243. package/src/registry/archetypes/index.json +41 -0
  244. package/src/registry/archetypes/portfolio.json +82 -0
  245. package/src/registry/archetypes/recipe-community.json +159 -0
  246. package/src/registry/archetypes/saas-dashboard.json +86 -0
  247. package/src/registry/architect/cross-cutting.json +45 -0
  248. package/src/registry/architect/domains/ecommerce.json +294 -0
  249. package/src/registry/architect/domains/financial-services.json +302 -0
  250. package/src/registry/architect/index.json +26 -0
  251. package/src/registry/architect/traits.json +379 -0
  252. package/src/registry/atoms.json +16 -0
  253. package/src/registry/chart-showcase.json +160 -0
  254. package/src/registry/chart.json +136 -0
  255. package/src/registry/components.json +8616 -0
  256. package/src/registry/core.json +216 -0
  257. package/src/registry/css.json +319 -0
  258. package/src/registry/data.json +135 -0
  259. package/src/registry/foundations.json +11 -0
  260. package/src/registry/icons.json +463 -0
  261. package/src/registry/index.json +101 -0
  262. package/src/registry/patterns/activity-feed.json +37 -0
  263. package/src/registry/patterns/article-content.json +27 -0
  264. package/src/registry/patterns/auth-form.json +37 -0
  265. package/src/registry/patterns/author-card.json +20 -0
  266. package/src/registry/patterns/card-grid.json +127 -0
  267. package/src/registry/patterns/category-nav.json +26 -0
  268. package/src/registry/patterns/chart-grid.json +36 -0
  269. package/src/registry/patterns/chat-interface.json +37 -0
  270. package/src/registry/patterns/checklist-card.json +55 -0
  271. package/src/registry/patterns/comparison-panel.json +27 -0
  272. package/src/registry/patterns/component-showcase.json +24 -0
  273. package/src/registry/patterns/contact-form.json +31 -0
  274. package/src/registry/patterns/cta-section.json +20 -0
  275. package/src/registry/patterns/data-table.json +37 -0
  276. package/src/registry/patterns/detail-header.json +83 -0
  277. package/src/registry/patterns/detail-panel.json +27 -0
  278. package/src/registry/patterns/explorer-shell.json +22 -0
  279. package/src/registry/patterns/filter-bar.json +33 -0
  280. package/src/registry/patterns/filter-sidebar.json +27 -0
  281. package/src/registry/patterns/form-sections.json +110 -0
  282. package/src/registry/patterns/goal-tracker.json +27 -0
  283. package/src/registry/patterns/hero.json +107 -0
  284. package/src/registry/patterns/index.json +47 -0
  285. package/src/registry/patterns/kpi-grid.json +36 -0
  286. package/src/registry/patterns/media-gallery.json +20 -0
  287. package/src/registry/patterns/order-history.json +20 -0
  288. package/src/registry/patterns/pagination.json +19 -0
  289. package/src/registry/patterns/photo-to-recipe.json +36 -0
  290. package/src/registry/patterns/pipeline-tracker.json +28 -0
  291. package/src/registry/patterns/post-list.json +27 -0
  292. package/src/registry/patterns/pricing-table.json +32 -0
  293. package/src/registry/patterns/scorecard.json +28 -0
  294. package/src/registry/patterns/search-bar.json +20 -0
  295. package/src/registry/patterns/specimen-grid.json +19 -0
  296. package/src/registry/patterns/stat-card.json +55 -0
  297. package/src/registry/patterns/stats-bar.json +55 -0
  298. package/src/registry/patterns/steps-card.json +55 -0
  299. package/src/registry/patterns/table-of-contents.json +19 -0
  300. package/src/registry/patterns/testimonials.json +21 -0
  301. package/src/registry/patterns/timeline.json +27 -0
  302. package/src/registry/patterns/token-inspector.json +21 -0
  303. package/src/registry/patterns/wizard.json +27 -0
  304. package/src/registry/recipe-auradecantism.json +69 -0
  305. package/src/registry/recipe-clean.json +65 -0
  306. package/src/registry/recipe-command-center.json +78 -0
  307. package/src/registry/router.json +73 -0
  308. package/src/registry/schema/README.md +197 -0
  309. package/src/registry/skeletons.json +259 -0
  310. package/src/registry/state.json +137 -0
  311. package/src/registry/tokens.json +40 -0
  312. package/src/router/hash.js +17 -0
  313. package/src/router/history.js +18 -0
  314. package/src/router/index.js +598 -0
  315. package/src/ssr/index.js +922 -0
  316. package/src/state/arrays.js +181 -0
  317. package/src/state/devtools.js +647 -0
  318. package/src/state/index.js +498 -0
  319. package/src/state/middleware.js +288 -0
  320. package/src/state/scheduler.js +206 -0
  321. package/src/state/store.js +300 -0
  322. package/src/tags/index.js +19 -0
  323. package/src/tannins/auth.js +396 -0
  324. package/src/test/dom.js +352 -0
  325. package/src/test/index.js +62 -0
  326. package/src/test/state.js +306 -0
  327. package/tools/a11y-audit.js +487 -0
  328. package/tools/analyzer.js +315 -0
  329. package/tools/audit.js +706 -0
  330. package/tools/builder.js +1422 -0
  331. package/tools/css-extract.js +188 -0
  332. package/tools/dev-server.js +316 -0
  333. package/tools/dts-gen.js +1260 -0
  334. package/tools/figma-components.js +329 -0
  335. package/tools/figma-patterns.js +516 -0
  336. package/tools/figma-plugin/code.js +453 -0
  337. package/tools/figma-plugin/manifest.json +14 -0
  338. package/tools/figma-plugin/ui.html +268 -0
  339. package/tools/figma-render.js +293 -0
  340. package/tools/figma-tokens.js +712 -0
  341. package/tools/figma-upload.js +318 -0
  342. package/tools/generate.js +738 -0
  343. package/tools/icons.js +133 -0
  344. package/tools/init-templates.js +265 -0
  345. package/tools/install-hooks.sh +5 -0
  346. package/tools/migrations/0.5.0.js +53 -0
  347. package/tools/migrations/0.6.0.js +95 -0
  348. package/tools/minify.js +170 -0
  349. package/tools/pre-commit +4 -0
  350. package/tools/registry.js +662 -0
  351. package/tools/reset-playground.js +61 -0
  352. package/tools/starter-templates/content-site/app.js +49 -0
  353. package/tools/starter-templates/content-site/essence.js +19 -0
  354. package/tools/starter-templates/content-site/pages.js +31 -0
  355. package/tools/starter-templates/ecommerce/app.js +50 -0
  356. package/tools/starter-templates/ecommerce/essence.js +19 -0
  357. package/tools/starter-templates/ecommerce/pages.js +31 -0
  358. package/tools/starter-templates/landing-page/app.js +38 -0
  359. package/tools/starter-templates/landing-page/essence.js +18 -0
  360. package/tools/starter-templates/landing-page/pages.js +21 -0
  361. package/tools/starter-templates/portfolio/app.js +45 -0
  362. package/tools/starter-templates/portfolio/essence.js +19 -0
  363. package/tools/starter-templates/portfolio/pages.js +33 -0
  364. package/tools/starter-templates/saas-dashboard/app.js +70 -0
  365. package/tools/starter-templates/saas-dashboard/essence.js +19 -0
  366. package/tools/starter-templates/saas-dashboard/pages.js +31 -0
  367. package/tools/verify-pack.js +203 -0
  368. package/types/chart.d.ts +77 -0
  369. package/types/components.d.ts +587 -0
  370. package/types/core.d.ts +89 -0
  371. package/types/css.d.ts +149 -0
  372. package/types/data.d.ts +238 -0
  373. package/types/form.d.ts +164 -0
  374. package/types/i18n.d.ts +51 -0
  375. package/types/icons.d.ts +27 -0
  376. package/types/index.d.ts +13 -0
  377. package/types/router.d.ts +116 -0
  378. package/types/ssr.d.ts +102 -0
  379. package/types/state.d.ts +83 -0
  380. package/types/tags.d.ts +62 -0
  381. package/types/tannins.d.ts +63 -0
  382. package/types/test.d.ts +48 -0
@@ -0,0 +1,453 @@
1
+ /**
2
+ * Decantr Figma Plugin — Main Thread.
3
+ * Reads pre-generated token/component/pattern manifests and
4
+ * creates Figma nodes (Variables, Component Sets, Frames).
5
+ */
6
+
7
+ // CDN base for published manifests
8
+ const CDN_BASE = 'https://unpkg.com/decantr@latest/dist/figma';
9
+
10
+ // ============================================================
11
+ // Plugin Entry
12
+ // ============================================================
13
+
14
+ figma.showUI(__html__, { width: 400, height: 520, themeColors: true });
15
+
16
+ figma.ui.onmessage = async (msg) => {
17
+ switch (msg.type) {
18
+ case 'generate':
19
+ await generate(msg.options);
20
+ break;
21
+ case 'generate-from-essence':
22
+ await generateFromEssence(msg.essence, msg.options);
23
+ break;
24
+ case 'cancel':
25
+ figma.closePlugin();
26
+ break;
27
+ }
28
+ };
29
+
30
+ // ============================================================
31
+ // Full Library Generation (Tier 1 & 2)
32
+ // ============================================================
33
+
34
+ async function generate(options) {
35
+ const { style, mode, shape } = options;
36
+
37
+ try {
38
+ figma.ui.postMessage({ type: 'progress', step: 'tokens', percent: 0 });
39
+
40
+ // Step 1: Fetch token manifest
41
+ const tokensUrl = `${CDN_BASE}/tokens/combined.tokens.json`;
42
+ const tokensResp = await fetchJSON(tokensUrl);
43
+ if (!tokensResp) throw new Error('Failed to fetch token manifest from CDN');
44
+
45
+ // Step 2: Create Variable Collections from tokens
46
+ const styleTokens = tokensResp[style];
47
+ if (styleTokens) {
48
+ await createVariableCollection(styleTokens, style);
49
+ }
50
+
51
+ figma.ui.postMessage({ type: 'progress', step: 'tokens', percent: 30 });
52
+
53
+ // Step 3: Create shape tokens
54
+ if (tokensResp.shapes) {
55
+ await createShapeCollection(tokensResp.shapes, shape);
56
+ }
57
+
58
+ figma.ui.postMessage({ type: 'progress', step: 'components', percent: 40 });
59
+
60
+ // Step 4: Fetch and create component sets
61
+ const componentsUrl = `${CDN_BASE}/components.json`;
62
+ const componentsResp = await fetchJSON(componentsUrl);
63
+ if (componentsResp?.componentSets) {
64
+ await createComponentSets(componentsResp.componentSets);
65
+ }
66
+
67
+ figma.ui.postMessage({ type: 'progress', step: 'patterns', percent: 70 });
68
+
69
+ // Step 5: Fetch and create pattern templates
70
+ const patternsUrl = `${CDN_BASE}/patterns.json`;
71
+ const patternsResp = await fetchJSON(patternsUrl);
72
+ if (patternsResp) {
73
+ await createPatternTemplates(patternsResp);
74
+ }
75
+
76
+ figma.ui.postMessage({ type: 'progress', step: 'done', percent: 100 });
77
+ figma.notify('Decantr design system generated successfully!');
78
+
79
+ } catch (e) {
80
+ figma.ui.postMessage({ type: 'error', message: e.message });
81
+ figma.notify(`Error: ${e.message}`, { error: true });
82
+ }
83
+ }
84
+
85
+ // ============================================================
86
+ // Scoped Generation from Essence (Tier 3)
87
+ // ============================================================
88
+
89
+ async function generateFromEssence(essenceStr, options) {
90
+ let essence;
91
+ try {
92
+ essence = JSON.parse(essenceStr);
93
+ } catch {
94
+ figma.ui.postMessage({ type: 'error', message: 'Invalid JSON in essence' });
95
+ return;
96
+ }
97
+
98
+ const style = essence.vintage?.style || options.style || 'auradecantism';
99
+ const mode = essence.vintage?.mode || options.mode || 'dark';
100
+
101
+ try {
102
+ figma.ui.postMessage({ type: 'progress', step: 'tokens', percent: 0 });
103
+
104
+ // Fetch tokens for the specified style only
105
+ const tokensUrl = `${CDN_BASE}/tokens/${style}.tokens.json`;
106
+ const tokens = await fetchJSON(tokensUrl);
107
+ if (tokens) {
108
+ await createVariableCollection(tokens, style);
109
+ }
110
+
111
+ figma.ui.postMessage({ type: 'progress', step: 'patterns', percent: 40 });
112
+
113
+ // Fetch patterns and filter to only those in the structure
114
+ const patternsUrl = `${CDN_BASE}/patterns.json`;
115
+ const patternsResp = await fetchJSON(patternsUrl);
116
+
117
+ if (patternsResp && essence.structure) {
118
+ // Collect all pattern IDs from the essence
119
+ const usedPatterns = new Set();
120
+ for (const page of essence.structure) {
121
+ if (page.blend) {
122
+ for (const item of page.blend) {
123
+ if (typeof item === 'string') usedPatterns.add(item);
124
+ else if (item.cols) item.cols.forEach(c => usedPatterns.add(c));
125
+ }
126
+ }
127
+ }
128
+
129
+ // Filter to only used patterns
130
+ const filteredPatterns = {
131
+ ...patternsResp,
132
+ patterns: (patternsResp.patterns || []).filter(p => usedPatterns.has(p.id)),
133
+ archetypes: [], // Skip archetypes for scoped generation
134
+ };
135
+
136
+ await createPatternTemplates(filteredPatterns);
137
+ }
138
+
139
+ figma.ui.postMessage({ type: 'progress', step: 'pages', percent: 70 });
140
+
141
+ // Generate page frames from structure
142
+ if (essence.structure) {
143
+ await createEssencePages(essence);
144
+ }
145
+
146
+ figma.ui.postMessage({ type: 'progress', step: 'done', percent: 100 });
147
+ figma.notify('Scoped design system generated from Essence!');
148
+
149
+ } catch (e) {
150
+ figma.ui.postMessage({ type: 'error', message: e.message });
151
+ figma.notify(`Error: ${e.message}`, { error: true });
152
+ }
153
+ }
154
+
155
+ // ============================================================
156
+ // Figma Node Creators
157
+ // ============================================================
158
+
159
+ async function createVariableCollection(dtcg, styleName) {
160
+ const collection = figma.variables.createVariableCollection(
161
+ dtcg.$name || `Decantr — ${styleName}`
162
+ );
163
+
164
+ // Rename default mode to Light
165
+ const lightModeId = collection.modes[0].modeId;
166
+ collection.renameMode(lightModeId, 'Light');
167
+
168
+ // Add Dark mode
169
+ const darkModeId = collection.addMode('Dark');
170
+
171
+ // Create variables for each token group
172
+ for (const [group, tokens] of Object.entries(dtcg)) {
173
+ if (group.startsWith('$') || typeof tokens !== 'object') continue;
174
+
175
+ for (const [key, token] of Object.entries(tokens)) {
176
+ if (!token.$type) continue;
177
+
178
+ const resolvedType = dtcgToFigmaType(token.$type);
179
+ const variable = figma.variables.createVariable(
180
+ `${group}/${key}`,
181
+ collection,
182
+ resolvedType
183
+ );
184
+
185
+ // Set values per mode
186
+ if (token.$value != null) {
187
+ const val = convertValue(token.$value, token.$type);
188
+ variable.setValueForMode(lightModeId, val);
189
+ variable.setValueForMode(darkModeId, val);
190
+ } else if (token.$extensions?.mode) {
191
+ if (token.$extensions.mode.light != null) {
192
+ variable.setValueForMode(
193
+ lightModeId,
194
+ convertValue(token.$extensions.mode.light, token.$type)
195
+ );
196
+ }
197
+ if (token.$extensions.mode.dark != null) {
198
+ variable.setValueForMode(
199
+ darkModeId,
200
+ convertValue(token.$extensions.mode.dark, token.$type)
201
+ );
202
+ }
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ async function createShapeCollection(shapeDTCG, activeShape) {
209
+ const collection = figma.variables.createVariableCollection('Decantr — Shapes');
210
+
211
+ const sharpModeId = collection.modes[0].modeId;
212
+ collection.renameMode(sharpModeId, 'Sharp');
213
+ const roundedModeId = collection.addMode('Rounded');
214
+ const pillModeId = collection.addMode('Pill');
215
+
216
+ const modeMap = { sharp: sharpModeId, rounded: roundedModeId, pill: pillModeId };
217
+
218
+ for (const [key, token] of Object.entries(shapeDTCG.radius || {})) {
219
+ const variable = figma.variables.createVariable(
220
+ `radius/${key}`,
221
+ collection,
222
+ 'FLOAT'
223
+ );
224
+
225
+ if (token.$extensions?.mode) {
226
+ for (const [shape, val] of Object.entries(token.$extensions.mode)) {
227
+ if (modeMap[shape]) {
228
+ variable.setValueForMode(
229
+ modeMap[shape],
230
+ parseDimension(val)
231
+ );
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+
238
+ async function createComponentSets(componentSets) {
239
+ const page = figma.createPage();
240
+ page.name = 'Components';
241
+
242
+ let y = 0;
243
+ for (const cs of componentSets) {
244
+ const setFrame = figma.createFrame();
245
+ setFrame.name = cs.name;
246
+ setFrame.layoutMode = 'HORIZONTAL';
247
+ setFrame.layoutWrap = 'WRAP';
248
+ setFrame.itemSpacing = 24;
249
+ setFrame.counterAxisSpacing = 24;
250
+ setFrame.paddingTop = 24;
251
+ setFrame.paddingRight = 24;
252
+ setFrame.paddingBottom = 24;
253
+ setFrame.paddingLeft = 24;
254
+ setFrame.y = y;
255
+
256
+ // Create individual variant components
257
+ for (const variant of (cs.variants || [])) {
258
+ const component = figma.createComponent();
259
+ component.name = variant.name;
260
+
261
+ if (variant.node?.absoluteRenderBounds) {
262
+ const bounds = variant.node.absoluteRenderBounds;
263
+ if (bounds.width > 0) component.resize(bounds.width, bounds.height || 40);
264
+ }
265
+
266
+ setFrame.appendChild(component);
267
+ }
268
+
269
+ page.appendChild(setFrame);
270
+ y += setFrame.height + 48;
271
+ }
272
+ }
273
+
274
+ async function createPatternTemplates(patternsData) {
275
+ // Patterns page
276
+ if (patternsData.patterns?.length > 0) {
277
+ const page = figma.createPage();
278
+ page.name = 'Patterns';
279
+
280
+ let y = 0;
281
+ for (const pattern of patternsData.patterns) {
282
+ const frame = createFrameFromSpec(pattern);
283
+ frame.y = y;
284
+ page.appendChild(frame);
285
+ y += (frame.height || 200) + 48;
286
+ }
287
+ }
288
+
289
+ // Skeletons page
290
+ if (patternsData.skeletons?.length > 0) {
291
+ const page = figma.createPage();
292
+ page.name = 'Skeletons';
293
+
294
+ let x = 0;
295
+ for (const skeleton of patternsData.skeletons) {
296
+ const frame = createFrameFromSpec(skeleton);
297
+ frame.x = x;
298
+ if (skeleton.width) frame.resize(skeleton.width, skeleton.height || 900);
299
+ page.appendChild(frame);
300
+ x += (skeleton.width || 1440) + 48;
301
+ }
302
+ }
303
+
304
+ // Archetypes page
305
+ if (patternsData.archetypes?.length > 0) {
306
+ const page = figma.createPage();
307
+ page.name = 'Archetypes';
308
+
309
+ let y = 0;
310
+ for (const archetype of patternsData.archetypes) {
311
+ const frame = createFrameFromSpec(archetype);
312
+ frame.y = y;
313
+ if (archetype.width) frame.resize(archetype.width, archetype.height || 900);
314
+ page.appendChild(frame);
315
+ y += (archetype.height || 900) + 48;
316
+ }
317
+ }
318
+ }
319
+
320
+ async function createEssencePages(essence) {
321
+ const page = figma.createPage();
322
+ page.name = 'Pages';
323
+
324
+ let y = 0;
325
+ for (const pageSpec of essence.structure || []) {
326
+ const frame = figma.createFrame();
327
+ frame.name = pageSpec.id;
328
+ frame.resize(1440, 900);
329
+ frame.layoutMode = 'VERTICAL';
330
+ frame.y = y;
331
+
332
+ // Add a label
333
+ const label = figma.createText();
334
+ await figma.loadFontAsync({ family: 'Inter', style: 'Regular' }).catch(() => {});
335
+ label.characters = `${pageSpec.id} — ${pageSpec.skeleton || 'default'}`;
336
+ frame.appendChild(label);
337
+
338
+ page.appendChild(frame);
339
+ y += 948;
340
+ }
341
+ }
342
+
343
+ // ============================================================
344
+ // Helpers
345
+ // ============================================================
346
+
347
+ function createFrameFromSpec(spec) {
348
+ const frame = figma.createFrame();
349
+ frame.name = spec.name || spec.id || 'Frame';
350
+
351
+ if (spec.layoutMode) frame.layoutMode = spec.layoutMode;
352
+ if (spec.itemSpacing != null) frame.itemSpacing = spec.itemSpacing;
353
+ if (spec.paddingTop != null) frame.paddingTop = spec.paddingTop;
354
+ if (spec.paddingRight != null) frame.paddingRight = spec.paddingRight;
355
+ if (spec.paddingBottom != null) frame.paddingBottom = spec.paddingBottom;
356
+ if (spec.paddingLeft != null) frame.paddingLeft = spec.paddingLeft;
357
+ if (spec.cornerRadius != null) frame.cornerRadius = spec.cornerRadius;
358
+ if (spec.clipsContent != null) frame.clipsContent = spec.clipsContent;
359
+
360
+ // Create child frames for regions
361
+ if (spec.regions) {
362
+ for (const region of spec.regions) {
363
+ if (region.visible === false) continue;
364
+ const child = createFrameFromSpec(region);
365
+ frame.appendChild(child);
366
+ }
367
+ }
368
+
369
+ // Create child frames for rows
370
+ if (spec.rows) {
371
+ for (const row of spec.rows) {
372
+ if (row.type === 'pattern') {
373
+ const child = createFrameFromSpec(row.frame || {});
374
+ child.name = row.id || 'pattern';
375
+ frame.appendChild(child);
376
+ } else if (row.type === 'columns') {
377
+ const rowFrame = figma.createFrame();
378
+ rowFrame.name = 'columns';
379
+ rowFrame.layoutMode = 'HORIZONTAL';
380
+ rowFrame.itemSpacing = row.itemSpacing || 16;
381
+
382
+ for (const col of row.columns || []) {
383
+ const colFrame = createFrameFromSpec(col.frame || {});
384
+ colFrame.name = col.id || 'column';
385
+ if (col.layoutGrow) colFrame.layoutGrow = col.layoutGrow;
386
+ rowFrame.appendChild(colFrame);
387
+ }
388
+
389
+ frame.appendChild(rowFrame);
390
+ }
391
+ }
392
+ }
393
+
394
+ return frame;
395
+ }
396
+
397
+ async function fetchJSON(url) {
398
+ try {
399
+ const resp = await fetch(url);
400
+ if (!resp.ok) return null;
401
+ return await resp.json();
402
+ } catch {
403
+ return null;
404
+ }
405
+ }
406
+
407
+ function dtcgToFigmaType(type) {
408
+ switch (type) {
409
+ case 'color': return 'COLOR';
410
+ case 'dimension':
411
+ case 'number':
412
+ case 'fontWeight':
413
+ case 'duration':
414
+ return 'FLOAT';
415
+ default:
416
+ return 'STRING';
417
+ }
418
+ }
419
+
420
+ function convertValue(value, type) {
421
+ if (type === 'color' && typeof value === 'string' && value.startsWith('#')) {
422
+ return hexToFigmaColor(value);
423
+ }
424
+ if (type === 'dimension') return parseDimension(value);
425
+ if (typeof value === 'number') return value;
426
+ if (typeof value === 'string') {
427
+ const num = parseFloat(value);
428
+ if (!isNaN(num) && (type === 'number' || type === 'fontWeight' || type === 'duration')) {
429
+ return num;
430
+ }
431
+ }
432
+ return String(value);
433
+ }
434
+
435
+ function hexToFigmaColor(hex) {
436
+ hex = hex.replace('#', '');
437
+ if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
438
+ return {
439
+ r: parseInt(hex.slice(0, 2), 16) / 255,
440
+ g: parseInt(hex.slice(2, 4), 16) / 255,
441
+ b: parseInt(hex.slice(4, 6), 16) / 255,
442
+ a: hex.length >= 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1,
443
+ };
444
+ }
445
+
446
+ function parseDimension(value) {
447
+ if (typeof value === 'number') return value;
448
+ if (typeof value !== 'string') return 0;
449
+ if (value.endsWith('rem')) return parseFloat(value) * 16;
450
+ if (value.endsWith('px')) return parseFloat(value);
451
+ if (value.endsWith('em')) return parseFloat(value) * 16;
452
+ return parseFloat(value) || 0;
453
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "Decantr Design System",
3
+ "id": "decantr-design-system",
4
+ "api": "1.0.0",
5
+ "main": "code.js",
6
+ "ui": "ui.html",
7
+ "editorType": ["figma"],
8
+ "networkAccess": {
9
+ "allowedDomains": ["https://unpkg.com", "https://cdn.jsdelivr.net"],
10
+ "reasoning": "Fetches published Decantr token and component manifests from CDN"
11
+ },
12
+ "capabilities": [],
13
+ "permissions": ["currentuser"]
14
+ }