@elabs-ai/components-flow 4.1.0 → 5.0.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 (268) hide show
  1. package/README.md +2 -1
  2. package/dist/canvas-shell/canvas-shell.js +13 -0
  3. package/dist/canvas-shell/canvas-shell.js.map +1 -0
  4. package/dist/canvas-shell/clamped-fit-offset.js +8 -0
  5. package/dist/canvas-shell/clamped-fit-offset.js.map +1 -0
  6. package/dist/canvas-shell/index.js +14 -0
  7. package/dist/canvas-shell/index.js.map +1 -0
  8. package/dist/canvas-shell/use-measured-nodes.js +8 -0
  9. package/dist/canvas-shell/use-measured-nodes.js.map +1 -0
  10. package/dist/chunk-3BTU2VWZ.js +42 -0
  11. package/dist/chunk-3BTU2VWZ.js.map +1 -0
  12. package/dist/chunk-3LK6OOXE.js +2 -0
  13. package/dist/chunk-3LK6OOXE.js.map +1 -0
  14. package/dist/chunk-3M3FQAD4.js +105 -0
  15. package/dist/chunk-3M3FQAD4.js.map +1 -0
  16. package/dist/chunk-3Y5TCGC3.js +2 -0
  17. package/dist/chunk-3Y5TCGC3.js.map +1 -0
  18. package/dist/chunk-4VYHZM3S.js +108 -0
  19. package/dist/chunk-4VYHZM3S.js.map +1 -0
  20. package/dist/chunk-5TVVM4GR.js +45 -0
  21. package/dist/chunk-5TVVM4GR.js.map +1 -0
  22. package/dist/chunk-5XT73MQQ.js +2 -0
  23. package/dist/chunk-5XT73MQQ.js.map +1 -0
  24. package/dist/chunk-6LQD5Y5R.js +24 -0
  25. package/dist/chunk-6LQD5Y5R.js.map +1 -0
  26. package/dist/chunk-6SR26QY2.js +18 -0
  27. package/dist/chunk-6SR26QY2.js.map +1 -0
  28. package/dist/chunk-75JJK6BH.js +157 -0
  29. package/dist/chunk-75JJK6BH.js.map +1 -0
  30. package/dist/chunk-A66JBW4X.js +2 -0
  31. package/dist/chunk-A66JBW4X.js.map +1 -0
  32. package/dist/chunk-A7AFM3LI.js +72 -0
  33. package/dist/chunk-A7AFM3LI.js.map +1 -0
  34. package/dist/chunk-ADHLQF56.js +2 -0
  35. package/dist/chunk-ADHLQF56.js.map +1 -0
  36. package/dist/chunk-ATO4LXJF.js +75 -0
  37. package/dist/chunk-ATO4LXJF.js.map +1 -0
  38. package/dist/chunk-ATXVXTMZ.js +227 -0
  39. package/dist/chunk-ATXVXTMZ.js.map +1 -0
  40. package/dist/chunk-B6IUPZU7.js +2 -0
  41. package/dist/chunk-B6IUPZU7.js.map +1 -0
  42. package/dist/chunk-BJ7UMCD3.js +53 -0
  43. package/dist/chunk-BJ7UMCD3.js.map +1 -0
  44. package/dist/chunk-BWGYV4JZ.js +2 -0
  45. package/dist/chunk-BWGYV4JZ.js.map +1 -0
  46. package/dist/chunk-CWNO63LQ.js +109 -0
  47. package/dist/chunk-CWNO63LQ.js.map +1 -0
  48. package/dist/chunk-DHPUXKCZ.js +76 -0
  49. package/dist/chunk-DHPUXKCZ.js.map +1 -0
  50. package/dist/chunk-DNX7G3KA.js +185 -0
  51. package/dist/chunk-DNX7G3KA.js.map +1 -0
  52. package/dist/chunk-DNZRAJAQ.js +41 -0
  53. package/dist/chunk-DNZRAJAQ.js.map +1 -0
  54. package/dist/chunk-ELCLRVD4.js +2 -0
  55. package/dist/chunk-ELCLRVD4.js.map +1 -0
  56. package/dist/chunk-ELOADZSX.js +2 -0
  57. package/dist/chunk-ELOADZSX.js.map +1 -0
  58. package/dist/chunk-EUNV2MR5.js +21 -0
  59. package/dist/chunk-EUNV2MR5.js.map +1 -0
  60. package/dist/chunk-FAFQ5KLB.js +43 -0
  61. package/dist/chunk-FAFQ5KLB.js.map +1 -0
  62. package/dist/chunk-FIRO6Y3R.js +128 -0
  63. package/dist/chunk-FIRO6Y3R.js.map +1 -0
  64. package/dist/chunk-GE73WQUK.js +2 -0
  65. package/dist/chunk-GE73WQUK.js.map +1 -0
  66. package/dist/chunk-GIARAOU6.js +39 -0
  67. package/dist/chunk-GIARAOU6.js.map +1 -0
  68. package/dist/chunk-HTLTCCFA.js +73 -0
  69. package/dist/chunk-HTLTCCFA.js.map +1 -0
  70. package/dist/chunk-I53W3CFN.js +56 -0
  71. package/dist/chunk-I53W3CFN.js.map +1 -0
  72. package/dist/chunk-IJVFPUXO.js +2 -0
  73. package/dist/chunk-IJVFPUXO.js.map +1 -0
  74. package/dist/chunk-JUPMOISE.js +51 -0
  75. package/dist/chunk-JUPMOISE.js.map +1 -0
  76. package/dist/chunk-K5RPSVO6.js +167 -0
  77. package/dist/chunk-K5RPSVO6.js.map +1 -0
  78. package/dist/chunk-K6U27YJ6.js +78 -0
  79. package/dist/chunk-K6U27YJ6.js.map +1 -0
  80. package/dist/chunk-KDVMGMAK.js +2 -0
  81. package/dist/chunk-KDVMGMAK.js.map +1 -0
  82. package/dist/chunk-LQOZCOK3.js +74 -0
  83. package/dist/chunk-LQOZCOK3.js.map +1 -0
  84. package/dist/chunk-NAQWNX2A.js +102 -0
  85. package/dist/chunk-NAQWNX2A.js.map +1 -0
  86. package/dist/chunk-NVJVWGUS.js +152 -0
  87. package/dist/chunk-NVJVWGUS.js.map +1 -0
  88. package/dist/chunk-O2F3R3PV.js +2 -0
  89. package/dist/chunk-O2F3R3PV.js.map +1 -0
  90. package/dist/chunk-OMFQHMA6.js +2 -0
  91. package/dist/chunk-OMFQHMA6.js.map +1 -0
  92. package/dist/chunk-PE6JXVOX.js +2 -0
  93. package/dist/chunk-PE6JXVOX.js.map +1 -0
  94. package/dist/chunk-ROJEIIA4.js +52 -0
  95. package/dist/chunk-ROJEIIA4.js.map +1 -0
  96. package/dist/chunk-ROYAQKJA.js +9 -0
  97. package/dist/chunk-ROYAQKJA.js.map +1 -0
  98. package/dist/chunk-SHCZK4DO.js +45 -0
  99. package/dist/chunk-SHCZK4DO.js.map +1 -0
  100. package/dist/chunk-SXXJV6YJ.js +79 -0
  101. package/dist/chunk-SXXJV6YJ.js.map +1 -0
  102. package/dist/chunk-T7S7NJFD.js +287 -0
  103. package/dist/chunk-T7S7NJFD.js.map +1 -0
  104. package/dist/chunk-TEZ3OXAY.js +63 -0
  105. package/dist/chunk-TEZ3OXAY.js.map +1 -0
  106. package/dist/chunk-TGWPM2CG.js +199 -0
  107. package/dist/chunk-TGWPM2CG.js.map +1 -0
  108. package/dist/chunk-TSB6RE7L.js +2 -0
  109. package/dist/chunk-TSB6RE7L.js.map +1 -0
  110. package/dist/chunk-UPV2MT4O.js +2 -0
  111. package/dist/chunk-UPV2MT4O.js.map +1 -0
  112. package/dist/chunk-UZJZY4A5.js +2 -0
  113. package/dist/chunk-UZJZY4A5.js.map +1 -0
  114. package/dist/chunk-W3TQMUGX.js +2 -0
  115. package/dist/chunk-W3TQMUGX.js.map +1 -0
  116. package/dist/chunk-WN46RIOC.js +29 -0
  117. package/dist/chunk-WN46RIOC.js.map +1 -0
  118. package/dist/chunk-XZFE2YYH.js +70 -0
  119. package/dist/chunk-XZFE2YYH.js.map +1 -0
  120. package/dist/chunk-YG3KXTZW.js +2 -0
  121. package/dist/chunk-YG3KXTZW.js.map +1 -0
  122. package/dist/chunk-ZATVF42E.js +65 -0
  123. package/dist/chunk-ZATVF42E.js.map +1 -0
  124. package/dist/chunk-ZXZTLQIX.js +19 -0
  125. package/dist/chunk-ZXZTLQIX.js.map +1 -0
  126. package/dist/flow-button-edge/flow-button-edge.js +10 -0
  127. package/dist/flow-button-edge/flow-button-edge.js.map +1 -0
  128. package/dist/flow-button-edge/index.js +11 -0
  129. package/dist/flow-button-edge/index.js.map +1 -0
  130. package/dist/flow-edge/flow-edge.js +10 -0
  131. package/dist/flow-edge/flow-edge.js.map +1 -0
  132. package/dist/flow-edge/index.js +11 -0
  133. package/dist/flow-edge/index.js.map +1 -0
  134. package/dist/flow-edge-path/flow-edge-path.js +12 -0
  135. package/dist/flow-edge-path/flow-edge-path.js.map +1 -0
  136. package/dist/flow-edge-path/index.js +13 -0
  137. package/dist/flow-edge-path/index.js.map +1 -0
  138. package/dist/flow-edge-tokens/edge-path-sampler.js +10 -0
  139. package/dist/flow-edge-tokens/edge-path-sampler.js.map +1 -0
  140. package/dist/flow-edge-tokens/flow-edge-tokens.js +12 -0
  141. package/dist/flow-edge-tokens/flow-edge-tokens.js.map +1 -0
  142. package/dist/flow-edge-tokens/index.js +17 -0
  143. package/dist/flow-edge-tokens/index.js.map +1 -0
  144. package/dist/flow-floating-edge/floating-edge-geometry.js +8 -0
  145. package/dist/flow-floating-edge/floating-edge-geometry.js.map +1 -0
  146. package/dist/flow-floating-edge/flow-floating-edge.js +11 -0
  147. package/dist/flow-floating-edge/flow-floating-edge.js.map +1 -0
  148. package/dist/flow-floating-edge/index.js +15 -0
  149. package/dist/flow-floating-edge/index.js.map +1 -0
  150. package/dist/flow-group-node/flow-group-node.js +12 -0
  151. package/dist/flow-group-node/flow-group-node.js.map +1 -0
  152. package/dist/flow-group-node/index.js +13 -0
  153. package/dist/flow-group-node/index.js.map +1 -0
  154. package/dist/flow-handle/flow-handle-anchor.js +8 -0
  155. package/dist/flow-handle/flow-handle-anchor.js.map +1 -0
  156. package/dist/flow-handle/index.js +9 -0
  157. package/dist/flow-handle/index.js.map +1 -0
  158. package/dist/flow-layout/backbone.js +8 -0
  159. package/dist/flow-layout/backbone.js.map +1 -0
  160. package/dist/flow-layout/flow-layout.js +10 -0
  161. package/dist/flow-layout/flow-layout.js.map +1 -0
  162. package/dist/flow-layout/index.js +21 -0
  163. package/dist/flow-layout/index.js.map +1 -0
  164. package/dist/flow-layout/layout-flow-elk.js +9 -0
  165. package/dist/flow-layout/layout-flow-elk.js.map +1 -0
  166. package/dist/flow-layout/layout-graph.js +9 -0
  167. package/dist/flow-layout/layout-graph.js.map +1 -0
  168. package/dist/flow-layout/use-auto-layout.js +10 -0
  169. package/dist/flow-layout/use-auto-layout.js.map +1 -0
  170. package/dist/flow-layout/use-flow-layout.js +9 -0
  171. package/dist/flow-layout/use-flow-layout.js.map +1 -0
  172. package/dist/flow-mini-map/flow-mini-map.js +8 -0
  173. package/dist/flow-mini-map/flow-mini-map.js.map +1 -0
  174. package/dist/flow-mini-map/index.js +9 -0
  175. package/dist/flow-mini-map/index.js.map +1 -0
  176. package/dist/flow-node/flow-node.js +11 -0
  177. package/dist/flow-node/flow-node.js.map +1 -0
  178. package/dist/flow-node/index.js +12 -0
  179. package/dist/flow-node/index.js.map +1 -0
  180. package/dist/flow-placeholder-node/flow-placeholder-node.js +9 -0
  181. package/dist/flow-placeholder-node/flow-placeholder-node.js.map +1 -0
  182. package/dist/flow-placeholder-node/index.js +10 -0
  183. package/dist/flow-placeholder-node/index.js.map +1 -0
  184. package/dist/flow-self-loop-edge/flow-self-loop-edge.js +20 -0
  185. package/dist/flow-self-loop-edge/flow-self-loop-edge.js.map +1 -0
  186. package/dist/flow-self-loop-edge/index.js +28 -0
  187. package/dist/flow-self-loop-edge/index.js.map +1 -0
  188. package/dist/flow-self-loop-edge/self-loop-geometry.js +12 -0
  189. package/dist/flow-self-loop-edge/self-loop-geometry.js.map +1 -0
  190. package/dist/flow-smart-edge/flow-smart-edge.js +11 -0
  191. package/dist/flow-smart-edge/flow-smart-edge.js.map +1 -0
  192. package/dist/flow-smart-edge/index.js +25 -0
  193. package/dist/flow-smart-edge/index.js.map +1 -0
  194. package/dist/flow-smart-edge/smart-edge-geometry.js +18 -0
  195. package/dist/flow-smart-edge/smart-edge-geometry.js.map +1 -0
  196. package/dist/flow-weighted-edge/back-edge-geometry.js +8 -0
  197. package/dist/flow-weighted-edge/back-edge-geometry.js.map +1 -0
  198. package/dist/flow-weighted-edge/edge-aria.js +10 -0
  199. package/dist/flow-weighted-edge/edge-aria.js.map +1 -0
  200. package/dist/flow-weighted-edge/edge-label-pill.js +8 -0
  201. package/dist/flow-weighted-edge/edge-label-pill.js.map +1 -0
  202. package/dist/flow-weighted-edge/flow-weighted-edge.js +16 -0
  203. package/dist/flow-weighted-edge/flow-weighted-edge.js.map +1 -0
  204. package/dist/flow-weighted-edge/index.js +34 -0
  205. package/dist/flow-weighted-edge/index.js.map +1 -0
  206. package/dist/flow-weighted-edge/weight-scale.js +10 -0
  207. package/dist/flow-weighted-edge/weight-scale.js.map +1 -0
  208. package/dist/helper-lines/get-helper-lines.js +8 -0
  209. package/dist/helper-lines/get-helper-lines.js.map +1 -0
  210. package/dist/helper-lines/helper-lines.js +9 -0
  211. package/dist/helper-lines/helper-lines.js.map +1 -0
  212. package/dist/helper-lines/index.js +17 -0
  213. package/dist/helper-lines/index.js.map +1 -0
  214. package/dist/helper-lines/use-helper-lines.js +10 -0
  215. package/dist/helper-lines/use-helper-lines.js.map +1 -0
  216. package/dist/index.d.ts +241 -2
  217. package/dist/index.js +167 -2306
  218. package/dist/index.js.map +1 -1
  219. package/dist/inspector-panel/index.js +9 -0
  220. package/dist/inspector-panel/index.js.map +1 -0
  221. package/dist/inspector-panel/inspector-panel.js +8 -0
  222. package/dist/inspector-panel/inspector-panel.js.map +1 -0
  223. package/dist/legend/index.js +10 -0
  224. package/dist/legend/index.js.map +1 -0
  225. package/dist/legend/legend.js +9 -0
  226. package/dist/legend/legend.js.map +1 -0
  227. package/dist/use-flow-groups/group-operations.js +20 -0
  228. package/dist/use-flow-groups/group-operations.js.map +1 -0
  229. package/dist/use-flow-groups/index.js +25 -0
  230. package/dist/use-flow-groups/index.js.map +1 -0
  231. package/dist/use-flow-groups/use-flow-groups.js +9 -0
  232. package/dist/use-flow-groups/use-flow-groups.js.map +1 -0
  233. package/dist/zoom-controls/index.js +9 -0
  234. package/dist/zoom-controls/index.js.map +1 -0
  235. package/dist/zoom-controls/zoom-controls.js +8 -0
  236. package/dist/zoom-controls/zoom-controls.js.map +1 -0
  237. package/package.json +14 -7
  238. package/src/__contract__/inspector-panel.contract.test.tsx +49 -0
  239. package/src/__contract__/legend.contract.test.tsx +49 -0
  240. package/src/canvas-shell/canvas-shell.stories.tsx +1 -0
  241. package/src/canvas-shell/canvas-shell.tsx +51 -4
  242. package/src/canvas-shell/clamped-fit-offset.test.ts +25 -0
  243. package/src/canvas-shell/clamped-fit-offset.ts +30 -0
  244. package/src/custom-nodes/custom-nodes.stories.tsx +718 -0
  245. package/src/flow-edge-tokens/edge-path-sampler.test.ts +108 -0
  246. package/src/flow-edge-tokens/edge-path-sampler.ts +362 -0
  247. package/src/flow-edge-tokens/flow-edge-tokens.stories.tsx +312 -0
  248. package/src/flow-edge-tokens/flow-edge-tokens.test.tsx +115 -0
  249. package/src/flow-edge-tokens/flow-edge-tokens.tsx +94 -0
  250. package/src/flow-edge-tokens/index.ts +12 -0
  251. package/src/flow-layout/backbone.test.ts +88 -0
  252. package/src/flow-layout/backbone.ts +58 -0
  253. package/src/flow-layout/layout-flow-elk.stories.tsx +154 -0
  254. package/src/flow-layout/layout-flow-elk.test.ts +152 -0
  255. package/src/flow-layout/layout-flow-elk.ts +399 -0
  256. package/src/flow-mini-map/flow-mini-map.stories.tsx +20 -0
  257. package/src/flow-node/flow-node.stories.tsx +40 -1
  258. package/src/flow-self-loop-edge/flow-self-loop-edge.test.tsx +38 -0
  259. package/src/flow-self-loop-edge/flow-self-loop-edge.tsx +7 -0
  260. package/src/flow-weighted-edge/edge-label-pill.test.tsx +20 -0
  261. package/src/flow-weighted-edge/flow-weighted-edge.stories.tsx +1 -0
  262. package/src/flow-weighted-edge/flow-weighted-edge.test.tsx +40 -0
  263. package/src/flow-weighted-edge/flow-weighted-edge.tsx +7 -0
  264. package/src/index.ts +13 -0
  265. package/src/inspector-panel/inspector-panel.tsx +9 -6
  266. package/src/legend/legend.test.tsx +11 -0
  267. package/src/legend/legend.tsx +15 -7
  268. package/src/templates-flow-workspace.stories.tsx +7 -2
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/legend/legend.tsx"],"sourcesContent":["import { useLocale } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { computeEdgeWeightScale, type WeightedEdgeLike } from \"../flow-weighted-edge/weight-scale\";\n\nexport interface LegendItem {\n label: string;\n /** Any CSS color or token reference, e.g. \"var(--chart-1)\". */\n color: string;\n}\n\n/**\n * Categorical legend props — a swatch-and-label list. This is the shape\n * `Legend` renders when `variant` is omitted, byte-for-byte unchanged from\n * before the `\"scale\"` variant existed.\n */\nexport interface LegendCategoricalProps {\n variant?: \"categorical\";\n items: LegendItem[];\n title?: string;\n className?: string;\n}\n\n/**\n * Continuous scale legend — a reading key that explains a WIDTH or COLOR\n * encoding as a *range*, not a set of discrete categories (e.g.\n * `FlowWeightedEdge`'s `data.weight` → stroke width, `data.value` → stroke\n * color). `kind: \"width\"` reuses `computeEdgeWeightScale` — the exact pure\n * scale `FlowWeightedEdge` calls — so the sample strokes drawn here can never\n * drift from the widths a real flow's edges render for the same weights.\n */\nexport interface LegendScaleProps {\n variant: \"scale\";\n /** Which continuous encoding this legend explains. */\n kind: \"width\" | \"color\";\n /** `[min, max]` of the underlying value the ramp represents. */\n domain: [number, number];\n /**\n * Formats a domain value for display at a tick.\n * @default the active `LocaleProvider` locale's `formatNumber` (host locale\n * when no provider is mounted) — never a hardcoded `toLocaleString()`.\n */\n format?: (value: number) => string;\n /**\n * Sample count for `kind: \"width\"`: `\"minmax\"` draws a min/max pair of\n * sample strokes, `\"minmedmax\"` adds the domain midpoint as a third\n * sample. `kind: \"color\"` always renders a fixed 5-stop gradient\n * regardless of this prop — five stops is what makes a 2-endpoint color\n * ramp legible as an ordered scale (see the color-ramp a11y note below),\n * independent of how many width samples are shown.\n * @default \"minmax\"\n */\n ticks?: \"minmax\" | \"minmedmax\";\n title?: string;\n className?: string;\n}\n\nexport type LegendProps = LegendCategoricalProps | LegendScaleProps;\n\n/**\n * Small legend mapping colors/types to labels for a canvas or chart\n * (`variant: \"categorical\"`, the default), or — via `variant=\"scale\"` — a\n * continuous width/color ramp with a domain, tick marks and formatting.\n */\nexport function Legend(props: LegendProps) {\n if (props.variant === \"scale\") {\n return <LegendScale {...props} />;\n }\n return <LegendCategorical {...props} />;\n}\n\nfunction LegendCategorical({ items, title, className }: LegendCategoricalProps) {\n return (\n <div\n className={cn(\n \"rounded-lg bg-surface-elevated/90 p-3 text-xs shadow-ring-sm backdrop-blur\",\n className,\n )}\n >\n {title ? <div className=\"mb-1.5 font-medium text-foreground\">{title}</div> : null}\n <ul className=\"space-y-1\">\n {items.map((item) => (\n <li key={item.label} className=\"flex items-center gap-2 text-muted-foreground\">\n <span\n aria-hidden=\"true\"\n className=\"size-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n {item.label}\n </li>\n ))}\n </ul>\n </div>\n );\n}\n\n/**\n * Width-ramp sample: a tick value plus the stroke width `computeEdgeWeightScale`\n * assigns it. The \"edges\" fed to the scale are exactly the requested tick\n * values, so the group's own min/max always equal the domain — the returned\n * widths are byte-identical to what `computeEdgeWeightScale` would compute for\n * real edges carrying these same weights (no duplicated min-max math).\n */\nfunction computeWidthSamples(\n domain: [number, number],\n ticks: \"minmax\" | \"minmedmax\",\n): { value: number; width: number }[] {\n const [min, max] = domain;\n const values = ticks === \"minmedmax\" ? [min, (min + max) / 2, max] : [min, max];\n const likeEdges: WeightedEdgeLike[] = values.map((weight, index) => ({\n id: String(index),\n data: { weight },\n }));\n const widths = computeEdgeWeightScale(likeEdges);\n return values.map((value, index) => ({ value, width: widths.get(String(index)) ?? 0 }));\n}\n\n/** Fractions along the domain the color ramp samples — fixed at 5 stops (see `LegendScaleProps.ticks`). */\nconst COLOR_STOP_FRACTIONS = [0, 0.25, 0.5, 0.75, 1] as const;\n\nfunction scaleKindLabel(kind: \"width\" | \"color\"): string {\n return kind === \"width\" ? \"width\" : \"color\";\n}\n\nfunction LegendScale({\n kind,\n domain,\n format,\n ticks = \"minmax\",\n title,\n className,\n}: LegendScaleProps) {\n // The active `LocaleProvider` locale, not a hardcoded `toLocaleString()` —\n // otherwise a `de-DE`-configured app still reads the domain ticks in the\n // browser's host locale.\n const { formatNumber } = useLocale();\n const format_ = format ?? formatNumber;\n const [min, max] = domain;\n // e.g. \"Edge width scale, 2 to 48, minimum to maximum\" — a single accessible\n // name for the whole reading key, since it is one keyboard tab stop, not one\n // per tick.\n const ariaLabel = `Edge ${scaleKindLabel(kind)} scale, ${format_(min)} to ${format_(max)}, minimum to maximum`;\n\n return (\n <div\n aria-label={ariaLabel}\n className={cn(\n \"flex flex-col gap-2 rounded-lg bg-surface-elevated/90 p-3 shadow-ring-sm backdrop-blur\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n className,\n )}\n role=\"group\"\n tabIndex={0}\n >\n {title ? <div className=\"text-body font-medium text-foreground\">{title}</div> : null}\n {kind === \"width\" ? (\n <LegendScaleWidth domain={domain} format={format_} ticks={ticks} />\n ) : (\n <LegendScaleColor domain={domain} format={format_} />\n )}\n </div>\n );\n}\n\nfunction LegendScaleWidth({\n domain,\n format,\n ticks,\n}: {\n domain: [number, number];\n format: (value: number) => string;\n ticks: \"minmax\" | \"minmedmax\";\n}) {\n const samples = computeWidthSamples(domain, ticks);\n return (\n <div className=\"flex flex-col gap-2\">\n {samples.map((sample, index) => (\n // Index disambiguates a zero-width domain, where every sample shares\n // the same value (and therefore the same width).\n <div key={`${index}-${sample.value}`} className=\"flex items-center gap-3\">\n {/* The thin end of the ramp (default floor 1.5px) is the weakest mark\n in the system — `--flow-edge` is the same token a real, unweighted\n edge draws, and `strokeLinecap=\"round\"` keeps that floor width from\n disappearing into a hairline. */}\n <svg aria-hidden=\"true\" className=\"shrink-0\" height={16} overflow=\"visible\" width={40}>\n <line\n stroke=\"var(--flow-edge)\"\n strokeLinecap=\"round\"\n strokeWidth={sample.width}\n x1={2}\n x2={38}\n y1={8}\n y2={8}\n />\n </svg>\n <span className=\"min-w-0 truncate text-meta tabular-nums text-muted-foreground\">\n {format(sample.value)}\n </span>\n </div>\n ))}\n </div>\n );\n}\n\nfunction LegendScaleColor({\n domain,\n format,\n}: {\n domain: [number, number];\n format: (value: number) => string;\n}) {\n const [min, max] = domain;\n const span = max - min;\n const stops = COLOR_STOP_FRACTIONS.map((fraction) => ({\n fraction,\n value: min + fraction * span,\n }));\n // Explicit 5-stop `color-mix` gradient (interpolated `in oklch`, matching how\n // the `--flow-edge-weak`/`--flow-edge-strong` tokens themselves are authored)\n // rather than a bare 2-color `linear-gradient`, so the ramp's perceptual\n // steps match the token authoring space instead of the browser's default\n // sRGB gradient interpolation.\n const gradient = `linear-gradient(to right, ${stops\n .map(({ fraction }) => {\n const percent = Math.round(fraction * 100);\n return `color-mix(in oklch, var(--flow-edge-strong) ${percent}%, var(--flow-edge-weak)) ${percent}%`;\n })\n .join(\", \")})`;\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n {/*\n WCAG 1.4.1 — this gradient alone cannot carry the ordering, so the\n numbered ticks below are the required second channel, not decoration.\n */}\n <div\n aria-hidden=\"true\"\n className=\"h-3 w-full rounded-full\"\n style={{ backgroundImage: gradient }}\n />\n <div className=\"flex justify-between gap-1\">\n {stops.map((stop, index) => (\n <span\n // Index disambiguates a zero-width domain, where every stop shares\n // the same formatted value.\n key={`${index}-${stop.value}`}\n className={cn(\n \"min-w-0 truncate text-meta tabular-nums text-muted-foreground\",\n index === 0 && \"text-start\",\n index === stops.length - 1 && \"text-end\",\n )}\n >\n {format(stop.value)}\n </span>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ //# sourceMappingURL=chunk-O2F3R3PV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ //# sourceMappingURL=chunk-OMFQHMA6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ //# sourceMappingURL=chunk-PE6JXVOX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,52 @@
1
+ "use client";
2
+ import {
3
+ createEdgePathSampler
4
+ } from "./chunk-T7S7NJFD.js";
5
+
6
+ // src/flow-edge-tokens/flow-edge-tokens.tsx
7
+ import { forwardRef, useMemo } from "react";
8
+ import { useReducedMotion } from "@elabs-ai/components-tokens";
9
+ import { cn } from "@elabs-ai/components-ui/lib/cn";
10
+ import { jsx } from "react/jsx-runtime";
11
+ var DEFAULT_EDGE_TOKEN_RADIUS = 4;
12
+ var FlowEdgeTokens = forwardRef(function FlowEdgeTokens2({ path, tokens, className, ...props }, ref) {
13
+ const reducedMotion = useReducedMotion();
14
+ const sampler = useMemo(() => createEdgePathSampler(path), [path]);
15
+ return /* @__PURE__ */ jsx(
16
+ "g",
17
+ {
18
+ ref,
19
+ "aria-hidden": "true",
20
+ "data-slot": "flow-edge-tokens",
21
+ "data-motion": reducedMotion ? "reduced" : "animated",
22
+ className: cn("pointer-events-none", className),
23
+ ...props,
24
+ children: sampler ? tokens.map((token) => {
25
+ const { x, y } = sampler.pointAt(token.progress);
26
+ return /* @__PURE__ */ jsx(
27
+ "circle",
28
+ {
29
+ "data-slot": "flow-edge-tokens-token",
30
+ "data-token-id": token.id,
31
+ cx: 0,
32
+ cy: 0,
33
+ r: token.radius ?? DEFAULT_EDGE_TOKEN_RADIUS,
34
+ strokeWidth: 1.5,
35
+ className: cn(
36
+ "fill-primary stroke-background",
37
+ !reducedMotion && "transition-transform duration-fast ease-standard"
38
+ ),
39
+ style: { transform: `translate(${x}px, ${y}px)` }
40
+ },
41
+ token.id
42
+ );
43
+ }) : null
44
+ }
45
+ );
46
+ });
47
+
48
+ export {
49
+ DEFAULT_EDGE_TOKEN_RADIUS,
50
+ FlowEdgeTokens
51
+ };
52
+ //# sourceMappingURL=chunk-ROJEIIA4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/flow-edge-tokens/flow-edge-tokens.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, useMemo, type SVGProps } from \"react\";\nimport { useReducedMotion } from \"@elabs-ai/components-tokens\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { createEdgePathSampler } from \"./edge-path-sampler\";\n\n/** Default token radius, in px. */\nexport const DEFAULT_EDGE_TOKEN_RADIUS = 4;\n\nexport interface FlowEdgeToken {\n /** Stable identity (e.g. a case id). Keys the rendered marker, so a token keeps its element — and its transition — as it moves. */\n id: string;\n /** Position along the path's arc length: `0` = source end, `1` = target end. Clamped; a non-finite value sits at `0`. */\n progress: number;\n /** Marker radius in px — e.g. scaled by congestion. @default 4 */\n radius?: number;\n}\n\nexport interface FlowEdgeTokensProps extends Omit<SVGProps<SVGGElement>, \"ref\" | \"children\"> {\n /** SVG path `d` the tokens travel along — the same string the edge draws. */\n path: string;\n /** Tokens to place. The parent owns `progress`; this part keeps no clock. */\n tokens: readonly FlowEdgeToken[];\n}\n\n/**\n * Markers (\"tokens\") placed along an edge's path — the presentational primitive\n * behind process replay. Each token is a circle at `progress` (0..1) of the\n * path's arc length.\n *\n * **Parent-driven, no internal clock.** Nothing here animates on its own: the\n * parent re-renders with new `progress` values (from a slider, a playback loop,\n * a play function). Position is a pure function of `(path, progress)` — see\n * `createEdgePathSampler` — so every render is deterministic and identical in\n * jsdom, on the server and in the browser.\n *\n * Between two renders a token glides with a short `duration-fast` transform\n * transition, so a parent stepping at a coarse rate still reads as motion. Under\n * reduced motion (`useReducedMotion()`: the ThemeProvider preference, else the\n * OS setting) the transition class is not rendered at all and a token jumps\n * straight to its new position; the `duration-fast` token also collapses to\n * `--motion-min` through the global motion gate (`data-motion-pref`).\n *\n * **Decorative to assistive technology.** The group is `aria-hidden` and ignores\n * pointer events: a moving dot has no stable meaning to announce. The consumer\n * owns the text alternative (a live case count, a summary). Colour is the one\n * `--primary` fill for every token, with a `--background` halo so a token stays\n * distinct from the edge stroke beneath it.\n *\n * Existing brand edges render this for you from `data.tokens`\n * (`FlowWeightedEdge`, `FlowSelfLoopEdge`); a custom edge renders it after its\n * `FlowEdgePath` with the same `path` string.\n */\nexport const FlowEdgeTokens = forwardRef<SVGGElement, FlowEdgeTokensProps>(function FlowEdgeTokens(\n { path, tokens, className, ...props },\n ref,\n) {\n const reducedMotion = useReducedMotion();\n const sampler = useMemo(() => createEdgePathSampler(path), [path]);\n\n return (\n <g\n ref={ref}\n aria-hidden=\"true\"\n data-slot=\"flow-edge-tokens\"\n data-motion={reducedMotion ? \"reduced\" : \"animated\"}\n className={cn(\"pointer-events-none\", className)}\n {...props}\n >\n {sampler\n ? tokens.map((token) => {\n const { x, y } = sampler.pointAt(token.progress);\n return (\n <circle\n key={token.id}\n data-slot=\"flow-edge-tokens-token\"\n data-token-id={token.id}\n cx={0}\n cy={0}\n r={token.radius ?? DEFAULT_EDGE_TOKEN_RADIUS}\n strokeWidth={1.5}\n className={cn(\n \"fill-primary stroke-background\",\n !reducedMotion && \"transition-transform duration-fast ease-standard\",\n )}\n style={{ transform: `translate(${x}px, ${y}px)` }}\n />\n );\n })\n : null}\n </g>\n );\n});\n"],"mappings":";;;;;;AAEA,SAAS,YAAY,eAA8B;AACnD,SAAS,wBAAwB;AACjC,SAAS,UAAU;AAsEL;AAlEP,IAAM,4BAA4B;AA8ClC,IAAM,iBAAiB,WAA6C,SAASA,gBAClF,EAAE,MAAM,QAAQ,WAAW,GAAG,MAAM,GACpC,KACA;AACA,QAAM,gBAAgB,iBAAiB;AACvC,QAAM,UAAU,QAAQ,MAAM,sBAAsB,IAAI,GAAG,CAAC,IAAI,CAAC;AAEjE,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,eAAY;AAAA,MACZ,aAAU;AAAA,MACV,eAAa,gBAAgB,YAAY;AAAA,MACzC,WAAW,GAAG,uBAAuB,SAAS;AAAA,MAC7C,GAAG;AAAA,MAEH,oBACG,OAAO,IAAI,CAAC,UAAU;AACpB,cAAM,EAAE,GAAG,EAAE,IAAI,QAAQ,QAAQ,MAAM,QAAQ;AAC/C,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,aAAU;AAAA,YACV,iBAAe,MAAM;AAAA,YACrB,IAAI;AAAA,YACJ,IAAI;AAAA,YACJ,GAAG,MAAM,UAAU;AAAA,YACnB,aAAa;AAAA,YACb,WAAW;AAAA,cACT;AAAA,cACA,CAAC,iBAAiB;AAAA,YACpB;AAAA,YACA,OAAO,EAAE,WAAW,aAAa,CAAC,OAAO,CAAC,MAAM;AAAA;AAAA,UAX3C,MAAM;AAAA,QAYb;AAAA,MAEJ,CAAC,IACD;AAAA;AAAA,EACN;AAEJ,CAAC;","names":["FlowEdgeTokens"]}
@@ -0,0 +1,9 @@
1
+ "use client";
2
+
3
+ // src/flow-handle/flow-handle-anchor.ts
4
+ var FLOW_HANDLE_ANCHOR_CLASS = "transition-none";
5
+
6
+ export {
7
+ FLOW_HANDLE_ANCHOR_CLASS
8
+ };
9
+ //# sourceMappingURL=chunk-ROYAQKJA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/flow-handle/flow-handle-anchor.ts"],"sourcesContent":["/**\n * The class every React Flow `<Handle>` in this package carries — and the one a\n * consumer's own custom node must carry too.\n *\n * A connector dot is a MEASUREMENT ANCHOR, not a moving part. React Flow measures\n * `handleBounds` from the DOM exactly once per layout change (`updateNodeInternals`,\n * fired from the effect that sees `sourcePosition`/`targetPosition` change) and then\n * draws every edge endpoint from that stored number until something invalidates it.\n * So a dot that is still ON ITS WAY to its new side when that measurement is taken is\n * measured at the wrong place — permanently, because nothing measures again.\n *\n * That is not hypothetical, and it is not only about author-written transitions:\n *\n * - `@elabs-ai/components-tokens`' reduced-motion backstop (`themes.css`, MOTION GATE)\n * sets `transition-duration: 0.01ms !important` on `*` so third-party engines that\n * ignore the `--t-*` tokens (Monaco, `@xyflow/react`, Streamdown) cannot animate.\n * `transition-property` is left at its initial value, `all` — so under\n * `prefers-reduced-motion: reduce` that rule does not remove a transition from a\n * handle, it CREATES one: every geometric property of every element becomes\n * transitioned, for one frame.\n * - One frame is all it takes. Measured on `ProcessMap direction=\"LR\"`: at the commit\n * that flips the handles from top/bottom to left/right, the dot's computed box was\n * still the OLD one (`getAnimations()` on it returned live `left`+`top`+`transform`\n * transitions), React Flow measured `{ x: 164, y: 45.5 }` where the settled DOM has\n * `{ x: 172, y: 37.5 }`, and every edge on the map then terminated up to 67 px away\n * from the dot it points at — for reduced-motion readers only, forever.\n *\n * `transition-property: none` is the fix at the right layer: it costs nothing under\n * normal motion (no rule animates a handle there anyway) and it makes the reposition\n * synchronous, so whenever React Flow measures, it measures the settled dot.\n *\n * The literal is written out here rather than assembled, because Tailwind extracts\n * candidates from source TEXT: this file is scanned, so the utility is emitted, and\n * components may interpolate the constant freely.\n */\nexport const FLOW_HANDLE_ANCHOR_CLASS = \"transition-none\";\n"],"mappings":";;;AAmCO,IAAM,2BAA2B;","names":[]}
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ import {
3
+ FlowEdgePath
4
+ } from "./chunk-SXXJV6YJ.js";
5
+
6
+ // src/flow-edge/flow-edge.tsx
7
+ import { getBezierPath } from "@xyflow/react";
8
+ import { jsx } from "react/jsx-runtime";
9
+ function FlowEdge({
10
+ id,
11
+ sourceX,
12
+ sourceY,
13
+ targetX,
14
+ targetY,
15
+ sourcePosition,
16
+ targetPosition,
17
+ markerEnd,
18
+ style
19
+ }) {
20
+ const [edgePath] = getBezierPath({
21
+ sourceX,
22
+ sourceY,
23
+ targetX,
24
+ targetY,
25
+ sourcePosition,
26
+ targetPosition
27
+ });
28
+ return /* @__PURE__ */ jsx(
29
+ FlowEdgePath,
30
+ {
31
+ id,
32
+ path: edgePath,
33
+ markerEnd,
34
+ "data-slot": "flow-edge",
35
+ stroke: "var(--flow-edge)",
36
+ strokeWidth: 1.5,
37
+ style
38
+ }
39
+ );
40
+ }
41
+
42
+ export {
43
+ FlowEdge
44
+ };
45
+ //# sourceMappingURL=chunk-SHCZK4DO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/flow-edge/flow-edge.tsx"],"sourcesContent":["import { getBezierPath, type EdgeProps } from \"@xyflow/react\";\nimport { FlowEdgePath } from \"../flow-edge-path\";\n\n/**\n * Branded bezier edge using the `--flow-edge` token. Register it in\n * `edgeTypes={{ brand: FlowEdge }}` and create edges with `type: \"brand\"`.\n *\n * Drawn through `FlowEdgePath`, so it carries the shared keyboard focus\n * indicator (#286) — never reach for React Flow's `BaseEdge` directly.\n */\nexport function FlowEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n markerEnd,\n style,\n}: EdgeProps) {\n const [edgePath] = getBezierPath({\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n });\n return (\n <FlowEdgePath\n id={id}\n path={edgePath}\n markerEnd={markerEnd}\n data-slot=\"flow-edge\"\n stroke=\"var(--flow-edge)\"\n strokeWidth={1.5}\n style={style}\n />\n );\n}\n"],"mappings":";;;;;;AAAA,SAAS,qBAAqC;AA8B1C;AApBG,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAc;AACZ,QAAM,CAAC,QAAQ,IAAI,cAAc;AAAA,IAC/B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,aAAU;AAAA,MACV,QAAO;AAAA,MACP,aAAa;AAAA,MACb;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -0,0 +1,79 @@
1
+ "use client";
2
+
3
+ // src/flow-edge-path/flow-edge-path.tsx
4
+ import { BaseEdge } from "@xyflow/react";
5
+ import { cn } from "@elabs-ai/components-ui/lib/cn";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ var FLOW_EDGE_FOCUS_RING_WIDTH = 3;
8
+ var FLOW_EDGE_FOCUS_CONTOUR_WIDTH = 6;
9
+ function FlowEdgePath({
10
+ path,
11
+ stroke,
12
+ strokeWidth,
13
+ strokeDasharray,
14
+ strokeOpacity,
15
+ markerEnd,
16
+ markerStart,
17
+ interactionWidth,
18
+ className,
19
+ style,
20
+ ...props
21
+ }) {
22
+ const focusDash = strokeDasharray ?? "none";
23
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
24
+ /* @__PURE__ */ jsx(
25
+ "path",
26
+ {
27
+ d: path,
28
+ fill: "none",
29
+ "aria-hidden": "true",
30
+ "data-slot": "flow-edge-focus-contour",
31
+ className: "pointer-events-none stroke-foreground opacity-0 [.react-flow\\_\\_edge:focus-visible_&]:opacity-100",
32
+ strokeWidth: strokeWidth + FLOW_EDGE_FOCUS_CONTOUR_WIDTH,
33
+ strokeDasharray: focusDash,
34
+ strokeLinecap: "round"
35
+ }
36
+ ),
37
+ /* @__PURE__ */ jsx(
38
+ "path",
39
+ {
40
+ d: path,
41
+ fill: "none",
42
+ "aria-hidden": "true",
43
+ "data-slot": "flow-edge-focus-ring",
44
+ className: "pointer-events-none stroke-ring opacity-0 [.react-flow\\_\\_edge:focus-visible_&]:opacity-100",
45
+ strokeWidth: strokeWidth + FLOW_EDGE_FOCUS_RING_WIDTH,
46
+ strokeDasharray: focusDash,
47
+ strokeLinecap: "round"
48
+ }
49
+ ),
50
+ /* @__PURE__ */ jsx(
51
+ BaseEdge,
52
+ {
53
+ ...props,
54
+ path,
55
+ markerEnd,
56
+ markerStart,
57
+ interactionWidth,
58
+ className: cn(
59
+ "transition-[stroke,stroke-width] duration-fast ease-standard motion-reduce:transition-none",
60
+ className
61
+ ),
62
+ style: {
63
+ stroke,
64
+ strokeWidth,
65
+ ...strokeDasharray ? { strokeDasharray } : null,
66
+ ...strokeOpacity !== void 0 ? { strokeOpacity } : null,
67
+ ...style
68
+ }
69
+ }
70
+ )
71
+ ] });
72
+ }
73
+
74
+ export {
75
+ FLOW_EDGE_FOCUS_RING_WIDTH,
76
+ FLOW_EDGE_FOCUS_CONTOUR_WIDTH,
77
+ FlowEdgePath
78
+ };
79
+ //# sourceMappingURL=chunk-SXXJV6YJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/flow-edge-path/flow-edge-path.tsx"],"sourcesContent":["import type { CSSProperties, SVGProps } from \"react\";\nimport { BaseEdge } from \"@xyflow/react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\n/**\n * Extra stroke width, in px, of the `--ring` band drawn around a focused edge.\n * The band sits *outside* the edge's own stroke, so it is visible whatever the\n * edge's resting width is.\n */\nexport const FLOW_EDGE_FOCUS_RING_WIDTH = 3;\n\n/**\n * Extra stroke width, in px, of the neutral contour drawn outside the `--ring`\n * band. This is the layer that carries the WCAG 1.4.11 bar: `--foreground`\n * measures 12.50:1 against `--canvas` in `light` and 16.25:1 in `dark`, where\n * `--ring` alone measures 1.30:1 in `light` (issue #286).\n */\nexport const FLOW_EDGE_FOCUS_CONTOUR_WIDTH = 6;\n\nexport interface FlowEdgePathProps extends Omit<\n SVGProps<SVGPathElement>,\n \"path\" | \"stroke\" | \"strokeWidth\" | \"style\" | \"ref\"\n> {\n /** SVG path `d` for this edge, from `getBezierPath`/`getSmoothStepPath`/… */\n path: string;\n /** Resting stroke paint — a `var(--token)` reference or a resolved colour. */\n stroke: string;\n /** Resting stroke width in px. The focus layers are drawn wider than this. */\n strokeWidth: number;\n /** Dash pattern, applied to the edge AND to both focus layers so a dashed edge keeps its shape when focused. */\n strokeDasharray?: string;\n /** Stroke opacity for the edge itself. Never applied to the focus layers. */\n strokeOpacity?: number;\n /** React Flow marker url, e.g. from `EdgeProps.markerEnd`. */\n markerEnd?: string;\n /** React Flow marker url, e.g. from `EdgeProps.markerStart`. */\n markerStart?: string;\n /** Width of the invisible pointer-target path React Flow draws over the edge. */\n interactionWidth?: number;\n /** Merged onto the edge path, last — so a consumer's `style.stroke` still wins. */\n style?: CSSProperties;\n}\n\n/**\n * The edge path every brand edge type draws, with the keyboard focus indicator\n * built in. **Use this instead of React Flow's `BaseEdge`** — a custom edge that\n * reaches for `BaseEdge` directly ships with no focus indicator at all, which is\n * the defect issue #286 records (and `no-raw-base-edge.test.ts` fails on).\n *\n * ## Why the indicator is drawn rather than restyled\n *\n * React Flow zeroes the native outline on a focused edge\n * (`.react-flow__edge:focus-visible { outline: none }`) and substitutes a stroke\n * recolour on `.react-flow__edge-path`. Every brand edge passes its stroke as an\n * **inline style** on that exact path — `BaseEdge` spreads `style` onto it — and\n * an inline declaration beats any stylesheet rule, so the substitute never\n * painted. Focusing an edge changed nothing on screen: WCAG 2.4.7 failed on\n * every edge of every canvas, in both themes.\n *\n * The obvious repair — move the stroke into a custom property so a stylesheet\n * rule can reach it — does **not** work here, and that was measured rather than\n * assumed: React Flow's own `.react-flow__edge-path { stroke: … }` ships\n * **unlayered**, and unlayered CSS outranks anything in `@layer utilities`,\n * where Tailwind puts every utility. A `stroke-[var(--flow-edge-stroke)]` class\n * would therefore lose to React Flow's `#b1b1b7` default and repaint every edge\n * in the library. `!important` and setting `--xy-edge-stroke-selected` were both\n * rejected in the issue.\n *\n * So the indicator is drawn on two paths this component owns, under the edge and\n * on the same geometry. Nothing can shadow them: not React Flow's stylesheet,\n * not a consumer's inline `style.stroke`, not a future edge type's own painting.\n * They are hidden (`opacity-0`) until the ancestor `g.react-flow__edge` matches\n * `:focus-visible`, which is a plain descendant selector on classes only this\n * component uses.\n *\n * ## Compound, because one colour is not enough\n *\n * The indicator is two layers, per the compound-indicator recipe in\n * `.claude/rules/theming.md`: a neutral `--foreground` contour at\n * `strokeWidth + 6`, and the `--ring` band at `strokeWidth + 3` over it. The\n * contour is the layer that clears WCAG 1.4.11's 3:1 bar against `--canvas` in\n * both reference themes (12.50:1 `light`, 16.25:1 `dark`); a bare `--ring`\n * stroke would not (1.30:1 in `light`). It is opacity + stroke only — no\n * shadow — so it survives `data-decoration=\"8|9|10\"`, which goes shadowless.\n */\nexport function FlowEdgePath({\n path,\n stroke,\n strokeWidth,\n strokeDasharray,\n strokeOpacity,\n markerEnd,\n markerStart,\n interactionWidth,\n className,\n style,\n ...props\n}: FlowEdgePathProps) {\n // The focus layers repeat the edge's own dash pattern so a dashed edge (the\n // `\"back\"` variant) does not gain a solid halo. `\"none\"` is explicit rather\n // than omitted because React Flow's `.react-flow__edge.animated path` rule\n // sets a dasharray on *every* path inside an animated edge, ours included.\n const focusDash = strokeDasharray ?? \"none\";\n\n return (\n <>\n {/* Outer neutral contour — the layer that carries the 3:1 bar. */}\n <path\n d={path}\n fill=\"none\"\n aria-hidden=\"true\"\n data-slot=\"flow-edge-focus-contour\"\n className=\"pointer-events-none stroke-foreground opacity-0 [.react-flow\\_\\_edge:focus-visible_&]:opacity-100\"\n strokeWidth={strokeWidth + FLOW_EDGE_FOCUS_CONTOUR_WIDTH}\n strokeDasharray={focusDash}\n strokeLinecap=\"round\"\n />\n {/* Inner --ring band, drawn over the contour and under the edge. */}\n <path\n d={path}\n fill=\"none\"\n aria-hidden=\"true\"\n data-slot=\"flow-edge-focus-ring\"\n className=\"pointer-events-none stroke-ring opacity-0 [.react-flow\\_\\_edge:focus-visible_&]:opacity-100\"\n strokeWidth={strokeWidth + FLOW_EDGE_FOCUS_RING_WIDTH}\n strokeDasharray={focusDash}\n strokeLinecap=\"round\"\n />\n <BaseEdge\n {...props}\n path={path}\n markerEnd={markerEnd}\n markerStart={markerStart}\n interactionWidth={interactionWidth}\n className={cn(\n \"transition-[stroke,stroke-width] duration-fast ease-standard motion-reduce:transition-none\",\n className,\n )}\n style={{\n stroke,\n strokeWidth,\n ...(strokeDasharray ? { strokeDasharray } : null),\n ...(strokeOpacity !== undefined ? { strokeOpacity } : null),\n ...style,\n }}\n />\n </>\n );\n}\n"],"mappings":";;;AACA,SAAS,gBAAgB;AACzB,SAAS,UAAU;AAuGf,mBAEE,KAFF;AAhGG,IAAM,6BAA6B;AAQnC,IAAM,gCAAgC;AAoEtC,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAsB;AAKpB,QAAM,YAAY,mBAAmB;AAErC,SACE,iCAEE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,GAAG;AAAA,QACH,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,aAAU;AAAA,QACV,WAAU;AAAA,QACV,aAAa,cAAc;AAAA,QAC3B,iBAAiB;AAAA,QACjB,eAAc;AAAA;AAAA,IAChB;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,GAAG;AAAA,QACH,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,aAAU;AAAA,QACV,WAAU;AAAA,QACV,aAAa,cAAc;AAAA,QAC3B,iBAAiB;AAAA,QACjB,eAAc;AAAA;AAAA,IAChB;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACA,OAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA,GAAI,kBAAkB,EAAE,gBAAgB,IAAI;AAAA,UAC5C,GAAI,kBAAkB,SAAY,EAAE,cAAc,IAAI;AAAA,UACtD,GAAG;AAAA,QACL;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,287 @@
1
+ "use client";
2
+
3
+ // src/flow-edge-tokens/edge-path-sampler.ts
4
+ var CURVE_STEPS = 32;
5
+ var ARC_STEP = Math.PI / 32;
6
+ var COMMAND = /[MmLlHhVvCcSsQqTtAaZz]/;
7
+ var PathScanner = class {
8
+ constructor(d) {
9
+ this.d = d;
10
+ }
11
+ d;
12
+ i = 0;
13
+ skipSeparators() {
14
+ while (this.i < this.d.length && /[\s,]/.test(this.d[this.i])) this.i++;
15
+ }
16
+ peekCommand() {
17
+ this.skipSeparators();
18
+ const ch = this.d[this.i];
19
+ return ch !== void 0 && COMMAND.test(ch) ? ch : null;
20
+ }
21
+ readCommand() {
22
+ const cmd = this.d[this.i];
23
+ this.i++;
24
+ return cmd;
25
+ }
26
+ atEnd() {
27
+ this.skipSeparators();
28
+ return this.i >= this.d.length;
29
+ }
30
+ hasNumber() {
31
+ this.skipSeparators();
32
+ const ch = this.d[this.i];
33
+ return ch !== void 0 && /[-+.\d]/.test(ch);
34
+ }
35
+ readNumber() {
36
+ this.skipSeparators();
37
+ const m = /^[-+]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?/.exec(this.d.slice(this.i));
38
+ if (!m) return Number.NaN;
39
+ this.i += m[0].length;
40
+ return Number(m[0]);
41
+ }
42
+ readFlag() {
43
+ this.skipSeparators();
44
+ const ch = this.d[this.i];
45
+ if (ch === "0" || ch === "1") {
46
+ this.i++;
47
+ return Number(ch);
48
+ }
49
+ return Number.NaN;
50
+ }
51
+ /** Skip one unparseable character so a malformed path can never loop forever. */
52
+ skip() {
53
+ this.i++;
54
+ }
55
+ };
56
+ function cubicAt(p0, p1, p2, p3, t) {
57
+ const u = 1 - t;
58
+ return u * u * u * p0 + 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3;
59
+ }
60
+ function quadAt(p0, p1, p2, t) {
61
+ const u = 1 - t;
62
+ return u * u * p0 + 2 * u * t * p1 + t * t * p2;
63
+ }
64
+ function vectorAngle(ux, uy, vx, vy) {
65
+ const sign = ux * vy - uy * vx < 0 ? -1 : 1;
66
+ const dot = ux * vx + uy * vy;
67
+ const len = Math.hypot(ux, uy) * Math.hypot(vx, vy);
68
+ return sign * Math.acos(Math.min(1, Math.max(-1, dot / len)));
69
+ }
70
+ function arcPoints(x1, y1, rxIn, ryIn, rotationDeg, largeArc, sweep, x2, y2) {
71
+ let rx = Math.abs(rxIn);
72
+ let ry = Math.abs(ryIn);
73
+ if (rx === 0 || ry === 0 || x1 === x2 && y1 === y2) return [{ x: x2, y: y2 }];
74
+ const phi = rotationDeg * Math.PI / 180;
75
+ const cos = Math.cos(phi);
76
+ const sin = Math.sin(phi);
77
+ const dx = (x1 - x2) / 2;
78
+ const dy = (y1 - y2) / 2;
79
+ const x1p = cos * dx + sin * dy;
80
+ const y1p = -sin * dx + cos * dy;
81
+ const lambda = x1p * x1p / (rx * rx) + y1p * y1p / (ry * ry);
82
+ if (lambda > 1) {
83
+ const s = Math.sqrt(lambda);
84
+ rx *= s;
85
+ ry *= s;
86
+ }
87
+ const num = rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p;
88
+ const den = rx * rx * y1p * y1p + ry * ry * x1p * x1p;
89
+ const coef = (largeArc === sweep ? -1 : 1) * Math.sqrt(Math.max(0, num / den));
90
+ const cxp = coef * rx * y1p / ry;
91
+ const cyp = -coef * ry * x1p / rx;
92
+ const cx = cos * cxp - sin * cyp + (x1 + x2) / 2;
93
+ const cy = sin * cxp + cos * cyp + (y1 + y2) / 2;
94
+ const theta1 = vectorAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
95
+ let delta = vectorAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry);
96
+ if (!sweep && delta > 0) delta -= 2 * Math.PI;
97
+ if (sweep && delta < 0) delta += 2 * Math.PI;
98
+ const steps = Math.max(1, Math.ceil(Math.abs(delta) / ARC_STEP));
99
+ const out = [];
100
+ for (let s = 1; s <= steps; s++) {
101
+ if (s === steps) {
102
+ out.push({ x: x2, y: y2 });
103
+ break;
104
+ }
105
+ const a = theta1 + delta * s / steps;
106
+ const ex = rx * Math.cos(a);
107
+ const ey = ry * Math.sin(a);
108
+ out.push({ x: cos * ex - sin * ey + cx, y: sin * ex + cos * ey + cy });
109
+ }
110
+ return out;
111
+ }
112
+ function flattenEdgePath(d) {
113
+ const scan = new PathScanner(d);
114
+ const subpaths = [];
115
+ let current = null;
116
+ let x = 0;
117
+ let y = 0;
118
+ let startX = 0;
119
+ let startY = 0;
120
+ let lastCubic = null;
121
+ let lastQuad = null;
122
+ let cmd = null;
123
+ const lineTo = (nx, ny) => {
124
+ if (!current) {
125
+ current = [{ x, y }];
126
+ subpaths.push(current);
127
+ }
128
+ current.push({ x: nx, y: ny });
129
+ x = nx;
130
+ y = ny;
131
+ };
132
+ while (!scan.atEnd()) {
133
+ const next = scan.peekCommand();
134
+ if (next) {
135
+ cmd = scan.readCommand();
136
+ } else if (!cmd || !scan.hasNumber()) {
137
+ scan.skip();
138
+ continue;
139
+ }
140
+ const active = cmd;
141
+ const rel = active === active.toLowerCase();
142
+ const ox = rel ? x : 0;
143
+ const oy = rel ? y : 0;
144
+ const upper = active.toUpperCase();
145
+ if (upper === "Z") {
146
+ lineTo(startX, startY);
147
+ current = null;
148
+ lastCubic = lastQuad = null;
149
+ cmd = null;
150
+ continue;
151
+ }
152
+ const n = [];
153
+ const arity = { M: 2, L: 2, H: 1, V: 1, C: 6, S: 4, Q: 4, T: 2, A: 7 }[upper] ?? 0;
154
+ for (let k = 0; k < arity; k++) {
155
+ n.push(upper === "A" && (k === 3 || k === 4) ? scan.readFlag() : scan.readNumber());
156
+ }
157
+ if (n.some((v) => !Number.isFinite(v))) break;
158
+ switch (upper) {
159
+ case "M": {
160
+ x = n[0] + ox;
161
+ y = n[1] + oy;
162
+ startX = x;
163
+ startY = y;
164
+ current = [{ x, y }];
165
+ subpaths.push(current);
166
+ cmd = rel ? "l" : "L";
167
+ lastCubic = lastQuad = null;
168
+ break;
169
+ }
170
+ case "L":
171
+ lineTo(n[0] + ox, n[1] + oy);
172
+ lastCubic = lastQuad = null;
173
+ break;
174
+ case "H":
175
+ lineTo(n[0] + ox, y);
176
+ lastCubic = lastQuad = null;
177
+ break;
178
+ case "V":
179
+ lineTo(x, n[0] + oy);
180
+ lastCubic = lastQuad = null;
181
+ break;
182
+ case "C":
183
+ case "S": {
184
+ const reflected = lastCubic;
185
+ let c1x = x;
186
+ let c1y = y;
187
+ if (upper === "C") {
188
+ c1x = n[0] + ox;
189
+ c1y = n[1] + oy;
190
+ } else if (reflected) {
191
+ c1x = 2 * x - reflected.x;
192
+ c1y = 2 * y - reflected.y;
193
+ }
194
+ const rest = upper === "C" ? n.slice(2) : n;
195
+ const c2x = rest[0] + ox;
196
+ const c2y = rest[1] + oy;
197
+ const ex = rest[2] + ox;
198
+ const ey = rest[3] + oy;
199
+ const x0 = x;
200
+ const y0 = y;
201
+ for (let s = 1; s <= CURVE_STEPS; s++) {
202
+ const t = s / CURVE_STEPS;
203
+ lineTo(cubicAt(x0, c1x, c2x, ex, t), cubicAt(y0, c1y, c2y, ey, t));
204
+ }
205
+ lastCubic = { x: c2x, y: c2y };
206
+ lastQuad = null;
207
+ break;
208
+ }
209
+ case "Q":
210
+ case "T": {
211
+ const reflected = lastQuad;
212
+ let qx = x;
213
+ let qy = y;
214
+ if (upper === "Q") {
215
+ qx = n[0] + ox;
216
+ qy = n[1] + oy;
217
+ } else if (reflected) {
218
+ qx = 2 * x - reflected.x;
219
+ qy = 2 * y - reflected.y;
220
+ }
221
+ const rest = upper === "Q" ? n.slice(2) : n;
222
+ const ex = rest[0] + ox;
223
+ const ey = rest[1] + oy;
224
+ const x0 = x;
225
+ const y0 = y;
226
+ for (let s = 1; s <= CURVE_STEPS; s++) {
227
+ const t = s / CURVE_STEPS;
228
+ lineTo(quadAt(x0, qx, ex, t), quadAt(y0, qy, ey, t));
229
+ }
230
+ lastQuad = { x: qx, y: qy };
231
+ lastCubic = null;
232
+ break;
233
+ }
234
+ case "A": {
235
+ const pts = arcPoints(x, y, n[0], n[1], n[2], n[3], n[4], n[5] + ox, n[6] + oy);
236
+ for (const p of pts) lineTo(p.x, p.y);
237
+ lastCubic = lastQuad = null;
238
+ break;
239
+ }
240
+ }
241
+ }
242
+ return subpaths;
243
+ }
244
+ function clamp01(t) {
245
+ if (!Number.isFinite(t)) return 0;
246
+ return Math.min(1, Math.max(0, t));
247
+ }
248
+ function createEdgePathSampler(d) {
249
+ const points = [];
250
+ const cumulative = [];
251
+ let total = 0;
252
+ for (const sub of flattenEdgePath(d)) {
253
+ sub.forEach((p, idx) => {
254
+ if (idx > 0) {
255
+ const prev = sub[idx - 1];
256
+ total += Math.hypot(p.x - prev.x, p.y - prev.y);
257
+ }
258
+ points.push(p);
259
+ cumulative.push(total);
260
+ });
261
+ }
262
+ if (points.length === 0) return null;
263
+ const pointAt = (progress) => {
264
+ const target = clamp01(progress) * total;
265
+ let lo = 0;
266
+ let hi = cumulative.length - 1;
267
+ while (lo < hi) {
268
+ const mid = lo + hi >> 1;
269
+ if (cumulative[mid] < target) lo = mid + 1;
270
+ else hi = mid;
271
+ }
272
+ if (lo === 0) return { ...points[0] };
273
+ const segStart = cumulative[lo - 1];
274
+ const segLen = cumulative[lo] - segStart;
275
+ const a = points[lo - 1];
276
+ const b = points[lo];
277
+ const t = segLen === 0 ? 1 : (target - segStart) / segLen;
278
+ return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
279
+ };
280
+ return { length: total, pointAt };
281
+ }
282
+
283
+ export {
284
+ flattenEdgePath,
285
+ createEdgePathSampler
286
+ };
287
+ //# sourceMappingURL=chunk-T7S7NJFD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/flow-edge-tokens/edge-path-sampler.ts"],"sourcesContent":["/**\n * Pure SVG path sampling — the position maths behind `FlowEdgeTokens`.\n *\n * `SVGGeometryElement.getPointAtLength` would do the same job in a browser, but\n * it needs a mounted element (so a layout effect and a second render) and jsdom\n * implements none of it. Parsing the `d` string here keeps a token's position a\n * plain function of `(path, progress)`: identical on the server, in jsdom and in\n * every browser, and testable without a DOM.\n *\n * Supports every path command (`M L H V C S Q T A Z`, absolute and relative) in\n * the spellings React Flow's path helpers emit (`M0,0 C…`, `M 0,0L …`, `M0 0`).\n * Curves and arcs are flattened into a polyline fine enough that a token sits on\n * the drawn stroke; progress is measured along that polyline's arc length, so a\n * token moves at an even speed regardless of how the curve's control points are\n * spaced.\n */\n\nexport interface EdgePathPoint {\n x: number;\n y: number;\n}\n\nexport interface EdgePathSampler {\n /** Total arc length of the path, in px. */\n length: number;\n /** The point at `progress` (0..1) of the path's arc length. Out-of-range or non-finite progress is clamped. */\n pointAt: (progress: number) => EdgePathPoint;\n}\n\n/** Polyline segments per cubic/quadratic curve — enough that the chord error stays sub-pixel at edge scale. */\nconst CURVE_STEPS = 32;\n/** Maximum angle, in radians, spanned by one polyline segment of an elliptical arc. */\nconst ARC_STEP = Math.PI / 32;\n\nconst COMMAND = /[MmLlHhVvCcSsQqTtAaZz]/;\n\n/** Cursor-based scanner: SVG path grammar allows `1-2`, `.5.5` and compact arc flags (`0 01`). */\nclass PathScanner {\n private i = 0;\n constructor(private readonly d: string) {}\n\n private skipSeparators() {\n while (this.i < this.d.length && /[\\s,]/.test(this.d[this.i]!)) this.i++;\n }\n\n peekCommand(): string | null {\n this.skipSeparators();\n const ch = this.d[this.i];\n return ch !== undefined && COMMAND.test(ch) ? ch : null;\n }\n\n readCommand(): string {\n const cmd = this.d[this.i]!;\n this.i++;\n return cmd;\n }\n\n atEnd(): boolean {\n this.skipSeparators();\n return this.i >= this.d.length;\n }\n\n hasNumber(): boolean {\n this.skipSeparators();\n const ch = this.d[this.i];\n return ch !== undefined && /[-+.\\d]/.test(ch);\n }\n\n readNumber(): number {\n this.skipSeparators();\n const m = /^[-+]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][-+]?\\d+)?/.exec(this.d.slice(this.i));\n if (!m) return Number.NaN;\n this.i += m[0].length;\n return Number(m[0]);\n }\n\n readFlag(): number {\n this.skipSeparators();\n const ch = this.d[this.i];\n if (ch === \"0\" || ch === \"1\") {\n this.i++;\n return Number(ch);\n }\n return Number.NaN;\n }\n\n /** Skip one unparseable character so a malformed path can never loop forever. */\n skip() {\n this.i++;\n }\n}\n\nfunction cubicAt(p0: number, p1: number, p2: number, p3: number, t: number): number {\n const u = 1 - t;\n return u * u * u * p0 + 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3;\n}\n\nfunction quadAt(p0: number, p1: number, p2: number, t: number): number {\n const u = 1 - t;\n return u * u * p0 + 2 * u * t * p1 + t * t * p2;\n}\n\nfunction vectorAngle(ux: number, uy: number, vx: number, vy: number): number {\n const sign = ux * vy - uy * vx < 0 ? -1 : 1;\n const dot = ux * vx + uy * vy;\n const len = Math.hypot(ux, uy) * Math.hypot(vx, vy);\n return sign * Math.acos(Math.min(1, Math.max(-1, dot / len)));\n}\n\n/** Flatten an endpoint-parameterised elliptical arc (SVG 1.1 §F.6.5) into points after the start. */\nfunction arcPoints(\n x1: number,\n y1: number,\n rxIn: number,\n ryIn: number,\n rotationDeg: number,\n largeArc: number,\n sweep: number,\n x2: number,\n y2: number,\n): EdgePathPoint[] {\n let rx = Math.abs(rxIn);\n let ry = Math.abs(ryIn);\n if (rx === 0 || ry === 0 || (x1 === x2 && y1 === y2)) return [{ x: x2, y: y2 }];\n\n const phi = (rotationDeg * Math.PI) / 180;\n const cos = Math.cos(phi);\n const sin = Math.sin(phi);\n const dx = (x1 - x2) / 2;\n const dy = (y1 - y2) / 2;\n const x1p = cos * dx + sin * dy;\n const y1p = -sin * dx + cos * dy;\n\n const lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);\n if (lambda > 1) {\n const s = Math.sqrt(lambda);\n rx *= s;\n ry *= s;\n }\n\n const num = rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p;\n const den = rx * rx * y1p * y1p + ry * ry * x1p * x1p;\n const coef = (largeArc === sweep ? -1 : 1) * Math.sqrt(Math.max(0, num / den));\n const cxp = (coef * rx * y1p) / ry;\n const cyp = (-coef * ry * x1p) / rx;\n const cx = cos * cxp - sin * cyp + (x1 + x2) / 2;\n const cy = sin * cxp + cos * cyp + (y1 + y2) / 2;\n\n const theta1 = vectorAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n let delta = vectorAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry);\n if (!sweep && delta > 0) delta -= 2 * Math.PI;\n if (sweep && delta < 0) delta += 2 * Math.PI;\n\n const steps = Math.max(1, Math.ceil(Math.abs(delta) / ARC_STEP));\n const out: EdgePathPoint[] = [];\n for (let s = 1; s <= steps; s++) {\n if (s === steps) {\n out.push({ x: x2, y: y2 });\n break;\n }\n const a = theta1 + (delta * s) / steps;\n const ex = rx * Math.cos(a);\n const ey = ry * Math.sin(a);\n out.push({ x: cos * ex - sin * ey + cx, y: sin * ex + cos * ey + cy });\n }\n return out;\n}\n\n/**\n * Flatten a path `d` string into subpath polylines. Each subpath starts at its\n * `M`. Parsing stops at the first malformed argument; everything before it is kept.\n */\nexport function flattenEdgePath(d: string): EdgePathPoint[][] {\n const scan = new PathScanner(d);\n const subpaths: EdgePathPoint[][] = [];\n let current: EdgePathPoint[] | null = null;\n let x = 0;\n let y = 0;\n let startX = 0;\n let startY = 0;\n // Reflected control point for S/T; reset by any other command.\n let lastCubic: EdgePathPoint | null = null;\n let lastQuad: EdgePathPoint | null = null;\n let cmd: string | null = null;\n\n const lineTo = (nx: number, ny: number) => {\n if (!current) {\n current = [{ x, y }];\n subpaths.push(current);\n }\n current.push({ x: nx, y: ny });\n x = nx;\n y = ny;\n };\n\n while (!scan.atEnd()) {\n const next = scan.peekCommand();\n if (next) {\n cmd = scan.readCommand();\n } else if (!cmd || !scan.hasNumber()) {\n scan.skip();\n continue;\n }\n // After an `M`, further coordinate pairs are implicit line-tos.\n const active: string = cmd!;\n const rel = active === active.toLowerCase();\n const ox = rel ? x : 0;\n const oy = rel ? y : 0;\n const upper = active.toUpperCase();\n\n if (upper === \"Z\") {\n lineTo(startX, startY);\n current = null;\n lastCubic = lastQuad = null;\n cmd = null;\n continue;\n }\n\n const n: number[] = [];\n const arity = { M: 2, L: 2, H: 1, V: 1, C: 6, S: 4, Q: 4, T: 2, A: 7 }[upper] ?? 0;\n for (let k = 0; k < arity; k++) {\n n.push(upper === \"A\" && (k === 3 || k === 4) ? scan.readFlag() : scan.readNumber());\n }\n if (n.some((v) => !Number.isFinite(v))) break;\n\n switch (upper) {\n case \"M\": {\n x = n[0]! + ox;\n y = n[1]! + oy;\n startX = x;\n startY = y;\n current = [{ x, y }];\n subpaths.push(current);\n cmd = rel ? \"l\" : \"L\";\n lastCubic = lastQuad = null;\n break;\n }\n case \"L\":\n lineTo(n[0]! + ox, n[1]! + oy);\n lastCubic = lastQuad = null;\n break;\n case \"H\":\n lineTo(n[0]! + ox, y);\n lastCubic = lastQuad = null;\n break;\n case \"V\":\n lineTo(x, n[0]! + oy);\n lastCubic = lastQuad = null;\n break;\n case \"C\":\n case \"S\": {\n const reflected: EdgePathPoint | null = lastCubic;\n let c1x = x;\n let c1y = y;\n if (upper === \"C\") {\n c1x = n[0]! + ox;\n c1y = n[1]! + oy;\n } else if (reflected) {\n c1x = 2 * x - reflected.x;\n c1y = 2 * y - reflected.y;\n }\n const rest = upper === \"C\" ? n.slice(2) : n;\n const c2x = rest[0]! + ox;\n const c2y = rest[1]! + oy;\n const ex = rest[2]! + ox;\n const ey = rest[3]! + oy;\n const x0 = x;\n const y0 = y;\n for (let s = 1; s <= CURVE_STEPS; s++) {\n const t = s / CURVE_STEPS;\n lineTo(cubicAt(x0, c1x, c2x, ex, t), cubicAt(y0, c1y, c2y, ey, t));\n }\n lastCubic = { x: c2x, y: c2y };\n lastQuad = null;\n break;\n }\n case \"Q\":\n case \"T\": {\n const reflected: EdgePathPoint | null = lastQuad;\n let qx = x;\n let qy = y;\n if (upper === \"Q\") {\n qx = n[0]! + ox;\n qy = n[1]! + oy;\n } else if (reflected) {\n qx = 2 * x - reflected.x;\n qy = 2 * y - reflected.y;\n }\n const rest = upper === \"Q\" ? n.slice(2) : n;\n const ex = rest[0]! + ox;\n const ey = rest[1]! + oy;\n const x0 = x;\n const y0 = y;\n for (let s = 1; s <= CURVE_STEPS; s++) {\n const t = s / CURVE_STEPS;\n lineTo(quadAt(x0, qx, ex, t), quadAt(y0, qy, ey, t));\n }\n lastQuad = { x: qx, y: qy };\n lastCubic = null;\n break;\n }\n case \"A\": {\n const pts = arcPoints(x, y, n[0]!, n[1]!, n[2]!, n[3]!, n[4]!, n[5]! + ox, n[6]! + oy);\n for (const p of pts) lineTo(p.x, p.y);\n lastCubic = lastQuad = null;\n break;\n }\n }\n }\n return subpaths;\n}\n\nfunction clamp01(t: number): number {\n if (!Number.isFinite(t)) return 0;\n return Math.min(1, Math.max(0, t));\n}\n\n/**\n * Build a sampler for a path `d` string, or `null` when the string describes no\n * geometry at all (empty, or not a path). A path that is a single point has\n * length 0 and samples to that point for every progress.\n *\n * Subpaths are walked in order and the jump between them is not counted — a\n * token never travels through the gap an `M` leaves.\n */\nexport function createEdgePathSampler(d: string): EdgePathSampler | null {\n const points: EdgePathPoint[] = [];\n const cumulative: number[] = [];\n let total = 0;\n for (const sub of flattenEdgePath(d)) {\n sub.forEach((p, idx) => {\n if (idx > 0) {\n const prev = sub[idx - 1]!;\n total += Math.hypot(p.x - prev.x, p.y - prev.y);\n }\n points.push(p);\n cumulative.push(total);\n });\n }\n if (points.length === 0) return null;\n\n const pointAt = (progress: number): EdgePathPoint => {\n const target = clamp01(progress) * total;\n // Binary search for the first vertex at or past `target`.\n let lo = 0;\n let hi = cumulative.length - 1;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (cumulative[mid]! < target) lo = mid + 1;\n else hi = mid;\n }\n if (lo === 0) return { ...points[0]! };\n const segStart = cumulative[lo - 1]!;\n const segLen = cumulative[lo]! - segStart;\n const a = points[lo - 1]!;\n const b = points[lo]!;\n const t = segLen === 0 ? 1 : (target - segStart) / segLen;\n return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };\n };\n\n return { length: total, pointAt };\n}\n"],"mappings":"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","names":[]}