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,364 @@
1
+ /**
2
+ * Interaction Layer — shared infrastructure for all chart types.
3
+ * Pure callback architecture — zero business logic.
4
+ * Tooltip, crosshair, zoom/pan, brush selection, legend toggle, keyboard nav.
5
+ * @module _interact
6
+ */
7
+
8
+ // --- Tooltip (enhanced) ---
9
+
10
+ /**
11
+ * Attach enhanced tooltip to chart.
12
+ * Supports Voronoi nearest-point, multi-series, custom render.
13
+ * @param {HTMLElement} container
14
+ * @param {SVGElement} svgEl
15
+ * @param {Object} meta — scene graph meta
16
+ * @param {Object} spec
17
+ */
18
+ export function attachTooltip(container, svgEl, meta, spec) {
19
+ const tooltip = document.createElement('div');
20
+ tooltip.className = 'd-chart-tooltip';
21
+ container.appendChild(tooltip);
22
+
23
+ const elements = svgEl.querySelectorAll('[data-series],[data-label]');
24
+ for (const el of elements) {
25
+ el.addEventListener('mouseenter', () => {
26
+ const value = el.getAttribute('data-value') || '';
27
+ const label = el.getAttribute('data-label') || el.getAttribute('data-series') || '';
28
+
29
+ if (typeof spec.tooltip === 'function') {
30
+ tooltip.textContent = '';
31
+ const result = spec.tooltip({ label, value, element: el });
32
+ if (typeof result === 'string') tooltip.textContent = result;
33
+ else if (result) tooltip.appendChild(result);
34
+ } else {
35
+ tooltip.textContent = label + (value ? ': ' + value : '');
36
+ }
37
+ tooltip.classList.add('d-chart-tooltip-visible');
38
+ });
39
+
40
+ el.addEventListener('mousemove', e => {
41
+ const rect = container.getBoundingClientRect();
42
+ tooltip.style.left = (e.clientX - rect.left + 12) + 'px';
43
+ tooltip.style.top = (e.clientY - rect.top - 8) + 'px';
44
+ });
45
+
46
+ el.addEventListener('mouseleave', () => {
47
+ tooltip.classList.remove('d-chart-tooltip-visible');
48
+ });
49
+ }
50
+ }
51
+
52
+ // --- Crosshair ---
53
+
54
+ /**
55
+ * Attach crosshair guides to cartesian chart.
56
+ * @param {HTMLElement} container
57
+ * @param {Object} meta — { margins, innerW, innerH }
58
+ * @param {Object} spec
59
+ */
60
+ export function attachCrosshair(container, meta, spec) {
61
+ if (!spec.crosshair) return;
62
+
63
+ const { margins, innerW, innerH } = meta;
64
+ const overlay = document.createElement('div');
65
+ overlay.style.cssText = `position:absolute;top:${margins.top}px;left:${margins.left}px;width:${innerW}px;height:${innerH}px;pointer-events:none`;
66
+ container.appendChild(overlay);
67
+
68
+ const vLine = document.createElement('div');
69
+ vLine.style.cssText = 'position:absolute;top:0;width:1px;height:100%;background:var(--d-muted);opacity:0;transition:opacity 0.1s;pointer-events:none';
70
+ overlay.appendChild(vLine);
71
+
72
+ const hLine = document.createElement('div');
73
+ hLine.style.cssText = 'position:absolute;left:0;width:100%;height:1px;background:var(--d-muted);opacity:0;transition:opacity 0.1s;pointer-events:none';
74
+ overlay.appendChild(hLine);
75
+
76
+ const inner = container.querySelector('.d-chart-inner');
77
+ if (!inner) return;
78
+
79
+ inner.style.cursor = 'crosshair';
80
+ inner.addEventListener('mousemove', e => {
81
+ const rect = inner.getBoundingClientRect();
82
+ const x = e.clientX - rect.left - margins.left;
83
+ const y = e.clientY - rect.top - margins.top;
84
+
85
+ if (x >= 0 && x <= innerW && y >= 0 && y <= innerH) {
86
+ vLine.style.left = x + 'px';
87
+ vLine.style.opacity = '0.5';
88
+ hLine.style.top = y + 'px';
89
+ hLine.style.opacity = '0.5';
90
+ }
91
+ });
92
+
93
+ inner.addEventListener('mouseleave', () => {
94
+ vLine.style.opacity = '0';
95
+ hLine.style.opacity = '0';
96
+ });
97
+ }
98
+
99
+ // --- Zoom/Pan ---
100
+
101
+ /**
102
+ * Attach zoom/pan behavior to chart.
103
+ * @param {HTMLElement} container
104
+ * @param {Object} meta
105
+ * @param {Object} spec
106
+ * @param {Function} rerender — callback to re-render with new viewport
107
+ */
108
+ export function attachZoom(container, meta, spec, rerender) {
109
+ if (!spec.zoom) return;
110
+
111
+ const inner = container.querySelector('.d-chart-inner');
112
+ if (!inner) return;
113
+
114
+ let scale = 1;
115
+ let panX = 0, panY = 0;
116
+ let dragging = false, lastX = 0, lastY = 0;
117
+
118
+ inner.addEventListener('wheel', e => {
119
+ e.preventDefault();
120
+ const delta = e.deltaY > 0 ? 0.9 : 1.1;
121
+ scale = Math.max(0.5, Math.min(10, scale * delta));
122
+
123
+ if (spec.onZoom) {
124
+ spec.onZoom({ scale, panX, panY });
125
+ }
126
+ }, { passive: false });
127
+
128
+ inner.addEventListener('mousedown', e => {
129
+ if (e.button !== 0) return;
130
+ dragging = true;
131
+ lastX = e.clientX;
132
+ lastY = e.clientY;
133
+ inner.style.cursor = 'grabbing';
134
+ });
135
+
136
+ document.addEventListener('mousemove', e => {
137
+ if (!dragging) return;
138
+ panX += e.clientX - lastX;
139
+ panY += e.clientY - lastY;
140
+ lastX = e.clientX;
141
+ lastY = e.clientY;
142
+
143
+ if (spec.onZoom) {
144
+ spec.onZoom({ scale, panX, panY });
145
+ }
146
+ });
147
+
148
+ document.addEventListener('mouseup', () => {
149
+ if (dragging) {
150
+ dragging = false;
151
+ inner.style.cursor = '';
152
+ }
153
+ });
154
+ }
155
+
156
+ // --- Brush selection ---
157
+
158
+ /**
159
+ * Attach brush selection to chart.
160
+ * @param {HTMLElement} container
161
+ * @param {Object} meta
162
+ * @param {Object} spec — { brush: 'x'|'y'|'xy', onBrush: fn }
163
+ */
164
+ export function attachBrush(container, meta, spec) {
165
+ if (!spec.brush || !spec.onBrush) return;
166
+
167
+ const { margins, innerW, innerH } = meta;
168
+ const inner = container.querySelector('.d-chart-inner');
169
+ if (!inner) return;
170
+
171
+ const brushEl = document.createElement('div');
172
+ brushEl.style.cssText = 'position:absolute;background:var(--d-primary-subtle);border:1px solid var(--d-primary-border);display:none;pointer-events:none;z-index:1';
173
+ inner.appendChild(brushEl);
174
+
175
+ let startX, startY, active = false;
176
+
177
+ inner.addEventListener('mousedown', e => {
178
+ if (e.button !== 0 || spec.zoom) return; // Don't conflict with zoom
179
+ const rect = inner.getBoundingClientRect();
180
+ startX = e.clientX - rect.left;
181
+ startY = e.clientY - rect.top;
182
+ active = true;
183
+ brushEl.style.display = 'block';
184
+ });
185
+
186
+ document.addEventListener('mousemove', e => {
187
+ if (!active) return;
188
+ const rect = inner.getBoundingClientRect();
189
+ const curX = e.clientX - rect.left;
190
+ const curY = e.clientY - rect.top;
191
+
192
+ const mode = spec.brush === true ? 'x' : spec.brush;
193
+ const x0 = mode === 'y' ? margins.left : Math.min(startX, curX);
194
+ const y0 = mode === 'x' ? margins.top : Math.min(startY, curY);
195
+ const w = mode === 'y' ? innerW : Math.abs(curX - startX);
196
+ const h = mode === 'x' ? innerH : Math.abs(curY - startY);
197
+
198
+ brushEl.style.left = x0 + 'px';
199
+ brushEl.style.top = y0 + 'px';
200
+ brushEl.style.width = w + 'px';
201
+ brushEl.style.height = h + 'px';
202
+ });
203
+
204
+ document.addEventListener('mouseup', () => {
205
+ if (!active) return;
206
+ active = false;
207
+ brushEl.style.display = 'none';
208
+
209
+ const rect = inner.getBoundingClientRect();
210
+ // Compute range from brush rect
211
+ const brushRect = brushEl.getBoundingClientRect();
212
+ if (brushRect.width > 5 || brushRect.height > 5) {
213
+ const range = {
214
+ x: [brushRect.left - rect.left - margins.left, brushRect.right - rect.left - margins.left],
215
+ y: [brushRect.top - rect.top - margins.top, brushRect.bottom - rect.top - margins.top]
216
+ };
217
+ spec.onBrush({ range });
218
+ }
219
+ });
220
+ }
221
+
222
+ // --- Click handler ---
223
+
224
+ /**
225
+ * Attach click handler to chart data elements.
226
+ * @param {SVGElement} svgEl
227
+ * @param {Object} meta
228
+ * @param {Object} spec — { onClick: fn }
229
+ */
230
+ export function attachClick(svgEl, meta, spec) {
231
+ if (!spec.onClick) return;
232
+
233
+ const elements = svgEl.querySelectorAll('[data-series],[data-label]');
234
+ for (const el of elements) {
235
+ el.style.cursor = 'pointer';
236
+ el.addEventListener('click', e => {
237
+ spec.onClick({
238
+ point: {
239
+ label: el.getAttribute('data-label') || '',
240
+ value: el.getAttribute('data-value') || ''
241
+ },
242
+ series: el.getAttribute('data-series') || '',
243
+ event: e
244
+ });
245
+ });
246
+ }
247
+ }
248
+
249
+ // --- Keyboard navigation ---
250
+
251
+ /**
252
+ * Attach keyboard navigation to chart.
253
+ * Arrow keys navigate between data elements, Enter activates.
254
+ * @param {SVGElement} svgEl
255
+ * @param {Object} spec
256
+ */
257
+ export function attachKeyboard(svgEl, spec) {
258
+ const focusable = svgEl.querySelectorAll('[tabindex]');
259
+ if (!focusable.length) return;
260
+
261
+ svgEl.addEventListener('keydown', e => {
262
+ const focused = svgEl.querySelector(':focus');
263
+ if (!focused) return;
264
+
265
+ const items = [...focusable];
266
+ const idx = items.indexOf(focused);
267
+
268
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
269
+ e.preventDefault();
270
+ const next = items[(idx + 1) % items.length];
271
+ next.focus();
272
+ } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
273
+ e.preventDefault();
274
+ const prev = items[(idx - 1 + items.length) % items.length];
275
+ prev.focus();
276
+ } else if (e.key === 'Escape') {
277
+ focused.blur();
278
+ }
279
+ });
280
+ }
281
+
282
+ // --- Legend toggle ---
283
+
284
+ /**
285
+ * Build interactive legend with toggle support.
286
+ * @param {Object[]} series — [{ key, color }]
287
+ * @param {Object} spec — { onLegendToggle }
288
+ * @returns {HTMLElement}
289
+ */
290
+ export function buildInteractiveLegend(series, spec) {
291
+ const legend = document.createElement('div');
292
+ legend.className = 'd-chart-legend';
293
+
294
+ const toggleState = new Map();
295
+
296
+ for (const s of series) {
297
+ const item = document.createElement('span');
298
+ item.className = 'd-chart-legend-item';
299
+
300
+ const swatch = document.createElement('span');
301
+ swatch.className = 'd-chart-legend-swatch';
302
+ swatch.style.background = s.color;
303
+ item.appendChild(swatch);
304
+ item.appendChild(document.createTextNode(s.key));
305
+
306
+ toggleState.set(s.key, true);
307
+
308
+ item.addEventListener('click', () => {
309
+ const visible = !toggleState.get(s.key);
310
+ toggleState.set(s.key, visible);
311
+ item.classList.toggle('d-chart-legend-disabled', !visible);
312
+
313
+ if (spec.onLegendToggle) {
314
+ spec.onLegendToggle({ series: s.key, visible });
315
+ }
316
+ });
317
+
318
+ legend.appendChild(item);
319
+ }
320
+
321
+ return legend;
322
+ }
323
+
324
+ // --- Spatial index (R-tree simplified) ---
325
+
326
+ /**
327
+ * Build spatial index from scene graph for hit testing.
328
+ * @param {Object} sceneGraph
329
+ * @returns {Object} index with .query(x, y) method
330
+ */
331
+ export function buildSpatialIndex(sceneGraph) {
332
+ const items = [];
333
+
334
+ function collect(node) {
335
+ if (!node) return;
336
+ if (node.data && (node.type === 'rect' || node.type === 'circle' || node.type === 'arc' || node.type === 'path')) {
337
+ items.push(node);
338
+ }
339
+ if (node.children) {
340
+ for (const child of node.children) collect(child);
341
+ }
342
+ }
343
+
344
+ collect(sceneGraph);
345
+
346
+ return {
347
+ query(x, y, radius = 10) {
348
+ const results = [];
349
+ for (const item of items) {
350
+ if (item.type === 'rect') {
351
+ if (x >= item.x && x <= item.x + item.w && y >= item.y && y <= item.y + item.h) {
352
+ results.push(item);
353
+ }
354
+ } else if (item.type === 'circle') {
355
+ const dx = x - item.cx, dy = y - item.cy;
356
+ if (Math.sqrt(dx * dx + dy * dy) <= (item.r || 0) + radius) {
357
+ results.push(item);
358
+ }
359
+ }
360
+ }
361
+ return results;
362
+ }
363
+ };
364
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Extended Palette — 8 seed → unlimited series + continuous color scales.
3
+ * @module _palette
4
+ */
5
+
6
+ /**
7
+ * Get chart color CSS variable for an index.
8
+ * 0-7: base palette, 8-31: extended, 32+: cycles.
9
+ * @param {number} index
10
+ * @returns {string}
11
+ */
12
+ export function chartColor(index) {
13
+ return `var(--d-chart-${index % 8})`;
14
+ }
15
+
16
+ /**
17
+ * Extended chart color with more variety beyond 8 series.
18
+ * Uses extended palette tokens generated by derive.js.
19
+ * @param {number} index
20
+ * @returns {string}
21
+ */
22
+ export function chartColorExtended(index) {
23
+ if (index < 8) return `var(--d-chart-${index})`;
24
+ if (index < 32) return `var(--d-chart-${index % 8}-ext-${Math.floor((index - 8) / 8) + 1})`;
25
+ return `var(--d-chart-${index % 8})`;
26
+ }
27
+
28
+ /** Base palette size */
29
+ export const PALETTE_SIZE = 8;
30
+
31
+ /** Extended palette size (with 3 extensions per base) */
32
+ export const PALETTE_SIZE_EXTENDED = 32;
33
+
34
+ /**
35
+ * Resolve palette to hex values for Canvas/WebGPU rendering.
36
+ * @param {number} count
37
+ * @param {HTMLElement} [el]
38
+ * @returns {string[]}
39
+ */
40
+ export function resolvePalette(count, el) {
41
+ const colors = [];
42
+ const target = el || (typeof document !== 'undefined' ? document.documentElement : null);
43
+ const style = target && typeof getComputedStyle === 'function' ? getComputedStyle(target) : null;
44
+
45
+ for (let i = 0; i < count; i++) {
46
+ const prop = i < 8 ? `--d-chart-${i}` : `--d-chart-${i % 8}-ext-${Math.floor((i - 8) / 8) + 1}`;
47
+ let color = style ? style.getPropertyValue(prop).trim() : '';
48
+ if (!color) color = FALLBACK[i % FALLBACK.length];
49
+ colors.push(color);
50
+ }
51
+
52
+ return colors;
53
+ }
54
+
55
+ /**
56
+ * Create a continuous color scale.
57
+ * @param {number[]} domain — [min, max] or [min, mid, max]
58
+ * @param {string[]} stops — hex color stops
59
+ * @returns {(value: number) => string}
60
+ */
61
+ export function colorScale(domain, stops) {
62
+ if (!stops || stops.length < 2) return () => stops?.[0] || '#000';
63
+ const segments = stops.length - 1;
64
+ const dMin = domain[0], dMax = domain[domain.length - 1];
65
+ const dRange = dMax - dMin || 1;
66
+
67
+ return function(value) {
68
+ const t = Math.max(0, Math.min(1, (value - dMin) / dRange));
69
+ const seg = Math.min(Math.floor(t * segments), segments - 1);
70
+ const localT = t * segments - seg;
71
+ return lerpColor(stops[seg], stops[seg + 1], localT);
72
+ };
73
+ }
74
+
75
+ // --- Built-in color scales ---
76
+
77
+ export const scales = {
78
+ /** Blue sequential */
79
+ blues: ['#f0f9ff', '#bae6fd', '#38bdf8', '#0284c7', '#075985'],
80
+ /** Red sequential */
81
+ reds: ['#fef2f2', '#fca5a5', '#ef4444', '#b91c1c', '#7f1d1d'],
82
+ /** Green sequential */
83
+ greens: ['#f0fdf4', '#86efac', '#22c55e', '#15803d', '#14532d'],
84
+ /** Red → White → Blue diverging */
85
+ divergingRdBu: ['#ef4444', '#fca5a5', '#fafafa', '#93c5fd', '#3b82f6'],
86
+ /** Purple → Orange diverging */
87
+ divergingPrOr: ['#7c3aed', '#c4b5fd', '#fafafa', '#fed7aa', '#f97316'],
88
+ /** Viridis-inspired */
89
+ viridis: ['#440154', '#3b528b', '#21918c', '#5ec962', '#fde725'],
90
+ /** Warm (yellow → red) */
91
+ warm: ['#fef3c7', '#fbbf24', '#f59e0b', '#ea580c', '#dc2626'],
92
+ /** Cool (cyan → purple) */
93
+ cool: ['#ecfeff', '#67e8f9', '#06b6d4', '#7c3aed', '#4c1d95'],
94
+ };
95
+
96
+ // --- Internals ---
97
+
98
+ const FALLBACK = ['#1366D9', '#7c3aed', '#0891b2', '#22c55e', '#f59e0b', '#ef4444', '#3b82f6', '#71717a'];
99
+
100
+ function lerpColor(hex1, hex2, t) {
101
+ const r1 = parseInt(hex1.slice(1, 3), 16), g1 = parseInt(hex1.slice(3, 5), 16), b1 = parseInt(hex1.slice(5, 7), 16);
102
+ const r2 = parseInt(hex2.slice(1, 3), 16), g2 = parseInt(hex2.slice(3, 5), 16), b2 = parseInt(hex2.slice(5, 7), 16);
103
+ const r = Math.round(r1 + (r2 - r1) * t), g = Math.round(g1 + (g2 - g1) * t), b = Math.round(b1 + (b2 - b1) * t);
104
+ return '#' + [r, g, b].map(c => Math.max(0, Math.min(255, c)).toString(16).padStart(2, '0')).join('');
105
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Renderer interface and auto-selection dispatch.
3
+ * Selects SVG, Canvas, or WebGPU based on data size or explicit override.
4
+ * Fallback chain: WebGPU → Canvas → SVG.
5
+ * @module _renderer
6
+ */
7
+
8
+ import { renderSVG } from './renderers/svg.js';
9
+ import { renderCanvas } from './renderers/canvas.js';
10
+
11
+ /** @type {number} Threshold below which SVG is used */
12
+ const SVG_THRESHOLD = 3000;
13
+
14
+ /**
15
+ * Auto-select and invoke the appropriate renderer.
16
+ * Dispatch: SVG (<3K) → WebGPU (3K+, if available) → Canvas (fallback).
17
+ * @param {Object} sceneGraph — scene graph from layout function
18
+ * @param {Object} spec — original chart spec (for renderer override)
19
+ * @returns {SVGElement|HTMLCanvasElement}
20
+ */
21
+ export function render(sceneGraph, spec) {
22
+ const renderer = spec.renderer || 'auto';
23
+ const dataLen = sceneGraph.meta?.dataLength || 0;
24
+
25
+ if (renderer === 'svg' || (renderer === 'auto' && dataLen < SVG_THRESHOLD)) {
26
+ return renderSVG(sceneGraph);
27
+ }
28
+
29
+ if (renderer === 'canvas') {
30
+ return renderCanvas(sceneGraph);
31
+ }
32
+
33
+ if (renderer === 'webgpu' || renderer === 'auto') {
34
+ // WebGPU-first for large datasets, Canvas as fallback
35
+ if (renderWebGPU) return renderWebGPU(sceneGraph);
36
+ return renderCanvas(sceneGraph);
37
+ }
38
+
39
+ return renderSVG(sceneGraph);
40
+ }
41
+
42
+ // WebGPU renderer reference (set when loaded)
43
+ let renderWebGPU = null;
44
+
45
+ /**
46
+ * Register a renderer implementation.
47
+ * @param {'webgpu'} type
48
+ * @param {Function} renderFn
49
+ */
50
+ export function registerRenderer(type, renderFn) {
51
+ if (type === 'webgpu') renderWebGPU = renderFn;
52
+ }