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,262 @@
1
+ /**
2
+ * Scene Graph — renderer-agnostic intermediate representation.
3
+ * Plain objects with `type` discriminant. Serializable, diffable, minimal.
4
+ * All layout functions produce scene nodes; renderers consume them.
5
+ * @module _scene
6
+ */
7
+
8
+ /**
9
+ * Root scene node — container for all chart elements.
10
+ * @param {number} width
11
+ * @param {number} height
12
+ * @param {Object[]} children
13
+ * @param {Object} [meta] — chart-level metadata (type, margins, scales)
14
+ * @returns {Object}
15
+ */
16
+ export function scene(width, height, children, meta) {
17
+ return { type: 'scene', width, height, children: children || [], meta: meta || {} };
18
+ }
19
+
20
+ /**
21
+ * Group node — logical grouping with optional transform.
22
+ * @param {Object} attrs — { transform, class, clip, opacity }
23
+ * @param {Object[]} children
24
+ * @returns {Object}
25
+ */
26
+ export function group(attrs, children) {
27
+ return { type: 'group', ...attrs, children: children || [] };
28
+ }
29
+
30
+ /**
31
+ * Path node — arbitrary SVG path data.
32
+ * @param {Object} attrs — { d, fill, stroke, strokeWidth, strokeDash, strokeLinecap, strokeLinejoin, class, data, key, opacity }
33
+ * @returns {Object}
34
+ */
35
+ export function path(attrs) {
36
+ return { type: 'path', ...attrs };
37
+ }
38
+
39
+ /**
40
+ * Rectangle node.
41
+ * @param {Object} attrs — { x, y, w, h, rx, ry, fill, stroke, strokeWidth, class, data, key, opacity }
42
+ * @returns {Object}
43
+ */
44
+ export function rect(attrs) {
45
+ return { type: 'rect', ...attrs };
46
+ }
47
+
48
+ /**
49
+ * Circle node.
50
+ * @param {Object} attrs — { cx, cy, r, fill, stroke, strokeWidth, class, data, key, opacity }
51
+ * @returns {Object}
52
+ */
53
+ export function circle(attrs) {
54
+ return { type: 'circle', ...attrs };
55
+ }
56
+
57
+ /**
58
+ * Text node.
59
+ * @param {Object} attrs — { x, y, content, anchor, baseline, fill, class, fontSize, fontWeight, rotate }
60
+ * @returns {Object}
61
+ */
62
+ export function text(attrs) {
63
+ return { type: 'text', ...attrs };
64
+ }
65
+
66
+ /**
67
+ * Line node.
68
+ * @param {Object} attrs — { x1, y1, x2, y2, stroke, strokeWidth, strokeDash, class }
69
+ * @returns {Object}
70
+ */
71
+ export function line(attrs) {
72
+ return { type: 'line', ...attrs };
73
+ }
74
+
75
+ /**
76
+ * Arc node — for pie/donut/gauge/radial charts.
77
+ * @param {Object} attrs — { cx, cy, innerR, outerR, startAngle, endAngle, fill, stroke, class, data, key, opacity }
78
+ * @returns {Object}
79
+ */
80
+ export function arc(attrs) {
81
+ return { type: 'arc', ...attrs };
82
+ }
83
+
84
+ /**
85
+ * Polygon node — closed shape from point array.
86
+ * @param {Object} attrs — { points: [{x,y}], fill, stroke, strokeWidth, class, data, key, opacity }
87
+ * @returns {Object}
88
+ */
89
+ export function polygon(attrs) {
90
+ return { type: 'polygon', ...attrs };
91
+ }
92
+
93
+ /**
94
+ * Image node — for embedding icons/images in charts.
95
+ * @param {Object} attrs — { x, y, w, h, href, class, data, key }
96
+ * @returns {Object}
97
+ */
98
+ export function image(attrs) {
99
+ return { type: 'image', ...attrs };
100
+ }
101
+
102
+ // --- Utility builders ---
103
+
104
+ /**
105
+ * Build axis tick scene nodes.
106
+ * @param {Array} ticks — [{ value, position, label }]
107
+ * @param {'x'|'y'} axis
108
+ * @param {number} innerW
109
+ * @param {number} innerH
110
+ * @returns {Object[]} scene nodes
111
+ */
112
+ export function axisTicks(ticks, axis, innerW, innerH) {
113
+ const nodes = [];
114
+ if (axis === 'x') {
115
+ for (const t of ticks) {
116
+ nodes.push(line({ x1: t.position, y1: innerH, x2: t.position, y2: innerH + 4, stroke: 'var(--d-border)', class: 'd-chart-tick' }));
117
+ nodes.push(text({ x: t.position, y: innerH + 18, content: t.label, anchor: 'middle', class: 'd-chart-axis' }));
118
+ }
119
+ } else {
120
+ for (const t of ticks) {
121
+ nodes.push(line({ x1: -4, y1: t.position, x2: 0, y2: t.position, stroke: 'var(--d-border)', class: 'd-chart-tick' }));
122
+ nodes.push(text({ x: -8, y: t.position + 4, content: t.label, anchor: 'end', class: 'd-chart-axis' }));
123
+ }
124
+ }
125
+ return nodes;
126
+ }
127
+
128
+ /**
129
+ * Build grid line scene nodes from Y ticks.
130
+ * @param {Array} ticks — [{ position }]
131
+ * @param {number} innerW
132
+ * @returns {Object[]}
133
+ */
134
+ export function gridLines(ticks, innerW) {
135
+ return ticks.map(t => line({ x1: 0, y1: t.position, x2: innerW, y2: t.position, class: 'd-chart-grid' }));
136
+ }
137
+
138
+ /**
139
+ * Convert arc params to SVG path data string.
140
+ * @param {number} cx
141
+ * @param {number} cy
142
+ * @param {number} outerR
143
+ * @param {number} innerR
144
+ * @param {number} startAngle
145
+ * @param {number} endAngle
146
+ * @returns {string}
147
+ */
148
+ export function arcToPath(cx, cy, outerR, innerR, startAngle, endAngle) {
149
+ const x1 = cx + outerR * Math.cos(startAngle);
150
+ const y1 = cy + outerR * Math.sin(startAngle);
151
+ const x2 = cx + outerR * Math.cos(endAngle);
152
+ const y2 = cy + outerR * Math.sin(endAngle);
153
+ const largeArc = endAngle - startAngle > Math.PI ? 1 : 0;
154
+
155
+ let d = `M${x1},${y1}A${outerR},${outerR},0,${largeArc},1,${x2},${y2}`;
156
+
157
+ if (innerR > 0) {
158
+ const x3 = cx + innerR * Math.cos(endAngle);
159
+ const y3 = cy + innerR * Math.sin(endAngle);
160
+ const x4 = cx + innerR * Math.cos(startAngle);
161
+ const y4 = cy + innerR * Math.sin(startAngle);
162
+ d += `L${x3},${y3}A${innerR},${innerR},0,${largeArc},0,${x4},${y4}Z`;
163
+ } else {
164
+ d += `L${cx},${cy}Z`;
165
+ }
166
+
167
+ return d;
168
+ }
169
+
170
+ /**
171
+ * Convert points array to SVG path data string (M/L commands).
172
+ * @param {{x:number, y:number}[]} points
173
+ * @returns {string}
174
+ */
175
+ export function pointsToPathD(points) {
176
+ if (!points.length) return '';
177
+ let d = `M${points[0].x},${points[0].y}`;
178
+ for (let i = 1; i < points.length; i++) d += `L${points[i].x},${points[i].y}`;
179
+ return d;
180
+ }
181
+
182
+ /**
183
+ * Convert points to smooth Catmull-Rom spline path.
184
+ * @param {{x:number, y:number}[]} points
185
+ * @param {number} [tension=0.5]
186
+ * @returns {string}
187
+ */
188
+ export function smoothPathD(points, tension = 0.5) {
189
+ if (points.length < 2) return pointsToPathD(points);
190
+ if (points.length === 2) return pointsToPathD(points);
191
+
192
+ let d = `M${points[0].x},${points[0].y}`;
193
+ for (let i = 0; i < points.length - 1; i++) {
194
+ const p0 = points[Math.max(0, i - 1)];
195
+ const p1 = points[i];
196
+ const p2 = points[i + 1];
197
+ const p3 = points[Math.min(points.length - 1, i + 2)];
198
+
199
+ const cp1x = p1.x + (p2.x - p0.x) / 6 * tension;
200
+ const cp1y = p1.y + (p2.y - p0.y) / 6 * tension;
201
+ const cp2x = p2.x - (p3.x - p1.x) / 6 * tension;
202
+ const cp2y = p2.y - (p3.y - p1.y) / 6 * tension;
203
+
204
+ d += `C${cp1x},${cp1y},${cp2x},${cp2y},${p2.x},${p2.y}`;
205
+ }
206
+ return d;
207
+ }
208
+
209
+ /**
210
+ * Convert area points (with y0/y1) to closed polygon path data.
211
+ * @param {{x:number, y0:number, y1:number}[]} points — y0=top, y1=bottom
212
+ * @returns {string}
213
+ */
214
+ export function areaPathD(points) {
215
+ if (!points.length) return '';
216
+ let d = `M${points[0].x},${points[0].y0}`;
217
+ for (let i = 1; i < points.length; i++) d += `L${points[i].x},${points[i].y0}`;
218
+ for (let i = points.length - 1; i >= 0; i--) d += `L${points[i].x},${points[i].y1}`;
219
+ d += 'Z';
220
+ return d;
221
+ }
222
+
223
+ /**
224
+ * Convert area points to smooth closed polygon path.
225
+ * Top edge uses Catmull-Rom spline, bottom edge stays straight.
226
+ * @param {{x:number, y0:number, y1:number}[]} points — y0=top, y1=bottom
227
+ * @param {number} [tension=0.5]
228
+ * @returns {string}
229
+ */
230
+ export function smoothAreaPathD(points, tension = 0.5) {
231
+ if (!points.length) return '';
232
+ // Top edge — smooth Catmull-Rom
233
+ const topPts = points.map(p => ({ x: p.x, y: p.y0 }));
234
+ let d = smoothPathD(topPts, tension);
235
+ // Bottom edge — straight, reversed
236
+ for (let i = points.length - 1; i >= 0; i--) d += `L${points[i].x},${points[i].y1}`;
237
+ d += 'Z';
238
+ return d;
239
+ }
240
+
241
+ /**
242
+ * Gradient scene node — for SVG <linearGradient> definitions.
243
+ * @param {Object} attrs — { id, x1, y1, x2, y2, stops: [{ offset, color, opacity }] }
244
+ * @returns {Object}
245
+ */
246
+ export function gradient(attrs) {
247
+ return { type: 'gradient', ...attrs };
248
+ }
249
+
250
+ /**
251
+ * Step path (horizontal-first, for step line charts).
252
+ * @param {{x:number, y:number}[]} points
253
+ * @returns {string}
254
+ */
255
+ export function stepPathD(points) {
256
+ if (points.length < 2) return pointsToPathD(points);
257
+ let d = `M${points[0].x},${points[0].y}`;
258
+ for (let i = 1; i < points.length; i++) {
259
+ d += `H${points[i].x}V${points[i].y}`;
260
+ }
261
+ return d;
262
+ }
@@ -0,0 +1,371 @@
1
+ import { createEffect } from '../state/index.js';
2
+ import { getTheme } from '../css/theme-registry.js';
3
+
4
+ /**
5
+ * Resolve a prop that may be a signal getter or a static value.
6
+ * @template T
7
+ * @param {T|Function} prop
8
+ * @returns {T}
9
+ */
10
+ export function resolve(prop) {
11
+ return typeof prop === 'function' ? prop() : prop;
12
+ }
13
+
14
+ // --- Scales ---
15
+
16
+ /**
17
+ * Linear scale: maps [domainMin, domainMax] → [rangeMin, rangeMax].
18
+ * @param {number[]} domain — [min, max]
19
+ * @param {number[]} range — [min, max] in pixels
20
+ * @returns {{ (v: number) => number, ticks: (count?: number) => number[], invert: (px: number) => number }}
21
+ */
22
+ export function scaleLinear(domain, range) {
23
+ const [d0, d1] = domain;
24
+ const [r0, r1] = range;
25
+ const span = d1 - d0 || 1;
26
+ const rSpan = r1 - r0;
27
+
28
+ function scale(v) {
29
+ return r0 + ((v - d0) / span) * rSpan;
30
+ }
31
+
32
+ scale.invert = function (px) {
33
+ return d0 + ((px - r0) / rSpan) * span;
34
+ };
35
+
36
+ scale.ticks = function (count = 5) {
37
+ const step = niceStep(span / count);
38
+ const start = Math.ceil(d0 / step) * step;
39
+ const result = [];
40
+ for (let v = start; v <= d1; v += step) {
41
+ result.push(+v.toPrecision(12));
42
+ }
43
+ return result;
44
+ };
45
+
46
+ return scale;
47
+ }
48
+
49
+ /**
50
+ * Band scale: maps discrete values → pixel bands with padding.
51
+ * @param {any[]} domain — discrete values
52
+ * @param {number[]} range — [min, max] in pixels
53
+ * @param {number} padding — 0..1, space between bands
54
+ * @returns {{ (v: any) => number, bandwidth: () => number }}
55
+ */
56
+ export function scaleBand(domain, range, padding = 0.2) {
57
+ const [r0, r1] = range;
58
+ const n = domain.length || 1;
59
+ const totalPad = padding * (n + 1);
60
+ const bandWidth = (r1 - r0) / (n + totalPad);
61
+ const step = bandWidth * (1 + padding);
62
+ const offset = bandWidth * padding;
63
+ const map = new Map();
64
+ for (let i = 0; i < domain.length; i++) {
65
+ map.set(domain[i], r0 + offset + i * step);
66
+ }
67
+
68
+ function scale(v) {
69
+ return map.get(v) ?? r0;
70
+ }
71
+
72
+ scale.bandwidth = function () {
73
+ return bandWidth;
74
+ };
75
+
76
+ return scale;
77
+ }
78
+
79
+ /**
80
+ * Time scale: maps Date values → pixel positions.
81
+ * @param {Date[]} domain — [min, max] dates
82
+ * @param {number[]} range — [min, max] in pixels
83
+ * @returns {{ (v: Date|number) => number, ticks: (count?: number) => Date[] }}
84
+ */
85
+ export function scaleTime(domain, range) {
86
+ const d0 = +domain[0];
87
+ const d1 = +domain[1];
88
+ const inner = scaleLinear([d0, d1], range);
89
+
90
+ function scale(v) {
91
+ return inner(+v);
92
+ }
93
+
94
+ scale.invert = function (px) {
95
+ return new Date(inner.invert(px));
96
+ };
97
+
98
+ scale.ticks = function (count = 5) {
99
+ return inner.ticks(count).map(t => new Date(t));
100
+ };
101
+
102
+ return scale;
103
+ }
104
+
105
+ // --- Extended scales ---
106
+
107
+ /**
108
+ * Logarithmic scale.
109
+ * @param {number[]} domain
110
+ * @param {number[]} range
111
+ * @param {number} [base=10]
112
+ * @returns {Function}
113
+ */
114
+ export function scaleLog(domain, range, base = 10) {
115
+ const [d0, d1] = domain;
116
+ const [r0, r1] = range;
117
+ const logBase = Math.log(base);
118
+ const ld0 = Math.log(Math.max(1e-10, d0)) / logBase;
119
+ const ld1 = Math.log(Math.max(1e-10, d1)) / logBase;
120
+ const span = ld1 - ld0 || 1;
121
+ const rSpan = r1 - r0;
122
+
123
+ function scale(v) {
124
+ const lv = Math.log(Math.max(1e-10, v)) / logBase;
125
+ return r0 + ((lv - ld0) / span) * rSpan;
126
+ }
127
+ scale.invert = px => Math.pow(base, ld0 + ((px - r0) / rSpan) * span);
128
+ scale.ticks = (count = 5) => {
129
+ const result = [];
130
+ const start = Math.ceil(ld0);
131
+ const end = Math.floor(ld1);
132
+ for (let i = start; i <= end; i++) result.push(Math.pow(base, i));
133
+ return result;
134
+ };
135
+ return scale;
136
+ }
137
+
138
+ /**
139
+ * Square root scale.
140
+ * @param {number[]} domain
141
+ * @param {number[]} range
142
+ * @returns {Function}
143
+ */
144
+ export function scaleSqrt(domain, range) {
145
+ const [d0, d1] = domain;
146
+ const [r0, r1] = range;
147
+ const sd0 = Math.sqrt(Math.max(0, d0));
148
+ const sd1 = Math.sqrt(Math.max(0, d1));
149
+ const span = sd1 - sd0 || 1;
150
+ const rSpan = r1 - r0;
151
+
152
+ function scale(v) {
153
+ const sv = Math.sqrt(Math.max(0, v));
154
+ return r0 + ((sv - sd0) / span) * rSpan;
155
+ }
156
+ scale.invert = px => Math.pow(sd0 + ((px - r0) / rSpan) * span, 2);
157
+ scale.ticks = (count = 5) => scaleLinear(domain, range).ticks(count);
158
+ return scale;
159
+ }
160
+
161
+ /**
162
+ * Point scale — discrete values → evenly spaced points.
163
+ * @param {any[]} domain
164
+ * @param {number[]} range
165
+ * @param {number} [padding=0.5]
166
+ * @returns {Function}
167
+ */
168
+ export function scalePoint(domain, range, padding = 0.5) {
169
+ const [r0, r1] = range;
170
+ const n = domain.length || 1;
171
+ const step = (r1 - r0) / (n - 1 + padding * 2) || 0;
172
+ const offset = r0 + step * padding;
173
+ const map = new Map();
174
+ for (let i = 0; i < domain.length; i++) {
175
+ map.set(domain[i], offset + i * step);
176
+ }
177
+ function scale(v) { return map.get(v) ?? r0; }
178
+ scale.step = () => step;
179
+ return scale;
180
+ }
181
+
182
+ /**
183
+ * Ordinal scale — discrete → discrete mapping.
184
+ * @param {any[]} domain
185
+ * @param {any[]} range
186
+ * @returns {Function}
187
+ */
188
+ export function scaleOrdinal(domain, range) {
189
+ const map = new Map();
190
+ for (let i = 0; i < domain.length; i++) {
191
+ map.set(domain[i], range[i % range.length]);
192
+ }
193
+ return function scale(v) { return map.get(v) ?? range[0]; };
194
+ }
195
+
196
+ /**
197
+ * Diverging scale — maps a domain with a midpoint.
198
+ * @param {number[]} domain — [min, mid, max]
199
+ * @param {any[]} range — [minVal, midVal, maxVal]
200
+ * @returns {Function}
201
+ */
202
+ export function scaleDiverging(domain, range) {
203
+ const [d0, dMid, d1] = domain;
204
+ const [r0, rMid, r1] = range;
205
+
206
+ return function scale(v) {
207
+ if (v <= dMid) {
208
+ const t = (v - d0) / (dMid - d0 || 1);
209
+ return typeof r0 === 'number' ? r0 + (rMid - r0) * t : r0;
210
+ }
211
+ const t = (v - dMid) / (d1 - dMid || 1);
212
+ return typeof rMid === 'number' ? rMid + (r1 - rMid) * t : rMid;
213
+ };
214
+ }
215
+
216
+ // --- Nice step for axis ticks ---
217
+
218
+ function niceStep(rawStep) {
219
+ const mag = Math.pow(10, Math.floor(Math.log10(rawStep)));
220
+ const norm = rawStep / mag;
221
+ let nice;
222
+ if (norm <= 1.5) nice = 1;
223
+ else if (norm <= 3) nice = 2;
224
+ else if (norm <= 7) nice = 5;
225
+ else nice = 10;
226
+ return nice * mag;
227
+ }
228
+
229
+ // --- Data utilities ---
230
+
231
+ /**
232
+ * Compute extent (min, max) of a numeric field.
233
+ * @param {Object[]} data
234
+ * @param {string} field
235
+ * @returns {[number, number]}
236
+ */
237
+ export function extent(data, field) {
238
+ let min = Infinity, max = -Infinity;
239
+ for (let i = 0; i < data.length; i++) {
240
+ const v = +data[i][field];
241
+ if (v < min) min = v;
242
+ if (v > max) max = v;
243
+ }
244
+ return [min, max];
245
+ }
246
+
247
+ /**
248
+ * Get unique values of a field (preserving order).
249
+ * @param {Object[]} data
250
+ * @param {string} field
251
+ * @returns {any[]}
252
+ */
253
+ export function unique(data, field) {
254
+ const seen = new Set();
255
+ const result = [];
256
+ for (let i = 0; i < data.length; i++) {
257
+ const v = data[i][field];
258
+ if (!seen.has(v)) {
259
+ seen.add(v);
260
+ result.push(v);
261
+ }
262
+ }
263
+ return result;
264
+ }
265
+
266
+ /**
267
+ * Group data by a field value.
268
+ * @param {Object[]} data
269
+ * @param {string} field
270
+ * @returns {Map<any, Object[]>}
271
+ */
272
+ export function groupBy(data, field) {
273
+ const groups = new Map();
274
+ for (let i = 0; i < data.length; i++) {
275
+ const key = data[i][field];
276
+ if (!groups.has(key)) groups.set(key, []);
277
+ groups.get(key).push(data[i]);
278
+ }
279
+ return groups;
280
+ }
281
+
282
+ /**
283
+ * LTTB downsampling — Largest Triangle Three Buckets.
284
+ * Preserves visual shape of time series for line/area charts.
285
+ * @param {Object[]} data — sorted by x
286
+ * @param {string} xField
287
+ * @param {string} yField
288
+ * @param {number} targetCount — desired output length
289
+ * @returns {Object[]}
290
+ */
291
+ export function downsampleLTTB(data, xField, yField, targetCount) {
292
+ if (data.length <= targetCount) return data;
293
+
294
+ const result = [data[0]]; // Always keep first
295
+ const bucketSize = (data.length - 2) / (targetCount - 2);
296
+
297
+ let prevIndex = 0;
298
+ for (let i = 1; i < targetCount - 1; i++) {
299
+ const bucketStart = Math.floor((i - 1) * bucketSize) + 1;
300
+ const bucketEnd = Math.min(Math.floor(i * bucketSize) + 1, data.length - 1);
301
+ const nextBucketStart = Math.min(Math.floor(i * bucketSize) + 1, data.length - 1);
302
+ const nextBucketEnd = Math.min(Math.floor((i + 1) * bucketSize) + 1, data.length - 1);
303
+
304
+ // Average of next bucket
305
+ let avgX = 0, avgY = 0, count = 0;
306
+ for (let j = nextBucketStart; j <= nextBucketEnd; j++) {
307
+ avgX += +data[j][xField];
308
+ avgY += +data[j][yField];
309
+ count++;
310
+ }
311
+ avgX /= count;
312
+ avgY /= count;
313
+
314
+ // Find point in current bucket with largest triangle area
315
+ let maxArea = -1;
316
+ let maxIndex = bucketStart;
317
+ const px = +data[prevIndex][xField];
318
+ const py = +data[prevIndex][yField];
319
+
320
+ for (let j = bucketStart; j <= bucketEnd; j++) {
321
+ const area = Math.abs(
322
+ (px - avgX) * (+data[j][yField] - py) -
323
+ (px - +data[j][xField]) * (avgY - py)
324
+ );
325
+ if (area > maxArea) {
326
+ maxArea = area;
327
+ maxIndex = j;
328
+ }
329
+ }
330
+
331
+ result.push(data[maxIndex]);
332
+ prevIndex = maxIndex;
333
+ }
334
+
335
+ result.push(data[data.length - 1]); // Always keep last
336
+ return result;
337
+ }
338
+
339
+ /**
340
+ * Pad a numeric extent by a fraction for visual breathing room.
341
+ * @param {[number, number]} ext
342
+ * @param {number} fraction — default 0.05 (5%)
343
+ * @returns {[number, number]}
344
+ */
345
+ export function padExtent(ext, fraction = 0.05) {
346
+ const span = ext[1] - ext[0] || 1;
347
+ const pad = span * fraction;
348
+ return [ext[0] - pad, ext[1] + pad];
349
+ }
350
+
351
+ /**
352
+ * Detect if values look like dates.
353
+ * @param {any[]} values
354
+ * @returns {boolean}
355
+ */
356
+ export function isDateLike(values) {
357
+ if (!values.length) return false;
358
+ const v = values[0];
359
+ if (v instanceof Date) return true;
360
+ if (typeof v === 'string' && !isNaN(Date.parse(v))) return true;
361
+ return false;
362
+ }
363
+
364
+ /**
365
+ * Parse a value as a Date if needed.
366
+ * @param {any} v
367
+ * @returns {Date}
368
+ */
369
+ export function toDate(v) {
370
+ return v instanceof Date ? v : new Date(v);
371
+ }