@motionscript/charts 0.0.0-stage → 0.1.0-alpha.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 (137) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +201 -0
  3. package/dist/bar-chart/bar-chart.d.ts +286 -0
  4. package/dist/bar-chart/bar-chart.d.ts.map +1 -0
  5. package/dist/bar-chart/bar-chart.js +610 -0
  6. package/dist/bar-chart/bar-chart.js.map +1 -0
  7. package/dist/bar-chart/category-axis.d.ts +77 -0
  8. package/dist/bar-chart/category-axis.d.ts.map +1 -0
  9. package/dist/bar-chart/category-axis.js +141 -0
  10. package/dist/bar-chart/category-axis.js.map +1 -0
  11. package/dist/bar-chart/chart-bars.d.ts +149 -0
  12. package/dist/bar-chart/chart-bars.d.ts.map +1 -0
  13. package/dist/bar-chart/chart-bars.js +236 -0
  14. package/dist/bar-chart/chart-bars.js.map +1 -0
  15. package/dist/bar-chart/index.d.ts +16 -0
  16. package/dist/bar-chart/index.d.ts.map +1 -0
  17. package/dist/bar-chart/index.js +12 -0
  18. package/dist/bar-chart/index.js.map +1 -0
  19. package/dist/bar-chart/shared.d.ts +146 -0
  20. package/dist/bar-chart/shared.d.ts.map +1 -0
  21. package/dist/bar-chart/shared.js +182 -0
  22. package/dist/bar-chart/shared.js.map +1 -0
  23. package/dist/browser/chunks/chunk-ZMBOBHYL.js +2 -0
  24. package/dist/browser/chunks/chunk-ZMBOBHYL.js.map +7 -0
  25. package/dist/browser/index.js +2 -0
  26. package/dist/browser/index.js.map +7 -0
  27. package/dist/browser/kit.js +2 -0
  28. package/dist/browser/kit.js.map +7 -0
  29. package/dist/browser/manifest.json +12 -0
  30. package/dist/engine.d.ts +27 -0
  31. package/dist/engine.d.ts.map +1 -0
  32. package/dist/engine.js +27 -0
  33. package/dist/engine.js.map +1 -0
  34. package/dist/index.d.ts +6 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +6 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/kit/chart-border.d.ts +28 -0
  39. package/dist/kit/chart-border.d.ts.map +1 -0
  40. package/dist/kit/chart-border.js +45 -0
  41. package/dist/kit/chart-border.js.map +1 -0
  42. package/dist/kit/chart-canvas.d.ts +56 -0
  43. package/dist/kit/chart-canvas.d.ts.map +1 -0
  44. package/dist/kit/chart-canvas.js +125 -0
  45. package/dist/kit/chart-canvas.js.map +1 -0
  46. package/dist/kit/chart-legend.d.ts +126 -0
  47. package/dist/kit/chart-legend.d.ts.map +1 -0
  48. package/dist/kit/chart-legend.js +212 -0
  49. package/dist/kit/chart-legend.js.map +1 -0
  50. package/dist/kit/shared.d.ts +251 -0
  51. package/dist/kit/shared.d.ts.map +1 -0
  52. package/dist/kit/shared.js +401 -0
  53. package/dist/kit/shared.js.map +1 -0
  54. package/dist/kit/vertical-axis.d.ts +99 -0
  55. package/dist/kit/vertical-axis.d.ts.map +1 -0
  56. package/dist/kit/vertical-axis.js +176 -0
  57. package/dist/kit/vertical-axis.js.map +1 -0
  58. package/dist/line-chart/chart-line.d.ts +91 -0
  59. package/dist/line-chart/chart-line.d.ts.map +1 -0
  60. package/dist/line-chart/chart-line.js +156 -0
  61. package/dist/line-chart/chart-line.js.map +1 -0
  62. package/dist/line-chart/chart-region.d.ts +47 -0
  63. package/dist/line-chart/chart-region.d.ts.map +1 -0
  64. package/dist/line-chart/chart-region.js +101 -0
  65. package/dist/line-chart/chart-region.js.map +1 -0
  66. package/dist/line-chart/horizontal-axis.d.ts +85 -0
  67. package/dist/line-chart/horizontal-axis.d.ts.map +1 -0
  68. package/dist/line-chart/horizontal-axis.js +158 -0
  69. package/dist/line-chart/horizontal-axis.js.map +1 -0
  70. package/dist/line-chart/index.d.ts +22 -0
  71. package/dist/line-chart/index.d.ts.map +1 -0
  72. package/dist/line-chart/index.js +19 -0
  73. package/dist/line-chart/index.js.map +1 -0
  74. package/dist/line-chart/line-chart.d.ts +347 -0
  75. package/dist/line-chart/line-chart.d.ts.map +1 -0
  76. package/dist/line-chart/line-chart.js +701 -0
  77. package/dist/line-chart/line-chart.js.map +1 -0
  78. package/dist/line-chart/shared.d.ts +171 -0
  79. package/dist/line-chart/shared.d.ts.map +1 -0
  80. package/dist/line-chart/shared.js +270 -0
  81. package/dist/line-chart/shared.js.map +1 -0
  82. package/dist/line-chart/x-scale.d.ts +84 -0
  83. package/dist/line-chart/x-scale.d.ts.map +1 -0
  84. package/dist/line-chart/x-scale.js +302 -0
  85. package/dist/line-chart/x-scale.js.map +1 -0
  86. package/dist/nodes.d.ts +20 -0
  87. package/dist/nodes.d.ts.map +1 -0
  88. package/dist/nodes.js +20 -0
  89. package/dist/nodes.js.map +1 -0
  90. package/dist/pie-chart/index.d.ts +18 -0
  91. package/dist/pie-chart/index.d.ts.map +1 -0
  92. package/dist/pie-chart/index.js +14 -0
  93. package/dist/pie-chart/index.js.map +1 -0
  94. package/dist/pie-chart/pie-chart.d.ts +170 -0
  95. package/dist/pie-chart/pie-chart.d.ts.map +1 -0
  96. package/dist/pie-chart/pie-chart.js +291 -0
  97. package/dist/pie-chart/pie-chart.js.map +1 -0
  98. package/dist/pie-chart/pie-legend.d.ts +60 -0
  99. package/dist/pie-chart/pie-legend.d.ts.map +1 -0
  100. package/dist/pie-chart/pie-legend.js +131 -0
  101. package/dist/pie-chart/pie-legend.js.map +1 -0
  102. package/dist/pie-chart/pie-ring.d.ts +266 -0
  103. package/dist/pie-chart/pie-ring.d.ts.map +1 -0
  104. package/dist/pie-chart/pie-ring.js +708 -0
  105. package/dist/pie-chart/pie-ring.js.map +1 -0
  106. package/dist/pie-chart/shared.d.ts +210 -0
  107. package/dist/pie-chart/shared.d.ts.map +1 -0
  108. package/dist/pie-chart/shared.js +240 -0
  109. package/dist/pie-chart/shared.js.map +1 -0
  110. package/package.json +69 -3
  111. package/registry.json +23 -0
  112. package/src/bar-chart/bar-chart.ts +804 -0
  113. package/src/bar-chart/category-axis.ts +177 -0
  114. package/src/bar-chart/chart-bars.ts +310 -0
  115. package/src/bar-chart/index.ts +31 -0
  116. package/src/bar-chart/shared.ts +354 -0
  117. package/src/engine.ts +26 -0
  118. package/src/index.ts +5 -0
  119. package/src/kit/chart-border.ts +61 -0
  120. package/src/kit/chart-canvas.ts +157 -0
  121. package/src/kit/chart-legend.ts +250 -0
  122. package/src/kit/shared.ts +675 -0
  123. package/src/kit/vertical-axis.ts +224 -0
  124. package/src/line-chart/chart-line.ts +208 -0
  125. package/src/line-chart/chart-region.ts +135 -0
  126. package/src/line-chart/horizontal-axis.ts +202 -0
  127. package/src/line-chart/index.ts +39 -0
  128. package/src/line-chart/line-chart.ts +894 -0
  129. package/src/line-chart/shared.ts +459 -0
  130. package/src/line-chart/x-scale.ts +367 -0
  131. package/src/nodes.ts +20 -0
  132. package/src/pie-chart/index.ts +37 -0
  133. package/src/pie-chart/pie-chart.ts +374 -0
  134. package/src/pie-chart/pie-legend.ts +159 -0
  135. package/src/pie-chart/pie-ring.ts +903 -0
  136. package/src/pie-chart/shared.ts +490 -0
  137. package/README.md +0 -4
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/line-chart/line-chart.ts", "../../src/line-chart/chart-line.ts", "../../src/line-chart/shared.ts", "../../src/line-chart/chart-region.ts", "../../src/line-chart/horizontal-axis.ts", "../../src/line-chart/x-scale.ts", "../../src/bar-chart/bar-chart.ts", "../../src/bar-chart/category-axis.ts", "../../src/bar-chart/chart-bars.ts", "../../src/bar-chart/shared.ts", "../../src/pie-chart/pie-chart.ts", "../../src/pie-chart/pie-legend.ts", "../../src/pie-chart/pie-ring.ts", "../../src/pie-chart/shared.ts", "../../src/nodes.ts"],
4
+ "sourcesContent": ["import {\n command,\n node,\n Node2D,\n Rect,\n easeOut,\n property,\n type Command,\n type CommandArgs,\n type DataRecord,\n type NodeConfig,\n type Node2DProps,\n type Vector2,\n} from \"@motionscript/core\"\n\nimport { ChartBorder } from \"../kit/chart-border\"\nimport { ChartCanvas } from \"../kit/chart-canvas\"\nimport { ChartLegend } from \"../kit/chart-legend\"\nimport { VerticalAxis } from \"../kit/vertical-axis\"\nimport {\n SPOTLIGHT_DIM,\n formatMarker,\n lerpAxisDomain,\n markerValues,\n resolveAxisDomain,\n resolveSeriesFill,\n resolveSeriesStroke,\n type AxisDomainConfig,\n type Range,\n} from \"../kit/shared\"\nimport { formatNumber } from \"@motionscript/core/component\"\nimport { ChartLine } from \"./chart-line\"\nimport { ChartRegion } from \"./chart-region\"\nimport { HorizontalAxis } from \"./horizontal-axis\"\nimport {\n DEFAULT_CHART_THEME,\n lerpChartTheme,\n resolveChartTheme,\n resolveRegionBox,\n resolveYScale,\n seriesPoints,\n type ChartConfig,\n type ChartTheme,\n type LineSeries,\n type PlotRegion,\n type RegionBox,\n} from \"./shared\"\nimport { resolveXScale, type XScale } from \"./x-scale\"\nimport { hold, sequence as sequenceOf, together, type Seekable } from \"@motionscript/core/component\"\n\nexport interface LineChartProps extends Node2DProps {\n /** One row per sample — a plain record of field name to value. */\n data: DataRecord[]\n /**\n * Key into each row for its x value, shared by every series. Named `xField`\n * rather than `x` because every node already has an `x` — its position.\n */\n xField: string\n /** One polyline per entry: which field it plots, its legend `label`, its `fill`. */\n series: LineSeries[]\n /** X-axis data domain: caption, scale range, and tick placement. */\n xAxis?: AxisDomainConfig\n /** Y-axis data domain: caption, scale range, and tick placement. */\n yAxis?: AxisDomainConfig\n /**\n * A slice of the plot to spotlight, in **data units**. Dashed rules mark the\n * bounded edges and every series fades outside the box — see\n * {@link LineChart.spotlightRegion}, which animates the same thing. `null`\n * (the default) is no region.\n */\n region?: PlotRegion | null\n /**\n * The chart's visual theme — series palette, axis text/grid/gutters, plot\n * panel paint, and legend styling. Every branch is optional and deep-merged\n * onto {@link DEFAULT_CHART_THEME}, so pass only what you want to override.\n */\n theme?: ChartTheme\n /** Whether the legend row below the plot is drawn at all. */\n showLegend?: boolean\n}\n\n/**\n * Headroom above the plot, in px.\n *\n * Two jobs, and the second is why it is real padding on the composition rather\n * than a number the point-mapping math imagines: a series peaking at `yMax`\n * isn't flush to the top edge, **and** the top y-marker — which is centred on\n * that top grid line — has somewhere to be. Without it the marker's upper half\n * is drawn above the chart's own box, outside the rectangle the editor draws\n * around the node.\n */\nconst TOP_HEADROOM = 24\n\n/**\n * The argument every per-series command takes: an index into `series`. Its own\n * kind rather than `number` so a host can offer the series by name.\n */\nconst SERIES_ARG = { key: \"seriesIndex\", kind: \"series\", default: 0, min: 0, step: 1 }\n\n/** How far the legend starts below its slot when it enters, in px. */\nconst LEGEND_SLIDE = 20\n\n/**\n * The two signals the region call-out is made of, as a props type for the\n * commands that move them.\n *\n * Written out rather than reusing `LineChartProps` because the pair is what\n * those commands actually touch, and because `regionReveal` is a signal of the\n * chart's own rather than part of its authored props.\n */\ntype RegionState = { region: PlotRegion | null; regionReveal: number }\n\n/**\n * How {@link LineChart.enter} divides its duration between its three beats: the\n * axes arriving, the lines tracing, and the legend coming up. Shares of the\n * whole, so they must sum to 1 — the entrance takes exactly the time it was\n * given (see the note on `enter`).\n */\nconst AXES_SHARE = 0.35\nconst LINES_SHARE = 0.5\nconst LEGEND_SHARE = 0.15\n\n/**\n * A multi-series line chart, composed from five independent parts rather than\n * one monolithic draw:\n *\n * - {@link ChartCanvas} — the plot panel with a two-way grid, under everything.\n * - {@link VerticalAxis} / {@link HorizontalAxis} — columns/rows of real text\n * markers that stagger in, each carrying its own axis caption.\n * - {@link ChartRegion} — the optional call-out marking a spotlit slice of the\n * plot: dashed rules on its bounded edges, and every series faded outside it.\n * - {@link ChartLine} (one per series) — the traced polylines over the canvas.\n * - {@link ChartLegend} — a row of entries below the chart, one per series.\n *\n * The parts are laid out from this node's `layoutBounds`: a left gutter for the\n * vertical axis, a bottom gutter for the horizontal axis, the plot filling\n * what's left, and the legend row beneath the whole chart. Every child's\n * position/size is bound reactively, so the chart re-flows if its box changes,\n * and the series lines read the same resolved scales as the axes, so points land\n * exactly on their markers.\n *\n * **Resting state is the finished chart.** Unlike the standalone component this\n * was ported from, nothing is built hidden: the studio paints a node's Initial\n * state on a paused canvas, and a chart that only appeared once a command had\n * run would be edited blind. {@link enter} arms every part back to its\n * pre-entrance state before animating, so the entrance itself is unchanged.\n *\n * Past the entrance, three things move the chart around:\n * {@link showSeries}/{@link hideSeries}/{@link spotlightSeries} pick out a\n * *series*, {@link spotlightRegion} marks a *slice of the plot* without changing\n * the scale, and {@link zoomTo} rescales the axes so a slice fills the plot. The\n * last two take the same {@link PlotRegion} and compose — a spotlit region\n * resolves against the live scales, so it travels with a zoom.\n */\n@node({\n key: \"lineChart\",\n parentKey: \"node\",\n forkable: true,\n layout: {\n children: \"freeform\",\n defaultWidthMode: \"fixed\",\n defaultHeightMode: \"fixed\",\n acceptsChildren: true,\n },\n seed: {\n width: 760,\n height: 460,\n },\n})\nexport class LineChart extends Node2D<LineChartProps> {\n @property({ default: () => [] as DataRecord[], kind: \"dataset\" }) declare data: DataRecord[]\n @property({ default: \"\" }) declare xField: string\n @property({ default: () => [] as LineSeries[], kind: \"series\" }) declare series: LineSeries[]\n /**\n * X/Y-axis data domain (label/range/ticks), tweenable and mappable like\n * `theme`: the mapper merges a partial input onto the previous resolved\n * domain (leaving `range`/`ticks` `undefined` — the auto-derive signal — on\n * first resolution), and the tween interpolates them numerically, so\n * `chart.to({ yAxis: { range: [0, 10] } }, 1)` smoothly rescales the plotted\n * lines. Like `theme`, the axis *label* text is captured once at construction.\n */\n @property({\n default: {},\n mapper: resolveAxisDomain,\n tween: lerpAxisDomain,\n })\n declare xAxis: AxisDomainConfig\n @property({\n default: {},\n mapper: resolveAxisDomain,\n tween: lerpAxisDomain,\n })\n declare yAxis: AxisDomainConfig\n /**\n * The chart's visual theme, tweenable and mappable like a `stroke`/`fill`\n * prop: the mapper merges the author's partial input onto the previous\n * resolved theme (or {@link DEFAULT_CHART_THEME} the first time) and resolves\n * every leaf; the tween interpolates two resolved themes leaf-by-leaf, so\n * `chart.to({ theme: next }, 1)` animates smoothly.\n */\n @property({\n default: DEFAULT_CHART_THEME,\n mapper: resolveChartTheme,\n tween: lerpChartTheme,\n })\n declare theme: ChartTheme\n /**\n * The spotlit slice of the plot in data units, or `null` for none. Held as a\n * plain reactive value rather than a tweened one: the box itself snaps, and\n * the *animation* is {@link regionReveal} drawing the rules on and the lines\n * dimming outside it.\n */\n @property({ default: null }) declare region: PlotRegion | null\n /**\n * How far the {@link region} call-out is drawn in, `[0, 1]` — the single\n * signal behind both halves of the effect: the rules grow out and the wash\n * fades up as it climbs, while every line's out-of-region stretch fades from\n * full down to the theme's `region.dimOpacity`. At 0 a chart with a `region`\n * set looks exactly like one without.\n */\n @property({ default: 0 }) declare regionReveal: number\n /**\n * Read once, in the constructor, like `series`: the legend is a part the\n * chart builds or doesn't, so a change reaches it by rebuilding the node.\n */\n @property({ default: true, animatable: false }) declare showLegend: boolean\n\n // ---- Part handles (built in the constructor) --------------------------\n private vAxis: VerticalAxis\n private hAxis: HorizontalAxis\n /** The plot panel — the box the grid, the lines and the region all share. */\n private plotRect: Rect\n private legend: ChartLegend | null = null\n private lines: ChartLine[] = []\n\n /**\n * `this.theme` read back through its *resolved* shape: the property's declared\n * type is the loose author-facing {@link ChartTheme} (so a partial theme is\n * accepted), but the mapper always stores the fully-resolved\n * {@link ChartConfig} at runtime — the same loose-declared/resolved-stored\n * idiom as a shape's `fill`/`stroke`.\n */\n private get resolvedTheme(): ChartConfig {\n return this.theme as unknown as ChartConfig\n }\n\n // ---- Derived scales ---------------------------------------------------\n\n /** Fallback marker count when neither axis's ticks are given. */\n private static readonly DEFAULT_MARKERS = 5\n\n /**\n * What the x column *is* — a number, an instant, or a place in the file — and\n * with it how each row is positioned, ticked and captioned. See `x-scale.ts`.\n */\n private get xColumn(): XScale {\n return resolveXScale(this.data, this.xField, this.xAxis.format)\n }\n\n private get xScale(): Range {\n return this.xAxis.range ?? this.xColumn.extent\n }\n\n private get yScale(): Range {\n // Auto-ranging steps into one fewer segment than the intended marker count\n // — the explicit ticks' count when given, else the default — so the tidy\n // bounds still frame the ticks the axis draws.\n const divisions =\n (this.yAxis.ticks?.length ?? LineChart.DEFAULT_MARKERS) - 1\n return resolveYScale(this.data, this.series, this.yAxis.range, divisions)\n }\n\n /** The vertical axis ticks: the explicit ones, else five across the scale. */\n private get resolvedYMarkers(): number[] {\n return (\n this.yAxis.ticks ??\n markerValues(this.yScale, LineChart.DEFAULT_MARKERS - 1)\n )\n }\n\n /**\n * The horizontal axis ticks: the explicit ones, else five across the scale.\n *\n * Asked of the column rather than spaced evenly, because only a numeric column\n * has round numbers between its samples. On a month column, five evenly spaced\n * values land two-thirds of the way through four different months; the ticks\n * a reader wants are Jan/Apr/Jul/Oct/Dec, which are rows.\n */\n private get resolvedXMarkers(): number[] {\n return (\n this.xAxis.ticks ??\n this.xColumn.ticks(this.xScale, LineChart.DEFAULT_MARKERS - 1)\n )\n }\n\n // ---- Plot geometry ------------------------------------------------------\n\n /**\n * The plot panel's size, **as laid out**.\n *\n * This used to be computed: six constants estimating what the axis gutters and\n * the legend row would take, subtracted from the node's box. The estimate was\n * never the layout — the vertical axis is `width: \"hug\"`, so its real width is\n * whatever its widest marker needs — and everything downstream inherited the\n * gap. The grid is drawn by {@link ChartCanvas} across its *own* allocation,\n * while the lines were mapped across the estimate, so a series sat a few\n * percent off the grid line it was supposed to touch, silently and by an\n * amount that changed with the theme.\n *\n * Asking the panel is exact by construction and cannot drift: the lines, the\n * region rules and the grid all resolve against one box, and it is the box\n * they are drawn in. Zero until the first layout pass, like any other\n * `layoutBounds` read — the bindings that call this re-run on the next frame.\n */\n private plotSize(): { width: number; height: number } {\n const { width, height } = this.plotRect.layoutBounds\n return { width, height }\n }\n\n /**\n * Map one series' data points into the plot's **centred** pixel space (origin\n * at the plot centre, y-up) — the local frame of the clip/line box that fills\n * the plot. x runs `[-w/2, +w/2]`, y runs `[-h/2, +h/2]`.\n */\n private seriesPixels(index: number): Vector2[] {\n const s = this.series[index]\n if (!s) return []\n const points = seriesPoints(this.data, this.xColumn, s)\n if (points.length === 0) return []\n const { width: w, height: h } = this.plotSize()\n const [xMin, xMax] = this.xScale\n const [yMin, yMax] = this.yScale\n // Plot centre is the origin; map the data fraction to [-half, +half].\n const xToPx = (x: number) =>\n xMax <= xMin ? 0 : ((x - xMin) / (xMax - xMin) - 0.5) * w\n const yToPx = (y: number) =>\n yMax <= yMin ? 0 : ((y - yMin) / (yMax - yMin) - 0.5) * h\n // `seriesPoints` already sorted them along x.\n return points.map(([x, y]) => ({ x: xToPx(x), y: yToPx(y) }))\n }\n\n /**\n * {@link region} mapped into the plot's **centred** pixel space — the same\n * frame {@link seriesPixels} returns, so the region's rules and each line's\n * clip resolve against one box from one place. Reads the live scales, so it\n * re-maps when `xAxis`/`yAxis` tween.\n */\n /**\n * Whether a call-out is authored at all — the question {@link enter} and\n * {@link spotlightRegion} branch on.\n *\n * Deliberately **not** `hasRegion(this.regionBox())`, which they used to ask.\n * That resolves the region into pixels, so it answers \"and has the chart been\n * laid out yet\" as well, and comes back false for a perfectly real region on a\n * chart that has not been measured. A generator never noticed, because it only\n * ever ran mid-playback; a command is *built* before the first layout pass, and\n * would have quietly dropped the region beat every time.\n *\n * What is left once the pixels are taken out is the same test\n * {@link resolveRegionBox} applies before it maps anything: a region bounding\n * *neither* axis is not a call-out, so `{}` counts as none exactly as `null`\n * does.\n */\n private hasCallout(): boolean {\n const region = this.region\n return region != null && (region.x !== undefined || region.y !== undefined)\n }\n\n private regionBox(): RegionBox {\n const { width: w, height: h } = this.plotSize()\n // The region lives in the plot's own centred frame, the same one\n // seriesPixels maps its points into.\n return resolveRegionBox(\n this.region,\n { left: -w / 2, right: w / 2, bottom: -h / 2, top: h / 2 },\n this.xScale,\n this.yScale\n )\n }\n\n // ---- Build ------------------------------------------------------------\n\n constructor(props?: NodeConfig<LineChart, LineChartProps>) {\n super(props)\n\n // The whole chart is composed from plain props/`data` — no context needed,\n // so the parts are built here (super() applied the props above).\n //\n // Every theme/axis-derived prop below that a *leaf* drawing part reads live\n // in `renderSelf` every frame — the canvas's fill/grid/extent, the border's\n // stroke, each line's stroke/points — is passed as a closure, not a one-time\n // snapshot, so `chart.to({ theme })` and `chart.to({ yAxis: { range } })`\n // genuinely animate the panel, grid, border, and plotted lines. The two axes\n // compose their child text nodes *once* (the marker count is fixed at\n // construction) but bind each marker's caption and position to the live\n // range/ticks, so a rescale re-labels and re-places the row as it tweens.\n const enabled = this.series.map((s) => s.enabled ?? true)\n const showLegend = this.showLegend !== false\n\n this.vAxis = new VerticalAxis({\n // Bound, not snapshotted — this is the half of a rescale the axis owns.\n // The marker *count* is still read once (it fixes how many text nodes get\n // built), which is why `zoomTo` keeps the count constant.\n range: () => this.yScale,\n markers: () => this.resolvedYMarkers,\n axis: () => this.resolvedTheme.yAxis,\n label: this.yAxis.label ?? \"\",\n width: \"hug\",\n height: \"fill\",\n })\n // The y axis is always numbers, so its format is simply the pattern.\n this.vAxis.labelFor = (value) =>\n this.yAxis.format === undefined\n ? formatMarker(value)\n : formatNumber(value, this.yAxis.format)\n\n this.hAxis = new HorizontalAxis({\n range: () => this.xScale,\n markers: () => this.resolvedXMarkers,\n axis: () => this.resolvedTheme.xAxis,\n label: this.xAxis.label ?? \"\",\n width: \"fill\",\n height: \"hug\",\n })\n // A tick on a month or category column is captioned with the cell it came\n // out of — `Jan`, not the instant it parsed to. Assigned rather than passed:\n // see the note on the field.\n this.hAxis.labelFor = (value) => this.xColumn.label(value)\n\n this.lines = this.series.map(\n (s, i) =>\n new ChartLine({\n points: () => this.seriesPixels(i),\n stroke: () => resolveSeriesStroke(this.resolvedTheme.seriesStyles, s, i),\n enabled: enabled[i],\n // Each line clips itself against the same box the rules are drawn\n // from, so the dim/bright boundary can't drift from the dashes.\n spotlight: () => this.regionBox(),\n // Ramped by the same reveal that draws the rules, so at reveal 0 the\n // line is uniform and the region invisible — one signal, both halves.\n dimOpacity: () =>\n 1 - (1 - this.resolvedTheme.region.dimOpacity) * this.regionReveal,\n })\n )\n\n if (showLegend) {\n this.legend = new ChartLegend({\n series: this.series,\n // The kit's legend paints a swatch, not a line, so the chart hands it\n // the *colour* each stroke resolves to rather than the stroke itself —\n // which is also what lets a bar chart's fill palette drive the same row.\n swatches: () =>\n this.series.map((s, i) =>\n resolveSeriesFill(this.resolvedTheme.seriesStyles, s, i)\n ),\n legend: () => this.resolvedTheme.legend,\n })\n }\n\n this.plotRect = new Rect({\n clip: true,\n children: [\n new ChartCanvas({\n fill: () => this.resolvedTheme.plotArea.fill,\n shadow: () => this.resolvedTheme.plotArea.shadow,\n xGridStroke: () => this.resolvedTheme.xAxis.lineStyle,\n yGridStroke: () => this.resolvedTheme.yAxis.lineStyle,\n // A grid line under every axis marker; the canvas maps the marker\n // values through the extents and skips the edges.\n xMarkers: () => this.resolvedXMarkers,\n yMarkers: () => this.resolvedYMarkers,\n xExtent: () => this.xScale,\n yExtent: () => this.yScale,\n }),\n // Region wash + dashed rules: over the grid, under the lines, so a\n // spotlit stretch of a series isn't tinted by the highlight meant to\n // pick it out.\n new ChartRegion({\n box: () => this.regionBox(),\n stroke: () => this.resolvedTheme.region.stroke,\n fill: () => this.resolvedTheme.region.fill,\n reveal: () => this.regionReveal,\n }),\n ...this.lines,\n new ChartBorder({\n stroke: () => this.resolvedTheme.plotArea.stroke,\n }),\n ],\n })\n\n // Invisible twin of the vertical axis, reserving the same left gutter under\n // the plot so the horizontal axis starts at the plot's left edge rather than\n // the chart's.\n const gutterSpacer = new VerticalAxis({\n opacity: 0,\n range: () => this.yScale,\n markers: () => this.resolvedYMarkers,\n axis: () => this.resolvedTheme.yAxis,\n label: this.yAxis.label ?? \"\",\n width: \"hug\",\n height: 0,\n })\n\n const below: Node2D[] = [this.hAxis]\n if (this.legend) below.push(this.legend)\n\n this.add(\n new Rect({ flow: \"vertical\",\n width: \"fill\",\n height: \"fill\",\n // See {@link TOP_HEADROOM}: the plot's top grid line is the one place a\n // marker would otherwise be drawn outside the chart's own box.\n padding: { top: TOP_HEADROOM },\n children: [\n new Rect({ flow: \"horizontal\",\n height: \"fill\",\n width: \"fill\",\n children: [this.vAxis, this.plotRect],\n }),\n new Rect({ flow: \"horizontal\",\n width: \"fill\",\n children: [\n gutterSpacer,\n new Rect({ flow: \"vertical\", width: \"fill\", gap: 20, children: below }),\n ],\n }),\n ],\n })\n )\n }\n\n // ---- Orchestrated entrance --------------------------------------------\n\n /**\n * Prime every part at its pre-entrance state — axis markers shifted and\n * transparent, lines untraced, legend dropped and transparent.\n *\n * Called by {@link enter} before its first frame so nothing flashes ahead of\n * its beat. Idempotent, and public so a scene can hide a chart ahead of time\n * without starting the entrance.\n */\n arm(): void {\n this.vAxis.arm()\n this.hAxis.arm()\n for (const line of this.lines) line.set({ progress: 0 })\n this.legend?.set({ opacity: 0, y: -LEGEND_SLIDE })\n }\n\n /**\n * Animate the whole chart in: the two axes stagger their markers in (vertical\n * bottom-to-top, horizontal left-to-right), the series lines trace\n * left-to-right, and the legend fades up last.\n *\n * `duration` is the **whole** entrance. The phases below are shares of it, not\n * multiples — a command that declares 1.4 seconds has to take 1.4 seconds,\n * because that is the slot the timeline lays out for it, the length its\n * parallel siblings are timed against, and its contribution to the scene's own\n * duration. (The component this was ported from scaled every phase *off* the\n * line trace and let the total fall where it may, which came out around 3×.)\n *\n * A chart authored with a `region` lands its call-out last, over lines that\n * have already traced — the same order a scene would get by calling\n * {@link spotlightRegion} right after this — and that beat comes out of the\n * same budget.\n */\n @command()\n enter(\n args: CommandArgs<Record<string, never>> & { duration: number }\n ): Seekable {\n const duration = args.duration\n const easing = args.easing ?? easeOut()\n this.arm()\n\n const revealing = this.hasCallout() && this.regionReveal === 0\n // The region call-out, when there is one, takes the last quarter.\n const body = revealing ? duration * 0.75 : duration\n\n // The same three phases, in the same order and with the same shares — placed\n // at their offsets rather than sequenced by a generator, so the whole\n // entrance can be asked what it looks like at a time. Each part writes to its\n // own node (the axes, the lines, the legend), which is why these compose as\n // steppers rather than as merged props.\n const axes = together(\n this.vAxis.enter({ data: { slide: 24 }, duration: body * AXES_SHARE, easing }),\n this.hAxis.enter({ data: { slide: 24 }, duration: body * AXES_SHARE, easing })\n )\n const lines = together(\n ...this.lines.map((line) => line.enter({ duration: body * LINES_SHARE, easing }))\n )\n const legend = this.legend\n ? [this.legend.to({ data: { opacity: 1, y: 0 }, duration: body * LEGEND_SHARE, easing })]\n : []\n\n const phases = sequenceOf(axes, lines, ...legend)\n\n return revealing\n ? sequenceOf(\n phases,\n // Typed to what it writes rather than to `LineChartProps`: `regionReveal`\n // is a signal of the chart's own, not part of its authored props.\n this.command<{ regionReveal: number }>(\n (t) => ({ regionReveal: t }),\n duration * 0.25,\n easing\n )\n )\n : phases\n }\n\n // ---- Region spotlight --------------------------------------------------\n\n /**\n * Spotlight a rectangular slice of the plot given in **data units**: dashed\n * rules draw themselves onto each bounded edge while every series fades\n * outside the box and stays full inside it.\n *\n * Either axis alone is a band — `{ x: [80, 100] }` picks out the last fifth\n * across all series, `{ y: [8, 10] }` a value band across the whole width —\n * and both together a box. The unbounded axis spans the plot and draws no\n * rule, since there's no data bound there to mark.\n *\n * Calling it while another region is already up cross-fades: the old call-out\n * retracts, then the new one draws in, each over half `duration`.\n *\n * Both the call-out's shape and its draw-on are plain signals — `region` and\n * {@link regionReveal} — so the whole thing is a function of `t`, including\n * the swap: which region is up is a step at the halfway point rather than an\n * effect that has to have happened.\n */\n @command({\n args: [\n // One argument, two controls: the method takes a `PlotRegion`, so the x\n // and y spans a host draws are declared inside it.\n {\n key: \"region\", kind: \"region\", properties: {\n x: { kind: \"range\", default: true, defaultMin: 0, defaultMax: 100 },\n y: { kind: \"range\", default: false, defaultMin: 0, defaultMax: 100 },\n },\n },\n ],\n })\n spotlightRegion(\n args: CommandArgs<{ region: PlotRegion }> & { duration?: number }\n ): Command<RegionState> {\n const region = LineChart.regionArg(args.data)\n const duration = args.duration ?? 0.5\n const easing = args.easing ?? easeOut()\n const from = this.regionReveal\n const previous = this.region\n\n // A region bounding neither axis is no call-out: spotlighting it is a clear.\n if (region.x === undefined && region.y === undefined) {\n return this.clearRegion({ duration, easing })\n }\n\n // Nothing up yet: the region is set from the first frame and just draws on.\n if (from <= 0 || !this.hasCallout()) {\n return this.command<RegionState>(\n (t) => ({ region, regionReveal: from + (1 - from) * t }),\n duration,\n easing\n )\n }\n\n // Something already up: retract it first so the rules don't jump across the\n // plot mid-draw. Each half eases on its own — which is why the easing is\n // applied here rather than handed to `animate`, whose easing would shape the\n // pair as one curve and leave both halves crawling past the seam.\n return this.command<RegionState>(\n (t) =>\n t < 0.5\n ? { region: previous, regionReveal: from * (1 - easing(t * 2)) }\n : { region, regionReveal: easing(t * 2 - 1) },\n duration\n )\n }\n\n /**\n * Drop the region call-out — the rules retract, the wash fades, and every\n * series comes back to full across its whole length. A no-op when nothing is\n * spotlit.\n */\n @command()\n clearRegion(\n args: CommandArgs<Record<string, never>> & { duration?: number } = {}\n ): Command<RegionState> {\n const duration = args.duration ?? 0.5\n const easing = args.easing ?? easeOut()\n const from = this.regionReveal\n\n if (from === 0) {\n // Nothing to retract, but the call still takes its slot: in the studio a\n // command's duration is what the timeline lays out and what parallel\n // siblings are timed against, so a no-op that took no frames would pull\n // everything after it forward. Same reason for `zoomTo`/`resetZoom` below.\n return this.command<RegionState>(\n () => ({ region: null, regionReveal: 0 }),\n duration\n )\n }\n\n // `region` is left alone until the retraction lands — cleared at `t === 1`\n // rather than up front, or there would be nothing left to retract.\n return this.command<RegionState>(\n (t) => (t >= 1 ? { region: null, regionReveal: 0 } : { regionReveal: from * (1 - t) }),\n duration,\n easing\n )\n }\n\n // ---- Zoom --------------------------------------------------------------\n\n /**\n * The domain to come back to, captured the first time {@link zoomTo} runs so a\n * later {@link resetZoom} restores the chart as authored — including ticks\n * that were auto-derived rather than given, which the props alone don't\n * record. Cleared by {@link resetZoom}, so a second zoom captures wherever the\n * chart genuinely rests then.\n */\n private home: { x: AxisDomainConfig; y: AxisDomainConfig } | null = null\n\n /**\n * The `region` a document handed a command, or `{}`. A command is added\n * before its arguments are filled in, so an unstated region is ordinary input\n * rather than a malformed one.\n */\n private static regionArg(data: { region?: PlotRegion | null } | undefined): PlotRegion {\n const region = data?.region\n return region != null && typeof region === \"object\" ? region : {}\n }\n\n /** A range read as a span, whichever way round the author wrote it. */\n private static span([a, b]: Range): Range {\n return a <= b ? [a, b] : [b, a]\n }\n\n /**\n * Rescale an axis to `range`, keeping the tick *count* it already has — the\n * axis tween interpolates ticks pairwise by index, so a constant count lets\n * each marker slide from its old value to its new one instead of the row\n * popping.\n */\n private static rescale(range: Range, ticks: number[]): AxisDomainConfig {\n const span = LineChart.span(range)\n const count = Math.max(2, ticks.length)\n return { range: span, ticks: markerValues(span, count - 1) }\n }\n\n /**\n * Zoom the plot to a slice of itself, given in **data units**: the named axes\n * rescale so that slice fills the plot, the grid and both marker rows\n * following the new scale, and every series re-tracing itself into the new\n * frame. All of it tweens, so the move reads as a camera push rather than a\n * cut.\n *\n * An **omitted axis is left as it is** rather than being fitted to what's\n * visible — so a zoom is exactly what was asked for, and two calls compose\n * predictably. A spotlit region, if one is up, follows the zoom: it resolves\n * against the same live scales, so its rules stay on their data bounds.\n */\n @command({\n args: [\n // One argument, two controls: the method takes a `PlotRegion`, so the x\n // and y spans a host draws are declared inside it.\n {\n key: \"region\", kind: \"region\", properties: {\n x: { kind: \"range\", default: true, defaultMin: 0, defaultMax: 100 },\n y: { kind: \"range\", default: false, defaultMin: 0, defaultMax: 100 },\n },\n },\n ],\n })\n zoomTo(\n args: CommandArgs<{ region: PlotRegion }> & { duration?: number }\n ): Seekable {\n const region = LineChart.regionArg(args.data)\n const duration = args.duration ?? 0.8\n const easing = args.easing ?? easeOut()\n if (!region.x && !region.y) return hold(duration)\n\n // Capture the resting domain *before* the first zoom moves it. Ticks are\n // read through the resolved getters rather than off `xAxis`/`yAxis`, so an\n // auto-derived tick row comes back as itself.\n this.home ??= {\n x: {\n label: this.xAxis.label,\n range: this.xScale,\n ticks: this.resolvedXMarkers,\n },\n y: {\n label: this.yAxis.label,\n range: this.yScale,\n ticks: this.resolvedYMarkers,\n },\n }\n\n const next: { xAxis?: AxisDomainConfig; yAxis?: AxisDomainConfig } = {}\n if (region.x) next.xAxis = LineChart.rescale(region.x, this.resolvedXMarkers)\n if (region.y) next.yAxis = LineChart.rescale(region.y, this.resolvedYMarkers)\n\n return this.to({ data: next, duration, easing })\n }\n\n /**\n * Pull back out to the domain the chart was authored with — the mirror of\n * {@link zoomTo}, and a no-op if it was never zoomed.\n */\n @command()\n resetZoom(\n args: CommandArgs<Record<string, never>> & { duration?: number } = {}\n ): Seekable {\n const duration = args.duration ?? 0.8\n const easing = args.easing ?? easeOut()\n const home = this.home\n if (!home) return hold(duration)\n\n // Cleared here rather than once the move lands. Nothing reads `home` while\n // it runs, and a value has no \"after\" to clear it in — what matters is that\n // the *next* `zoomTo` captures wherever the chart genuinely rests by then,\n // and both this and its capture happen as the command list is walked.\n this.home = null\n return this.to({ data: { xAxis: home.x, yAxis: home.y }, duration, easing })\n }\n\n // ---- Per-series -------------------------------------------------------\n\n /**\n * Show series `index`: **trace** its line in left-to-right — the same draw as\n * {@link enter}, not an opacity fade — in lockstep with its legend entry\n * growing in.\n *\n * `enabled` is set as part of the command's own value rather than before it,\n * so the line is drawable for every `t` the trace covers and undrawn outside\n * it — which is what makes arriving here backwards show the same thing as\n * arriving forwards.\n */\n @command({ args: [SERIES_ARG] })\n showSeries(\n args: CommandArgs<{ seriesIndex: number }> & { duration?: number }\n ): Seekable {\n const index = args.data?.seriesIndex ?? 0\n const duration = args.duration ?? 0.35\n const easing = args.easing ?? easeOut()\n const line = this.lines[index]\n if (!line) return hold(duration)\n\n return together(\n line.enterFrom(0, duration, easing),\n ...(this.legend ? [this.legend.showSeries({ data: { index }, duration, easing })] : [])\n )\n }\n\n /**\n * Hide series `index`: **retrace** its line back out (`progress` 1 → 0) as its\n * legend entry shrinks away — the mirror of {@link showSeries}.\n */\n @command({ args: [SERIES_ARG] })\n hideSeries(\n args: CommandArgs<{ seriesIndex: number }> & { duration?: number }\n ): Seekable {\n const index = args.data?.seriesIndex ?? 0\n const duration = args.duration ?? 0.35\n const easing = args.easing ?? easeOut()\n const line = this.lines[index]\n if (!line) return hold(duration)\n\n return together(\n line.enterFrom(1, duration, easing, 0),\n ...(this.legend ? [this.legend.hideSeries({ data: { index }, duration, easing })] : [])\n )\n }\n\n /**\n * Fade out all other series — each line **and its legend entry** — to focus\n * attention on `index`, which comes back to full. The legend dims in place\n * (opacity only), so no entry moves and the row doesn't reflow.\n */\n @command({ args: [SERIES_ARG] })\n spotlightSeries(\n args: CommandArgs<{ seriesIndex: number }> & { duration?: number }\n ): Seekable {\n const index = args.data?.seriesIndex ?? 0\n const duration = args.duration ?? 0.35\n const easing = args.easing ?? easeOut()\n return together(\n ...this.lines.map((line, i) =>\n line.to({ data: { opacity: i === index ? 1 : SPOTLIGHT_DIM }, duration, easing })\n ),\n ...(this.legend\n ? [this.legend.spotlightSeries({ data: { index }, duration, easing })]\n : [])\n )\n }\n}\n", "import {\n type AssetScope,\n Graphics2D,\n Node2D,\n command,\n easeOut,\n strokeOps,\n property,\n type Command,\n type CommandArgs,\n type EasingFunction,\n type Node2DProps,\n type RenderContext2D,\n type Stroke,\n type StrokeResolved,\n type Vector2,\n} from \"@motionscript/core\"\n\nimport { SPOTLIGHT_DIM } from \"../kit/shared\"\nimport {\n EMPTY_REGION_BOX,\n clipPolylineToBox,\n hasRegion,\n type RegionBox,\n} from \"./shared\"\nimport { declarePaints } from \"@motionscript/core/component\"\n\nexport interface ChartLineProps extends Node2DProps {\n /**\n * The series polyline, already mapped into this node's local pixel space\n * (origin at centre, y-up) by the parent {@link LineChart}. Ordered along x.\n */\n points: Vector2[]\n /** Line colour + weight (+ optional dash/cap/join) — the per-series stroke. */\n stroke: Stroke\n /**\n * Reveal of the line in `[0, 1]`: 0 hides it, 1 traces it fully from its left\n * end to its right.\n *\n * Defaults to **1**, unlike the standalone chart this was ported from: in the\n * studio a node's resting state is what the paused editor canvas paints, so a\n * chart that only appeared once a command had run would edit blind. {@link\n * enter} drives it from 0 regardless of where it rests, so the animation is\n * unchanged.\n */\n progress: number\n /**\n * Whether this line is drawn at all. `false` hides it outright — the switch\n * the chart flips alongside its legend's per-series toggle.\n */\n enabled: boolean\n /**\n * A slice of the plot to spotlight, in this node's own pixel space. When set\n * to a real box, the stretches of the line *outside* it drop to\n * {@link ChartLineProps.dimOpacity} while the stretches inside stay full.\n */\n spotlight: RegionBox\n /** Opacity the out-of-region stretches fade to. */\n dimOpacity: number\n}\n\n/**\n * A single series line of a {@link LineChart}: one rounded polyline traced\n * through pre-mapped pixel `points`, drawn as one {@link Graphics2D} command list.\n * Sized and positioned by the parent, which shares the plot's coordinate frame.\n *\n * Two signals drive it: {@link ChartLineProps.progress} traces the line in\n * (animated by {@link enter}) and `enabled` shows or hides it wholesale.\n */\nexport class ChartLine extends Node2D<ChartLineProps> {\n @property({ default: () => [] as Vector2[] }) declare points: Vector2[]\n @property({ default: [], mapper: strokeOps.resolve, tween: strokeOps.lerp })\n declare stroke: Stroke\n @property({ default: 1 }) declare progress: number\n @property({ default: true }) declare enabled: boolean\n @property({ default: () => EMPTY_REGION_BOX }) declare spotlight: RegionBox\n @property({ default: SPOTLIGHT_DIM }) declare dimOpacity: number\n\n /**\n * This node extends `Node2D` rather than `ShapeNode`, so nothing declares the\n * paint props it carries — see {@link declarePaints}.\n */\n override declareAssets(assets: AssetScope): void {\n super.declareAssets(assets)\n declarePaints(this, assets)\n }\n\n protected renderSelf(ctx: RenderContext2D): void {\n if (!this.enabled) return\n const pts = this.points\n if (pts.length < 2) return\n\n const p = Math.max(0, Math.min(1, this.progress))\n if (p <= 0) return\n\n const drawn = this.tracePoints(pts, p)\n if (drawn.length < 2) return\n const stroke = this.stroke as StrokeResolved[]\n const radius = (stroke[0]?.weight ?? 0) * 2 // round the joins\n\n const box = this.spotlight\n if (!hasRegion(box)) {\n ctx.draw(new Graphics2D().line({ points: drawn, radius }).stroke(stroke))\n return\n }\n\n // Spotlit: the whole line goes down to `dimOpacity`, then the stretches\n // inside the region are re-drawn over it at full strength. Two passes rather\n // than one dim + one bright *complementary* pair, so the bright run has\n // continuous geometry through the region and its joins round the way an\n // unspotlit line's do — and so the boundary lands exactly on the region edge.\n ctx.draw(\n new Graphics2D()\n .line({ points: drawn, radius })\n .stroke(stroke)\n .opacity(Math.max(0, Math.min(1, this.dimOpacity)))\n )\n\n // One `draw` per run: shapes chained before a `stroke()` are combined into a\n // single surface, and stroking a union of disjoint open paths doesn't paint\n // — so a line that leaves the region and comes back would lose both halves.\n for (const run of clipPolylineToBox(drawn, box)) {\n ctx.draw(new Graphics2D().line({ points: run, radius }).stroke(stroke))\n }\n }\n\n /**\n * The prefix of `pts` revealed at fraction `p ∈ [0, 1]`, measuring the reveal\n * by *cumulative arc length* so the line traces at a steady pace regardless of\n * uneven point spacing. Returns the whole-point prefix plus one interpolated\n * point partway along the active segment.\n */\n private tracePoints(pts: Vector2[], p: number): Vector2[] {\n if (p >= 1 || pts.length < 2) return pts\n if (p <= 0) return []\n\n const segLen: number[] = []\n let total = 0\n for (let i = 1; i < pts.length; i++) {\n const d = Math.hypot(pts[i].x - pts[i - 1].x, pts[i].y - pts[i - 1].y)\n segLen.push(d)\n total += d\n }\n if (total === 0) return pts\n\n let target = total * p\n const out: Vector2[] = [pts[0]]\n for (let i = 0; i < segLen.length; i++) {\n if (target >= segLen[i]) {\n out.push(pts[i + 1])\n target -= segLen[i]\n } else {\n const t = segLen[i] === 0 ? 0 : target / segLen[i]\n out.push({\n x: pts[i].x + (pts[i + 1].x - pts[i].x) * t,\n y: pts[i].y + (pts[i + 1].y - pts[i].y) * t,\n })\n break\n }\n }\n return out\n }\n\n /**\n * Trace the line in from its left end to its right over `duration` seconds.\n * Drives `progress` `0 → 1`.\n *\n * A `Command` rather than a generator, so the trace can be *evaluated* at a\n * time instead of run to one — which is what lets a scene containing this\n * chart be scrubbed in constant time. It no longer needs to prime `progress`\n * to 0 first: the value at `t` is a function of `t`, so a repeat call re-traces\n * by definition rather than by having reset something.\n */\n @command()\n enter(args: CommandArgs<Record<string, never>> & { duration: number }): Command<ChartLineProps> {\n return this.enterFrom(0, args.duration, args.easing ?? easeOut())\n }\n\n /**\n * The trace, with both ends stated: `progress` runs `from` → `to`.\n *\n * What {@link LineChart.showSeries} and `hideSeries` are both made of — they\n * are the same motion in opposite directions, and expressing them as one thing\n * with endpoints is what stops the pair drifting apart.\n *\n * `enabled` rides along in the same value rather than being set before or after\n * the command. A line is drawable for every `t` the trace covers and undrawn\n * outside it, which is what makes arriving at a time backwards show what\n * arriving forwards does — a flag flipped by a side effect would not.\n */\n enterFrom(\n from: number,\n duration: number,\n easing: EasingFunction = easeOut(),\n to = 1\n ): Command<ChartLineProps> {\n return this.command<ChartLineProps>(\n (t) => {\n const progress = from + (to - from) * t\n // Hidden only once it has fully retraced, so the last frame of a hide is\n // the empty plot rather than a line that vanished a frame early.\n return { progress, enabled: progress > 0 }\n },\n duration,\n easing\n )\n }\n}\n", "/**\n * Types and pure helpers specific to the {@link LineChart} family: the traced\n * polylines, the spotlit region they dim outside of, and the theme those two\n * add to the shared chart vocabulary.\n *\n * Everything a bar or a pie chart would want too — what a series is, how an axis\n * is scaled and formatted, the theme leaf resolvers, the legend and plot-panel\n * branches — lives in `nodes/chart-kit/shared.ts` and is imported here. What's\n * left below is what only a line chart has: points sorted along an x column,\n * a region clipped out of a polyline, and the `region` branch of the theme.\n *\n * Kept renderer-agnostic (no `Node` imports) so every part shares one\n * scale/format/palette/theme vocabulary, and so the studio's own inspector\n * mapping (`line-chart-props.ts`) can build a partial {@link ChartTheme} without\n * pulling the node classes in.\n */\n\nimport {\n Fills,\n fillOps,\n lerpNumber,\n strokeOps,\n type DataRecord,\n type Fill,\n type FillResolved,\n type Stroke,\n type StrokeResolved,\n} from \"@motionscript/core\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_PLOT_AREA,\n DEFAULT_SERIES_STROKES,\n DEFAULT_X_AXIS,\n DEFAULT_Y_AXIS,\n SPOTLIGHT_DIM,\n lerpAxisConfig,\n lerpLegendConfig,\n lerpPlotArea,\n lerpSeriesStrokes,\n niceStep,\n num,\n resolveAxisConfig,\n resolveLegendConfig,\n resolvePlotArea,\n resolveSeriesStrokes,\n type AxisConfig,\n type ChartSeries,\n type LegendConfig,\n type LegendTheme,\n type PlotAreaConfig,\n type Range,\n} from \"../kit/shared\"\nimport type { XScale } from \"./x-scale\"\n\n/**\n * One series in a {@link LineChart}: a polyline traced through the values in one\n * column of the chart's `data`, drawn in `fill` (or the theme's `seriesStyles`\n * palette when omitted) and captioned by `label` in the legend.\n *\n * The family-wide {@link ChartSeries} under a name that says what it draws here;\n * the chart's own `xField` prop names the column carrying the shared x values,\n * so every series is read off the same rows — one row per sample, one column per\n * series.\n */\nexport type LineSeries = ChartSeries\n\n// ---- Plot regions -------------------------------------------------------\n\n/**\n * A rectangular slice of the plot, in **data units** — the author-facing input\n * to {@link LineChart.spotlightRegion} / {@link LineChart.zoomTo}.\n *\n * Either axis may be omitted, and that's the point: `{ x: [60, 100] }` is a\n * vertical band spanning the full height, `{ y: [8, 10] }` a horizontal band\n * spanning the full width, and both together a box. A reversed range is read\n * the same as the forward one — a region is a span, not a direction.\n */\nexport interface PlotRegion {\n /** Horizontal span in x-axis data units. Omit to span the full plot width. */\n x?: Range\n /** Vertical span in y-axis data units. Omit to span the full plot height. */\n y?: Range\n}\n\n/**\n * A {@link PlotRegion} resolved into the plot's **centred pixel space** (origin\n * at the plot centre, y-up) — the same local frame the series points live in,\n * so a line can be clipped against it directly.\n *\n * `xBounded`/`yBounded` carry which axes the author actually constrained: an\n * unbounded axis has its edges pinned to the plot's own and draws no rule.\n */\nexport interface RegionBox {\n left: number\n right: number\n bottom: number\n top: number\n /** The author gave an x range — the left/right edges are real data bounds. */\n xBounded: boolean\n /** The author gave a y range — the bottom/top edges are real data bounds. */\n yBounded: boolean\n}\n\n/** The empty box, used as the \"no region\" resting value. */\nexport const EMPTY_REGION_BOX: RegionBox = {\n left: 0,\n right: 0,\n bottom: 0,\n top: 0,\n xBounded: false,\n yBounded: false,\n}\n\n/** Is `box` a real, non-degenerate region — i.e. is there anything to spotlight? */\nexport function hasRegion(box: RegionBox): boolean {\n return box.right > box.left && box.top > box.bottom\n}\n\n/**\n * Resolve a {@link PlotRegion} against the plot's pixel box and the chart's\n * scales. `plot` is the plot rectangle in centred pixel space, and\n * `xScale`/`yScale` the data ranges those edges span.\n *\n * An omitted axis takes the plot's own edges, so the region spans it. A given\n * axis is clamped to the plot, so a range running off the scale still produces\n * a box inside the panel.\n */\nexport function resolveRegionBox(\n region: PlotRegion | null | undefined,\n plot: { left: number; right: number; bottom: number; top: number },\n xScale: Range,\n yScale: Range\n): RegionBox {\n if (!region || (region.x === undefined && region.y === undefined)) {\n return EMPTY_REGION_BOX\n }\n\n const span = (\n range: Range | undefined,\n [min, max]: Range,\n lo: number,\n hi: number\n ): [number, number, boolean] => {\n if (range === undefined || max <= min) return [lo, hi, false]\n const a = Math.min(range[0], range[1])\n const b = Math.max(range[0], range[1])\n const toPx = (v: number) => lo + ((v - min) / (max - min)) * (hi - lo)\n return [\n Math.max(lo, Math.min(hi, toPx(a))),\n Math.max(lo, Math.min(hi, toPx(b))),\n true,\n ]\n }\n\n const [left, right, xBounded] = span(region.x, xScale, plot.left, plot.right)\n const [bottom, top, yBounded] = span(region.y, yScale, plot.bottom, plot.top)\n return { left, right, bottom, top, xBounded, yBounded }\n}\n\n/** A bare `{x, y}` — declared locally so this module stays free of node imports. */\ninterface Vector2Like {\n x: number\n y: number\n}\n\n/**\n * The parts of `points` that fall **inside** `box`, as a list of contiguous\n * sub-polylines — the geometry behind the region spotlight: a series is drawn\n * dimmed in full, then these runs are re-drawn over it at full strength.\n *\n * Segments are clipped against the box rather than filtered by endpoint, so a\n * line entering the region mid-segment is cut exactly on the boundary.\n */\nexport function clipPolylineToBox(\n points: Vector2Like[],\n box: RegionBox\n): Vector2Like[][] {\n if (points.length < 2 || !hasRegion(box)) return []\n\n const runs: Vector2Like[][] = []\n let run: Vector2Like[] = []\n\n for (let i = 1; i < points.length; i++) {\n const seg = clipSegment(points[i - 1], points[i], box)\n if (!seg) {\n // Segment misses the box entirely — close whatever run was open.\n if (run.length >= 2) runs.push(run)\n run = []\n continue\n }\n const [a, b] = seg\n // Continue the open run when this clipped segment starts where the last one\n // ended; otherwise the line left the box and came back, which is a new run.\n if (run.length > 0 && samePoint(run[run.length - 1], a)) run.push(b)\n else {\n if (run.length >= 2) runs.push(run)\n run = [a, b]\n }\n }\n if (run.length >= 2) runs.push(run)\n return runs\n}\n\n/** Points within half a pixel are the same point, for run-joining purposes. */\nfunction samePoint(a: Vector2Like, b: Vector2Like): boolean {\n return Math.abs(a.x - b.x) < 0.5 && Math.abs(a.y - b.y) < 0.5\n}\n\n/**\n * Liang–Barsky clip of one segment against the axis-aligned `box`. Returns the\n * surviving `[start, end]`, or `null` when the segment lies wholly outside.\n */\nfunction clipSegment(\n a: Vector2Like,\n b: Vector2Like,\n box: RegionBox\n): [Vector2Like, Vector2Like] | null {\n const dx = b.x - a.x\n const dy = b.y - a.y\n let t0 = 0\n let t1 = 1\n\n // Each edge as (p, q): the segment survives where p·t <= q.\n const edges: [number, number][] = [\n [-dx, a.x - box.left],\n [dx, box.right - a.x],\n [-dy, a.y - box.bottom],\n [dy, box.top - a.y],\n ]\n for (const [p, q] of edges) {\n if (p === 0) {\n // Parallel to this edge: outside it means the whole segment is out.\n if (q < 0) return null\n continue\n }\n const r = q / p\n if (p < 0) {\n if (r > t1) return null\n if (r > t0) t0 = r\n } else {\n if (r < t0) return null\n if (r < t1) t1 = r\n }\n }\n return [\n { x: a.x + t0 * dx, y: a.y + t0 * dy },\n { x: a.x + t1 * dx, y: a.y + t1 * dy },\n ]\n}\n\n// ---- Theme ---------------------------------------------------------------\n\n/**\n * How a spotlit {@link PlotRegion} is drawn — the dashed rules on its bounded\n * edges, the wash filling it, and how far the series fade outside it.\n */\nexport interface RegionConfig {\n /** The dashed rule marking each bounded edge. */\n stroke: Stroke\n /** Wash filling the region, drawn under the series lines. */\n fill: Fill\n /** Opacity the stretch of every series *outside* the region fades to. */\n dimOpacity: number\n}\n\n/** A {@link LineChart}'s full, resolved visual theme. */\nexport interface ChartConfig {\n /**\n * Default per-series line stroke, cycled by series index (`i % length`). A\n * series' own `fill`, when set, overrides just the colour.\n */\n seriesStyles: Stroke[]\n xAxis: AxisConfig\n yAxis: AxisConfig\n /** The plot panel: border, fill, and drop shadow. */\n plotArea: PlotAreaConfig\n legend: LegendConfig\n /** The spotlit-region annotation — see {@link RegionConfig}. */\n region: RegionConfig\n}\n\n/**\n * Author-facing partial theme, passed as {@link LineChart}'s `theme` prop —\n * every branch optional and deep-merged onto {@link DEFAULT_CHART_THEME}.\n */\nexport type ChartTheme = {\n seriesStyles?: Stroke[]\n xAxis?: Partial<AxisConfig>\n yAxis?: Partial<AxisConfig>\n plotArea?: Partial<PlotAreaConfig>\n legend?: LegendTheme\n region?: Partial<RegionConfig>\n}\n\n/**\n * The legend of a line chart: the shared one, with a small rounded-square\n * swatch in place of the wide chip. A bar's or a wedge's swatch stands in for\n * an area of paint and is drawn like one; a line's stands in for a colour, and\n * a square reads as the sample it is.\n */\nconst LINE_CHART_LEGEND: LegendConfig = {\n ...DEFAULT_LEGEND,\n markerStyle: {\n ...DEFAULT_LEGEND.markerStyle,\n width: 20,\n height: 20,\n borderRadius: 6,\n },\n}\n\n/** The chart's out-of-the-box look. */\nexport const DEFAULT_CHART_THEME: ChartConfig = {\n seriesStyles: DEFAULT_SERIES_STROKES,\n xAxis: DEFAULT_X_AXIS,\n yAxis: DEFAULT_Y_AXIS,\n plotArea: DEFAULT_PLOT_AREA,\n legend: LINE_CHART_LEGEND,\n region: {\n // Brighter than the grid and dashed, so the boundary reads as a call-out\n // rather than another axis line; `butt` caps keep the dash lengths exact.\n stroke: {\n weight: 3,\n fill: CHART_PALETTE.regionColor,\n dash: [14, 12],\n cap: \"butt\",\n },\n // A wash barely above the panel — enough to tint the band, not enough to\n // mute the series drawn over it.\n fill: Fills.color(CHART_PALETTE.regionColor, { opacity: 0.08 }),\n dimOpacity: SPOTLIGHT_DIM,\n },\n}\n\nfunction resolveRegionConfig(\n region: Partial<RegionConfig> | undefined,\n previous: RegionConfig\n): RegionConfig {\n return {\n stroke: strokeOps.resolve(region?.stroke ?? previous.stroke),\n fill: fillOps.resolve(region?.fill ?? previous.fill),\n dimOpacity: region?.dimOpacity ?? previous.dimOpacity,\n }\n}\n\nfunction lerpRegionConfig(\n from: RegionConfig,\n to: RegionConfig,\n t: number\n): RegionConfig {\n return {\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n fill: fillOps.lerp(\n from.fill as FillResolved[],\n to.fill as FillResolved[],\n t\n ),\n dimOpacity: lerpNumber(from.dimOpacity, to.dimOpacity, t),\n }\n}\n\n/**\n * {@link LineChart}'s `theme` @property mapper: merges the author's partial\n * {@link ChartTheme} onto `previous` (falling back to\n * {@link DEFAULT_CHART_THEME} on first resolution), fully resolving every leaf.\n */\nexport function resolveChartTheme(\n theme: ChartTheme | undefined,\n previous?: ChartConfig\n): ChartConfig {\n const base = previous ?? DEFAULT_CHART_THEME\n return {\n seriesStyles: resolveSeriesStrokes(theme?.seriesStyles, base.seriesStyles),\n xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),\n yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),\n plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n region: resolveRegionConfig(theme?.region, base.region),\n }\n}\n\n/** {@link LineChart}'s `theme` @property tween — leaf by leaf. */\nexport function lerpChartTheme(\n from: ChartConfig,\n to: ChartConfig,\n t: number\n): ChartConfig {\n return {\n seriesStyles: lerpSeriesStrokes(from.seriesStyles, to.seriesStyles, t),\n xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),\n yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),\n plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n region: lerpRegionConfig(from.region, to.region, t),\n }\n}\n\n// ---- Scales --------------------------------------------------------------\n\n/**\n * Resolve one series' `[x, y]` points out of the chart's rows: each row's\n * position on the {@link XScale} paired with its own `field` column, sorted\n * along x so the polyline traces left-to-right whatever order the rows arrived\n * in.\n *\n * A row missing its y column contributes a 0 rather than a gap — these charts\n * draw continuous lines, so dropping the point would silently bridge the hole.\n *\n * Takes the resolved scale rather than the column *name*, because what a row's x\n * is depends on what kind of column it came out of: a number, an instant, or a\n * place in the file. See `x-scale.ts`.\n */\nexport function seriesPoints(\n data: DataRecord[],\n x: XScale,\n series: LineSeries\n): [x: number, y: number][] {\n return data\n .map((row, i): [number, number] => [x.at(i), num(row, series.field)])\n .sort((a, b) => a[0] - b[0])\n}\n\n/** Min/max across every series' column, the fallback when unset. */\nexport function yExtent(data: DataRecord[], series: LineSeries[]): Range {\n let lo = Infinity\n let hi = -Infinity\n for (const row of data)\n for (const s of series) {\n const v = num(row, s.field)\n if (v < lo) lo = v\n if (v > hi) hi = v\n }\n return lo <= hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * Effective vertical scale: the explicit `range`, else the data's y extent\n * nudged out to tidy step bounds so markers read as round numbers.\n *\n * `divisions` is how many segments the auto-range is stepped into (one fewer\n * than a marker count) — it only shapes the tidy step when `range` is omitted.\n */\nexport function resolveYScale(\n data: DataRecord[],\n series: LineSeries[],\n range: Range | undefined,\n divisions: number\n): Range {\n if (range) return range\n const [lo, hi] = yExtent(data, series)\n const segments = Math.max(1, Math.round(divisions))\n const step = niceStep((hi - lo) / segments || 1)\n return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step]\n}\n", "import {\n type AssetScope,\n Graphics2D,\n Node2D,\n fillOps,\n strokeOps,\n property,\n type Fill,\n type FillResolved,\n type Node2DProps,\n type RenderContext2D,\n type Stroke,\n type StrokeResolved,\n} from \"@motionscript/core\"\n\nimport {\n DEFAULT_CHART_THEME,\n EMPTY_REGION_BOX,\n hasRegion,\n type RegionBox,\n} from \"./shared\"\nimport { declarePaints } from \"@motionscript/core/component\"\n\nexport interface ChartRegionProps extends Node2DProps {\n /**\n * The highlighted slice, already resolved into this node's local pixel space\n * (origin at centre, y-up) by the parent {@link LineChart}. A degenerate box\n * draws nothing.\n */\n box: RegionBox\n /** Dashed rule marking each *bounded* edge of the box. */\n stroke: Stroke\n /** Wash filling the box, under the series lines. */\n fill: Fill\n /**\n * Reveal of the annotation in `[0, 1]`: 0 hides it, 1 draws it fully. Driven\n * by the chart's own `regionReveal`, so the rules and the series' dimming move\n * as one.\n */\n reveal: number\n}\n\n/**\n * The **region call-out** of a {@link LineChart}: a washed rectangle over the\n * plot with a dashed rule down each edge the author actually bounded. Sized and\n * positioned entirely from the pre-mapped {@link ChartRegionProps.box} rather\n * than its own `layoutBounds` — the box already carries the plot geometry.\n *\n * It draws only the *marking*; fading the series outside the region is\n * {@link ChartLine}'s job (it clips its own polyline against the same box), so\n * the two can't disagree about where the boundary is.\n *\n * Only bounded edges get a rule: a region given just an `x` range spans the full\n * plot height, so its top and bottom edges are the plot's own border, not a data\n * bound — drawing dashes there would assert a limit the author didn't set.\n */\nexport class ChartRegion extends Node2D<ChartRegionProps> {\n @property({ default: () => EMPTY_REGION_BOX }) declare box: RegionBox\n @property({\n default: DEFAULT_CHART_THEME.region.stroke,\n mapper: strokeOps.resolve,\n tween: strokeOps.lerp,\n })\n declare stroke: Stroke\n @property({\n default: DEFAULT_CHART_THEME.region.fill,\n mapper: fillOps.resolve,\n tween: fillOps.lerp,\n })\n declare fill: Fill\n @property({ default: 0 }) declare reveal: number\n\n /**\n * This node extends `Node2D` rather than `ShapeNode`, so nothing declares the\n * paint props it carries — see {@link declarePaints}.\n */\n override declareAssets(assets: AssetScope): void {\n super.declareAssets(assets)\n declarePaints(this, assets)\n }\n\n protected renderSelf(ctx: RenderContext2D): void {\n const box = this.box\n if (!hasRegion(box)) return\n\n const r = Math.max(0, Math.min(1, this.reveal))\n if (r <= 0) return\n\n const width = box.right - box.left\n const height = box.top - box.bottom\n const cx = (box.left + box.right) / 2\n const cy = (box.bottom + box.top) / 2\n\n // Wash first, so the rules sit over their own tint.\n const layers = this.fill as FillResolved[]\n if (layers.length > 0) {\n ctx.draw(\n new Graphics2D()\n .rect({ x: cx, y: cy, width, height })\n .fill(this.fill)\n .opacity(r)\n )\n }\n\n // One rule per bounded edge, growing from the region's centre outward as\n // `reveal` climbs — the same \"trace it in\" beat as the series lines.\n const stroke = this.stroke as StrokeResolved[]\n if (stroke.length === 0) return\n\n // One `draw` per rule rather than four line ops under a single paint:\n // shapes chained before a `stroke()` are combined into one surface, and\n // stroking a union of disjoint open paths doesn't paint.\n const rule = (points: { x: number; y: number }[]) =>\n ctx.draw(new Graphics2D().line({ points }).stroke(this.stroke).opacity(r))\n\n if (box.xBounded) {\n const half = (height / 2) * r\n for (const x of [box.left, box.right]) {\n rule([\n { x, y: cy - half },\n { x, y: cy + half },\n ])\n }\n }\n if (box.yBounded) {\n const half = (width / 2) * r\n for (const y of [box.bottom, box.top]) {\n rule([\n { x: cx - half, y },\n { x: cx + half, y },\n ])\n }\n }\n }\n}\n", "import {\n command,\n Node2D,\n Rect,\n Text,\n easeOut,\n property,\n insetsOps,\n textOps,\n type CommandArgs,\n type NodeConfig,\n type Node2DProps,\n} from \"@motionscript/core\";\n\nimport {\n DEFAULT_X_AXIS,\n enterBudget,\n formatMarker,\n type AxisConfig,\n type Range,\n} from \"../kit/shared\";\nimport { at, compose, type Seekable } from \"@motionscript/core/component\"\n\nexport interface HorizontalAxisProps extends Node2DProps {\n /** `[min, max]` of the horizontal scale the markers are positioned within. */\n range: Range;\n /** Tick values (in data units) to draw along the axis. */\n markers: number[];\n /** Marker/caption text style, grid stroke, and gutters — see {@link AxisConfig}. */\n axis: AxisConfig;\n /**\n * Caption drawn centred in the gutter beneath the marker row. Empty string\n * (the default) hides it.\n */\n label: string;\n}\n\n/** How far a marker starts above its slot when it enters, in px. */\nconst SLIDE = 24;\n\n/** How a tick value is captioned — see {@link HorizontalAxis.labelFor}. */\nexport type MarkerLabel = (value: number) => string;\n\n/**\n * The horizontal axis of a {@link LineChart}: a **row of {@link Text} nodes**,\n * one per marker value, running left-to-right. Pure layout — the mirror of the\n * kit's `VerticalAxis`, including its bound (not snapshotted) marker text and\n * positions, and its landed resting state.\n *\n * Markers are always placed **by value** across the range, whatever they say:\n * `2026-03` sits three months along, not three slots along. That is what keeps\n * it distinct from a bar chart's bottom axis, which centres a caption on each\n * slot and has no scale at all — the two only look alike. What the values are\n * captioned *as* is the chart's to decide; see {@link labelFor}.\n *\n * {@link enter} staggers the markers in **left-to-right**: each starts lifted a\n * little above its slot and transparent, then fades and slides down into place.\n */\nexport class HorizontalAxis extends Node2D<HorizontalAxisProps> {\n @property({ default: () => [0, 1] as Range }) declare range: Range;\n @property({ default: () => [] as number[] }) declare markers: number[];\n @property({ default: DEFAULT_X_AXIS }) declare axis: AxisConfig;\n @property({ default: \"\" }) declare label: string;\n\n /**\n * How a tick value is captioned. Defaults to the numeric formatting the\n * vertical axis uses; a chart whose x column names months or categories\n * replaces it (see `x-scale.ts`).\n *\n * A plain field rather than a `@property`, because it is a **policy, not a\n * value**: there is nothing to tween between two formatters, and a signal\n * holding a function is a signal every `set`/`to` on this node would have to\n * know not to touch. Read inside each marker's `text` binding, so replacing it\n * before the first frame is enough — which is when the chart does it.\n */\n labelFor: MarkerLabel = formatMarker;\n\n /** One text node per marker, ordered left → right, for the staggered enter. */\n private markerNodes: Text[] = [];\n /** The caption node, or null when `label` is empty. */\n private labelNode: Text | null = null;\n\n /** The live tick values, left → right — re-read every frame, not snapshotted. */\n private get sortedMarkers(): number[] {\n return [...this.markers].sort((a, b) => a - b);\n }\n\n private valueAt(index: number): number | undefined {\n return this.sortedMarkers[index];\n }\n\n /** Tick `i`'s fraction across the live `range`, 0 at the left edge. */\n private fracAt(index: number): number {\n const v = this.valueAt(index);\n if (v === undefined) return 0;\n const [min, max] = this.range;\n return max <= min ? 0 : (v - min) / (max - min);\n }\n\n constructor(props?: NodeConfig<HorizontalAxis, HorizontalAxisProps>) {\n super(props);\n\n const tickPad = insetsOps.resolve(this.axis.tickPadding).top;\n const titlePad = insetsOps.resolve(this.axis.titlePadding).top;\n\n this.markerNodes = this.markers.map((_, i) => {\n const marker: Text = new Text({\n ...textOps.styleProps(this.axis.labelStyle),\n text: () => {\n const v = this.valueAt(i);\n return v === undefined ? \"\" : this.labelFor(v);\n },\n x: () => {\n const width = this.layoutBounds.width;\n const centred = (this.fracAt(i) - 0.5) * width;\n // Held inside the axis's right edge, which is also the *chart's*: the\n // last marker is centred on the last grid line, so half of `2026-06`\n // — or of `3300` — would be drawn outside the node's box and outside\n // the rectangle the editor draws around it. Only the right edge is\n // held, because the left one has the vertical axis's whole gutter\n // beyond it, and a marker overhanging into that is inside the chart.\n const limit = width / 2 - marker.layoutBounds.width / 2;\n return Math.min(centred, limit);\n },\n });\n return marker;\n });\n\n const children: Node2D[] = [\n new Rect({\n width: \"fill\",\n height: \"hug\",\n padding: { top: tickPad },\n children: this.markerNodes,\n }),\n ];\n if (this.label !== \"\") {\n this.labelNode = new Text({\n ...textOps.styleProps(this.axis.titleStyle),\n text: this.label,\n });\n children.push(this.labelNode);\n }\n\n this.add(\n new Rect({ flow: \"vertical\", gap: titlePad, height: \"hug\", width: \"fill\", children }),\n );\n }\n\n /**\n * Primes every marker (and the caption) at its pre-entrance state: lifted\n * above its slot and transparent. Idempotent — see {@link VerticalAxis.arm}.\n */\n arm(slide = SLIDE): void {\n for (const marker of this.markerNodes) marker.set({ opacity: 0, y: slide });\n this.labelNode?.set({ opacity: 0, y: slide });\n }\n\n /**\n * Stagger the markers in left to right, then slide the caption in after them.\n *\n * A {@link Seekable} rather than a generator. The offsets were always static —\n * `i * step` per marker — but expressed as `wait` inside a generator they only\n * existed while it ran, so knowing what the axis looked like partway through\n * meant running it from zero. Placed explicitly, the same stagger can be asked\n * for a time, which is what lets a chart scene scrub in constant time.\n *\n * `duration` is the **whole** entrance — see {@link VerticalAxis.enter} and\n * {@link enterBudget}.\n */\n @command()\n enter(\n args: CommandArgs<{ stagger?: number; slide?: number }> & { duration: number },\n ): Seekable {\n const { stagger, slide = SLIDE } = args.data ?? {};\n const duration = args.duration;\n const easing = args.easing ?? easeOut();\n const nodes = this.markerNodes;\n if (nodes.length === 0) return compose([]);\n const { step, per, caption } = enterBudget(\n duration,\n nodes.length,\n this.labelNode !== null,\n stagger,\n );\n\n this.arm(slide);\n\n // Left marker leads: markerNodes is already left → right.\n const markers = nodes.map((node, i) =>\n at(i * step, node.to({ data: { opacity: 1, y: 0 }, duration: per, easing })),\n );\n const lastMarkerEnds = nodes.length === 0 ? 0 : (nodes.length - 1) * step + per;\n\n return compose([\n ...markers,\n ...(this.labelNode\n ? [at(lastMarkerEnds, this.labelNode.to({ data: { opacity: 1, y: 0 }, duration: caption, easing }))]\n : []),\n ]);\n }\n}\n", "/**\n * What a line chart's x column *means* — the one question that has to be\n * answered before a row can be given a position along the bottom of the plot.\n *\n * A y column is always a magnitude. An x column is one of three things, and\n * which one is a property of the data rather than something the author states:\n *\n * - **numeric** — `0, 5, 10` — plot the number.\n * - **time** — `Jan`, `2026-03`, `2026-W12`, `2026-03-14` — plot the instant,\n * so samples a month apart sit a month apart and a gap in the data reads as a\n * gap.\n * - **ordinal** — anything else — plot the row's position, evenly spaced.\n *\n * Before this, every x column went through `num()`, which reads a non-numeric\n * cell as `0`. A `Month` column of `Jan…Dec` therefore stacked all twelve\n * points on one x and drew a vertical line — no error, no empty chart, just a\n * wrong picture. That is the failure this module exists to remove: every column\n * now plots as *something*, and the something is the reading a person would\n * give it.\n *\n * The captions come back off the rows rather than out of a formatter. A tick on\n * a time or ordinal scale is chosen *from the data* (see {@link XScale.ticks}),\n * so the label it wants is the cell the author wrote — `Jan`, not `1970-01-01`,\n * and `2026-W12`, not the Monday it resolves to. Only a tick that is not a\n * sample — an author-set range, or a `zoomTo` mid-flight — needs formatting,\n * and that is the one case {@link formatInstant} covers.\n */\n\nimport type { DataRecord } from \"@motionscript/core\"\n\nimport {\n formatMarker,\n markerValues,\n num,\n text,\n type Range,\n} from \"../kit/shared\"\nimport { formatNumber } from \"@motionscript/core/component\"\n\n/** How an x column's cells become positions along the axis. */\nexport type XScaleKind = \"numeric\" | \"time\" | \"ordinal\"\n\n/**\n * An x column resolved into positions, captions and ticks.\n *\n * The three kinds differ in all three answers, so they are three closures built\n * once per (data, column) rather than a kind tag every caller switches on.\n */\nexport interface XScale {\n kind: XScaleKind\n /** Position of row `index` along the axis, in this scale's own units. */\n at(index: number): number\n /** `[min, max]` across the rows — the scale when no range is set. */\n extent: Range\n /** The caption for a tick at `value`. */\n label(value: number): string\n /** `divisions + 1` tick values spanning `range`, ends included. */\n ticks(range: Range, divisions: number): number[]\n}\n\n// ---- Time parsing --------------------------------------------------------\n\nconst DAY = 86_400_000\n\nconst MONTHS = [\n \"january\",\n \"february\",\n \"march\",\n \"april\",\n \"may\",\n \"june\",\n \"july\",\n \"august\",\n \"september\",\n \"october\",\n \"november\",\n \"december\",\n]\n\n/**\n * The year a cell that names no year is dated into.\n *\n * Any year would place `Jan…Dec` in the right order at the right spacing; a\n * leap year is the one that also spaces February correctly against a column\n * that *does* carry years, so the two read alike.\n */\nconst NOMINAL_YEAR = 2024\n\n/**\n * Month index for any unambiguous abbreviation of a month name — `jan`, `sept`,\n * `january` — or -1.\n *\n * Three letters minimum, and then a prefix match, which is what lets `sept`\n * through without letting `janitor` through: a longer word has to still *be*\n * the month.\n */\nfunction monthIndex(word: string): number {\n if (word.length < 3) return -1\n return MONTHS.findIndex((month) => month.startsWith(word))\n}\n\n/** The Monday of ISO week `week` in `year`, as an epoch timestamp. */\nfunction isoWeekStart(year: number, week: number): number {\n // ISO week 1 is the week containing 4 January, and weeks start on Monday.\n const jan4 = Date.UTC(year, 0, 4)\n const weekday = (new Date(jan4).getUTCDay() + 6) % 7 // Monday = 0\n return jan4 - weekday * DAY + (week - 1) * 7 * DAY\n}\n\n/**\n * One cell read as an instant, or `null` when it isn't one.\n *\n * Deliberately **not** `Date.parse`. That accepts `\"5\"`, `\"Q3\"` and a good deal\n * else besides, differs between engines on anything non-ISO, and reads a bare\n * `2026-03-14` as UTC but `2026/03/14` as local — three ways for a column to be\n * silently misread as time. Every form below is matched explicitly, and an\n * ambiguous all-numeric date (`03/04/2026` — March or April, depending on where\n * the author lives) is deliberately not one of them.\n */\nexport function parseInstant(raw: unknown): number | null {\n if (typeof raw !== \"string\") return null\n const s = raw.trim().toLowerCase()\n if (s === \"\") return null\n\n // 2026-03-14 · 2026/03/14 · 2026-03 · 2026/03\n const ymd = /^(\\d{4})[-/](\\d{1,2})(?:[-/](\\d{1,2}))?$/.exec(s)\n if (ymd) {\n const month = Number(ymd[2])\n const day = ymd[3] === undefined ? 1 : Number(ymd[3])\n if (month < 1 || month > 12 || day < 1 || day > 31) return null\n return Date.UTC(Number(ymd[1]), month - 1, day)\n }\n\n // 2026-W12 · 2026W12 · w12 · week 12\n const week = /^(?:(\\d{4})[-\\s]?)?w(?:ee)?k?[-\\s]?(\\d{1,2})$/.exec(s)\n if (week) {\n const n = Number(week[2])\n if (n < 1 || n > 53) return null\n return isoWeekStart(\n week[1] === undefined ? NOMINAL_YEAR : Number(week[1]),\n n\n )\n }\n\n // jan · january · jan 2026 · january-2026 · 2026 jan\n const named = /^(?:(\\d{4})[-\\s]+)?([a-z]+)(?:[-\\s]+(\\d{4}))?$/.exec(s)\n if (named) {\n const month = monthIndex(named[2])\n if (month >= 0) {\n const year = named[1] ?? named[3]\n const y = year === undefined ? NOMINAL_YEAR : Number(year)\n return Date.UTC(y, month, 1)\n }\n }\n\n // 14 mar 2026 · mar 14 2026 · mar 14, 2026\n const dayNamed =\n /^(?:(\\d{1,2})[-\\s]+([a-z]+)|([a-z]+)[-\\s]+(\\d{1,2}),?)[-\\s]+(\\d{4})$/.exec(\n s\n )\n if (dayNamed) {\n const month = monthIndex(dayNamed[2] ?? dayNamed[3])\n const day = Number(dayNamed[1] ?? dayNamed[4])\n if (month >= 0 && day >= 1 && day <= 31) {\n return Date.UTC(Number(dayNamed[5]), month, day)\n }\n }\n\n return null\n}\n\n/**\n * An instant captioned at the coarsest granularity the axis's own span can tell\n * apart — a decade of samples reads as years, a year of them as months.\n *\n * Only reached for a tick that is *not* one of the rows (an author-set range, a\n * `zoomTo` part-way through). A tick that is a sample is captioned with the\n * cell the author wrote; see the note at the top of this file.\n */\nexport function formatInstant(value: number, span: number): string {\n const date = new Date(value)\n const year = date.getUTCFullYear()\n const month = MONTHS[date.getUTCMonth()].slice(0, 3)\n const label = month.charAt(0).toUpperCase() + month.slice(1)\n if (span > 4 * 365 * DAY) return String(year)\n if (span > 60 * DAY) return `${label} ${year}`\n return `${label} ${date.getUTCDate()}`\n}\n\n// ---- Column reading ------------------------------------------------------\n\n/**\n * Whether every non-empty cell of `field` holds a number.\n *\n * The same rule `numericFields` applies, asked of one column: a single text\n * cell in an otherwise numeric column takes the whole column off the numeric\n * scale, because plotting that row at 0 is the failure this module is here to\n * stop.\n */\nfunction isNumericColumn(data: DataRecord[], field: string): boolean {\n let sawValue = false\n for (const row of data) {\n const v = row[field]\n if (v === undefined || v === null || v === \"\") continue\n if (typeof v !== \"number\") return false\n sawValue = true\n }\n return sawValue\n}\n\n/**\n * Every non-empty cell of `field` parsed as an instant, or `null` if any one of\n * them isn't one — in which case the column isn't a time column at all.\n *\n * A blank cell keeps its slot, so the returned list stays index-aligned with\n * the rows, and takes the nearest known instant: a hole in an x column is a row\n * whose date the file didn't say, and stacking it on the epoch would drag the\n * whole scale back fifty years to make room for it.\n */\nfunction instantColumn(data: DataRecord[], field: string): number[] | null {\n const parsed: (number | null)[] = []\n let sawValue = false\n for (const row of data) {\n const raw = row[field]\n if (raw === undefined || raw === null || raw === \"\") {\n parsed.push(null)\n continue\n }\n const t = parseInstant(raw)\n if (t === null) return null\n parsed.push(t)\n sawValue = true\n }\n if (!sawValue) return null\n\n const first = parsed.find((t): t is number => t !== null) as number\n let last = first\n return parsed.map((t) => (t === null ? last : (last = t)))\n}\n\n/** `[min, max]` of `values`, or `[0, 1]` when there is nothing to span. */\nfunction extentOf(values: number[]): Range {\n let lo = Infinity\n let hi = -Infinity\n for (const v of values) {\n if (v < lo) lo = v\n if (v > hi) hi = v\n }\n return lo <= hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * Ticks spanning `range`, each one **a sample** — a row the author can point at\n * in their file — placed as near as it can be to where an evenly spaced tick\n * would have gone.\n *\n * Two things have to hold at once, and picking either alone gets it wrong.\n * Ticks must be *samples*, because a caption between two of them has nothing to\n * say (two-thirds of the way through March is not a month, and on a category\n * column it is not anything at all). And they must be *spread*, because the\n * labels sit side by side in a row: taking every nth sample instead puts five\n * dates in the two weeks a burst of rows happens to cover and prints them on\n * top of each other, which is what a column with a gap in it does.\n *\n * Walking the targets rather than the samples is what satisfies both. A run of\n * rows too close together to caption collapses to one tick — consecutive\n * targets pick the same sample and the duplicate is dropped — so a chart asked\n * for five ticks across a gappy column draws four honest ones instead of five\n * overlapping.\n */\nfunction sampledTicks(\n positions: number[],\n [min, max]: Range,\n divisions: number\n): number[] {\n const inside = positions.filter((v) => v >= min && v <= max)\n const unique = [...new Set(inside)].sort((a, b) => a - b)\n if (unique.length === 0) return markerValues([min, max], divisions)\n\n const nearest = (target: number) =>\n unique.reduce((best, v) =>\n Math.abs(v - target) < Math.abs(best - target) ? v : best\n )\n\n const out: number[] = []\n for (const target of markerValues([min, max], divisions)) {\n const tick = nearest(target)\n if (out[out.length - 1] !== tick) out.push(tick)\n }\n return out\n}\n\n/**\n * The caption a tick carries on a sample-based scale: the cell of the row it\n * came from, or `fallback` when the tick isn't a sample after all.\n */\nfunction labelBySample(\n positions: number[],\n captions: string[],\n value: number,\n fallback: (value: number) => string\n): string {\n const i = positions.indexOf(value)\n return i >= 0 ? captions[i] : fallback(value)\n}\n\n/**\n * How `field` positions the rows: the number it holds, the instant it names, or\n * failing both, the row's own place in the file.\n *\n * Resolved from the data on every read rather than cached on the node: it is\n * cheap (one pass to classify, one to position), and a cache keyed on a `data`\n * array that `buildScene` rebuilds on every edit would miss every time anyway.\n */\nexport function resolveXScale(\n data: DataRecord[],\n field: string,\n /**\n * The number-format pattern for a *numeric* column. Ignored by the other two\n * kinds, and correctly so: a tick on those is captioned with the cell the\n * author wrote, and `Jan` has no decimal places.\n */\n pattern?: string\n): XScale {\n if (field !== \"\" && isNumericColumn(data, field)) {\n const positions = data.map((row) => num(row, field))\n return {\n kind: \"numeric\",\n at: (i) => positions[i] ?? 0,\n extent: extentOf(positions),\n label: (value) =>\n pattern === undefined ? formatMarker(value) : formatNumber(value, pattern),\n // Round numbers, evenly spaced — a continuous axis is read by its scale\n // rather than by which samples happen to be on it.\n ticks: markerValues,\n }\n }\n\n const captions = data.map((row) => text(row, field))\n const instants = field === \"\" ? null : instantColumn(data, field)\n\n if (instants) {\n const extent = extentOf(instants)\n const span = extent[1] - extent[0]\n return {\n kind: \"time\",\n at: (i) => instants[i] ?? 0,\n extent,\n label: (value) =>\n labelBySample(instants, captions, value, (v) =>\n formatInstant(v, span)\n ),\n ticks: (range, divisions) => sampledTicks(instants, range, divisions),\n }\n }\n\n const positions = data.map((_, i) => i)\n return {\n kind: \"ordinal\",\n at: (i) => i,\n // `[0, n-1]` rather than the tidy `[0, n]`: the first and last rows sit on\n // the plot's edges, which is where a reader looks for them.\n extent: positions.length > 0 ? [0, positions.length - 1] : [0, 1],\n label: (value) => captions[Math.round(value)] ?? \"\",\n ticks: (range, divisions) => sampledTicks(positions, range, divisions),\n }\n}\n", "import {\n command,\n node,\n Node2D,\n Rect,\n easeOut,\n property,\n type CommandArgs,\n type DataRecord,\n type Command,\n type EasingFunction,\n type NodeConfig,\n type Node2DProps,\n} from \"@motionscript/core\"\n\nimport { ChartBorder } from \"../kit/chart-border\"\nimport { ChartCanvas } from \"../kit/chart-canvas\"\nimport { ChartLegend } from \"../kit/chart-legend\"\nimport { VerticalAxis } from \"../kit/vertical-axis\"\nimport {\n clampUnit,\n formatMarker,\n lerpAxisDomain,\n markerValues,\n num,\n resolveAxisDomain,\n type AxisDomainConfig,\n type Range,\n} from \"../kit/shared\"\nimport { formatNumber } from \"@motionscript/core/component\"\nimport {\n drive,\n hold,\n sequence as sequenceOf,\n together,\n type Seekable,\n} from \"@motionscript/core/component\"\nimport { CategoryAxis } from \"./category-axis\"\nimport { ChartBars, type BarGeometry } from \"./chart-bars\"\nimport {\n DEFAULT_BAR_CHART_THEME,\n barCategories,\n barSpans,\n lerpBarChartTheme,\n resolveBarChartTheme,\n resolveBarFill,\n resolveBarScale,\n stackCapIndex,\n type BarChartConfig,\n type BarChartTheme,\n type BarSeries,\n} from \"./shared\"\n\nexport interface BarChartProps extends Node2DProps {\n /** One row per category — a plain record of field name to value. */\n data: DataRecord[]\n /**\n * Key into each row for its category caption, drawn beneath the baseline.\n * Named `categoryField` rather than `category` for the same reason the line\n * chart's is `xField`: it names a *column*, not a value.\n */\n categoryField: string\n /** One entry per bar series: which column it plots, its `label`, its `fill`. */\n series: BarSeries[]\n /**\n * When `true` every category's series stack into one column; when `false`\n * (the default) each series gets its own bar, grouped side by side within the\n * category's slot.\n */\n stacked?: boolean\n /** The value axis's domain: caption, scale range, and tick placement. */\n valueAxis?: AxisDomainConfig\n /** The category axis's domain. Only its `label` is read — the slots are the rows. */\n categoryAxis?: AxisDomainConfig\n /**\n * The chart's visual theme — bar palette and geometry, axis furniture, plot\n * panel paint, legend, value figures. Every branch is optional and deep-merged\n * onto {@link DEFAULT_BAR_CHART_THEME}.\n */\n theme?: BarChartTheme\n /** Whether the legend row below the plot is drawn at all. */\n showLegend?: boolean\n /** Whether each column prints the figure it plots. */\n showValues?: boolean\n}\n\n/**\n * Layout constants for the composition, all in px — the line chart's, so the two\n * sit at the same size in the same box and a scene can swap one for the other\n * without re-tuning its layout.\n */\n/** Left gutter reserved for the value axis markers. */\nconst Y_AXIS_GUTTER = 64\n/** Bottom gutter (within the chart area) reserved for the category captions. */\nconst X_AXIS_GUTTER = 40\n/** Height reserved below the chart area for the legend row. */\nconst LEGEND_GUTTER = 56\n/** Extra left/bottom room when an axis caption is shown. */\nconst Y_LABEL_GUTTER = 34\nconst X_LABEL_GUTTER = 30\n/** Headroom above the plot, so a column reaching the top marker isn't flush to it. */\nconst TOP_HEADROOM = 24\n/** Right gutter so the last slot isn't flush with the box edge. */\nconst RIGHT_GUTTER = 20\n\n/** How far the legend starts below its slot when it enters, in px. */\nconst LEGEND_SLIDE = 20\n\n/**\n * How {@link BarChart.enter} divides its duration between its three beats: the\n * axes arriving, the columns rising, and the legend coming up. Shares of the\n * whole, so they must sum to 1 — the entrance takes exactly the time it was\n * given, for the reason spelt out on `LineChart.enter`.\n */\n/**\n * The signals the bar chart's own commands move, as props types.\n *\n * Written out rather than reusing `BarChartProps` because these are signals of\n * the chart's own — how far the columns have risen, and which category is lit —\n * rather than part of what an author states about it. Same reason the line chart\n * types its `RegionState` separately.\n */\ntype CategorySpotlight = { spotlight: number | null; spotlightReveal: number }\ntype BarGrowth = { growth: number }\n\nconst AXES_SHARE = 0.35\nconst BARS_SHARE = 0.5\nconst LEGEND_SHARE = 0.15\n\n/**\n * A grouped or stacked bar chart, composed from independent parts rather than\n * one monolithic draw — the sibling of the {@link LineChart} family, built the\n * same way and out of several of the same pieces:\n *\n * - `ChartCanvas` — the plot panel with a horizontal grid, under everything.\n * - `VerticalAxis` — the numeric value markers, staggering in bottom-to-top.\n * - {@link CategoryAxis} — the row of names beneath the baseline, one per row of\n * the data, staggering in left-to-right.\n * - {@link ChartBars} (one per series) — that series' column in every category,\n * plus the figures printed on them.\n * - `ChartLegend` — a row of entries below the chart, one per series.\n *\n * The parts are laid out from this node's `layoutBounds` exactly as the line\n * chart's are: a left gutter for the value axis, a bottom gutter for the\n * captions, the plot filling what's left, and the legend row beneath the whole\n * thing. Every child's position and size is bound reactively, so the chart\n * re-flows if its box changes, and the bars read the same resolved scale the\n * axis does, so a column's end lands exactly on its marker.\n *\n * **Resting state is the finished chart** — the same bargain the line chart\n * makes: the studio paints a node's Initial state on a paused canvas, so a chart\n * that only appeared once a command had run would be edited blind. {@link enter}\n * arms every part back to its pre-entrance state before animating.\n *\n * **Zero is always on the scale** (see `barValueExtent`), and the columns\n * grow about it rather than about the plot floor, so a negative value draws\n * downward out of the same `growth` signal that draws a positive one up.\n *\n * Past the entrance, two things move the chart:\n * {@link showSeries}/{@link hideSeries}/{@link spotlightSeries} pick out a\n * *series* — and hiding one **reflows the group**, the remaining columns\n * widening into the space as it goes, rather than leaving a hole where it was —\n * while {@link spotlightCategory}/{@link clearCategory} pick out a *column of\n * the table*, dimming every other category and its caption with it.\n */\n@node({\n key: \"barChart\",\n parentKey: \"node\",\n forkable: true,\n layout: {\n children: \"freeform\",\n defaultWidthMode: \"fixed\",\n defaultHeightMode: \"fixed\",\n acceptsChildren: true,\n },\n seed: {\n width: 760,\n height: 460,\n },\n})\nexport class BarChart extends Node2D<BarChartProps> {\n @property({ default: () => [] as DataRecord[] }) declare data: DataRecord[]\n @property({ default: \"\" }) declare categoryField: string\n @property({ default: () => [] as BarSeries[] }) declare series: BarSeries[]\n @property({ default: false }) declare stacked: boolean\n /**\n * The value axis's domain (label/range/ticks), tweenable and mappable exactly\n * as the line chart's axes are: the mapper merges a partial input onto the\n * previous resolved domain and the tween interpolates numerically, so\n * `chart.to({ data: { valueAxis: { range: [0, 10] } }, duration: 1 })` smoothly\n * rescales every column. The axis *label* text is captured once at construction.\n */\n @property({\n default: {},\n mapper: resolveAxisDomain,\n tween: lerpAxisDomain,\n })\n declare valueAxis: AxisDomainConfig\n @property({\n default: {},\n mapper: resolveAxisDomain,\n tween: lerpAxisDomain,\n })\n declare categoryAxis: AxisDomainConfig\n @property({\n default: DEFAULT_BAR_CHART_THEME,\n mapper: resolveBarChartTheme,\n tween: lerpBarChartTheme,\n })\n declare theme: BarChartTheme\n\n /**\n * Global rise of every column in `[0, 1]`, measured about the baseline: 0\n * collapses them all onto it, 1 draws them at full length. Each\n * {@link ChartBars}' own `growth` is bound to this, so tweening this one\n * signal raises the whole chart in proportion.\n */\n @property({ default: 1 }) declare growth: number\n\n /**\n * How much of the slot each series currently claims, one entry per series.\n *\n * A *weight* rather than a boolean, because that is what makes hiding a series\n * an animation instead of a jump: the grouped columns divide the slot in\n * proportion to these, so tweening one to 0 has the remaining columns widen\n * into the space as it shrinks away, and a stacked segment's contribution to\n * the stack fades out with it. Seeded from each series' `enabled` field.\n */\n @property({ default: () => [] as number[] }) declare weights: number[]\n\n /**\n * The category currently spotlit, or `null` for none. Held as a plain reactive\n * value rather than a tweened one: the *choice* snaps, and the animation is\n * {@link spotlightReveal} dimming everything else.\n */\n @property({ default: null }) declare spotlight: number | null\n\n /** How far the category spotlight is drawn in, `[0, 1]`. */\n @property({ default: 0 }) declare spotlightReveal: number\n\n // ---- Part handles (built in the constructor) --------------------------\n private vAxis: VerticalAxis\n private cAxis: CategoryAxis\n private legend: ChartLegend | null = null\n private barSets: ChartBars[] = []\n\n /**\n * `this.theme` read back through its *resolved* shape — the same\n * loose-declared/resolved-stored idiom as a shape's `fill`/`stroke`, and as\n * the line chart's own theme.\n */\n private get resolvedTheme(): BarChartConfig {\n return this.theme as unknown as BarChartConfig\n }\n\n // ---- Derived scale ----------------------------------------------------\n\n /** Fallback marker count when the value axis states no ticks. */\n private static readonly DEFAULT_MARKERS = 5\n\n private get valueScale(): Range {\n const divisions =\n (this.valueAxis.ticks?.length ?? BarChart.DEFAULT_MARKERS) - 1\n return resolveBarScale(\n this.data,\n this.series,\n this.stacked,\n this.valueAxis.range,\n divisions\n )\n }\n\n /**\n * A value written down as this chart's value axis says — the axis ticks and\n * the figures printed on the columns both read through here, so the two\n * cannot disagree about what a number looks like.\n */\n private valueLabel(value: number): string {\n const pattern = this.valueAxis.format\n return pattern === undefined\n ? formatMarker(value)\n : formatNumber(value, pattern)\n }\n\n /** The value axis ticks: the explicit ones, else five across the scale. */\n private get resolvedMarkers(): number[] {\n return (\n this.valueAxis.ticks ??\n markerValues(this.valueScale, BarChart.DEFAULT_MARKERS - 1)\n )\n }\n\n // ---- Plot geometry, all derived from layoutBounds -----------------------\n\n private get hasValueLabel(): boolean {\n return this.valueAxis.label !== \"\"\n }\n private get hasCategoryLabel(): boolean {\n return this.categoryAxis.label !== \"\"\n }\n\n private get leftInset(): number {\n return Y_AXIS_GUTTER + (this.hasValueLabel ? Y_LABEL_GUTTER : 0)\n }\n private get bottomInset(): number {\n return X_AXIS_GUTTER + (this.hasCategoryLabel ? X_LABEL_GUTTER : 0)\n }\n\n /** The plot rectangle in node space: `{ left, right, top, bottom }` (y-up). */\n private plotBox(): {\n left: number\n right: number\n top: number\n bottom: number\n } {\n const W = this.layoutBounds.width\n const H = this.layoutBounds.height\n const areaTop = H / 2\n const areaBottom = -H / 2 + (this.legend ? LEGEND_GUTTER : 0)\n const left = -W / 2 + this.leftInset\n const right = W / 2 - RIGHT_GUTTER\n const top = areaTop - TOP_HEADROOM\n const bottom = areaBottom + this.bottomInset\n return { left, right, top, bottom }\n }\n\n /** Width and height of the plot, in px. */\n private plotSize(): { w: number; h: number } {\n const { left, right, top, bottom } = this.plotBox()\n return { w: right - left, h: top - bottom }\n }\n\n /** Map a value onto the plot's **centred** pixel space (origin at its centre, y-up). */\n private valueToY(value: number): number {\n const [min, max] = this.valueScale\n const { h } = this.plotSize()\n if (max <= min) return -h / 2\n return ((value - min) / (max - min) - 0.5) * h\n }\n\n /**\n * Pixel y of the line every column grows from.\n *\n * Zero, clamped onto the scale — so an author who *did* crop the axis to\n * `[40, 100]` gets columns rising from the plot floor rather than from a\n * baseline off the bottom of the panel.\n */\n private get baselinePx(): number {\n const [min, max] = this.valueScale\n return this.valueToY(Math.min(Math.max(0, min), max))\n }\n\n // ---- Slot geometry ----------------------------------------------------\n\n /** Width of one category's slot across the plot. */\n private get slotWidth(): number {\n const n = this.data.length\n return n > 0 ? this.plotSize().w / n : 0\n }\n\n /** Centre x of category `i`'s slot, in the plot's centred space. */\n private slotCenter(index: number): number {\n return -this.plotSize().w / 2 + this.slotWidth * (index + 0.5)\n }\n\n /** The part of a slot the columns fill — the rest is the channel between categories. */\n private get slotInner(): number {\n return this.slotWidth * clampUnit(this.resolvedTheme.bar.slotFill)\n }\n\n /** Series `i`'s live share of the slot; 0 once it has been hidden. */\n private weightAt(index: number): number {\n return Math.max(0, this.weights[index] ?? 1)\n }\n\n /** Total live share across the series — the slot's inner width divides by this. */\n private get totalWeight(): number {\n let sum = 0\n for (let i = 0; i < this.series.length; i++) sum += this.weightAt(i)\n return sum > 0 ? sum : 1\n }\n\n /** Live share of every series *before* `index`, for the grouped offset. */\n private weightBefore(index: number): number {\n let sum = 0\n for (let i = 0; i < index; i++) sum += this.weightAt(i)\n return sum\n }\n\n /**\n * Series `index`'s columns, mapped into the plot's centred pixel space.\n *\n * Both modes come out of one walk over the rows: a grouped series divides the\n * slot with its siblings and every column runs from zero, a stacked one takes\n * the whole slot and each column runs from the running total below it. The\n * live {@link weights} scale the grouped widths and the stacked contributions\n * alike, which is what lets `hideSeries` reflow rather than punch a hole.\n */\n private seriesBars(index: number): BarGeometry[] {\n const series = this.series[index]\n if (!series) return []\n const stacked = this.stacked\n const inner = this.slotInner\n const gap = this.resolvedTheme.bar.groupGap\n const unit = inner / this.totalWeight\n const weight = this.weightAt(index)\n\n return this.data.map((row, rowIndex) => {\n // The stack is built from the *live* contributions, so a series fading out\n // lowers the segments above it as it goes instead of dropping them by a\n // step when it finally switches off. A grouped row needs no such scaling —\n // there its weight moves the column's width, not its length.\n const spans = barSpans(\n stacked ? this.weightedRow(row) : row,\n this.series,\n stacked\n )\n const span = spans[index] ?? { from: 0, to: 0, value: 0 }\n const cap = stacked ? stackCapIndex(row, this.series) : index\n\n const slotLeft = this.slotCenter(rowIndex) - inner / 2\n const x = stacked\n ? this.slotCenter(rowIndex)\n : slotLeft + unit * this.weightBefore(index) + (unit * weight) / 2\n const width = stacked\n ? inner\n : Math.max(0, unit * weight - (this.series.length > 1 ? gap : 0))\n\n return {\n x,\n width,\n from: this.valueToY(span.from),\n to: this.valueToY(span.to),\n round: cap === index,\n // The figure printed is the row's own value, not the weighted one — a\n // series on its way out should not be seen counting down.\n value: num(row, series.field),\n }\n })\n }\n\n /**\n * `row` with each series' value scaled by that series' live {@link weights}\n * entry — what the stacked spans are measured from.\n */\n private weightedRow(row: DataRecord): DataRecord {\n const scaled: DataRecord = { ...row }\n this.series.forEach((s, i) => {\n const value = row[s.field]\n if (typeof value === \"number\") scaled[s.field] = value * this.weightAt(i)\n })\n return scaled\n }\n\n /**\n * How far each category is dimmed by the spotlight, one entry per row —\n * 1 while nothing is spotlit, falling to the theme's `dimOpacity` for every\n * category but the chosen one as the reveal climbs.\n */\n private categoryDims(): number[] {\n const focus = clampUnit(this.spotlightReveal)\n const dim = this.resolvedTheme.spotlight.dimOpacity\n const chosen = this.spotlight\n return this.data.map((_, i) =>\n focus <= 0 || chosen === i ? 1 : 1 - (1 - dim) * focus\n )\n }\n\n // ---- Build ------------------------------------------------------------\n\n constructor(props?: NodeConfig<BarChart, BarChartProps>) {\n super(props)\n\n // The whole chart is composed from plain props/`data` — no context needed,\n // so the parts are built here (super() applied the props above). Everything\n // a *leaf* reads live in `renderSelf` — each series' geometry, paint and\n // dim, the panel's fill, the border's stroke — is passed as a closure rather\n // than a snapshot, so `chart.to({ theme })` and\n // `chart.to({ valueAxis: { range } })` genuinely animate the drawn chart.\n const enabled = this.series.map((s) => s.enabled ?? true)\n this.weights = enabled.map((on) => (on ? 1 : 0))\n const showLegend = props?.showLegend !== false\n const showValues = props?.showValues === true\n\n this.vAxis = new VerticalAxis({\n range: () => this.valueScale,\n markers: () => this.resolvedMarkers,\n axis: () => this.resolvedTheme.yAxis,\n label: this.valueAxis.label ?? \"\",\n width: \"hug\",\n height: \"fill\",\n })\n this.vAxis.labelFor = (value) => this.valueLabel(value)\n\n this.cAxis = new CategoryAxis({\n categories: barCategories(this.data, this.categoryField),\n axis: () => this.resolvedTheme.xAxis,\n label: this.categoryAxis.label ?? \"\",\n dims: () => this.categoryDims(),\n width: \"fill\",\n height: \"hug\",\n })\n\n this.barSets = this.series.map(\n (s, i) =>\n new ChartBars({\n bars: () => this.seriesBars(i),\n baseline: () => this.baselinePx,\n fill: () => resolveBarFill(this.resolvedTheme.seriesFills, s, i),\n barStroke: () => this.resolvedTheme.bar.stroke,\n barShadow: () => this.resolvedTheme.bar.shadow,\n cornerRadius: () => this.resolvedTheme.bar.cornerRadius,\n growth: () => this.growth,\n dims: () => this.categoryDims(),\n showValues,\n valueStyle: this.resolvedTheme.valueStyle,\n enabled: enabled[i],\n })\n )\n // The figures on the columns are the same numbers the value axis ticks, so\n // they read through the same format.\n for (const set of this.barSets) set.labelFor = (value) => this.valueLabel(value)\n\n if (showLegend) {\n this.legend = new ChartLegend({\n series: this.series,\n swatches: () =>\n this.series.map((s, i) =>\n resolveBarFill(this.resolvedTheme.seriesFills, s, i)\n ),\n legend: () => this.resolvedTheme.legend,\n })\n }\n\n const plot = new Rect({\n clip: true,\n children: [\n new ChartCanvas({\n fill: () => this.resolvedTheme.plotArea.fill,\n shadow: () => this.resolvedTheme.plotArea.shadow,\n xGridStroke: () => this.resolvedTheme.xAxis.lineStyle,\n yGridStroke: () => this.resolvedTheme.yAxis.lineStyle,\n // No vertical rules: the horizontal axis is categorical, so there is\n // no numeric position along it for a grid line to mark. The channel\n // between slots does that job, and better.\n xMarkers: [],\n yMarkers: () => this.resolvedMarkers,\n xExtent: () => [0, 1] as Range,\n yExtent: () => this.valueScale,\n }),\n ...this.barSets,\n new ChartBorder({\n stroke: () => this.resolvedTheme.plotArea.stroke,\n }),\n ],\n })\n\n // Invisible twin of the value axis, reserving the same left gutter under the\n // plot so the category row starts at the plot's left edge rather than the\n // chart's — and so a caption lands under the columns it names.\n const gutterSpacer = new VerticalAxis({\n opacity: 0,\n range: () => this.valueScale,\n markers: () => this.resolvedMarkers,\n axis: () => this.resolvedTheme.yAxis,\n label: this.valueAxis.label ?? \"\",\n width: \"hug\",\n height: 0,\n })\n\n const below: Node2D[] = [this.cAxis]\n if (this.legend) below.push(this.legend)\n\n this.add(\n new Rect({ flow: \"vertical\",\n width: \"fill\",\n height: \"fill\",\n children: [\n new Rect({ flow: \"horizontal\",\n height: \"fill\",\n width: \"fill\",\n children: [this.vAxis, plot],\n }),\n new Rect({ flow: \"horizontal\",\n width: \"fill\",\n children: [\n gutterSpacer,\n new Rect({ flow: \"vertical\", width: \"fill\", gap: 20, children: below }),\n ],\n }),\n ],\n })\n )\n }\n\n // ---- Orchestrated entrance --------------------------------------------\n\n /**\n * Prime every part at its pre-entrance state — axis markers and captions\n * shifted and transparent, columns collapsed onto the baseline, legend dropped\n * and transparent.\n *\n * Called by {@link enter} before its first frame so nothing flashes ahead of\n * its beat. Idempotent, and public so a scene can hide a chart ahead of time\n * without starting the entrance.\n */\n arm(): void {\n this.vAxis.arm()\n this.cAxis.arm()\n this.growth = 0\n this.legend?.set({ opacity: 0, y: -LEGEND_SLIDE })\n }\n\n /**\n * Animate the whole chart in: the two axes stagger their markers and captions\n * in, the columns rise from the baseline, and the legend fades up last.\n *\n * `duration` is the **whole** entrance. The phases are shares of it, not\n * multiples — a command that declares 1.4 seconds has to take 1.4 seconds,\n * because that is the slot the timeline lays out for it, the length its\n * parallel siblings are timed against, and its contribution to the scene's own\n * duration.\n */\n @command()\n enter(args: CommandArgs<Record<string, never>> & { duration: number }): Seekable {\n const { duration } = args\n const easing = args.easing ?? easeOut()\n this.arm()\n\n return sequenceOf(\n together(\n this.vAxis.enter({ data: { slide: 24 }, duration: duration * AXES_SHARE, easing }),\n this.cAxis.enter({ data: { slide: 24 }, duration: duration * AXES_SHARE, easing })\n ),\n this.riseBars(duration * BARS_SHARE, easing),\n ...(this.legend\n ? [this.legend.to({ data: { opacity: 1, y: 0 }, duration: duration * LEGEND_SHARE, easing })]\n : [])\n )\n }\n\n /** Tween {@link growth} `0 → 1`, raising every column about the baseline. */\n private riseBars(\n duration: number,\n easing: EasingFunction\n ): Command<BarGrowth> {\n // No priming to 0 first: the value at `t` is a function of `t`, so a repeat\n // call re-raises by definition rather than by having reset something.\n return this.command<BarGrowth>((t) => ({ growth: t }), duration, easing)\n }\n\n /**\n * Show series `index`: bring its columns back, the group reflowing to make\n * room as they arrive, in lockstep with its legend entry growing in.\n */\n @command({ args: [{ key: \"seriesIndex\", kind: \"series\", default: 0, min: 0, step: 1 }] })\n showSeries(args: CommandArgs<{ seriesIndex: number }>): Seekable {\n const { seriesIndex = 0 } = args.data ?? {}\n return this.toggleSeries(seriesIndex, 1, args.duration ?? 0.35, args.easing ?? easeOut())\n }\n\n /**\n * Hide series `index`: its columns narrow away (or, stacked, sink out of the\n * stack) while the rest of the group widens into the space, its legend entry\n * shrinking beside them — the mirror of {@link showSeries}.\n */\n @command({ args: [{ key: \"seriesIndex\", kind: \"series\", default: 0, min: 0, step: 1 }] })\n hideSeries(args: CommandArgs<{ seriesIndex: number }>): Seekable {\n const { seriesIndex = 0 } = args.data ?? {}\n return this.toggleSeries(seriesIndex, 0, args.duration ?? 0.35, args.easing ?? easeOut())\n }\n\n /**\n * Move series `index`'s slot share to `target`, with its legend entry.\n *\n * One method for both directions, so the pair cannot drift apart. `enabled`\n * rides in the same value as the weight rather than being set before or after,\n * which is what makes arriving at a time backwards show what arriving forwards\n * does.\n */\n private toggleSeries(\n index: number,\n target: number,\n duration: number,\n easing: EasingFunction\n ): Seekable {\n const bars = this.barSets[index]\n if (!bars) return hold(duration)\n\n return together(\n this.tweenWeight(index, target, duration, easing),\n drive(duration, (t) => {\n const share = t >= 1 ? target : Math.max(target, 1 - t)\n bars.set({ enabled: target > 0 || share > 0 })\n }),\n ...(this.legend\n ? [\n target > 0\n ? this.legend.showSeries({ data: { index }, duration, easing })\n : this.legend.hideSeries({ data: { index }, duration, easing }),\n ]\n : [])\n )\n }\n\n /**\n * Fade out every other series — its columns **and its legend entry** — to\n * focus attention on `index`, which comes back to full. Nothing moves, so the\n * group doesn't reflow.\n */\n @command({ args: [{ key: \"seriesIndex\", kind: \"series\", default: 0, min: 0, step: 1 }] })\n spotlightSeries(args: CommandArgs<{ seriesIndex: number }>): Seekable {\n const { seriesIndex = 0 } = args.data ?? {}\n const duration = args.duration ?? 0.35\n const easing = args.easing ?? easeOut()\n const dim = this.resolvedTheme.spotlight.dimOpacity\n return together(\n ...this.barSets.map((bars, i) =>\n bars.to({ data: { opacity: i === seriesIndex ? 1 : dim }, duration, easing })\n ),\n ...(this.legend\n ? [this.legend.spotlightSeries({ data: { index: seriesIndex }, duration, easing })]\n : [])\n )\n }\n\n /** Ease series `index`'s slot share toward `target`, reflowing the group. */\n private tweenWeight(\n index: number,\n target: number,\n duration: number,\n easing: EasingFunction\n ): Seekable {\n // Read through `weightAt` rather than off the array, so a chart whose stored\n // weights are short (or absent) still tweens from the 1 it is drawing at.\n const from = this.series.map((_, i) => this.weightAt(i))\n const start = from[index] ?? 1\n if (start === target) return hold(duration)\n\n return drive(duration, (t) => {\n const next = [...from]\n next[index] = start + (target - start) * easing(t)\n this.weights = next\n })\n }\n\n // ---- Per-category ------------------------------------------------------\n\n /**\n * Spotlight one **category** — one column of the table rather than one series:\n * every other category's bars and its caption fade to the theme's\n * `dimOpacity`, and the chosen one stays full.\n *\n * The counterpart of the line chart's `spotlightRegion`, and the same shape of\n * thing: it picks out a slice of the plot without changing what is drawn.\n * Calling it while another category is already lit moves the focus directly —\n * only the dimming changes, so there is nothing to retract first.\n */\n @command({ args: [{ key: \"categoryIndex\", kind: \"number\", default: 0, min: 0, step: 1 }] })\n spotlightCategory(args: CommandArgs<{ categoryIndex: number }>): Command<CategorySpotlight> {\n const { categoryIndex = 0 } = args.data ?? {}\n const duration = args.duration ?? 0.4\n const easing = args.easing ?? easeOut()\n if (categoryIndex < 0 || categoryIndex >= this.data.length) return this.holdSpotlight(duration)\n const from = this.spotlightReveal\n // Which category is lit is part of the value, set from the first frame: the\n // focus moves directly, so unlike the line chart's region there is nothing to\n // retract before it.\n return this.command<CategorySpotlight>(\n (t) => ({ spotlight: categoryIndex, spotlightReveal: from + (1 - from) * t }),\n duration,\n easing\n )\n }\n\n /**\n * Drop the category spotlight, bringing every category back to full. A no-op\n * when nothing is lit — but it still takes its slot on the timeline, for the\n * reason `LineChart.clearRegion` does.\n */\n @command()\n clearCategory(args: CommandArgs<Record<string, never>>): Command<CategorySpotlight> {\n const duration = args.duration ?? 0.4\n const easing = args.easing ?? easeOut()\n const from = this.spotlightReveal\n if (from === 0) return this.holdSpotlight(duration)\n\n // The lit category is held until the fade lands — cleared at `t === 1` rather\n // than up front, or there would be nothing left to fade.\n return this.command<CategorySpotlight>(\n (t) =>\n t >= 1\n ? { spotlight: null, spotlightReveal: 0 }\n : { spotlightReveal: from * (1 - t) },\n duration,\n easing\n )\n }\n\n /** The no-op both of the above return — see {@link hold}. */\n private holdSpotlight(duration: number): Command<CategorySpotlight> {\n return this.command<CategorySpotlight>(() => ({}), duration)\n }\n}\n", "import {\n command,\n Node2D,\n Rect,\n Text,\n easeOut,\n property,\n insetsOps,\n textOps,\n type CommandArgs,\n type NodeConfig,\n type Node2DProps,\n} from \"@motionscript/core\"\n\nimport {\n DEFAULT_X_AXIS,\n clampUnit,\n enterBudget,\n type AxisConfig,\n} from \"../kit/shared\"\nimport { at, compose, hold, type Seekable } from \"@motionscript/core/component\"\n\nexport interface CategoryAxisProps extends Node2DProps {\n /** One caption per category slot, left to right — the chart's rows, in order. */\n categories: string[]\n /** Caption/title text style and gutters — see {@link AxisConfig}. */\n axis: AxisConfig\n /**\n * Caption drawn centred in the gutter beneath the category row. Empty string\n * (the default) hides it.\n */\n label: string\n /**\n * Per-category opacity multiplier — how the chart's category spotlight reaches\n * the captions, so the name under a dimmed column dims with it. Missing\n * entries read as 1.\n */\n dims: number[]\n}\n\n/** How far a caption starts above its slot when it enters, in px. */\nconst SLIDE = 24\n\n/**\n * The **categorical** horizontal axis of a {@link BarChart}: a row of\n * {@link Text} captions, one per row of the data, each centred on its slot\n * beneath the baseline.\n *\n * Not a variant of the line chart's `HorizontalAxis` and deliberately not\n * folded into it: that axis places markers by *value* across a numeric range,\n * this one divides its width into equal slots and centres a name in each. The\n * two look alike on screen and share nothing underneath — one reads a scale,\n * the other counts rows.\n *\n * The slot arithmetic is the same as the plot's, because this node is laid out\n * to exactly the plot's width (the chart reserves the axis gutter with an\n * invisible twin of the value axis, as the line chart does), so a caption lands\n * under the columns it names without either side being told about the other.\n *\n * {@link enter} staggers the captions in left-to-right, each lifted a little\n * above its slot and transparent; the resting state is the landed one, so a\n * chart that is never told to enter still reads as finished.\n */\nexport class CategoryAxis extends Node2D<CategoryAxisProps> {\n @property({ default: () => [] as string[] }) declare categories: string[]\n @property({ default: DEFAULT_X_AXIS }) declare axis: AxisConfig\n @property({ default: \"\" }) declare label: string\n @property({ default: () => [] as number[] }) declare dims: number[]\n\n /** One caption per category, ordered left → right, for the staggered enter. */\n private captionNodes: Text[] = []\n /** The axis title, or null when `label` is empty. */\n private labelNode: Text | null = null\n\n /** Centre x of slot `i`, in the node's centred space. */\n private slotCenter(index: number): number {\n const n = this.categories.length\n if (n <= 0) return 0\n const width = this.layoutBounds.width\n return -width / 2 + (width / n) * (index + 0.5)\n }\n\n constructor(props?: NodeConfig<CategoryAxis, CategoryAxisProps>) {\n super(props)\n\n const tickPad = insetsOps.resolve(this.axis.tickPadding).top\n const titlePad = insetsOps.resolve(this.axis.titlePadding).top\n\n // Each caption sits in a hug box carrying the spotlight dim, so the\n // entrance can animate the text's own opacity without the two fighting over\n // one value — opacity multiplies down the tree, which is exactly the\n // composition wanted here.\n const slots = this.categories.map((caption, i) => {\n const text = new Text({\n ...textOps.styleProps(this.axis.labelStyle),\n text: caption,\n })\n this.captionNodes.push(text)\n return new Rect({\n width: \"hug\",\n height: \"hug\",\n flow: \"freeform\",\n opacity: () => clampUnit(this.dims[i] ?? 1),\n x: () => this.slotCenter(i),\n children: [text],\n })\n })\n\n const children: Node2D[] = [\n new Rect({\n width: \"fill\",\n height: \"hug\",\n padding: { top: tickPad },\n children: slots,\n }),\n ]\n if (this.label !== \"\") {\n this.labelNode = new Text({\n ...textOps.styleProps(this.axis.titleStyle),\n text: this.label,\n })\n children.push(this.labelNode)\n }\n\n this.add(new Rect({ flow: \"vertical\", gap: titlePad, height: \"hug\", width: \"fill\", children }))\n }\n\n /**\n * Primes every caption (and the title) at its pre-entrance state: lifted above\n * its slot and transparent. Idempotent, so the chart can arm its whole\n * composition before the first frame is drawn.\n */\n arm(slide = SLIDE): void {\n for (const caption of this.captionNodes) caption.set({ opacity: 0, y: slide })\n this.labelNode?.set({ opacity: 0, y: slide })\n }\n\n /**\n * Slide + fade every caption in from above, staggered left-to-right, then\n * bring the title down behind them.\n *\n * `args.duration` is the **whole** entrance — the stagger and the title are\n * carved out of it rather than added on top. See {@link enterBudget}.\n *\n * `args.data.stagger` is seconds between successive captions (derived when\n * omitted); `args.data.slide` is the starting upward offset in px (node space\n * is y-up, so a positive start slides *down* into place).\n */\n @command()\n enter(args: CommandArgs<{ stagger?: number; slide?: number }> & { duration: number }): Seekable {\n const { stagger, slide = SLIDE } = args.data ?? {}\n const easing = args.easing ?? easeOut()\n const nodes = this.captionNodes\n if (nodes.length === 0) return hold(args.duration)\n const { step, per, caption } = enterBudget(\n args.duration,\n nodes.length,\n this.labelNode !== null,\n stagger\n )\n\n this.arm(slide)\n\n // The stagger was `wait(i * step)` inside a generator, so its offsets only\n // existed while it ran. They were always `i * step`; placed, the same\n // entrance can be asked what it looks like partway through.\n const lastCaptionEnds = (nodes.length - 1) * step + per\n return compose([\n ...nodes.map((node, i) =>\n at(i * step, node.to({ data: { opacity: 1, y: 0 }, duration: per, easing }))\n ),\n ...(this.labelNode\n ? [at(lastCaptionEnds, this.labelNode.to({ data: { opacity: 1, y: 0 }, duration: caption, easing }))]\n : []),\n ])\n }\n}\n", "import {\n command,\n type AssetScope,\n Graphics2D,\n Node2D,\n Text,\n easeOut,\n fillOps,\n shadowOps,\n strokeOps,\n property,\n textOps,\n type Command,\n type CommandArgs,\n type Fill,\n type NodeConfig,\n type Node2DProps,\n type RenderContext2D,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n} from \"@motionscript/core\"\n\nimport { clampUnit, formatMarker } from \"../kit/shared\"\nimport type { MarkerLabel } from \"../kit/vertical-axis\"\nimport { DEFAULT_BAR_CHART_THEME } from \"./shared\"\nimport { declarePaints } from \"@motionscript/core/component\"\n\n/**\n * One drawn column, already mapped into the plot's **centred pixel space**\n * (origin at the plot centre, y-up) by the parent {@link BarChart}.\n *\n * `from`/`to` are the two ends of the column rather than a base and a height,\n * for the same reason {@link BarSpan} is a span: it is the one shape that fits a\n * grouped bar, a stacked segment and a negative value without a special case.\n */\nexport interface BarGeometry {\n /** Centre x of the column. */\n x: number\n /** Column width in px. */\n width: number\n /** Pixel y of the column's base — the baseline when grouped, the running stack top when stacked. */\n from: number\n /** Pixel y of its far end, at full {@link ChartBars.growth}. */\n to: number\n /** Whether the far end takes the theme's corner radius (the stack's cap). */\n round: boolean\n /** The value this column plots, for the figure printed on it. */\n value: number\n}\n\n/** Gap between a grouped column's end and the figure printed over it, in px. */\nconst VALUE_GAP = 10\n/** A segment shorter than this many multiples of the type size prints no figure. */\nconst VALUE_FIT = 1.6\n/** Fallback type size when the theme's value style leaves it unset or autofit. */\nconst VALUE_SIZE = 24\n\nexport interface ChartBarsProps extends Node2DProps {\n /** This series' columns, one per row of the chart's data. */\n bars: BarGeometry[]\n /** Pixel y of value 0 — where every column grows *from*. */\n baseline: number\n /** Column paint — this series' own, or the theme palette's entry for its index. */\n fill: Fill\n /** Optional outline on every column. */\n barStroke: Stroke\n /** Optional drop shadow on every column. */\n barShadow: Shadow\n /** Radius of the far-end corners, in px. */\n cornerRadius: number\n /**\n * Rise of every column in `[0, 1]`, measured **about the baseline**: 0\n * collapses them all onto it, 1 draws them at full length.\n *\n * Scaling about the baseline rather than about each column's own base is what\n * keeps a stack flush mid-animation — every segment's base and end travel by\n * the same factor, so the segments stay stacked instead of the upper ones\n * floating above the still-growing lower ones. It also means a negative\n * column grows *down* out of the same signal.\n */\n growth: number\n /**\n * Per-column opacity multiplier, by row index — how the chart's category\n * spotlight reaches a single column without touching the series it belongs\n * to. Missing entries read as 1.\n */\n dims: number[]\n /** Whether the figure each column plots is printed on it. */\n showValues: boolean\n /** Type style for those figures. */\n valueStyle: TextStyle\n /**\n * Whether this series draws at all — the wholesale switch behind\n * `BarChart.showSeries`/`hideSeries`, alongside the slot weight that animates\n * the reflow. Off skips the columns *and* their figures.\n */\n enabled: boolean\n}\n\n/**\n * One **series** of a {@link BarChart}, drawn as one command list: its column in\n * every category, plus the figures printed on them.\n *\n * A node per series rather than a node per bar, deliberately — it is the same\n * unit `ChartLine` is, which is what lets `showSeries`/`hideSeries`/\n * `spotlightSeries` mean exactly the same thing on a bar chart as on a line\n * chart: enable the node, or tween its opacity. The columns' *positions* come\n * from the parent (it owns the slot arithmetic both modes share), so this part\n * only has to know how to draw a span and grow it.\n *\n * The figures are real {@link Text} children rather than glyphs in the command\n * list, so they take the theme's type style, are measured by the layout engine,\n * and ride the node's own opacity — which is why hiding the series takes its\n * figures with it for free.\n */\nexport class ChartBars extends Node2D<ChartBarsProps> {\n @property({ default: () => [] as BarGeometry[] }) declare bars: BarGeometry[]\n @property({ default: 0 }) declare baseline: number\n @property({\n default: [],\n mapper: fillOps.resolve,\n tween: fillOps.lerp,\n })\n declare fill: Fill\n @property({\n default: DEFAULT_BAR_CHART_THEME.bar.stroke,\n mapper: strokeOps.resolve,\n tween: strokeOps.lerp,\n })\n declare barStroke: Stroke\n @property({\n default: DEFAULT_BAR_CHART_THEME.bar.shadow,\n mapper: shadowOps.resolve,\n tween: shadowOps.lerp,\n })\n declare barShadow: Shadow\n @property({ default: DEFAULT_BAR_CHART_THEME.bar.cornerRadius })\n declare cornerRadius: number\n @property({ default: 1 }) declare growth: number\n @property({ default: () => [] as number[] }) declare dims: number[]\n @property({ default: false }) declare showValues: boolean\n @property({ default: DEFAULT_BAR_CHART_THEME.valueStyle })\n declare valueStyle: TextStyle\n @property({ default: true }) declare enabled: boolean\n\n /**\n * How a column's figure is written down — the same seam the two axes carry,\n * so a chart's value axis and the numbers printed on its bars can't end up\n * saying `$1,200` in one place and `1200` in the other.\n */\n labelFor: MarkerLabel = formatMarker\n\n constructor(props?: NodeConfig<ChartBars, ChartBarsProps>) {\n super(props)\n\n // One caption per column, built once (the row count is fixed at\n // construction, like an axis's marker count) and bound to the live geometry\n // so it travels with its column as the chart grows, rescales or reflows.\n if (!this.showValues) return\n for (let i = 0; i < this.bars.length; i++) {\n this.add(\n new Text({\n ...textOps.styleProps(this.valueStyle),\n text: () => this.captionAt(i),\n x: () => this.bars[i]?.x ?? 0,\n y: () => this.captionY(i),\n opacity: () => this.dimAt(i),\n })\n )\n }\n }\n\n /**\n * How far column `i` is dimmed — the chart's category spotlight, and the\n * series switch on top of it.\n *\n * The captions read this rather than being hidden by `renderSelf`'s guard:\n * they are real child nodes, so a `return` in the draw pass would leave them\n * on screen over a series that isn't there.\n */\n private dimAt(index: number): number {\n if (!this.enabled) return 0\n return clampUnit(this.dims[index] ?? 1)\n }\n\n /** The two ends of column `i` at the current {@link growth}. */\n private endsAt(index: number): { base: number; end: number } | null {\n const bar = this.bars[index]\n if (!bar) return null\n const g = clampUnit(this.growth)\n const base = this.baseline + (bar.from - this.baseline) * g\n const end = this.baseline + (bar.to - this.baseline) * g\n return { base, end }\n }\n\n /** The type size the figures are set at, guarding the autofit/unset cases. */\n private get valueSize(): number {\n const size = this.valueStyle.fontSize\n return typeof size === \"number\" ? size : VALUE_SIZE\n }\n\n /**\n * The figure printed on column `i`, or `\"\"` where there is no room for it.\n *\n * Blanking the text rather than dropping the node keeps the caption list\n * index-aligned with the columns, so a chart that rescales into more room\n * simply starts printing again.\n */\n private captionAt(index: number): string {\n const bar = this.bars[index]\n if (!bar || bar.value === 0) return \"\"\n // An interior segment prints inside itself, so it has to be tall enough to\n // hold a line of type; a grouped column prints in the headroom above its\n // end, where the only constraint is the plot's own top.\n if (this.insideAt(index)) {\n const ends = this.endsAt(index)\n if (!ends) return \"\"\n if (Math.abs(ends.end - ends.base) < this.valueSize * VALUE_FIT) return \"\"\n }\n return this.labelFor(bar.value)\n }\n\n /** Whether column `i`'s figure sits inside the column (a stacked segment) or past its end. */\n private insideAt(index: number): boolean {\n const bar = this.bars[index]\n return bar !== undefined && Math.abs(bar.from - this.baseline) > 0.5\n }\n\n /** Where column `i`'s figure sits, in the node's centred y-up space. */\n private captionY(index: number): number {\n const ends = this.endsAt(index)\n if (!ends) return 0\n if (this.insideAt(index)) return (ends.base + ends.end) / 2\n // Text is centred on its `y`, so clear the column's end by the gap plus half\n // a line — on whichever side of the baseline the column actually reached.\n const direction = ends.end >= ends.base ? 1 : -1\n return ends.end + direction * (VALUE_GAP + this.valueSize / 2)\n }\n\n /**\n * This node extends `Node2D` rather than `ShapeNode`, so nothing declares the\n * paint props it carries — see {@link declarePaints}. The column stroke and\n * shadow are named past the conventional slots, so they are passed explicitly.\n */\n override declareAssets(assets: AssetScope): void {\n super.declareAssets(assets)\n declarePaints(this, assets, {\n fills: (this.barShadow as ShadowResolved[]).map((shadow) => shadow.fill),\n strokes: [this.barStroke as StrokeResolved[]],\n })\n }\n\n protected renderSelf(ctx: RenderContext2D): void {\n if (!this.enabled) return\n for (let i = 0; i < this.bars.length; i++) {\n const bar = this.bars[i]\n const ends = this.endsAt(i)\n if (!ends || bar.width <= 0) continue\n const height = Math.abs(ends.end - ends.base)\n if (height <= 0) continue\n\n const radius = bar.round ? Math.min(this.cornerRadius, height) : 0\n // A column per `Graphics2D` rather than one list for the series: the\n // category spotlight dims individual columns, and opacity is a property\n // of the command list rather than of a shape within it.\n const g = new Graphics2D()\n const rect = g.rect({\n x: bar.x,\n y: (ends.base + ends.end) / 2,\n width: bar.width,\n height,\n // This node's space is y-up, but the renderer's corner-radius keys are\n // labelled in canvas y-down space — so a column reaching *up* rounds\n // through the `bottom*` keys, and one reaching down through the `top*`\n // ones. Only the far end rounds, which is what makes a stack read as a\n // single capped column.\n cornerRadius:\n radius > 0\n ? ends.end >= ends.base\n ? { bottomLeft: radius, bottomRight: radius, topLeft: 0, topRight: 0 }\n : { topLeft: radius, topRight: radius, bottomLeft: 0, bottomRight: 0 }\n : 0,\n })\n rect.shadow(this.barShadow)\n rect.fill(this.fill)\n rect.stroke(this.barStroke)\n ctx.draw(g.opacity(this.dimAt(i)))\n }\n }\n\n /**\n * Raise every column from the baseline by tweening {@link growth} `0 → 1`.\n *\n * The mirror of `ChartLine.enter`: same signature, same contract that the\n * resting state is the finished one, so the chart arms this back to 0 before\n * animating rather than the node hiding itself at construction.\n */\n @command()\n enter(args: CommandArgs<Record<string, never>> & { duration: number }): Command<ChartBarsGrowth> {\n // No priming to 0 first: the value at `t` is a function of `t`, so a repeat\n // call re-raises by definition rather than by having reset something.\n return this.command<ChartBarsGrowth>((t) => ({ growth: t }), args.duration, args.easing ?? easeOut())\n }\n}\n\n/** The one signal {@link ChartBars.enter} moves. */\ntype ChartBarsGrowth = { growth: number }\n", "/**\n * Types and pure helpers specific to the {@link BarChart} family: how a row's\n * values become the spans its columns cover, how the value axis is scaled around\n * them, and the theme the bars add to the shared chart vocabulary.\n *\n * Everything a line or a pie chart would want too — what a series is, the axis\n * domain model, the theme leaf resolvers, the legend and plot-panel branches —\n * lives in `nodes/chart-kit/shared.ts` and is imported here.\n *\n * Kept renderer-agnostic (no `Node` imports) so the studio's inspector mapping\n * (`bar-chart-props.ts`) can build a partial {@link BarChartTheme} without\n * pulling the node classes in.\n */\n\nimport {\n fillOps,\n lerpNumber,\n shadowOps,\n strokeOps,\n textOps,\n type DataRecord,\n type Fill,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n} from \"@motionscript/core\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_PLOT_AREA,\n DEFAULT_SERIES_FILLS,\n DEFAULT_X_AXIS,\n DEFAULT_Y_AXIS,\n SPOTLIGHT_DIM,\n lerpAxisConfig,\n lerpLegendConfig,\n lerpPlotArea,\n lerpSeriesFills,\n niceStep,\n num,\n resolveAxisConfig,\n resolveLegendConfig,\n resolvePlotArea,\n resolveSeriesFills,\n text,\n type AxisConfig,\n type ChartSeries,\n type LegendConfig,\n type LegendTheme,\n type PlotAreaConfig,\n type Range,\n} from \"../kit/shared\"\n\n/**\n * One series in a {@link BarChart}: a column of the table drawn as one bar per\n * row — side by side with its siblings within each category's slot, or stacked\n * on top of them.\n *\n * The family-wide {@link ChartSeries} under a name that says what it draws here.\n */\nexport type BarSeries = ChartSeries\n\n/**\n * One drawn bar's span in **data units**: the value its base sits at and the\n * value its far end reaches.\n *\n * A span rather than a height because that is the one shape both modes fit. A\n * grouped bar runs from zero to its value; a stacked segment runs from the\n * running total below it to that total plus its own value. Negative values fall\n * out of the same arithmetic — the span simply runs the other way — so a chart\n * with a loss in it needs no special case, only a baseline that isn't the floor.\n */\nexport interface BarSpan {\n /** Value the bar's base sits at — 0 when grouped, the running total when stacked. */\n from: number\n /** Value its far end reaches. */\n to: number\n /** The value this bar plots — `to - from`, kept for the figure printed on it. */\n value: number\n}\n\n/**\n * The spans one row's bars cover, one per series, in series order.\n *\n * Stacking is a running sum in the order the series are listed, which is also\n * the order they're drawn: series 0 sits on the baseline and each later one\n * rides on what's below it. A zero-valued series still yields a (degenerate)\n * span so the returned list stays index-aligned with the series list.\n */\nexport function barSpans(\n row: DataRecord,\n series: BarSeries[],\n stacked: boolean\n): BarSpan[] {\n let running = 0\n return series.map((s) => {\n const value = num(row, s.field)\n if (!stacked) return { from: 0, to: value, value }\n const from = running\n running += value\n return { from, to: running, value }\n })\n}\n\n/**\n * The index of the series whose segment ends a row's stack — the one that gets\n * the rounded cap, so a stack reads as one capped column rather than a pile of\n * separate boxes. `-1` when every value in the row is zero.\n *\n * Read off the *last non-zero* series rather than the last one listed, so a\n * series that happens to be 0 in this row doesn't take the cap and leave the\n * segment below it square.\n */\nexport function stackCapIndex(row: DataRecord, series: BarSeries[]): number {\n let last = -1\n for (let i = 0; i < series.length; i++) {\n if (num(row, series[i].field) !== 0) last = i\n }\n return last\n}\n\n/** The captions beneath the baseline: one per row, read off the category column. */\nexport function barCategories(data: DataRecord[], field: string): string[] {\n return data.map((row) => text(row, field))\n}\n\n/**\n * The `[min, max]` of everything the chart draws, **zero always included**.\n *\n * Zero is in the range whatever the data says, because a bar chart's baseline is\n * zero: a scale starting at 40 draws a column three times its neighbour for a\n * 10% difference, which is the classic way to lie with one. A chart that really\n * does want a cropped axis says so with an explicit range.\n */\nexport function barValueExtent(\n data: DataRecord[],\n series: BarSeries[],\n stacked: boolean\n): Range {\n let lo = 0\n let hi = 0\n for (const row of data) {\n for (const span of barSpans(row, series, stacked)) {\n lo = Math.min(lo, span.from, span.to)\n hi = Math.max(hi, span.from, span.to)\n }\n }\n return lo < hi ? [lo, hi] : [0, 1]\n}\n\n/**\n * Effective value scale: the explicit `range`, else the data's extent nudged out\n * to tidy step bounds so the markers read as round numbers.\n *\n * `divisions` is how many segments the auto-range is stepped into (one fewer\n * than the marker count), so the ticks the axis draws land on the bounds this\n * picks.\n */\nexport function resolveBarScale(\n data: DataRecord[],\n series: BarSeries[],\n stacked: boolean,\n range: Range | undefined,\n divisions: number\n): Range {\n if (range) return range\n const [lo, hi] = barValueExtent(data, series, stacked)\n const segments = Math.max(1, Math.round(divisions))\n const step = niceStep((hi - lo) / segments || 1)\n return [Math.floor(lo / step) * step, Math.ceil(hi / step) * step]\n}\n\n// ---- Theme ---------------------------------------------------------------\n\n/** How the columns themselves are drawn and how the slot is divided up. */\nexport interface BarStyleConfig {\n /**\n * Radius of a column's far-end corners, in px. Only the outer end rounds — an\n * interior stacked segment stays square, so the stack reads as one column.\n */\n cornerRadius: number\n /**\n * Fraction of its category's slot the bars fill, `[0, 1]`. The remainder is\n * the channel between one category and the next, which is what makes a bar\n * chart read as groups rather than as a solid block.\n */\n slotFill: number\n /** Gap between the grouped bars *within* one slot, in px. Ignored when stacked. */\n groupGap: number\n /** Optional outline on every column. */\n stroke: Stroke\n /** Optional drop shadow on every column. */\n shadow: Shadow\n}\n\n/** A {@link BarChart}'s full, resolved visual theme. */\nexport interface BarChartConfig {\n /**\n * Default per-series bar paint, cycled by series index (`i % length`). A\n * series' own `fill`, when set, replaces its entry outright.\n */\n seriesFills: Fill[]\n bar: BarStyleConfig\n /** The category axis beneath the baseline. */\n xAxis: AxisConfig\n /** The numeric value axis up the left. */\n yAxis: AxisConfig\n plotArea: PlotAreaConfig\n legend: LegendConfig\n /** The figure printed on each column, when the chart prints them. */\n valueStyle: TextStyle\n /** How far everything *outside* a spotlit series or category fades. */\n spotlight: { dimOpacity: number }\n}\n\n/** Author-facing partial theme — every branch optional and deep-merged. */\nexport type BarChartTheme = {\n seriesFills?: Fill[]\n bar?: Partial<BarStyleConfig>\n xAxis?: Partial<AxisConfig>\n yAxis?: Partial<AxisConfig>\n plotArea?: Partial<PlotAreaConfig>\n legend?: LegendTheme\n valueStyle?: TextStyle\n spotlight?: Partial<BarChartConfig[\"spotlight\"]>\n}\n\n/** The bar chart's out-of-the-box look. */\nexport const DEFAULT_BAR_CHART_THEME: BarChartConfig = {\n seriesFills: DEFAULT_SERIES_FILLS,\n bar: {\n cornerRadius: 6,\n slotFill: 0.72,\n groupGap: 6,\n stroke: [],\n shadow: [],\n },\n // A category axis names its slots, so its captions are set like a caption\n // rather than like a numeric marker: same size, less weight than the line\n // chart's ticks, and centred on the slot.\n xAxis: {\n ...DEFAULT_X_AXIS,\n labelStyle: { ...DEFAULT_X_AXIS.labelStyle, fontWeight: 600 },\n tickPadding: 16,\n },\n yAxis: DEFAULT_Y_AXIS,\n plotArea: DEFAULT_PLOT_AREA,\n legend: DEFAULT_LEGEND,\n valueStyle: {\n fontSize: 24,\n fontWeight: 700,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n },\n spotlight: { dimOpacity: SPOTLIGHT_DIM },\n}\n\nfunction resolveBarStyle(\n bar: Partial<BarStyleConfig> | undefined,\n previous: BarStyleConfig\n): BarStyleConfig {\n return {\n cornerRadius: bar?.cornerRadius ?? previous.cornerRadius,\n slotFill: bar?.slotFill ?? previous.slotFill,\n groupGap: bar?.groupGap ?? previous.groupGap,\n stroke: strokeOps.resolve(bar?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(bar?.shadow ?? previous.shadow),\n }\n}\n\nfunction lerpBarStyle(\n from: BarStyleConfig,\n to: BarStyleConfig,\n t: number\n): BarStyleConfig {\n return {\n cornerRadius: lerpNumber(from.cornerRadius, to.cornerRadius, t),\n slotFill: lerpNumber(from.slotFill, to.slotFill, t),\n groupGap: lerpNumber(from.groupGap, to.groupGap, t),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\n/**\n * {@link BarChart}'s `theme` @property mapper: merges the author's partial\n * {@link BarChartTheme} onto `previous` (falling back to\n * {@link DEFAULT_BAR_CHART_THEME} on first resolution), fully resolving every\n * leaf so the tween below always has resolved data to interpolate.\n */\nexport function resolveBarChartTheme(\n theme: BarChartTheme | undefined,\n previous?: BarChartConfig\n): BarChartConfig {\n const base = previous ?? DEFAULT_BAR_CHART_THEME\n return {\n seriesFills: resolveSeriesFills(theme?.seriesFills, base.seriesFills),\n bar: resolveBarStyle(theme?.bar, base.bar),\n xAxis: resolveAxisConfig(theme?.xAxis, base.xAxis),\n yAxis: resolveAxisConfig(theme?.yAxis, base.yAxis),\n plotArea: resolvePlotArea(theme?.plotArea, base.plotArea),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n valueStyle: textOps.resolveStyle(theme?.valueStyle, base.valueStyle),\n spotlight: {\n dimOpacity: theme?.spotlight?.dimOpacity ?? base.spotlight.dimOpacity,\n },\n }\n}\n\n/** {@link BarChart}'s `theme` @property tween — leaf by leaf. */\nexport function lerpBarChartTheme(\n from: BarChartConfig,\n to: BarChartConfig,\n t: number\n): BarChartConfig {\n return {\n seriesFills: lerpSeriesFills(from.seriesFills, to.seriesFills, t),\n bar: lerpBarStyle(from.bar, to.bar, t),\n xAxis: lerpAxisConfig(from.xAxis, to.xAxis, t),\n yAxis: lerpAxisConfig(from.yAxis, to.yAxis, t),\n plotArea: lerpPlotArea(from.plotArea, to.plotArea, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n valueStyle: textOps.lerpStyle(from.valueStyle, to.valueStyle, t),\n spotlight: {\n dimOpacity: lerpNumber(\n from.spotlight.dimOpacity,\n to.spotlight.dimOpacity,\n t\n ),\n },\n }\n}\n\n/** The paint series `index` is drawn in: its own override, else the palette's entry. */\nexport function resolveBarFill(\n palette: Fill[],\n series: BarSeries,\n index: number\n): Fill {\n if (series.fill != null) return fillOps.resolve(series.fill)\n if (palette.length === 0) return \"white\"\n return palette[index % palette.length]\n}\n", "import {\n Rect,\n command,\n node,\n Node2D,\n easeOut,\n property,\n type CommandArgs,\n type DataRecord,\n type EasingFunction,\n type NodeConfig,\n type Node2DProps,\n} from \"@motionscript/core\"\n\nimport { clampUnit } from \"../kit/shared\"\nimport { PieLegend } from \"./pie-legend\"\nimport { PieRing } from \"./pie-ring\"\nimport {\n DEFAULT_PIE_CHART_THEME,\n lerpPieChartTheme,\n resolvePieChartTheme,\n resolvePieSlices,\n type PieChartConfig,\n type PieChartTheme,\n type PieLabelMode,\n type PieLabelPlacement,\n type PieSlice,\n} from \"./shared\"\nimport {\n drive,\n hold,\n sequence as sequenceOf,\n together,\n type Seekable,\n} from \"@motionscript/core/component\"\n\nexport interface PieChartProps extends Node2DProps {\n /** One row per slice — a plain record of field name to value. */\n data: DataRecord[]\n /** Key into each row for its slice caption, used on the labels and the legend. */\n categoryField: string\n /** Key into each row for its slice magnitude — the wedge's share of the ring. */\n valueField: string\n /**\n * The chart's visual theme — slice palette, ring geometry, label and centre\n * type, legend. Every branch is optional and deep-merged onto\n * {@link DEFAULT_PIE_CHART_THEME}.\n */\n theme?: PieChartTheme\n /** What the ring's labels say. */\n labelMode?: PieLabelMode\n /** Where they sit: inside their wedge, outside on a leader, or nowhere. */\n labelPlacement?: PieLabelPlacement\n /** Whether the legend column beside the ring is drawn at all. */\n showLegend?: boolean\n /** Gap between the ring and the legend, in px. */\n gap?: number\n /** Text set in the ring's hole — see {@link revealCenter}. */\n centerLabel?: string\n /** Smaller second line under {@link centerLabel}. */\n centerCaption?: string\n /**\n * Whether the hole's summary is drawn at rest. `true` (the default) is the\n * plain answer; a scene that wants the conclusion to land on a later beat sets\n * this `false` and calls {@link revealCenter} when it gets there.\n */\n showCenter?: boolean\n}\n\n/**\n * How {@link PieChart.enter} divides its duration: the wedges sweeping in, then\n * the labels and the legend arriving together. Shares of the whole, so they sum\n * to 1 — the entrance takes exactly the time it was given, for the reason spelt\n * out on `LineChart.enter`.\n */\nconst SWEEP_SHARE = 0.65\nconst LABEL_SHARE = 0.35\n\n/**\n * A pie or donut chart, composed from two independent parts rather than one\n * monolithic draw — the third of the chart family, built the same way as the\n * line and bar charts beside it:\n *\n * - {@link PieRing} — the wedges, each sized by its share of the total, with its\n * label set inside its own band or pinned outside on a leader line, and the\n * summary in the hole. The only part of the family that draws a path.\n * - {@link PieLegend} — a column of swatch-and-caption rows naming each slice.\n *\n * The two are laid out by a plain horizontal-flow {@link Rect}: the ring fills whatever the\n * hugging legend leaves, both centred, so the chart re-flows if its box changes.\n *\n * **One mark per row, not per column** — which is what makes this the odd one of\n * the three. A line or a bar chart plots *series*, so its colours belong to\n * columns and hiding one is a sensible thing to ask for. A pie plots rows, so its\n * palette is indexed by row and there is nothing to hide: dropping a slice would\n * silently re-cut every other wedge's share, which is a different chart rather\n * than the same chart with a series switched off. {@link selectSlice} and its\n * siblings are what a pie has instead — a slice swells out of the ring, its hole\n * edge staying put, and everything else dims behind it.\n *\n * **Resting state is the finished chart** — the same bargain the other two make:\n * the studio paints a node's Initial state on a paused canvas, so a chart that\n * only appeared once a command had run would be edited blind. {@link enter} arms\n * every part back to its pre-entrance state before animating. The one exception\n * is the hole's summary, which a scene can hold back with `showCenter: false`\n * because it is usually the chart's *conclusion* rather than part of its setup.\n */\n@node({\n key: \"pieChart\",\n parentKey: \"node\",\n forkable: true,\n layout: {\n children: \"freeform\",\n defaultWidthMode: \"fixed\",\n defaultHeightMode: \"fixed\",\n acceptsChildren: true,\n },\n seed: {\n width: 680,\n height: 480,\n },\n})\nexport class PieChart extends Node2D<PieChartProps> {\n @property({ default: () => [] as DataRecord[] }) declare data: DataRecord[]\n @property({ default: \"\" }) declare categoryField: string\n @property({ default: \"\" }) declare valueField: string\n @property({\n default: DEFAULT_PIE_CHART_THEME,\n mapper: resolvePieChartTheme,\n tween: lerpPieChartTheme,\n })\n declare theme: PieChartTheme\n @property({ default: \"percent\" }) declare labelMode: PieLabelMode\n @property({ default: \"leader\" }) declare labelPlacement: PieLabelPlacement\n @property({ default: 40 }) declare gap: number\n @property({ default: \"\" }) declare centerLabel: string\n @property({ default: \"\" }) declare centerCaption: string\n\n /**\n * How far each slice is currently pushed out of the ring, `0`–`1` per index.\n *\n * Held here rather than on the ring because two parts read it: the wedges grow\n * out by it, and the legend entries dim by it. One signal, one tween, and the\n * two can't drift.\n */\n @property({ default: () => [] as number[] }) declare pops: number[]\n\n // ---- Part handles (built in the constructor) --------------------------\n private ring: PieRing\n private legend: PieLegend | null = null\n\n /** `this.theme` read back through its *resolved* shape — see `LineChart.theme`. */\n private get resolvedTheme(): PieChartConfig {\n return this.theme as unknown as PieChartConfig\n }\n\n /** The slices the ring and the legend draw, resolved from the rows and the palette. */\n private get slices(): PieSlice[] {\n return resolvePieSlices(\n this.data,\n this.categoryField,\n this.valueField,\n this.resolvedTheme.sliceFills\n )\n }\n\n /**\n * How far each slice is dimmed by the current selection, one entry per slice —\n * 1 while nothing is selected, and for the selected slice itself; falling\n * toward the theme's `dimOpacity` for the rest in proportion to how far the\n * selection has grown.\n */\n private sliceDims(): number[] {\n let focus = 0\n for (let i = 0; i < this.data.length; i++) {\n focus = Math.max(focus, this.popAt(i))\n }\n const dim = this.resolvedTheme.spotlight.dimOpacity\n if (focus <= 0) return this.data.map(() => 1)\n return this.data.map(\n (_, i) => 1 - (1 - clampUnit(dim)) * focus * (1 - this.popAt(i))\n )\n }\n\n /** How far slice `index` is currently pushed out, in `[0, 1]`. */\n private popAt(index: number): number {\n return clampUnit(this.pops[index] ?? 0)\n }\n\n constructor(props?: NodeConfig<PieChart, PieChartProps>) {\n super(props)\n\n // The whole chart is composed from plain props/`data` — no context needed,\n // so the parts are built here (super() applied the props above). The slice\n // *structure* (one wedge and one legend row each) is read once, like every\n // other chart's series list; everything the parts draw with is passed as a\n // closure, so `chart.to({ theme })` genuinely re-paints the ring.\n const slices = this.slices\n const showLegend = props?.showLegend !== false\n this.pops = slices.map(() => 0)\n\n this.ring = new PieRing({\n width: \"fill\",\n height: \"fill\",\n slices,\n ring: () => this.resolvedTheme.ring,\n label: () => this.resolvedTheme.label,\n centerStyle: () => this.resolvedTheme.center,\n labelMode: this.labelMode,\n labelPlacement: this.labelPlacement,\n pops: () => this.pops,\n dimOpacity: () => this.resolvedTheme.spotlight.dimOpacity,\n centerLabel: this.centerLabel,\n centerCaption: this.centerCaption,\n centerReveal: props?.showCenter === false ? 0 : 1,\n })\n\n const children: Node2D[] = [this.ring]\n if (showLegend) {\n this.legend = new PieLegend({\n slices,\n legend: () => this.resolvedTheme.legend,\n dims: () => this.sliceDims(),\n })\n children.push(this.legend)\n }\n\n this.add(\n new Rect({ flow: \"horizontal\",\n width: \"fill\",\n height: \"fill\",\n gap: this.gap,\n align: \"center\",\n children,\n })\n )\n }\n\n // ---- Orchestrated entrance --------------------------------------------\n\n /**\n * Prime every part at its pre-entrance state — no wedges swept, no labels, the\n * legend shifted and transparent.\n *\n * Called by {@link enter} before its first frame so nothing flashes ahead of\n * its beat. Idempotent, and public so a scene can hide a chart ahead of time\n * without starting the entrance. The hole's summary is deliberately untouched:\n * it has its own signal, so a scene that set it up to land later keeps that.\n */\n arm(): void {\n this.ring.set({ growth: 0, labelReveal: 0 })\n this.legend?.arm()\n }\n\n /**\n * Animate the whole chart in: the wedges sweep in clockwise from the theme's\n * `startAngle`, then the labels fade up and the legend staggers in together.\n *\n * `duration` is the **whole** entrance; the phases are shares of it.\n */\n @command()\n enter(\n args: CommandArgs<Record<string, never>> & { duration: number }\n ): Seekable {\n const duration = args.duration\n const easing = args.easing ?? easeOut()\n this.arm()\n\n return sequenceOf(\n this.ring.sweep({ duration: duration * SWEEP_SHARE, easing }),\n together(\n this.ring.revealLabels({ duration: duration * LABEL_SHARE, easing }),\n this.legend\n ? this.legend.enter({ duration: duration * LABEL_SHARE, easing })\n : hold(duration * LABEL_SHARE)\n )\n )\n }\n\n /**\n * Fade the hole's summary up.\n *\n * Held out of {@link enter} on purpose: what sits in a donut's hole is usually\n * the chart's conclusion, so the scene lands it on the beat it belongs to\n * rather than giving it away with the first frame. Pair it with\n * `showCenter: false`, which is what leaves the hole clear to begin with.\n */\n @command()\n revealCenter(\n args: CommandArgs<Record<string, never>> & { duration?: number } = {}\n ): Seekable {\n return this.ring.revealCenter({ duration: args.duration ?? 0.6, easing: args.easing ?? easeOut() })\n }\n\n // ---- Selection ---------------------------------------------------------\n\n /** True when slice `index` is currently grown out past the ring's rim. */\n isSelected(index: number): boolean {\n return this.popAt(index) > 0.5\n }\n\n /**\n * Grow slice `index` out past the ring's rim and dim the rest of the chart\n * behind it, leaving any other selected slice where it is.\n */\n @command({ args: [{ key: \"sliceIndex\", kind: \"number\", default: 0, min: 0, step: 1 }] })\n selectSlice(\n args: CommandArgs<{ sliceIndex: number }> & { duration?: number }\n ): Seekable {\n const { sliceIndex: index } = args.data as { sliceIndex: number }\n const duration = args.duration ?? 0.45\n const easing = args.easing ?? easeOut()\n return this.tweenPops((i) => (i === index ? 1 : this.popAt(i)), duration, easing)\n }\n\n /** Settle slice `index` back flush with the ring. */\n @command({ args: [{ key: \"sliceIndex\", kind: \"number\", default: 0, min: 0, step: 1 }] })\n deselectSlice(\n args: CommandArgs<{ sliceIndex: number }> & { duration?: number }\n ): Seekable {\n const { sliceIndex: index } = args.data as { sliceIndex: number }\n const duration = args.duration ?? 0.45\n const easing = args.easing ?? easeOut()\n return this.tweenPops((i) => (i === index ? 0 : this.popAt(i)), duration, easing)\n }\n\n /** Move the focus to slice `index` alone, settling whatever else was out. */\n @command({ args: [{ key: \"sliceIndex\", kind: \"number\", default: 0, min: 0, step: 1 }] })\n selectOnlySlice(\n args: CommandArgs<{ sliceIndex: number }> & { duration?: number }\n ): Seekable {\n const { sliceIndex: index } = args.data as { sliceIndex: number }\n const duration = args.duration ?? 0.45\n const easing = args.easing ?? easeOut()\n return this.tweenPops((i) => (i === index ? 1 : 0), duration, easing)\n }\n\n /** Settle every slice flush and bring the whole chart back to full. */\n @command()\n clearSelection(\n args: CommandArgs<Record<string, never>> & { duration?: number } = {}\n ): Seekable {\n const duration = args.duration ?? 0.45\n const easing = args.easing ?? easeOut()\n return this.tweenPops(() => 0, duration, easing)\n }\n\n /**\n * Ease every slice's pop from where it is now to what `pop` asks for.\n *\n * Consumes its full duration even when nothing moves: in the studio a\n * command's duration is what the timeline lays out and what its parallel\n * siblings are timed against, so a no-op that took no frames would pull\n * everything after it forward.\n *\n * `from` and the targets are read when the command is *built*, which for a\n * driven scene is with the node in the state its own start time implies — so a\n * select that follows another starts from where that one left the ring.\n */\n private tweenPops(\n pop: (index: number) => number,\n duration: number,\n easing: EasingFunction\n ): Seekable {\n const from = this.data.map((_, i) => this.popAt(i))\n const targets = this.data.map((_, i) => clampUnit(pop(i)))\n if (from.every((value, i) => value === targets[i])) return hold(duration)\n\n return drive(duration, (t) => {\n const e = easing(t)\n this.pops = from.map((value, i) => value + (targets[i] - value) * e)\n })\n }\n}\n", "import {\n command,\n Rect,\n Text,\n easeOut,\n property,\n textOps,\n type CommandArgs,\n type NodeConfig,\n type RectProps,\n} from \"@motionscript/core\"\n\nimport {\n DEFAULT_LEGEND,\n clampUnit,\n type LegendConfig,\n} from \"../kit/shared\"\nimport { at, compose, hold, type Seekable } from \"@motionscript/core/component\"\nimport type { PieSlice } from \"./shared\"\n\nexport interface PieLegendProps extends RectProps {\n /** One entry per slice, in ring order. */\n slices: PieSlice[]\n /** Padding, caption style, gaps, and swatch geometry — see {@link LegendConfig}. */\n legend: LegendConfig\n /**\n * Per-slice opacity multiplier, by index — the same signal that dims the ring,\n * so an entry fades in step with the wedge it names. Missing entries read as 1.\n */\n dims: number[]\n}\n\n/** How far an entry starts to the right of its slot when it enters, in px. */\nconst SLIDE = 16\n\n/**\n * The legend of a {@link PieChart}: a **vertical-flow column** of entries, one per\n * slice, each pairing the slice's caption with its colour swatch.\n *\n * A column rather than the cartesian charts' row, because it sits *beside* the\n * ring rather than beneath the plot — a pie is as tall as it is wide, so the\n * space it leaves over is a vertical strip. It is otherwise the same entry: the\n * swatch carries the colour, the caption reads in the legend's neutral ink, and\n * both take the shared {@link LegendConfig}.\n *\n * There is no show/hide here. A pie's marks are its *rows*, and a row that\n * shouldn't be in the chart shouldn't be in the table — removing one would\n * silently re-cut every other wedge's share, which is a different chart rather\n * than the same chart with something hidden. Selection is what a pie has\n * instead, and it arrives through {@link dims}.\n */\nexport class PieLegend extends Rect {\n @property({ default: () => [] as PieSlice[] }) declare slices: PieSlice[]\n @property({ default: DEFAULT_LEGEND }) declare legend: LegendConfig\n @property({ default: () => [] as number[] }) declare dims: number[]\n\n /** One entry per slice, ordered top → bottom, for the staggered entrance. */\n private items: Rect[] = []\n\n constructor(props?: NodeConfig<PieLegend, PieLegendProps>) {\n // `flow` with the rest of the fixed geometry, and for the reason\n // {@link ChartLegend} spells out: a `Rect` given none lays its children out\n // `freeform`, so an unstated column is every caption drawn on top of the\n // last one.\n super({\n ...(props as RectProps),\n flow: \"vertical\",\n align: \"centerLeft\",\n width: \"hug\",\n height: \"hug\",\n })\n\n // Fixed column geometry last, so it wins over what super() applied.\n this.set({ gap: this.legend.itemGap / 3, padding: this.legend.padding })\n\n for (let i = 0; i < this.slices.length; i++) {\n const item = this.buildItem(this.slices[i])\n this.items.push(item)\n // The selection dim rides a wrapper rather than the entry itself, so the\n // entrance can animate the entry's own opacity without the two fighting\n // over one value — opacity multiplies down the tree, which is exactly the\n // composition wanted here.\n this.add(\n new Rect({\n width: \"hug\",\n height: \"hug\",\n flow: \"freeform\",\n opacity: () => clampUnit(this.dims[i] ?? 1),\n children: [item],\n })\n )\n }\n }\n\n /** One entry: the slice's colour swatch beside its caption. */\n private buildItem(slice: PieSlice): Rect {\n const marker = this.legend.markerStyle\n return new Rect({ flow: \"horizontal\",\n gap: 12,\n align: \"center\",\n width: \"hug\",\n height: \"hug\",\n children: [\n new Rect({\n width: marker.height,\n height: marker.height,\n cornerRadius: marker.borderRadius,\n fill: slice.fill,\n stroke: marker.stroke,\n shadow: marker.shadow,\n }),\n new Text({\n ...textOps.styleProps(this.legend.textStyle),\n text: slice.category,\n }),\n ],\n })\n }\n\n /**\n * Prime every entry at its pre-entrance state: shifted right and transparent.\n *\n * Idempotent, and separate from {@link enter} so the chart can arm its whole\n * composition before the first frame is drawn — the resting state is the\n * finished legend, so there is nothing hidden to reveal unless a scene asks.\n */\n arm(slide = SLIDE): void {\n for (const item of this.items) item.set({ opacity: 0, x: slide })\n }\n\n /**\n * Slide + fade every entry in from the right, staggered top-to-bottom.\n *\n * `duration` is the **whole** entrance: the stagger is carved out of it rather\n * than added on top, so the command takes exactly the time the timeline laid\n * out for it.\n */\n @command()\n enter(\n args: CommandArgs<Record<string, never>> & { duration: number }\n ): Seekable {\n const duration = args.duration\n const easing = args.easing ?? easeOut()\n const items = this.items\n if (items.length === 0) return hold(duration)\n this.arm()\n\n // Each gap is a fifth of an entry's own move, the rhythm the axes use;\n // solved for from the budget rather than added to it. The offsets were\n // always `i * step` — written as `wait` inside a generator they merely only\n // existed while it ran.\n const per = duration / (1 + Math.max(0, items.length - 1) / 5)\n const step = per / 5\n\n return compose(\n items.map((item, i) => at(i * step, item.to({ data: { opacity: 1, x: 0 }, duration: per, easing })))\n )\n }\n}\n", "import {\n Rect,\n command,\n type AssetScope,\n Graphics2D,\n Node2D,\n PathBuilder,\n Text,\n easeOut,\n fillOps,\n property,\n strokeOps,\n textOps,\n type Command,\n type CommandArgs,\n type FillResolved,\n type NodeConfig,\n type Node2DProps,\n type RenderContext2D,\n type TextStyle,\n type Vector2,\n} from \"@motionscript/core\"\n\nimport { clampUnit, formatPercent } from \"../kit/shared\"\nimport {\n DEFAULT_PIE_CHART_THEME,\n pieArcs,\n relaxLabels,\n ringPoint,\n type CenterConfig,\n type LabelConfig,\n type PieArc,\n type PieLabelMode,\n type PieLabelPlacement,\n type PieSlice,\n type RingConfig,\n} from \"./shared\"\nimport { declarePaints } from \"@motionscript/core/component\"\n\nexport interface PieRingProps extends Node2DProps {\n /** One entry per slice: its `category`, `value` and `fill`. */\n slices: PieSlice[]\n /** Ring geometry and paint — gap, hole, start angle, selection growth. */\n ring: RingConfig\n /** How the labels are set, and the leader lines that point at them. */\n label: LabelConfig\n /**\n * How the hole's summary is set.\n *\n * Named `centerStyle` rather than `center` because every node already has a\n * `center` — the anchor its position is measured from.\n */\n centerStyle: CenterConfig\n /** What the labels say, or `\"none\"` to drop them entirely. */\n labelMode: PieLabelMode\n /** Where the labels sit: inside their wedge, outside on a leader, or nowhere. */\n labelPlacement: PieLabelPlacement\n /**\n * Sweep of the ring in `[0, 1]`: 0 draws nothing, 1 the full circle. The\n * wedges grow around clockwise from the ring's `startAngle` as this rises.\n */\n growth: number\n /**\n * Fade of the leader lines and labels in `[0, 1]`, held separate from\n * {@link growth} so they arrive *after* the ring has swept in.\n */\n labelReveal: number\n /**\n * How far each slice is currently pushed out of the ring, `0`–`1` per index.\n * Owned by the chart, so the ring and the legend read one signal.\n */\n pops: number[]\n /** Opacity everything *unselected* falls back to while any slice is selected. */\n dimOpacity: number\n /**\n * Text set in the middle of the ring's hole — the largest uninterrupted area\n * the chart owns, and on a donut otherwise empty. A one-line summary there\n * routinely does more work than the whole ring around it. Empty (the default)\n * leaves the hole clear; ignored when there is no hole.\n */\n centerLabel: string\n /** Smaller second line under {@link centerLabel}. Empty (the default) drops it. */\n centerCaption: string\n /**\n * Fade of the hole's summary in `[0, 1]`. Its own signal, untouched by the\n * ring's entrance — a hole label is usually a *conclusion*, so a scene that\n * wants it to land on a later beat sets this to 0 and calls\n * `PieChart.revealCenter` when it gets there.\n */\n centerReveal: number\n}\n\n/** Vertical breathing room between one label block and the next, in px. */\nconst LABEL_GAP = 12\n/** Breathing room past the outermost label, so it never touches the node's edge, in px. */\nconst LABEL_PAD = 14\n/** Rough width of one glyph as a fraction of the font size, for gutter sizing. */\nconst GLYPH_RATIO = 0.62\n/** Gap between a wedge's outer edge and where its leader lifts off, in px. */\nconst LEADER_LIFTOFF = 6\n/** How far past the ring's outer edge a leader runs before it elbows, in px. */\nconst LEADER_LENGTH = 34\n/** Horizontal run from the elbow to the label, in px. */\nconst LABEL_STUB = 26\n/** Gap between a leader's end and the label text it points at, in px. */\nconst TEXT_INSET = 8\n/**\n * Floor on the elbow's horizontal distance from the ring's axis, as a fraction\n * of the leader radius — so a label pushed clear above (or below) the ring still\n * elbows out sideways rather than running straight down the 12 o'clock line.\n */\nconst MIN_ELBOW = 0.18\n/** Segments used to tessellate a full turn of arc; a wedge takes its share. */\nconst ARC_STEPS = 240\n/** Rounding applied to the leader's elbow, in px. */\nconst ELBOW_RADIUS = 8\n/** Side padding used when nothing is leadered out — no gutter to reserve. */\nconst BARE_PAD = 2\n/** An inside label's caption size, as a fraction of its leading line. */\nconst INSIDE_CAPTION_RATIO = 0.44\n/**\n * Where an inside label sits on a **solid pie** (no hole to split the band\n * with), as a fraction of the wedge's radius — far enough out that the label\n * clears the point every wedge converges on.\n */\nconst PIE_LABEL_RADIUS = 0.62\n/** Fraction of the hole's diameter its summary is allowed to run to. */\nconst CENTER_LABEL_FIT = 0.78\n/** Fraction of the room inside a wedge that its label is allowed to run to. */\nconst INSIDE_LABEL_FIT = 0.9\n/** Below this, a wedge's bisector counts as axis-aligned and that term stops binding. */\nconst AXIS_EPSILON = 1e-3\n/** Fallback type size when a theme style leaves it unset or on autofit. */\nconst FALLBACK_SIZE = 26\n\n/** A style's numeric size, guarding the autofit and unset cases. */\nfunction sizeOf(style: TextStyle, fallback = FALLBACK_SIZE): number {\n const size = style.fontSize\n return typeof size === \"number\" ? size : fallback\n}\n\n/** Where one slice's label sits and the polyline that points at it. */\ninterface LabelPlacement {\n /** Index of the slice. */\n index: number\n /** Which side of the ring the label sits on: +1 right, −1 left. */\n side: 1 | -1\n /** Leader polyline: wedge edge → elbow → label. */\n leader: Vector2[]\n /** Point the label's inner edge is pinned to. */\n text: Vector2\n}\n\n/**\n * A ring-band wedge over `a0`→`a1` (radians, clockwise from 12 o'clock) spanning\n * `rInner`→`rOuter`, centred on `cx`. Ring wedges trace the outer arc, step in,\n * and trace the inner arc back; full pie wedges (`rInner <= 0`) close through the\n * centre.\n *\n * `halfGap` is **half the gap to the neighbouring slice, in px**, shaved off both\n * ends of the wedge. It's converted to an angle *per radius* (`halfGap / r` — a\n * wider angle near the hole than out at the rim), which is what keeps the channel\n * between two slices the same width all the way across the band. A fixed angular\n * gap instead reads as a thin pizza slice: right at the rim, pinched shut at the\n * hole. The inset is capped at the wedge's own half-span so a sliver of a slice\n * narrows to nothing rather than turning inside out.\n */\nfunction wedgePath(\n a0: number,\n a1: number,\n rInner: number,\n rOuter: number,\n halfGap: number,\n cx: number\n): PathBuilder {\n const path = new PathBuilder()\n const span = a1 - a0\n const steps = Math.max(\n 2,\n Math.ceil((Math.abs(span) / (Math.PI * 2)) * ARC_STEPS)\n )\n /** Angular inset at radius `r` that puts the wedge's edge `halfGap` px off the divide. */\n const insetAt = (r: number): number =>\n r <= 0 ? 0 : Math.min(halfGap / r, span / 2.2)\n const arcTo = (r: number, i: number): Vector2 => {\n const inset = insetAt(r)\n const from = a0 + inset\n const to = a1 - inset\n const point = ringPoint(r, from + ((to - from) * i) / steps)\n return { x: point.x + cx, y: point.y }\n }\n\n if (rInner <= 0) {\n // Solid pie wedge: the sides converge on the centre, so only the outer rim\n // carries the gap.\n path.moveTo(cx, 0)\n for (let i = 0; i <= steps; i++) {\n const p = arcTo(rOuter, i)\n path.lineTo(p.x, p.y)\n }\n path.close()\n return path\n }\n\n for (let i = 0; i <= steps; i++) {\n const p = arcTo(rOuter, i)\n if (i === 0) path.moveTo(p.x, p.y)\n else path.lineTo(p.x, p.y)\n }\n // Back along the inner arc — its own (wider) inset makes each side edge a\n // straight chord sitting `halfGap` px off the divide at both ends.\n for (let i = steps; i >= 0; i--) {\n const p = arcTo(rInner, i)\n path.lineTo(p.x, p.y)\n }\n path.close()\n return path\n}\n\n/**\n * The ring itself: one wedge per {@link PieSlice}, sized by its share of the\n * total, swept clockwise from the theme's `startAngle` with a `gap` of clear\n * space between neighbours — a *constant-width* channel, the same at the hole as\n * at the rim (see {@link wedgePath}). A **selected** slice reaches further out\n * while its hole edge stays put — so it reads as that band swelling out of the\n * ring rather than breaking off it — and the rest of the ring dims behind it,\n * leaders and labels included.\n *\n * This is the only part of the chart family that draws a path: the wedges are\n * filled {@link PathBuilder} polygons approximating each arc, and the leaders are\n * stroked polylines — both in the node's centred, y-up space, so neither depends\n * on SVG arc-sweep conventions. The **labels are real {@link Text} nodes**\n * (stacked in a hug vertical flow and pinned by their inner edge), so no\n * glyph-width guesswork decides where a caption starts.\n *\n * ### Label placement\n * Each label sits either **inside** its wedge or **outside** on a leader.\n *\n * An inside label is centred on its slice's band and leads with the number, the\n * caption set small underneath — inside a wedge the reader already knows which\n * slice they're on, so the magnitude is the thing being read. It costs no gutter\n * and no eye round-trip, so a ring labelled that way draws considerably bigger in\n * the same box.\n *\n * A leader label sits outside on the end of a polyline that lifts off its wedge,\n * elbows, and runs horizontally into the text. Labels are split into a left and a\n * right column by which way their slice's bisector points, and each column is\n * de-conflicted independently (see {@link relaxLabels}): a label takes the spot\n * nearest its wedge that still clears its neighbour, and the column is squeezed\n * to fit the node's height rather than spilling out of it. The elbow then rides on\n * a circle just outside the ring **at the label's settled y**, so the run into the\n * text is always horizontal and a nudged label never drags its leader back across\n * the ring.\n *\n * Only the sides that actually carry a leader reserve a gutter, and the ring's\n * centre slides into whatever the other side left over ({@link centerX}) — so a\n * ring whose slices all leader one way doesn't leave a bald patch on the other.\n */\nexport class PieRing extends Node2D<PieRingProps> {\n @property({ default: () => [] as PieSlice[] }) declare slices: PieSlice[]\n @property({ default: DEFAULT_PIE_CHART_THEME.ring }) declare ring: RingConfig\n @property({ default: DEFAULT_PIE_CHART_THEME.label })\n declare label: LabelConfig\n @property({ default: DEFAULT_PIE_CHART_THEME.center })\n declare centerStyle: CenterConfig\n @property({ default: \"percent\" }) declare labelMode: PieLabelMode\n @property({ default: \"leader\" }) declare labelPlacement: PieLabelPlacement\n @property({ default: 1 }) declare growth: number\n @property({ default: 1 }) declare labelReveal: number\n @property({ default: () => [] as number[] }) declare pops: number[]\n @property({ default: DEFAULT_PIE_CHART_THEME.spotlight.dimOpacity })\n declare dimOpacity: number\n @property({ default: \"\" }) declare centerLabel: string\n @property({ default: \"\" }) declare centerCaption: string\n @property({ default: 1 }) declare centerReveal: number\n\n /** Each slice's fill, resolved once so `renderSelf` doesn't re-resolve every frame. */\n private fills: FillResolved[][] = []\n\n constructor(props?: NodeConfig<PieRing, PieRingProps>) {\n super(props)\n\n this.fills = this.slices.map((slice) => fillOps.resolve(slice.fill))\n\n // One label per *drawn* slice (a zero-valued slice gets no wedge, so no\n // label either), in whichever of the two forms the chart asked for. Both are\n // real Text stacked in a hug vertical flow — the difference is only what they are\n // pinned to and which line leads.\n //\n // The *point* each label is pinned to stays reactive and follows the ring as\n // the box resizes or its slice pops out; only the choice of form, which\n // depends on props alone, is settled here.\n if (this.labelPlacement !== \"none\" && this.labelMode !== \"none\") {\n for (const arc of this.arcs) this.addLabel(arc)\n }\n\n // The hole's summary, pinned to the ring's centre so it tracks the sideways\n // shift a one-sided label column puts on the ring.\n if (this.hasCenterLabel) this.addCenterLabel()\n }\n\n /** Build slice `arc`'s label, in whichever form the chart's placement asks for. */\n private addLabel(arc: PieArc): void {\n const slice = this.slices[arc.index]\n // Fades in with the ring's labels, then dims along with its own slice\n // whenever another slice takes the focus.\n const opacity = () =>\n clampUnit(this.labelReveal) * this.sliceOpacity(arc.index)\n const wantsCategory =\n this.labelMode === \"both\" || this.labelMode === \"category\"\n const wantsPercent =\n this.labelMode === \"both\" || this.labelMode === \"percent\"\n\n if (this.labelPlacement === \"inside\") {\n // The number leads and the caption sits under it: inside a wedge the\n // reader already knows *which* wedge they are looking at, so the magnitude\n // is what the label is for. Both sizes are bound rather than read once —\n // the fitted size derives from the ring's radius, which isn't known until\n // the node has been laid out.\n const inside: Node2D[] = []\n if (wantsPercent) {\n inside.push(\n new Text({\n ...textOps.styleProps(this.label.insideStyle),\n text: formatPercent(arc.fraction),\n fontSize: () => this.insideFont,\n })\n )\n }\n if (wantsCategory) {\n inside.push(\n new Text({\n ...textOps.styleProps(this.label.insideStyle),\n text: slice.category,\n fontSize: () =>\n this.labelMode === \"category\"\n ? this.insideFont\n : this.insideFont * INSIDE_CAPTION_RATIO,\n })\n )\n }\n this.add(\n new Rect({ flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 0,\n align: \"center\",\n opacity,\n center: () => this.insideAnchor(arc.index),\n children: inside,\n })\n )\n return\n }\n\n const side = this.sideOf(arc.mid)\n const at = () => this.placementFor(arc.index)?.text ?? { x: 0, y: 0 }\n const children: Node2D[] = []\n if (wantsCategory) {\n children.push(\n new Text({\n ...textOps.styleProps(this.label.textStyle),\n text: slice.category,\n textAlign: side > 0 ? \"left\" : \"right\",\n })\n )\n }\n if (wantsPercent) {\n children.push(\n new Text({\n ...textOps.styleProps(this.label.percentStyle),\n text: formatPercent(arc.fraction),\n textAlign: side > 0 ? \"left\" : \"right\",\n })\n )\n }\n this.add(\n new Rect({ flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 2,\n align: side > 0 ? \"centerLeft\" : \"centerRight\",\n opacity,\n // Exactly one of these is set; `undefined` anchors are ignored.\n centerLeft: side > 0 ? at : undefined,\n centerRight: side > 0 ? undefined : at,\n children,\n })\n )\n }\n\n /** Build the hole's summary block. */\n private addCenterLabel(): void {\n const children: Node2D[] = []\n if (this.centerLabel !== \"\") {\n children.push(\n new Text({\n ...textOps.styleProps(this.centerStyle.labelStyle),\n text: this.centerLabel,\n fontSize: () => this.centerFont,\n })\n )\n }\n if (this.centerCaption !== \"\") {\n children.push(\n new Text({\n ...textOps.styleProps(this.centerStyle.captionStyle),\n text: this.centerCaption,\n fontSize: () =>\n this.centerFont *\n (sizeOf(this.centerStyle.captionStyle, 22) /\n sizeOf(this.centerStyle.labelStyle, 46)),\n })\n )\n }\n this.add(\n new Rect({ flow: \"vertical\",\n width: \"hug\",\n height: \"hug\",\n gap: 2,\n align: \"center\",\n opacity: () => clampUnit(this.centerReveal),\n center: () => ({ x: this.centerX, y: 0 }),\n children,\n })\n )\n }\n\n // ---- Arcs --------------------------------------------------------------\n\n /** The drawn slices' angular spans, re-read every frame so a value tween re-cuts them. */\n private get arcs(): PieArc[] {\n return pieArcs(this.slices, this.ring.startAngle)\n }\n\n /** Which side a leader off bisector `mid` runs to: +1 right, −1 left. */\n private sideOf(mid: number): 1 | -1 {\n return Math.sin(mid) >= 0 ? 1 : -1\n }\n\n /** Whether any slice is labelled on a leader — the only thing that costs a gutter. */\n private get hasLeaderLabels(): boolean {\n return (\n this.labelPlacement === \"leader\" &&\n this.labelMode !== \"none\" &&\n this.arcs.length > 0\n )\n }\n\n // ---- Label sizing ------------------------------------------------------\n\n /**\n * How wide a horizontal label centred in slice `arc`'s band can run before it\n * leaves the wedge, in px.\n *\n * Two things bound it, and which one binds depends entirely on where the wedge\n * sits. Stepping sideways from the label's centre moves you *radially* by\n * `dx·sin(mid)` and *tangentially* by `dx·cos(mid)`, so a wedge at 3 o'clock is\n * limited by the band's thickness (its label runs straight across the band)\n * while a wedge at 12 o'clock is limited by its own arc (its label runs along\n * the band, which is much roomier). Taking the smaller of the two is what stops\n * a label overflowing the ring on one chart and being pointlessly shrunk on the\n * next.\n *\n * Measured off the *unpopped* radius, so a slice growing out of the ring\n * doesn't resize its own text mid-animation.\n */\n private insideRoom(arc: PieArc): number {\n const outer = this.outerRadius\n const hole = this.holeRadius\n const band = hole > 0 ? outer - hole : outer * (1 - PIE_LABEL_RADIUS) * 2\n const mid = hole > 0 ? (hole + outer) / 2 : outer * PIE_LABEL_RADIUS\n const sin = Math.abs(Math.sin(arc.mid))\n const cos = Math.abs(Math.cos(arc.mid))\n const acrossBand = sin > AXIS_EPSILON ? band / sin : Infinity\n const alongArc = cos > AXIS_EPSILON ? ((arc.a1 - arc.a0) * mid) / cos : Infinity\n return Math.max(0, Math.min(acrossBand, alongArc))\n }\n\n /** Widest line, in glyphs at the leading line's size, an inside label runs to. */\n private insideGlyphs(arc: PieArc): number {\n const category = this.slices[arc.index]?.category.length ?? 0\n const percent =\n this.labelMode === \"both\" || this.labelMode === \"percent\"\n ? formatPercent(arc.fraction).length\n : 0\n // The caption is set at a fraction of the leading line, except when it *is*\n // the leading line.\n const caption =\n this.labelMode === \"category\"\n ? category\n : this.labelMode === \"both\"\n ? category * INSIDE_CAPTION_RATIO\n : 0\n return Math.max(1, percent, caption)\n }\n\n /**\n * Font size of an inside label's leading line: what the theme asked for,\n * shrunk if that wouldn't fit inside its wedge. **One size across every inside\n * label** — the smallest any of them needs — because labels at mismatched\n * sizes read as a mistake rather than as emphasis.\n *\n * So the theme's `insideStyle.fontSize` is an upper bound, not a promise: ask\n * for the size the chart wants and the ring keeps it legal.\n */\n private get insideFont(): number {\n let size = sizeOf(this.label.insideStyle, FALLBACK_SIZE * 1.9)\n for (const arc of this.arcs) {\n const fits =\n (this.insideRoom(arc) * INSIDE_LABEL_FIT) /\n (this.insideGlyphs(arc) * GLYPH_RATIO)\n size = Math.min(size, fits)\n }\n return Math.max(1, size)\n }\n\n /** Whether the hole carries a summary at all. */\n private get hasCenterLabel(): boolean {\n return this.centerLabel !== \"\" || this.centerCaption !== \"\"\n }\n\n /**\n * Font size of the hole's summary: the configured size, capped so it still\n * fits across the hole. The cap matters because the hole's radius derives from\n * the box, so a chart that reflows smaller would otherwise push its summary out\n * through the ring.\n */\n private get centerFont(): number {\n const wanted = sizeOf(this.centerStyle.labelStyle, 46)\n const hole = this.holeRadius\n if (hole <= 0) return wanted\n const captionRatio =\n sizeOf(this.centerStyle.captionStyle, 22) / Math.max(1, wanted)\n // Both lines have to clear the hole, and the caption is set at a fraction of\n // this size — so what binds is whichever is wider once drawn, not whichever\n // has more characters.\n const glyphs = Math.max(\n 1,\n this.centerLabel.length,\n this.centerCaption.length * captionRatio\n )\n return Math.min(wanted, (hole * 2 * CENTER_LABEL_FIT) / (glyphs * GLYPH_RATIO))\n }\n\n // ---- Ring geometry, all derived from layoutBounds ------------------------\n\n /** Rim radius of a fully selected slice as a multiple of the ring's, clamped sane. */\n private get growFactor(): number {\n return Math.max(1, Math.min(1.6, this.ring.selectedGrow))\n }\n\n /** How far past the ring's rim a fully selected slice reaches, in px. */\n private get maxPop(): number {\n return this.outerRadius * (this.growFactor - 1)\n }\n\n /** Height of one **leader** label block, in px — the unit those columns are spaced by. */\n private get labelRowHeight(): number {\n // Inside labels are stacked in the band, not in a column beside the ring, so\n // they cost no vertical reserve.\n if (!this.hasLeaderLabels) return 0\n const caption = sizeOf(this.label.textStyle) * 1.25\n const percent = sizeOf(this.label.percentStyle) * 1.25\n if (this.labelMode === \"both\") return caption + percent + 2\n return this.labelMode === \"category\" ? caption : percent\n }\n\n /** Widest line, in glyphs, that a leader label for slice `index` can run to. */\n private captionGlyphs(index: number): number {\n // \"100%\" is the widest a percentage gets; captions are their own length.\n const percent =\n this.labelMode === \"percent\" || this.labelMode === \"both\" ? 4 : 0\n const category =\n this.labelMode === \"category\" || this.labelMode === \"both\"\n ? (this.slices[index]?.category.length ?? 0)\n : 0\n return Math.max(percent, category)\n }\n\n /**\n * Horizontal room reserved on each side for the leaders and their labels, in\n * px — measured **per side**, from the slices that actually leader out that\n * way. A ring whose labels are all inside reserves nothing and draws bigger.\n *\n * Excludes the ring's own selection-growth allowance, which scales with the\n * radius and so is divided out in {@link outerRadius} instead.\n */\n private get labelGutters(): { left: number; right: number } {\n const gutters = { left: BARE_PAD, right: BARE_PAD }\n if (!this.hasLeaderLabels) return gutters\n const glyph = sizeOf(this.label.textStyle) * GLYPH_RATIO\n for (const arc of this.arcs) {\n const room =\n LEADER_LENGTH +\n LABEL_STUB +\n TEXT_INSET +\n this.captionGlyphs(arc.index) * glyph +\n LABEL_PAD\n const key = this.sideOf(arc.mid) > 0 ? \"right\" : \"left\"\n gutters[key] = Math.max(gutters[key], room)\n }\n return gutters\n }\n\n /**\n * How far the ring's centre sits from the node's, in px. Zero while the\n * gutters match; when the labels all fall one way it slides the ring into the\n * empty half so the ring-plus-labels group stays centred in the box instead of\n * the ring sitting dead centre with a bald patch beside it.\n */\n private get centerX(): number {\n const { left, right } = this.labelGutters\n return (left - right) / 2\n }\n\n /**\n * Radius of the ring's outer edge, fitted to whatever the label gutters leave.\n * A fully selected slice reaches `radius × growFactor`, so the room has to\n * cover that — hence the divisor, which keeps even the grown slice inside the\n * box (and holds the ring the same size whether or not anything is selected).\n */\n private get outerRadius(): number {\n const { left, right } = this.labelGutters\n const room = Math.min(\n (this.layoutBounds.width - left - right) / 2,\n this.layoutBounds.height / 2 - this.labelRowHeight - 4\n )\n return Math.max(0, room / this.growFactor)\n }\n\n /** Radius of the hole in the middle: a fraction of the outer radius, or explicit px. */\n private get holeRadius(): number {\n const configured = this.ring.holeRadius\n if (configured <= 0) return 0\n const px = configured < 1 ? configured * this.outerRadius : configured\n return Math.min(px, this.outerRadius * 0.95)\n }\n\n /** Radius of the circle every leader elbows on, clear of even a popped-out slice. */\n private get elbowRadius(): number {\n return this.outerRadius + this.maxPop + LEADER_LENGTH\n }\n\n /** How far slice `index` is currently pushed out, in `[0, 1]`. */\n private popOf(index: number): number {\n return clampUnit(this.pops[index] ?? 0)\n }\n\n /** Radius of slice `index`'s outer edge, its selection growth included. */\n private anchorRadius(index: number): number {\n return this.outerRadius + this.maxPop * this.popOf(index)\n }\n\n /**\n * Radius an inside label sits at on slice `index`: halfway across the band on a\n * donut, and further out than halfway on a solid pie, where \"halfway\" would\n * crowd the point every wedge converges on. Rides the slice's own pop, so an\n * inside label travels out with the wedge it belongs to.\n */\n private bandMidRadius(index: number): number {\n const rim = this.anchorRadius(index)\n const hole = this.holeRadius\n return hole > 0 ? (hole + rim) / 2 : rim * PIE_LABEL_RADIUS\n }\n\n /** Node2D-space point slice `index`'s inside label is centred on. */\n private insideAnchor(index: number): Vector2 {\n const arc = this.arcs.find((a) => a.index === index)\n if (arc === undefined) return { x: this.centerX, y: 0 }\n const point = ringPoint(this.bandMidRadius(index), arc.mid)\n return { x: point.x + this.centerX, y: point.y }\n }\n\n /**\n * How selected the ring is overall, in `[0, 1]` — the most-grown slice. Rides\n * the same tween the pops do, so the dim below arrives with the growth rather\n * than needing an animation of its own.\n */\n private get selectionStrength(): number {\n let strongest = 0\n for (let i = 0; i < this.slices.length; i++) {\n strongest = Math.max(strongest, this.popOf(i))\n }\n return strongest\n }\n\n /**\n * Opacity slice `index` (and its leader and label) is drawn at: full while\n * nothing is selected, falling toward {@link dimOpacity} in proportion to how\n * selected the *rest* of the ring is. A slice that's selected itself stays at\n * full — the two factors cancel.\n */\n private sliceOpacity(index: number): number {\n const focus = this.selectionStrength\n if (focus <= 0) return 1\n return 1 - (1 - clampUnit(this.dimOpacity)) * focus * (1 - this.popOf(index))\n }\n\n // ---- Label placement ---------------------------------------------------\n\n /**\n * Where every leader label sits and how its leader gets there. Labels are split\n * into a right and a left column by their slice's bisector, each column is\n * spread so nothing overlaps (and nothing leaves the box), and each leader is\n * then built as wedge edge → elbow → label. The elbow rides the leader circle at\n * the label's *settled* y, which keeps the final run horizontal and the whole\n * polyline outside the ring however far the label was nudged.\n */\n private placements(): LabelPlacement[] {\n if (!this.hasLeaderLabels) return []\n const arcs = this.arcs\n if (arcs.length === 0 || this.outerRadius <= 0) return []\n\n const radius = this.elbowRadius\n const rowHeight = this.labelRowHeight\n const top = this.layoutBounds.height / 2 - rowHeight / 2\n const bottom = -top\n const cx = this.centerX\n\n interface Slot {\n index: number\n side: 1 | -1\n mid: number\n ideal: number\n y: number\n }\n const right: Slot[] = []\n const left: Slot[] = []\n for (const arc of arcs) {\n const side = this.sideOf(arc.mid)\n const ideal = ringPoint(radius, arc.mid).y\n ;(side > 0 ? right : left).push({\n index: arc.index,\n side,\n mid: arc.mid,\n ideal,\n y: ideal,\n })\n }\n relaxLabels(right, rowHeight + LABEL_GAP, top, bottom)\n relaxLabels(left, rowHeight + LABEL_GAP, top, bottom)\n\n return [...right, ...left].map((slot) => {\n const start = ringPoint(\n this.anchorRadius(slot.index) + LEADER_LIFTOFF,\n slot.mid\n )\n // The elbow sits where the label's y crosses the leader circle. Solving\n // x² + y² = radius² for x keeps it *on* that circle (never inside the\n // ring); a label pushed past the circle's top or bottom falls back to the\n // sideways floor so its leader still reads as pointing outward.\n const onCircle = Math.sqrt(\n Math.max(0, radius * radius - slot.y * slot.y)\n )\n const elbowX = cx + slot.side * Math.max(onCircle, radius * MIN_ELBOW)\n const endX = cx + slot.side * (radius + LABEL_STUB)\n\n return {\n index: slot.index,\n side: slot.side,\n leader: [\n { x: start.x + cx, y: start.y },\n { x: elbowX, y: slot.y },\n { x: endX, y: slot.y },\n ],\n text: { x: endX + slot.side * TEXT_INSET, y: slot.y },\n }\n })\n }\n\n /** This frame's placement for slice `index`, or `undefined` if it isn't leadered. */\n private placementFor(index: number): LabelPlacement | undefined {\n return this.placements().find((placement) => placement.index === index)\n }\n\n // ---- Draw --------------------------------------------------------------\n\n /**\n * The ring's paints live nowhere `ShapeNode` would look: one fill per slice,\n * resolved once into {@link fills}, plus the leader line's own stroke. Both are\n * passed explicitly — see {@link declarePaints}.\n */\n override declareAssets(assets: AssetScope): void {\n super.declareAssets(assets)\n declarePaints(this, assets, {\n fills: this.fills,\n strokes: [strokeOps.resolve(this.label.leaderStroke)],\n })\n }\n\n protected renderSelf(ctx: RenderContext2D): void {\n const rOuter = this.outerRadius\n if (rOuter <= 0) return\n\n const arcs = this.arcs\n if (arcs.length === 0) return\n\n const sweep = clampUnit(this.growth)\n const revealEnd = this.ring.startAngle * (Math.PI / 180) + Math.PI * 2 * sweep\n const hole = this.holeRadius\n const halfGap = Math.max(0, this.ring.gap) / 2\n\n // One shared frame (the whole ring square, grown slices included) pinned onto\n // every wedge path, so `Graphics2D.path()` doesn't re-centre each wedge on its\n // own bbox and collapse them all onto the origin. It stays symmetric about the\n // *node's* origin — so path coordinates land exactly in node space — and is\n // grown by the ring's sideways offset rather than moved with it, which would\n // undo the very shift it has to contain.\n const cx = this.centerX\n const half = rOuter + this.maxPop + 1 + Math.abs(cx)\n const centerBounds: [number, number, number, number] = [\n -half,\n -half,\n half,\n half,\n ]\n // Explicit width/height alongside centerBounds: a wedge path's command list\n // carries no implied extent, so a slice filled with an image or a video needs\n // this to size its decode.\n const span = half * 2\n\n for (const arc of arcs) {\n // Clip the wedge to the growth-revealed arc; skip it until the sweep\n // reaches it, so the ring draws in as one clockwise wipe.\n const a1 = Math.min(arc.a1, revealEnd)\n if (a1 <= arc.a0) continue\n\n const path = wedgePath(\n arc.a0,\n a1,\n hole,\n this.anchorRadius(arc.index),\n halfGap,\n cx\n )\n const g = new Graphics2D().path(\n path.toPathState({ centerBounds, width: span, height: span })\n )\n g.shadow(this.ring.shadow)\n g.fill(this.fills[arc.index] ?? [])\n g.stroke(this.ring.stroke)\n ctx.draw(g.opacity(this.sliceOpacity(arc.index)))\n }\n\n // Leaders. Each polyline gets its own `Graphics2D`: several open polylines\n // chained into one command list are combined into a single surface before the\n // stroke, which closes each contour back on itself and paints the leaders as\n // thin triangles.\n const reveal = clampUnit(this.labelReveal)\n if (reveal <= 0.001) return\n for (const placement of this.placements()) {\n ctx.draw(\n new Graphics2D()\n .line({ points: placement.leader, radius: ELBOW_RADIUS })\n .stroke(this.label.leaderStroke)\n .opacity(reveal * this.sliceOpacity(placement.index))\n )\n }\n }\n\n // ---- Entrance ----------------------------------------------------------\n\n /** Tween {@link growth} `0 → 1`, wiping the wedges in around the ring. */\n @command()\n sweep(\n args: CommandArgs<Record<string, never>> & { duration: number }\n ): Command<RingSweep> {\n // No priming to 0 first — the value at `t` is a function of `t`, so a repeat\n // call re-sweeps by definition rather than by having reset something.\n return this.command<RingSweep>((t) => ({ growth: t }), args.duration, args.easing ?? easeOut())\n }\n\n /** Tween {@link labelReveal} `0 → 1`, fading the leaders and captions up. */\n @command()\n revealLabels(\n args: CommandArgs<Record<string, never>> & { duration: number }\n ): Command<RingLabels> {\n return this.command<RingLabels>((t) => ({ labelReveal: t }), args.duration, args.easing ?? easeOut())\n }\n\n /**\n * Fade the hole's summary up. Deliberately not part of the ring's entrance: a\n * hole label is usually the chart's conclusion, so the scene decides when it\n * lands.\n */\n @command()\n revealCenter(\n args: CommandArgs<Record<string, never>> & { duration?: number } = {}\n ): Command<RingCenter> {\n const from = clampUnit(this.centerReveal)\n return this.command<RingCenter>(\n (t) => ({ centerReveal: from + (1 - from) * t }),\n args.duration ?? 0.6,\n args.easing ?? easeOut()\n )\n }\n}\n\n/** The signals the ring's own commands move, as props types. */\ntype RingSweep = { growth: number }\ntype RingLabels = { labelReveal: number }\ntype RingCenter = { centerReveal: number }\n", "/**\n * Types and pure helpers specific to the {@link PieChart} family: how rows\n * become slices, how slices become arcs on a ring, where the labels around it\n * end up, and the theme all of that is drawn from.\n *\n * The one chart in the family that draws **one mark per row** rather than one\n * per column, which is why almost nothing about its geometry is shared: there is\n * no scale, no axis and no baseline, only a circle divided by share. What it\n * *does* share — the legend, the theme leaf resolvers, the palette, the\n * percentage format — comes from `nodes/chart-kit/shared.ts`.\n *\n * Kept renderer-agnostic (no `Node` imports) so the studio's inspector mapping\n * (`pie-chart-props.ts`) can build a partial {@link PieChartTheme} without\n * pulling the node classes in.\n */\n\nimport {\n lerpNumber,\n shadowOps,\n strokeOps,\n textOps,\n type DataRecord,\n type Fill,\n type Shadow,\n type ShadowResolved,\n type Stroke,\n type StrokeResolved,\n type TextStyle,\n type Vector2,\n} from \"@motionscript/core\"\n\nimport {\n CHART_PALETTE,\n DEFAULT_LEGEND,\n DEFAULT_SERIES_FILLS,\n SPOTLIGHT_DIM,\n lerpLegendConfig,\n lerpSeriesFills,\n num,\n resolveLegendConfig,\n resolveSeriesFills,\n text,\n type LegendConfig,\n type LegendTheme,\n} from \"../kit/shared\"\n\n/**\n * One **resolved** slice of a {@link PieChart}: its caption, its magnitude, and\n * the paint its wedge and legend swatch take.\n *\n * This is what the ring and the legend consume, not what a scene authors: the\n * chart takes tabular `data` plus two field names and resolves it into these, so\n * the parts stay unaware of where a slice's caption, magnitude and paint came\n * from.\n */\nexport interface PieSlice {\n /** Legend caption and label text for this slice. */\n category: string\n /** This slice's magnitude. Its wedge covers `value / (sum of all values)`. */\n value: number\n /** Wedge and swatch paint. */\n fill: Fill\n}\n\n/**\n * Resolve tabular rows into slices: one per row, captioned by the `category`\n * column, sized by the `value` column, painted from `palette` by row order\n * (cycling when the palette is shorter than the data).\n *\n * Paint comes from the palette rather than from the rows because a pie draws one\n * mark per *row* — unlike the line and bar charts, where a colour belongs to a\n * series (a whole column) and rides on the series entry instead. Keeping it out\n * of the rows is what lets a pie be fed straight from a CSV of plain numbers.\n */\nexport function resolvePieSlices(\n data: DataRecord[],\n category: string,\n value: string,\n palette: Fill[]\n): PieSlice[] {\n return data.map((row, i) => ({\n category: text(row, category),\n value: num(row, value),\n fill:\n palette.length > 0 ? palette[i % palette.length] : (\"white\" as Fill),\n }))\n}\n\n/** What a {@link PieChart}'s labels say. */\nexport type PieLabelMode =\n /**\n * The category and its percentage, two lines. Their order flips with the\n * placement: a **leader** label reads category over percentage, an **inside**\n * label reads the percentage big over a small category caption — inside a\n * wedge the reader already knows which slice they're on, so the magnitude is\n * the thing being read.\n */\n | \"both\"\n /** The category only. */\n | \"category\"\n /** The percentage only — the pick when a legend already names the categories. */\n | \"percent\"\n /** No labels and no leader lines at all. */\n | \"none\"\n\n/**\n * Where a slice's label sits.\n *\n * `\"inside\"` drops the leader line entirely and centres the label in the wedge's\n * own band, which costs no gutter — so the ring draws bigger — and costs the eye\n * no round trip. `\"leader\"` is the classic treatment, and what a sliver too\n * narrow to hold text needs.\n */\nexport type PieLabelPlacement = \"inside\" | \"leader\" | \"none\"\n\n/** Degrees → radians. */\nexport const DEG = Math.PI / 180\n\n/** A slice's plotted value: negatives and non-finite values count as 0. */\nexport function sliceValue(slice: PieSlice): number {\n return slice.value > 0 && Number.isFinite(slice.value) ? slice.value : 0\n}\n\n/** Sum of every slice's plotted value — the denominator every share is taken against. */\nexport function sliceTotal(slices: PieSlice[]): number {\n let sum = 0\n for (const slice of slices) sum += sliceValue(slice)\n return sum\n}\n\n/**\n * The point at radius `r` and angle `a` on a ring centred on the origin, in the\n * node's centred, **y-up** space. Angles run **clockwise from 12 o'clock**:\n * `a = 0` points straight up (+y), and increasing `a` sweeps toward +x then −y.\n */\nexport function ringPoint(r: number, a: number): Vector2 {\n return { x: r * Math.sin(a), y: r * Math.cos(a) }\n}\n\n/** One slice's resolved angular span on the ring. */\nexport interface PieArc {\n /** Index of the slice in the source list. */\n index: number\n /** Start angle, radians clockwise from 12 o'clock. */\n a0: number\n /** End angle, radians clockwise from 12 o'clock. */\n a1: number\n /** Bisector angle — where this slice's label and pop-out point. */\n mid: number\n /** This slice's share of the total, in `[0, 1]`. */\n fraction: number\n}\n\n/**\n * Resolve slices into per-slice arcs: each covers exactly its share of the\n * circle, walking clockwise from `startDegrees` (0 = 12 o'clock), with the arcs\n * abutting. Zero/negative slices are skipped entirely; an all-zero list yields\n * no arcs.\n *\n * Arcs are the *undivided* shares — the gap between neighbours is not carved out\n * here, because a fixed angular gap reads as a wedge: wide at the rim, pinched\n * shut at the hole. It's applied when the wedge is drawn instead, as a\n * constant-width inset off both ends of every arc, so a slice still covers its\n * true percentage of the circle.\n */\nexport function pieArcs(slices: PieSlice[], startDegrees: number): PieArc[] {\n const total = sliceTotal(slices)\n if (total <= 0) return []\n\n const arcs: PieArc[] = []\n let cursor = startDegrees * DEG\n slices.forEach((slice, index) => {\n const value = sliceValue(slice)\n if (value <= 0) return\n const fraction = value / total\n const a0 = cursor\n const a1 = a0 + fraction * Math.PI * 2\n cursor = a1\n arcs.push({ index, a0, a1, mid: (a0 + a1) / 2, fraction })\n })\n return arcs\n}\n\n/** A label competing for vertical space in one of the two label columns. */\nexport interface LabelSlot {\n /** Where the label would sit if nothing else were in the way. */\n ideal: number\n /** Where it ends up once the column has been de-conflicted. Written by {@link relaxLabels}. */\n y: number\n}\n\n/**\n * Spread one column of labels so no two overlap, keeping each as close to its\n * `ideal` y as possible and the whole column inside `[bottom, top]`.\n *\n * Two passes, the standard treatment. Walking top-to-bottom, each label takes\n * the highest slot at or below its ideal that still clears the label above it by\n * `minGap`; if that pushes the tail past `bottom`, a reverse pass lifts it back\n * up. When there are more labels than the column can hold, the reverse pass wins\n * and the crowding is shared evenly rather than dumped off the bottom edge.\n *\n * Mutates each slot's `y` and returns `slots` re-ordered top-to-bottom.\n */\nexport function relaxLabels<T extends LabelSlot>(\n slots: T[],\n minGap: number,\n top: number,\n bottom: number\n): T[] {\n slots.sort((a, b) => b.ideal - a.ideal)\n\n let ceiling = top\n for (const slot of slots) {\n slot.y = Math.min(slot.ideal, ceiling)\n ceiling = slot.y - minGap\n }\n\n let floor = bottom\n for (let i = slots.length - 1; i >= 0; i--) {\n slots[i].y = Math.max(slots[i].y, floor)\n floor = slots[i].y + minGap\n }\n\n return slots\n}\n\n// ---- Theme ---------------------------------------------------------------\n\n/** The ring's geometry and paint — everything about the wedges but their colour. */\nexport interface RingConfig {\n /**\n * Width of the channel between adjacent slices, in px, held constant all the\n * way across the band — the same clearance at the hole as at the rim. (An\n * angular gap would splay open toward the rim instead.)\n */\n gap: number\n /**\n * Radius of the hole punched through the middle. Below 1 it reads as a\n * fraction of the ring's outer radius (`0.6` leaves a band 40% of the radius\n * thick); 1 and up as an explicit pixel radius; `0` fills the middle in,\n * drawing solid pie wedges.\n */\n holeRadius: number\n /** Angle in degrees where the first slice begins, clockwise from 12 o'clock. */\n startAngle: number\n /**\n * How far out a selected slice's rim reaches, as a **multiple of the ring's\n * outer radius**. Being relative, the emphasis scales with the chart instead\n * of shrinking on a big one and swamping a small one. Clamped to `[1, 1.6]`.\n */\n selectedGrow: number\n /** Optional outline on every wedge. */\n stroke: Stroke\n /** Optional drop shadow on every wedge. */\n shadow: Shadow\n}\n\n/** How the ring's labels are set, and the leader lines that point at them. */\nexport interface LabelConfig {\n /** The category caption on a label. */\n textStyle: TextStyle\n /** The percentage on a label. */\n percentStyle: TextStyle\n /**\n * Ink for a label set **inside** its wedge, both lines. Separate because an\n * inside label sits on the wedge's own paint rather than on the background, so\n * it usually wants the opposite end of the value range from everything else.\n */\n insideStyle: TextStyle\n /** The leader line running from a wedge out to its label. */\n leaderStroke: Stroke\n}\n\n/** The summary set in the ring's hole. */\nexport interface CenterConfig {\n labelStyle: TextStyle\n captionStyle: TextStyle\n}\n\n/** A {@link PieChart}'s full, resolved visual theme. */\nexport interface PieChartConfig {\n /** Wedge and swatch paint, cycled by **row** index (`i % length`). */\n sliceFills: Fill[]\n ring: RingConfig\n label: LabelConfig\n center: CenterConfig\n legend: LegendConfig\n /** How far everything outside the selection fades while any slice is selected. */\n spotlight: { dimOpacity: number }\n}\n\n/** Author-facing partial theme — every branch optional and deep-merged. */\nexport type PieChartTheme = {\n sliceFills?: Fill[]\n ring?: Partial<RingConfig>\n label?: Partial<LabelConfig>\n center?: Partial<CenterConfig>\n legend?: LegendTheme\n spotlight?: Partial<PieChartConfig[\"spotlight\"]>\n}\n\nconst LABEL_TEXT_STYLE: TextStyle = {\n fontSize: 26,\n fontWeight: 600,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n}\n\n/** The pie chart's out-of-the-box look. */\nexport const DEFAULT_PIE_CHART_THEME: PieChartConfig = {\n sliceFills: DEFAULT_SERIES_FILLS,\n ring: {\n gap: 6,\n // A donut rather than a solid pie by default: the hole is the largest\n // uninterrupted area the chart owns and the one place a conclusion can be\n // set. `0` here would give that away before anyone had asked for it.\n holeRadius: 0.6,\n startAngle: 0,\n selectedGrow: 1.08,\n stroke: [],\n shadow: [],\n },\n label: {\n textStyle: LABEL_TEXT_STYLE,\n percentStyle: { ...LABEL_TEXT_STYLE, fontWeight: 700 },\n // Inside labels sit on the wedge's paint, which is a mid-tone from the\n // palette — so they lead with the panel colour rather than the axis ink.\n insideStyle: { ...LABEL_TEXT_STYLE, fontWeight: 800, fill: \"#15130e\" },\n leaderStroke: { weight: 2, fill: CHART_PALETTE.markerColor },\n },\n center: {\n labelStyle: {\n fontSize: 46,\n fontWeight: 800,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n },\n captionStyle: {\n fontSize: 22,\n fontWeight: 500,\n textAlign: \"center\",\n fill: CHART_PALETTE.markerColor,\n },\n },\n legend: DEFAULT_LEGEND,\n spotlight: { dimOpacity: SPOTLIGHT_DIM },\n}\n\nfunction resolveRingConfig(\n ring: Partial<RingConfig> | undefined,\n previous: RingConfig\n): RingConfig {\n return {\n gap: ring?.gap ?? previous.gap,\n holeRadius: ring?.holeRadius ?? previous.holeRadius,\n startAngle: ring?.startAngle ?? previous.startAngle,\n selectedGrow: ring?.selectedGrow ?? previous.selectedGrow,\n stroke: strokeOps.resolve(ring?.stroke ?? previous.stroke),\n shadow: shadowOps.resolve(ring?.shadow ?? previous.shadow),\n }\n}\n\nfunction lerpRingConfig(\n from: RingConfig,\n to: RingConfig,\n t: number\n): RingConfig {\n return {\n gap: lerpNumber(from.gap, to.gap, t),\n holeRadius: lerpNumber(from.holeRadius, to.holeRadius, t),\n startAngle: lerpNumber(from.startAngle, to.startAngle, t),\n selectedGrow: lerpNumber(from.selectedGrow, to.selectedGrow, t),\n stroke: strokeOps.lerp(\n from.stroke as StrokeResolved[],\n to.stroke as StrokeResolved[],\n t\n ),\n shadow: shadowOps.lerp(\n from.shadow as ShadowResolved[],\n to.shadow as ShadowResolved[],\n t\n ),\n }\n}\n\nfunction resolveLabelConfig(\n label: Partial<LabelConfig> | undefined,\n previous: LabelConfig\n): LabelConfig {\n return {\n textStyle: textOps.resolveStyle(label?.textStyle, previous.textStyle),\n percentStyle: textOps.resolveStyle(\n label?.percentStyle,\n previous.percentStyle\n ),\n insideStyle: textOps.resolveStyle(label?.insideStyle, previous.insideStyle),\n leaderStroke: strokeOps.resolve(\n label?.leaderStroke ?? previous.leaderStroke\n ),\n }\n}\n\nfunction lerpLabelConfig(\n from: LabelConfig,\n to: LabelConfig,\n t: number\n): LabelConfig {\n return {\n textStyle: textOps.lerpStyle(from.textStyle, to.textStyle, t),\n percentStyle: textOps.lerpStyle(from.percentStyle, to.percentStyle, t),\n insideStyle: textOps.lerpStyle(from.insideStyle, to.insideStyle, t),\n leaderStroke: strokeOps.lerp(\n from.leaderStroke as StrokeResolved[],\n to.leaderStroke as StrokeResolved[],\n t\n ),\n }\n}\n\nfunction resolveCenterConfig(\n center: Partial<CenterConfig> | undefined,\n previous: CenterConfig\n): CenterConfig {\n return {\n labelStyle: textOps.resolveStyle(center?.labelStyle, previous.labelStyle),\n captionStyle: textOps.resolveStyle(\n center?.captionStyle,\n previous.captionStyle\n ),\n }\n}\n\nfunction lerpCenterConfig(\n from: CenterConfig,\n to: CenterConfig,\n t: number\n): CenterConfig {\n return {\n labelStyle: textOps.lerpStyle(from.labelStyle, to.labelStyle, t),\n captionStyle: textOps.lerpStyle(from.captionStyle, to.captionStyle, t),\n }\n}\n\n/**\n * {@link PieChart}'s `theme` @property mapper: merges the author's partial\n * {@link PieChartTheme} onto `previous` (falling back to\n * {@link DEFAULT_PIE_CHART_THEME} on first resolution), fully resolving every\n * leaf so the tween below always has resolved data to interpolate.\n */\nexport function resolvePieChartTheme(\n theme: PieChartTheme | undefined,\n previous?: PieChartConfig\n): PieChartConfig {\n const base = previous ?? DEFAULT_PIE_CHART_THEME\n return {\n sliceFills: resolveSeriesFills(theme?.sliceFills, base.sliceFills),\n ring: resolveRingConfig(theme?.ring, base.ring),\n label: resolveLabelConfig(theme?.label, base.label),\n center: resolveCenterConfig(theme?.center, base.center),\n legend: resolveLegendConfig(theme?.legend, base.legend),\n spotlight: {\n dimOpacity: theme?.spotlight?.dimOpacity ?? base.spotlight.dimOpacity,\n },\n }\n}\n\n/** {@link PieChart}'s `theme` @property tween — leaf by leaf. */\nexport function lerpPieChartTheme(\n from: PieChartConfig,\n to: PieChartConfig,\n t: number\n): PieChartConfig {\n return {\n // A palette is a list of independent paints rather than one value, so it\n // interpolates pairwise by index; one that grows or shrinks holds the longer\n // side's tail, exactly as the cartesian charts' palettes do.\n sliceFills: lerpSeriesFills(from.sliceFills, to.sliceFills, t),\n ring: lerpRingConfig(from.ring, to.ring, t),\n label: lerpLabelConfig(from.label, to.label, t),\n center: lerpCenterConfig(from.center, to.center, t),\n legend: lerpLegendConfig(from.legend, to.legend, t),\n spotlight: {\n dimOpacity: lerpNumber(\n from.spotlight.dimOpacity,\n to.spotlight.dimOpacity,\n t\n ),\n },\n }\n}\n", "import { LineChart } from \"./line-chart\";\nimport { BarChart } from \"./bar-chart\";\nimport { PieChart } from \"./pie-chart\";\n\n/**\n * Every node type this package publishes.\n *\n * A host registers these by handing the array to an engine —\n * `new Engine(platform, { nodes: NODES })` — which reads each class's `@node()`\n * key. The decorator only *declares* that key: nothing is registered by the mere\n * act of importing a module, so a registry holds exactly what its owner asked\n * for and two engines in one process can differ about it.\n *\n * Listing class **values** is also what survives bundling. This package declares\n * `sideEffects: false`, which licenses a bundler to drop a module nothing\n * imports a value from, and a document names a node type by string — so\n * `import \"./line-chart\"` for its side effect is exactly what gets shaken out, where an\n * array of bindings is a data dependency that cannot be.\n */\nexport const NODES = [LineChart, BarChart, PieChart];\n"],
5
+ "mappings": "mUAAA,OACE,WAAAA,EACA,QAAAC,GACA,UAAAC,GACA,QAAAC,GACA,WAAAC,EACA,YAAAC,MAOK,qBAiBP,OAAS,gBAAAC,OAAoB,+BC9B7B,OAEE,cAAAC,GACA,UAAAC,GACA,WAAAC,GACA,WAAAC,GACA,aAAAC,GACA,YAAAC,OASK,qBCCP,OACE,SAAAC,GACA,WAAAC,GACA,cAAAC,GACA,aAAAC,OAMK,qBA+EA,IAAMC,GAA8B,CACzC,KAAM,EACN,MAAO,EACP,OAAQ,EACR,IAAK,EACL,SAAU,GACV,SAAU,IAIN,SAAUC,GAAUC,EAAc,CACtC,OAAOA,EAAI,MAAQA,EAAI,MAAQA,EAAI,IAAMA,EAAI,MAC/C,CAWM,SAAUC,GACdC,EACAC,EACAC,EACAC,EAAa,CAEb,GAAI,CAACH,GAAWA,EAAO,IAAM,QAAaA,EAAO,IAAM,OACrD,OAAOJ,GAGT,IAAMQ,EAAO,CACXC,EACA,CAACC,EAAKC,CAAG,EACTC,EACAC,IAC6B,CAC7B,GAAIJ,IAAU,QAAaE,GAAOD,EAAK,MAAO,CAACE,EAAIC,EAAI,EAAK,EAC5D,IAAMC,EAAI,KAAK,IAAIL,EAAM,CAAC,EAAGA,EAAM,CAAC,CAAC,EAC/BM,GAAI,KAAK,IAAIN,EAAM,CAAC,EAAGA,EAAM,CAAC,CAAC,EAC/BO,GAAQC,IAAcL,GAAOK,GAAIP,IAAQC,EAAMD,IAASG,EAAKD,GACnE,MAAO,CACL,KAAK,IAAIA,EAAI,KAAK,IAAIC,EAAIG,GAAKF,CAAC,CAAC,CAAC,EAClC,KAAK,IAAIF,EAAI,KAAK,IAAIC,EAAIG,GAAKD,EAAC,CAAC,CAAC,EAClC,GAEJ,EAEM,CAACG,EAAMC,EAAOC,CAAQ,EAAIZ,EAAKJ,EAAO,EAAGE,EAAQD,EAAK,KAAMA,EAAK,KAAK,EACtE,CAACgB,EAAQC,EAAKC,CAAQ,EAAIf,EAAKJ,EAAO,EAAGG,EAAQF,EAAK,OAAQA,EAAK,GAAG,EAC5E,MAAO,CAAE,KAAAa,EAAM,MAAAC,EAAO,OAAAE,EAAQ,IAAAC,EAAK,SAAAF,EAAU,SAAAG,CAAQ,CACvD,CAgBM,SAAUC,GACdC,EACAvB,EAAc,CAEd,GAAIuB,EAAO,OAAS,GAAK,CAACxB,GAAUC,CAAG,EAAG,MAAO,CAAA,EAEjD,IAAMwB,EAAwB,CAAA,EAC1BC,EAAqB,CAAA,EAEzB,QAASC,EAAI,EAAGA,EAAIH,EAAO,OAAQG,IAAK,CACtC,IAAMC,EAAMC,GAAYL,EAAOG,EAAI,CAAC,EAAGH,EAAOG,CAAC,EAAG1B,CAAG,EACrD,GAAI,CAAC2B,EAAK,CAEJF,EAAI,QAAU,GAAGD,EAAK,KAAKC,CAAG,EAClCA,EAAM,CAAA,EACN,QACF,CACA,GAAM,CAACb,EAAGC,CAAC,EAAIc,EAGXF,EAAI,OAAS,GAAKI,GAAUJ,EAAIA,EAAI,OAAS,CAAC,EAAGb,CAAC,EAAGa,EAAI,KAAKZ,CAAC,GAE7DY,EAAI,QAAU,GAAGD,EAAK,KAAKC,CAAG,EAClCA,EAAM,CAACb,EAAGC,CAAC,EAEf,CACA,OAAIY,EAAI,QAAU,GAAGD,EAAK,KAAKC,CAAG,EAC3BD,CACT,CAGA,SAASK,GAAUjB,EAAgBC,EAAc,CAC/C,OAAO,KAAK,IAAID,EAAE,EAAIC,EAAE,CAAC,EAAI,IAAO,KAAK,IAAID,EAAE,EAAIC,EAAE,CAAC,EAAI,EAC5D,CAMA,SAASe,GACPhB,EACAC,EACAb,EAAc,CAEd,IAAM8B,EAAKjB,EAAE,EAAID,EAAE,EACbmB,EAAKlB,EAAE,EAAID,EAAE,EACfoB,EAAK,EACLC,EAAK,EAGHC,EAA4B,CAChC,CAAC,CAACJ,EAAIlB,EAAE,EAAIZ,EAAI,IAAI,EACpB,CAAC8B,EAAI9B,EAAI,MAAQY,EAAE,CAAC,EACpB,CAAC,CAACmB,EAAInB,EAAE,EAAIZ,EAAI,MAAM,EACtB,CAAC+B,EAAI/B,EAAI,IAAMY,EAAE,CAAC,GAEpB,OAAW,CAACuB,EAAGC,CAAC,IAAKF,EAAO,CAC1B,GAAIC,IAAM,EAAG,CAEX,GAAIC,EAAI,EAAG,OAAO,KAClB,QACF,CACA,IAAMC,EAAID,EAAID,EACd,GAAIA,EAAI,EAAG,CACT,GAAIE,EAAIJ,EAAI,OAAO,KACfI,EAAIL,IAAIA,EAAKK,EACnB,KAAO,CACL,GAAIA,EAAIL,EAAI,OAAO,KACfK,EAAIJ,IAAIA,EAAKI,EACnB,CACF,CACA,MAAO,CACL,CAAE,EAAGzB,EAAE,EAAIoB,EAAKF,EAAI,EAAGlB,EAAE,EAAIoB,EAAKD,CAAE,EACpC,CAAE,EAAGnB,EAAE,EAAIqB,EAAKH,EAAI,EAAGlB,EAAE,EAAIqB,EAAKF,CAAE,EAExC,CAoDA,IAAMO,GAAkC,CACtC,GAAGC,EACH,YAAa,CACX,GAAGA,EAAe,YAClB,MAAO,GACP,OAAQ,GACR,aAAc,IAKLC,EAAmC,CAC9C,aAAcC,GACd,MAAOC,EACP,MAAOC,GACP,SAAUC,GACV,OAAQN,GACR,OAAQ,CAGN,OAAQ,CACN,OAAQ,EACR,KAAMO,EAAc,YACpB,KAAM,CAAC,GAAI,EAAE,EACb,IAAK,QAIP,KAAMC,GAAM,MAAMD,EAAc,YAAa,CAAE,QAAS,GAAI,CAAE,EAC9D,WAAYE,IAIhB,SAASC,GACP9C,EACA+C,EAAsB,CAEtB,MAAO,CACL,OAAQC,GAAU,QAAQhD,GAAQ,QAAU+C,EAAS,MAAM,EAC3D,KAAME,GAAQ,QAAQjD,GAAQ,MAAQ+C,EAAS,IAAI,EACnD,WAAY/C,GAAQ,YAAc+C,EAAS,WAE/C,CAEA,SAASG,GACPC,EACAC,EACA,EAAS,CAET,MAAO,CACL,OAAQJ,GAAU,KAChBG,EAAK,OACLC,EAAG,OACH,CAAC,EAEH,KAAMH,GAAQ,KACZE,EAAK,KACLC,EAAG,KACH,CAAC,EAEH,WAAYC,GAAWF,EAAK,WAAYC,EAAG,WAAY,CAAC,EAE5D,CAOM,SAAUE,GACdC,EACAR,EAAsB,CAEtB,IAAMS,EAAOT,GAAYT,EACzB,MAAO,CACL,aAAcmB,GAAqBF,GAAO,aAAcC,EAAK,YAAY,EACzE,MAAOE,GAAkBH,GAAO,MAAOC,EAAK,KAAK,EACjD,MAAOE,GAAkBH,GAAO,MAAOC,EAAK,KAAK,EACjD,SAAUG,GAAgBJ,GAAO,SAAUC,EAAK,QAAQ,EACxD,OAAQI,GAAoBL,GAAO,OAAQC,EAAK,MAAM,EACtD,OAAQV,GAAoBS,GAAO,OAAQC,EAAK,MAAM,EAE1D,CAGM,SAAUK,GACdV,EACAC,EACA,EAAS,CAET,MAAO,CACL,aAAcU,GAAkBX,EAAK,aAAcC,EAAG,aAAc,CAAC,EACrE,MAAOW,GAAeZ,EAAK,MAAOC,EAAG,MAAO,CAAC,EAC7C,MAAOW,GAAeZ,EAAK,MAAOC,EAAG,MAAO,CAAC,EAC7C,SAAUY,GAAab,EAAK,SAAUC,EAAG,SAAU,CAAC,EACpD,OAAQa,GAAiBd,EAAK,OAAQC,EAAG,OAAQ,CAAC,EAClD,OAAQF,GAAiBC,EAAK,OAAQC,EAAG,OAAQ,CAAC,EAEtD,CAiBM,SAAUc,GACdC,EACAC,EACAC,EAAkB,CAElB,OAAOF,EACJ,IAAI,CAACG,EAAK9C,IAAwB,CAAC4C,EAAE,GAAG5C,CAAC,EAAG+C,EAAID,EAAKD,EAAO,KAAK,CAAC,CAAC,EACnE,KAAK,CAAC3D,EAAGC,IAAMD,EAAE,CAAC,EAAIC,EAAE,CAAC,CAAC,CAC/B,CAGM,SAAU6D,GAAQL,EAAoBE,EAAoB,CAC9D,IAAI7D,EAAK,IACLC,EAAK,KACT,QAAW6D,KAAOH,EAChB,QAAW,KAAKE,EAAQ,CACtB,IAAMxD,EAAI0D,EAAID,EAAK,EAAE,KAAK,EACtBzD,EAAIL,IAAIA,EAAKK,GACbA,EAAIJ,IAAIA,EAAKI,EACnB,CACF,OAAOL,GAAMC,EAAK,CAACD,EAAIC,CAAE,EAAI,CAAC,EAAG,CAAC,CACpC,CASM,SAAUgE,GACdN,EACAE,EACAhE,EACAqE,EAAiB,CAEjB,GAAIrE,EAAO,OAAOA,EAClB,GAAM,CAACG,EAAIC,CAAE,EAAI+D,GAAQL,EAAME,CAAM,EAC/BM,EAAW,KAAK,IAAI,EAAG,KAAK,MAAMD,CAAS,CAAC,EAC5CE,EAAOC,IAAUpE,EAAKD,GAAMmE,GAAY,CAAC,EAC/C,MAAO,CAAC,KAAK,MAAMnE,EAAKoE,CAAI,EAAIA,EAAM,KAAK,KAAKnE,EAAKmE,CAAI,EAAIA,CAAI,CACnE,CDjbA,OAAS,iBAAAE,OAAqB,yWA4CjBC,EAAP,cAAyBC,EAAsB,CAa1C,cAAcC,EAAkB,CACvC,MAAM,cAAcA,CAAM,EAC1BH,GAAc,KAAMG,CAAM,CAC5B,CAEU,WAAWC,EAAoB,CACvC,GAAI,CAAC,KAAK,QAAS,OACnB,IAAMC,EAAM,KAAK,OACjB,GAAIA,EAAI,OAAS,EAAG,OAEpB,IAAMC,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,KAAK,QAAQ,CAAC,EAChD,GAAIA,GAAK,EAAG,OAEZ,IAAMC,EAAQ,KAAK,YAAYF,EAAKC,CAAC,EACrC,GAAIC,EAAM,OAAS,EAAG,OACtB,IAAMC,EAAS,KAAK,OACdC,GAAUD,EAAO,CAAC,GAAG,QAAU,GAAK,EAEpCE,EAAM,KAAK,UACjB,GAAI,CAACC,GAAUD,CAAG,EAAG,CACnBN,EAAI,KAAK,IAAIQ,GAAU,EAAG,KAAK,CAAE,OAAQL,EAAO,OAAAE,CAAM,CAAE,EAAE,OAAOD,CAAM,CAAC,EACxE,MACF,CAOAJ,EAAI,KACF,IAAIQ,GAAU,EACX,KAAK,CAAE,OAAQL,EAAO,OAAAE,CAAM,CAAE,EAC9B,OAAOD,CAAM,EACb,QAAQ,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,KAAK,UAAU,CAAC,CAAC,CAAC,EAMvD,QAAWK,KAAOC,GAAkBP,EAAOG,CAAG,EAC5CN,EAAI,KAAK,IAAIQ,GAAU,EAAG,KAAK,CAAE,OAAQC,EAAK,OAAAJ,CAAM,CAAE,EAAE,OAAOD,CAAM,CAAC,CAE1E,CAQQ,YAAYH,EAAgBC,EAAS,CAC3C,GAAIA,GAAK,GAAKD,EAAI,OAAS,EAAG,OAAOA,EACrC,GAAIC,GAAK,EAAG,MAAO,CAAA,EAEnB,IAAMS,EAAmB,CAAA,EACrBC,EAAQ,EACZ,QAASC,EAAI,EAAGA,EAAIZ,EAAI,OAAQY,IAAK,CACnC,IAAMC,EAAI,KAAK,MAAMb,EAAIY,CAAC,EAAE,EAAIZ,EAAIY,EAAI,CAAC,EAAE,EAAGZ,EAAIY,CAAC,EAAE,EAAIZ,EAAIY,EAAI,CAAC,EAAE,CAAC,EACrEF,EAAO,KAAKG,CAAC,EACbF,GAASE,CACX,CACA,GAAIF,IAAU,EAAG,OAAOX,EAExB,IAAIc,EAASH,EAAQV,EACfc,EAAiB,CAACf,EAAI,CAAC,CAAC,EAC9B,QAASY,EAAI,EAAGA,EAAIF,EAAO,OAAQE,IACjC,GAAIE,GAAUJ,EAAOE,CAAC,EACpBG,EAAI,KAAKf,EAAIY,EAAI,CAAC,CAAC,EACnBE,GAAUJ,EAAOE,CAAC,MACb,CACL,IAAMI,EAAIN,EAAOE,CAAC,IAAM,EAAI,EAAIE,EAASJ,EAAOE,CAAC,EACjDG,EAAI,KAAK,CACP,EAAGf,EAAIY,CAAC,EAAE,GAAKZ,EAAIY,EAAI,CAAC,EAAE,EAAIZ,EAAIY,CAAC,EAAE,GAAKI,EAC1C,EAAGhB,EAAIY,CAAC,EAAE,GAAKZ,EAAIY,EAAI,CAAC,EAAE,EAAIZ,EAAIY,CAAC,EAAE,GAAKI,EAC3C,EACD,KACF,CAEF,OAAOD,CACT,CAaA,MAAME,EAA+D,CACnE,OAAO,KAAK,UAAU,EAAGA,EAAK,SAAUA,EAAK,QAAUC,GAAO,CAAE,CAClE,CAcA,UACEC,EACAC,EACAC,EAAyBH,GAAO,EAChCI,EAAK,EAAC,CAEN,OAAO,KAAK,QACTN,GAAK,CACJ,IAAMO,EAAWJ,GAAQG,EAAKH,GAAQH,EAGtC,MAAO,CAAE,SAAAO,EAAU,QAASA,EAAW,CAAC,CAC1C,EACAH,EACAC,CAAM,CAEV,GAxIsDG,EAAA,CAArDC,GAAS,CAAE,QAAS,IAAM,CAAA,CAAe,CAAE,gCAEpCD,EAAA,CADPC,GAAS,CAAE,QAAS,CAAA,EAAI,OAAQC,GAAU,QAAS,MAAOA,GAAU,IAAI,CAAE,gCAEzCF,EAAA,CAAjCC,GAAS,CAAE,QAAS,CAAC,CAAE,kCACaD,EAAA,CAApCC,GAAS,CAAE,QAAS,EAAI,CAAE,iCAC4BD,EAAA,CAAtDC,GAAS,CAAE,QAAS,IAAME,EAAgB,CAAE,mCACCH,EAAA,CAA7CC,GAAS,CAAE,QAASG,CAAa,CAAE,oCAkGpCJ,EAAA,CADCK,GAAO,6BE7KV,OAEE,cAAAC,GACA,UAAAC,GACA,WAAAC,GACA,aAAAC,GACA,YAAAC,OAOK,qBAQP,OAAS,iBAAAC,OAAqB,0WAmCjBC,EAAP,cAA2BC,EAAwB,CAoB9C,cAAcC,EAAkB,CACvC,MAAM,cAAcA,CAAM,EAC1BH,GAAc,KAAMG,CAAM,CAC5B,CAEU,WAAWC,EAAoB,CACvC,IAAMC,EAAM,KAAK,IACjB,GAAI,CAACC,GAAUD,CAAG,EAAG,OAErB,IAAME,EAAI,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,KAAK,MAAM,CAAC,EAC9C,GAAIA,GAAK,EAAG,OAEZ,IAAMC,EAAQH,EAAI,MAAQA,EAAI,KACxBI,EAASJ,EAAI,IAAMA,EAAI,OACvBK,GAAML,EAAI,KAAOA,EAAI,OAAS,EAC9BM,GAAMN,EAAI,OAASA,EAAI,KAAO,EAgBpC,GAbe,KAAK,KACT,OAAS,GAClBD,EAAI,KACF,IAAIQ,GAAU,EACX,KAAK,CAAE,EAAGF,EAAI,EAAGC,EAAI,MAAAH,EAAO,OAAAC,CAAM,CAAE,EACpC,KAAK,KAAK,IAAI,EACd,QAAQF,CAAC,CAAC,EAMF,KAAK,OACT,SAAW,EAAG,OAKzB,IAAMM,EAAQC,GACZV,EAAI,KAAK,IAAIQ,GAAU,EAAG,KAAK,CAAE,OAAAE,CAAM,CAAE,EAAE,OAAO,KAAK,MAAM,EAAE,QAAQP,CAAC,CAAC,EAE3E,GAAIF,EAAI,SAAU,CAChB,IAAMU,EAAQN,EAAS,EAAKF,EAC5B,QAAWS,IAAK,CAACX,EAAI,KAAMA,EAAI,KAAK,EAClCQ,EAAK,CACH,CAAE,EAAAG,EAAG,EAAGL,EAAKI,CAAI,EACjB,CAAE,EAAAC,EAAG,EAAGL,EAAKI,CAAI,EAClB,CAEL,CACA,GAAIV,EAAI,SAAU,CAChB,IAAMU,EAAQP,EAAQ,EAAKD,EAC3B,QAAWU,IAAK,CAACZ,EAAI,OAAQA,EAAI,GAAG,EAClCQ,EAAK,CACH,CAAE,EAAGH,EAAKK,EAAM,EAAAE,CAAC,EACjB,CAAE,EAAGP,EAAKK,EAAM,EAAAE,CAAC,EAClB,CAEL,CACF,GA5EuDC,GAAA,CAAtDC,GAAS,CAAE,QAAS,IAAMC,EAAgB,CAAE,6BAMrCF,GAAA,CALPC,GAAS,CACR,QAASE,EAAoB,OAAO,OACpC,OAAQC,GAAU,QAClB,MAAOA,GAAU,KAClB,gCAOOJ,GAAA,CALPC,GAAS,CACR,QAASE,EAAoB,OAAO,KACpC,OAAQE,GAAQ,QAChB,MAAOA,GAAQ,KAChB,8BAEiCL,GAAA,CAAjCC,GAAS,CAAE,QAAS,CAAC,CAAE,gCCtE1B,OACE,WAAAK,GACA,UAAAC,GACA,QAAAC,GACA,QAAAC,GACA,WAAAC,GACA,YAAAC,GACA,aAAAC,GACA,WAAAC,OAIK,qBASP,OAAS,MAAAC,GAAI,WAAAC,OAA8B,0WAiBrCC,GAAQ,GAoBDC,EAAP,cAA8BC,EAA2B,CAiB7D,SAAwBC,EAGhB,YAAsB,CAAA,EAEtB,UAAyB,KAGjC,IAAY,eAAa,CACvB,MAAO,CAAC,GAAG,KAAK,OAAO,EAAE,KAAK,CAACC,EAAGC,IAAMD,EAAIC,CAAC,CAC/C,CAEQ,QAAQC,EAAa,CAC3B,OAAO,KAAK,cAAcA,CAAK,CACjC,CAGQ,OAAOA,EAAa,CAC1B,IAAMC,EAAI,KAAK,QAAQD,CAAK,EAC5B,GAAIC,IAAM,OAAW,MAAO,GAC5B,GAAM,CAACC,EAAKC,CAAG,EAAI,KAAK,MACxB,OAAOA,GAAOD,EAAM,GAAKD,EAAIC,IAAQC,EAAMD,EAC7C,CAEA,YAAYE,EAAuD,CACjE,MAAMA,CAAK,EAEX,IAAMC,EAAUC,GAAU,QAAQ,KAAK,KAAK,WAAW,EAAE,IACnDC,EAAWD,GAAU,QAAQ,KAAK,KAAK,YAAY,EAAE,IAE3D,KAAK,YAAc,KAAK,QAAQ,IAAI,CAACE,EAAGC,IAAK,CAC3C,IAAMC,EAAe,IAAIC,GAAK,CAC5B,GAAGC,GAAQ,WAAW,KAAK,KAAK,UAAU,EAC1C,KAAM,IAAK,CACT,IAAMX,EAAI,KAAK,QAAQQ,CAAC,EACxB,OAAOR,IAAM,OAAY,GAAK,KAAK,SAASA,CAAC,CAC/C,EACA,EAAG,IAAK,CACN,IAAMY,EAAQ,KAAK,aAAa,MAC1BC,GAAW,KAAK,OAAOL,CAAC,EAAI,IAAOI,EAOnCE,EAAQF,EAAQ,EAAIH,EAAO,aAAa,MAAQ,EACtD,OAAO,KAAK,IAAII,EAASC,CAAK,CAChC,EACD,EACD,OAAOL,CACT,CAAC,EAED,IAAMM,EAAqB,CACzB,IAAIC,GAAK,CACP,MAAO,OACP,OAAQ,MACR,QAAS,CAAE,IAAKZ,CAAO,EACvB,SAAU,KAAK,YAChB,GAEC,KAAK,QAAU,KACjB,KAAK,UAAY,IAAIM,GAAK,CACxB,GAAGC,GAAQ,WAAW,KAAK,KAAK,UAAU,EAC1C,KAAM,KAAK,MACZ,EACDI,EAAS,KAAK,KAAK,SAAS,GAG9B,KAAK,IACH,IAAIC,GAAK,CAAE,KAAM,WAAY,IAAKV,EAAU,OAAQ,MAAO,MAAO,OAAQ,SAAAS,CAAQ,CAAE,CAAC,CAEzF,CAMA,IAAIE,EAAQxB,GAAK,CACf,QAAWgB,KAAU,KAAK,YAAaA,EAAO,IAAI,CAAE,QAAS,EAAG,EAAGQ,CAAK,CAAE,EAC1E,KAAK,WAAW,IAAI,CAAE,QAAS,EAAG,EAAGA,CAAK,CAAE,CAC9C,CAeA,MACEC,EAA8E,CAE9E,GAAM,CAAE,QAAAC,EAAS,MAAAF,EAAQxB,EAAK,EAAKyB,EAAK,MAAQ,CAAA,EAC1CE,EAAWF,EAAK,SAChBG,EAASH,EAAK,QAAUI,GAAO,EAC/BC,EAAQ,KAAK,YACnB,GAAIA,EAAM,SAAW,EAAG,OAAO/B,GAAQ,CAAA,CAAE,EACzC,GAAM,CAAE,KAAAgC,EAAM,IAAAC,EAAK,QAAAC,CAAO,EAAKC,GAC7BP,EACAG,EAAM,OACN,KAAK,YAAc,KACnBJ,CAAO,EAGT,KAAK,IAAIF,CAAK,EAGd,IAAMW,EAAUL,EAAM,IAAI,CAACM,EAAMrB,IAC/BjB,GAAGiB,EAAIgB,EAAMK,EAAK,GAAG,CAAE,KAAM,CAAE,QAAS,EAAG,EAAG,CAAC,EAAI,SAAUJ,EAAK,OAAAJ,CAAM,CAAE,CAAC,CAAC,EAExES,EAAiBP,EAAM,SAAW,EAAI,GAAKA,EAAM,OAAS,GAAKC,EAAOC,EAE5E,OAAOjC,GAAQ,CACb,GAAGoC,EACH,GAAI,KAAK,UACL,CAACrC,GAAGuC,EAAgB,KAAK,UAAU,GAAG,CAAE,KAAM,CAAE,QAAS,EAAG,EAAG,CAAC,EAAI,SAAUJ,EAAS,OAAAL,CAAM,CAAE,CAAC,CAAC,EACjG,CAAA,EACL,CACH,GA7IsDU,GAAA,CAArDC,GAAS,CAAE,QAAS,IAAM,CAAC,EAAG,CAAC,CAAU,CAAE,+BACSD,GAAA,CAApDC,GAAS,CAAE,QAAS,IAAM,CAAA,CAAc,CAAE,iCACID,GAAA,CAA9CC,GAAS,CAAE,QAASC,CAAc,CAAE,8BACFF,GAAA,CAAlCC,GAAS,CAAE,QAAS,EAAE,CAAE,+BA6GzBD,GAAA,CADCG,GAAO,6BCrIV,OAAS,gBAAAC,OAAoB,+BAyB7B,IAAMC,GAAM,MAENC,GAAS,CACb,UACA,WACA,QACA,QACA,MACA,OACA,OACA,SACA,YACA,UACA,WACA,YAUIC,GAAe,KAUrB,SAASC,GAAWC,EAAY,CAC9B,OAAIA,EAAK,OAAS,EAAU,GACrBH,GAAO,UAAWI,GAAUA,EAAM,WAAWD,CAAI,CAAC,CAC3D,CAGA,SAASE,GAAaC,EAAcC,EAAY,CAE9C,IAAMC,EAAO,KAAK,IAAIF,EAAM,EAAG,CAAC,EAC1BG,GAAW,IAAI,KAAKD,CAAI,EAAE,UAAS,EAAK,GAAK,EACnD,OAAOA,EAAOC,EAAUV,IAAOQ,EAAO,GAAK,EAAIR,EACjD,CAYM,SAAUW,GAAaC,EAAY,CACvC,GAAI,OAAOA,GAAQ,SAAU,OAAO,KACpC,IAAMC,EAAID,EAAI,KAAI,EAAG,YAAW,EAChC,GAAIC,IAAM,GAAI,OAAO,KAGrB,IAAMC,EAAM,2CAA2C,KAAKD,CAAC,EAC7D,GAAIC,EAAK,CACP,IAAMT,EAAQ,OAAOS,EAAI,CAAC,CAAC,EACrBC,EAAMD,EAAI,CAAC,IAAM,OAAY,EAAI,OAAOA,EAAI,CAAC,CAAC,EACpD,OAAIT,EAAQ,GAAKA,EAAQ,IAAMU,EAAM,GAAKA,EAAM,GAAW,KACpD,KAAK,IAAI,OAAOD,EAAI,CAAC,CAAC,EAAGT,EAAQ,EAAGU,CAAG,CAChD,CAGA,IAAMP,EAAO,gDAAgD,KAAKK,CAAC,EACnE,GAAIL,EAAM,CACR,IAAM,EAAI,OAAOA,EAAK,CAAC,CAAC,EACxB,OAAI,EAAI,GAAK,EAAI,GAAW,KACrBF,GACLE,EAAK,CAAC,IAAM,OAAYN,GAAe,OAAOM,EAAK,CAAC,CAAC,EACrD,CAAC,CAEL,CAGA,IAAMQ,EAAQ,iDAAiD,KAAKH,CAAC,EACrE,GAAIG,EAAO,CACT,IAAMX,EAAQF,GAAWa,EAAM,CAAC,CAAC,EACjC,GAAIX,GAAS,EAAG,CACd,IAAME,EAAOS,EAAM,CAAC,GAAKA,EAAM,CAAC,EAC1BC,EAAIV,IAAS,OAAYL,GAAe,OAAOK,CAAI,EACzD,OAAO,KAAK,IAAIU,EAAGZ,EAAO,CAAC,CAC7B,CACF,CAGA,IAAMa,EACJ,uEAAuE,KACrEL,CAAC,EAEL,GAAIK,EAAU,CACZ,IAAMb,EAAQF,GAAWe,EAAS,CAAC,GAAKA,EAAS,CAAC,CAAC,EAC7CH,EAAM,OAAOG,EAAS,CAAC,GAAKA,EAAS,CAAC,CAAC,EAC7C,GAAIb,GAAS,GAAKU,GAAO,GAAKA,GAAO,GACnC,OAAO,KAAK,IAAI,OAAOG,EAAS,CAAC,CAAC,EAAGb,EAAOU,CAAG,CAEnD,CAEA,OAAO,IACT,CAUM,SAAUI,GAAcC,EAAeC,EAAY,CACvD,IAAMC,EAAO,IAAI,KAAKF,CAAK,EACrBb,EAAOe,EAAK,eAAc,EAC1BjB,EAAQJ,GAAOqB,EAAK,YAAW,CAAE,EAAE,MAAM,EAAG,CAAC,EAC7CC,EAAQlB,EAAM,OAAO,CAAC,EAAE,YAAW,EAAKA,EAAM,MAAM,CAAC,EAC3D,OAAIgB,EAAO,EAAI,IAAMrB,GAAY,OAAOO,CAAI,EACxCc,EAAO,GAAKrB,GAAY,GAAGuB,CAAK,IAAIhB,CAAI,GACrC,GAAGgB,CAAK,IAAID,EAAK,WAAU,CAAE,EACtC,CAYA,SAASE,GAAgBC,EAAoBC,EAAa,CACxD,IAAIC,EAAW,GACf,QAAWC,KAAOH,EAAM,CACtB,IAAMI,EAAID,EAAIF,CAAK,EACnB,GAAI,EAAmBG,GAAM,MAAQA,IAAM,IAC3C,IAAI,OAAOA,GAAM,SAAU,MAAO,GAClCF,EAAW,GACb,CACA,OAAOA,CACT,CAWA,SAASG,GAAcL,EAAoBC,EAAa,CACtD,IAAMK,EAA4B,CAAA,EAC9BJ,EAAW,GACf,QAAWC,KAAOH,EAAM,CACtB,IAAMb,EAAMgB,EAAIF,CAAK,EACrB,GAAyBd,GAAQ,MAAQA,IAAQ,GAAI,CACnDmB,EAAO,KAAK,IAAI,EAChB,QACF,CACA,IAAMC,EAAIrB,GAAaC,CAAG,EAC1B,GAAIoB,IAAM,KAAM,OAAO,KACvBD,EAAO,KAAKC,CAAC,EACbL,EAAW,EACb,CACA,GAAI,CAACA,EAAU,OAAO,KAGtB,IAAIM,EADUF,EAAO,KAAMC,GAAmBA,IAAM,IAAI,EAExD,OAAOD,EAAO,IAAKC,GAAOA,IAAM,KAAOC,EAAQA,EAAOD,CAAG,CAC3D,CAGA,SAASE,GAASC,EAAgB,CAChC,IAAIC,EAAK,IACLC,EAAK,KACT,QAAWR,KAAKM,EACVN,EAAIO,IAAIA,EAAKP,GACbA,EAAIQ,IAAIA,EAAKR,GAEnB,OAAOO,GAAMC,EAAK,CAACD,EAAIC,CAAE,EAAI,CAAC,EAAG,CAAC,CACpC,CAqBA,SAASC,GACPC,EACA,CAACC,EAAKC,CAAG,EACTC,EAAiB,CAEjB,IAAMC,EAASJ,EAAU,OAAQV,GAAMA,GAAKW,GAAOX,GAAKY,CAAG,EACrDG,EAAS,CAAC,GAAG,IAAI,IAAID,CAAM,CAAC,EAAE,KAAK,CAAC,EAAGE,IAAM,EAAIA,CAAC,EACxD,GAAID,EAAO,SAAW,EAAG,OAAOE,EAAa,CAACN,EAAKC,CAAG,EAAGC,CAAS,EAElE,IAAMK,EAAWC,GACfJ,EAAO,OAAO,CAACK,EAAMpB,IACnB,KAAK,IAAIA,EAAImB,CAAM,EAAI,KAAK,IAAIC,EAAOD,CAAM,EAAInB,EAAIoB,CAAI,EAGvDC,EAAgB,CAAA,EACtB,QAAWF,KAAUF,EAAa,CAACN,EAAKC,CAAG,EAAGC,CAAS,EAAG,CACxD,IAAMS,EAAOJ,EAAQC,CAAM,EACvBE,EAAIA,EAAI,OAAS,CAAC,IAAMC,GAAMD,EAAI,KAAKC,CAAI,CACjD,CACA,OAAOD,CACT,CAMA,SAASE,GACPb,EACAc,EACAjC,EACAkC,EAAmC,CAEnC,IAAMC,EAAIhB,EAAU,QAAQnB,CAAK,EACjC,OAAOmC,GAAK,EAAIF,EAASE,CAAC,EAAID,EAASlC,CAAK,CAC9C,CAUM,SAAUoC,GACd/B,EACAC,EAMA+B,EAAgB,CAEhB,GAAI/B,IAAU,IAAMF,GAAgBC,EAAMC,CAAK,EAAG,CAChD,IAAMa,EAAYd,EAAK,IAAKG,GAAQ8B,EAAI9B,EAAKF,CAAK,CAAC,EACnD,MAAO,CACL,KAAM,UACN,GAAK6B,GAAMhB,EAAUgB,CAAC,GAAK,EAC3B,OAAQrB,GAASK,CAAS,EAC1B,MAAQnB,GACNqC,IAAY,OAAYE,EAAavC,CAAK,EAAIrB,GAAaqB,EAAOqC,CAAO,EAG3E,MAAOX,EAEX,CAEA,IAAMO,EAAW5B,EAAK,IAAKG,GAAQgC,GAAKhC,EAAKF,CAAK,CAAC,EAC7CmC,EAAWnC,IAAU,GAAK,KAAOI,GAAcL,EAAMC,CAAK,EAEhE,GAAImC,EAAU,CACZ,IAAMC,EAAS5B,GAAS2B,CAAQ,EAC1BxC,EAAOyC,EAAO,CAAC,EAAIA,EAAO,CAAC,EACjC,MAAO,CACL,KAAM,OACN,GAAKP,GAAMM,EAASN,CAAC,GAAK,EAC1B,OAAAO,EACA,MAAQ1C,GACNgC,GAAcS,EAAUR,EAAUjC,EAAQS,GACxCV,GAAcU,EAAGR,CAAI,CAAC,EAE1B,MAAO,CAAC0C,EAAOrB,IAAcJ,GAAauB,EAAUE,EAAOrB,CAAS,EAExE,CAEA,IAAMH,EAAYd,EAAK,IAAI,CAACuC,EAAGT,IAAMA,CAAC,EACtC,MAAO,CACL,KAAM,UACN,GAAKA,GAAMA,EAGX,OAAQhB,EAAU,OAAS,EAAI,CAAC,EAAGA,EAAU,OAAS,CAAC,EAAI,CAAC,EAAG,CAAC,EAChE,MAAQnB,GAAUiC,EAAS,KAAK,MAAMjC,CAAK,CAAC,GAAK,GACjD,MAAO,CAAC2C,EAAOrB,IAAcJ,GAAaC,EAAWwB,EAAOrB,CAAS,EAEzE,CL9TA,OAAS,QAAAuB,GAAM,YAAYC,GAAY,YAAAC,OAA+B,2WA2ChEC,GAAe,GAMfC,GAAa,CAAE,IAAK,cAAe,KAAM,SAAU,QAAS,EAAG,IAAK,EAAG,KAAM,CAAC,EAG9EC,GAAe,GAkBfC,GAAa,IACbC,GAAc,GACdC,GAAe,IAiDRC,EAAN,cAAwBC,EAAsB,eA2D3C,MACA,MAEA,SACA,OAA6B,KAC7B,MAAqB,CAAA,EAS7B,IAAY,eAAa,CACvB,OAAO,KAAK,KACd,CAKQ,OAAgB,gBAAkB,EAM1C,IAAY,SAAO,CACjB,OAAOC,GAAc,KAAK,KAAM,KAAK,OAAQ,KAAK,MAAM,MAAM,CAChE,CAEA,IAAY,QAAM,CAChB,OAAO,KAAK,MAAM,OAAS,KAAK,QAAQ,MAC1C,CAEA,IAAY,QAAM,CAIhB,IAAMC,GACH,KAAK,MAAM,OAAO,QAAUC,EAAU,iBAAmB,EAC5D,OAAOC,GAAc,KAAK,KAAM,KAAK,OAAQ,KAAK,MAAM,MAAOF,CAAS,CAC1E,CAGA,IAAY,kBAAgB,CAC1B,OACE,KAAK,MAAM,OACXG,EAAa,KAAK,OAAQF,EAAU,gBAAkB,CAAC,CAE3D,CAUA,IAAY,kBAAgB,CAC1B,OACE,KAAK,MAAM,OACX,KAAK,QAAQ,MAAM,KAAK,OAAQA,EAAU,gBAAkB,CAAC,CAEjE,CAqBQ,UAAQ,CACd,GAAM,CAAE,MAAAG,EAAO,OAAAC,CAAM,EAAK,KAAK,SAAS,aACxC,MAAO,CAAE,MAAAD,EAAO,OAAAC,CAAM,CACxB,CAOQ,aAAaC,EAAa,CAChC,IAAMC,EAAI,KAAK,OAAOD,CAAK,EAC3B,GAAI,CAACC,EAAG,MAAO,CAAA,EACf,IAAMC,EAASC,GAAa,KAAK,KAAM,KAAK,QAASF,CAAC,EACtD,GAAIC,EAAO,SAAW,EAAG,MAAO,CAAA,EAChC,GAAM,CAAE,MAAOE,EAAG,OAAQC,CAAC,EAAK,KAAK,SAAQ,EACvC,CAACC,EAAMC,CAAI,EAAI,KAAK,OACpB,CAACC,EAAMC,CAAI,EAAI,KAAK,OAEpBC,EAASC,GACbJ,GAAQD,EAAO,IAAMK,EAAIL,IAASC,EAAOD,GAAQ,IAAOF,EACpDQ,EAASC,GACbJ,GAAQD,EAAO,IAAMK,EAAIL,IAASC,EAAOD,GAAQ,IAAOH,EAE1D,OAAOH,EAAO,IAAI,CAAC,CAACS,EAAGE,CAAC,KAAO,CAAE,EAAGH,EAAMC,CAAC,EAAG,EAAGC,EAAMC,CAAC,CAAC,EAAG,CAC9D,CAwBQ,YAAU,CAChB,IAAMC,EAAS,KAAK,OACpB,OAAOA,GAAU,OAASA,EAAO,IAAM,QAAaA,EAAO,IAAM,OACnE,CAEQ,WAAS,CACf,GAAM,CAAE,MAAOV,EAAG,OAAQC,CAAC,EAAK,KAAK,SAAQ,EAG7C,OAAOU,GACL,KAAK,OACL,CAAE,KAAM,CAACX,EAAI,EAAG,MAAOA,EAAI,EAAG,OAAQ,CAACC,EAAI,EAAG,IAAKA,EAAI,CAAC,EACxD,KAAK,OACL,KAAK,MAAM,CAEf,CAIA,YAAYW,EAA6C,CACvD,MAAMA,CAAK,EAaX,IAAMC,EAAU,KAAK,OAAO,IAAKhB,GAAMA,EAAE,SAAW,EAAI,EAClDiB,EAAa,KAAK,aAAe,GAEvC,KAAK,MAAQ,IAAIC,GAAa,CAI5B,MAAO,IAAM,KAAK,OAClB,QAAS,IAAM,KAAK,iBACpB,KAAM,IAAM,KAAK,cAAc,MAC/B,MAAO,KAAK,MAAM,OAAS,GAC3B,MAAO,MACP,OAAQ,OACT,EAED,KAAK,MAAM,SAAYC,GACrB,KAAK,MAAM,SAAW,OAClBC,EAAaD,CAAK,EAClBE,GAAaF,EAAO,KAAK,MAAM,MAAM,EAE3C,KAAK,MAAQ,IAAIG,EAAe,CAC9B,MAAO,IAAM,KAAK,OAClB,QAAS,IAAM,KAAK,iBACpB,KAAM,IAAM,KAAK,cAAc,MAC/B,MAAO,KAAK,MAAM,OAAS,GAC3B,MAAO,OACP,OAAQ,MACT,EAID,KAAK,MAAM,SAAYH,GAAU,KAAK,QAAQ,MAAMA,CAAK,EAEzD,KAAK,MAAQ,KAAK,OAAO,IACvB,CAACnB,EAAGuB,IACF,IAAIC,EAAU,CACZ,OAAQ,IAAM,KAAK,aAAaD,CAAC,EACjC,OAAQ,IAAME,GAAoB,KAAK,cAAc,aAAczB,EAAGuB,CAAC,EACvE,QAASP,EAAQO,CAAC,EAGlB,UAAW,IAAM,KAAK,UAAS,EAG/B,WAAY,IACV,GAAK,EAAI,KAAK,cAAc,OAAO,YAAc,KAAK,aACzD,CAAC,EAGFN,IACF,KAAK,OAAS,IAAIS,GAAY,CAC5B,OAAQ,KAAK,OAIb,SAAU,IACR,KAAK,OAAO,IAAI,CAAC1B,EAAGuB,IAClBI,GAAkB,KAAK,cAAc,aAAc3B,EAAGuB,CAAC,CAAC,EAE5D,OAAQ,IAAM,KAAK,cAAc,OAClC,GAGH,KAAK,SAAW,IAAIK,GAAK,CACvB,KAAM,GACN,SAAU,CACR,IAAIC,GAAY,CACd,KAAM,IAAM,KAAK,cAAc,SAAS,KACxC,OAAQ,IAAM,KAAK,cAAc,SAAS,OAC1C,YAAa,IAAM,KAAK,cAAc,MAAM,UAC5C,YAAa,IAAM,KAAK,cAAc,MAAM,UAG5C,SAAU,IAAM,KAAK,iBACrB,SAAU,IAAM,KAAK,iBACrB,QAAS,IAAM,KAAK,OACpB,QAAS,IAAM,KAAK,OACrB,EAID,IAAIC,EAAY,CACd,IAAK,IAAM,KAAK,UAAS,EACzB,OAAQ,IAAM,KAAK,cAAc,OAAO,OACxC,KAAM,IAAM,KAAK,cAAc,OAAO,KACtC,OAAQ,IAAM,KAAK,aACpB,EACD,GAAG,KAAK,MACR,IAAIC,GAAY,CACd,OAAQ,IAAM,KAAK,cAAc,SAAS,OAC3C,GAEJ,EAKD,IAAMC,EAAe,IAAId,GAAa,CACpC,QAAS,EACT,MAAO,IAAM,KAAK,OAClB,QAAS,IAAM,KAAK,iBACpB,KAAM,IAAM,KAAK,cAAc,MAC/B,MAAO,KAAK,MAAM,OAAS,GAC3B,MAAO,MACP,OAAQ,EACT,EAEKe,EAAkB,CAAC,KAAK,KAAK,EAC/B,KAAK,QAAQA,EAAM,KAAK,KAAK,MAAM,EAEvC,KAAK,IACH,IAAIL,GAAK,CAAE,KAAM,WACf,MAAO,OACP,OAAQ,OAGR,QAAS,CAAE,IAAK5C,EAAY,EAC5B,SAAU,CACR,IAAI4C,GAAK,CAAE,KAAM,aACf,OAAQ,OACR,MAAO,OACP,SAAU,CAAC,KAAK,MAAO,KAAK,QAAQ,EACrC,EACD,IAAIA,GAAK,CAAE,KAAM,aACf,MAAO,OACP,SAAU,CACRI,EACA,IAAIJ,GAAK,CAAE,KAAM,WAAY,MAAO,OAAQ,IAAK,GAAI,SAAUK,CAAK,CAAE,GAEzE,GAEJ,CAAC,CAEN,CAYA,KAAG,CACD,KAAK,MAAM,IAAG,EACd,KAAK,MAAM,IAAG,EACd,QAAWC,KAAQ,KAAK,MAAOA,EAAK,IAAI,CAAE,SAAU,CAAC,CAAE,EACvD,KAAK,QAAQ,IAAI,CAAE,QAAS,EAAG,EAAG,CAAChD,EAAY,CAAE,CACnD,CAoBA,MACEiD,EAA+D,CAE/D,IAAMC,EAAWD,EAAK,SAChBE,EAASF,EAAK,QAAUG,EAAO,EACrC,KAAK,IAAG,EAER,IAAMC,EAAY,KAAK,WAAU,GAAM,KAAK,eAAiB,EAEvDC,EAAOD,EAAYH,EAAW,IAAOA,EAOrCK,EAAO1D,GACX,KAAK,MAAM,MAAM,CAAE,KAAM,CAAE,MAAO,EAAE,EAAI,SAAUyD,EAAOrD,GAAY,OAAAkD,CAAM,CAAE,EAC7E,KAAK,MAAM,MAAM,CAAE,KAAM,CAAE,MAAO,EAAE,EAAI,SAAUG,EAAOrD,GAAY,OAAAkD,CAAM,CAAE,CAAC,EAE1EK,EAAQ3D,GACZ,GAAG,KAAK,MAAM,IAAKmD,GAASA,EAAK,MAAM,CAAE,SAAUM,EAAOpD,GAAa,OAAAiD,CAAM,CAAE,CAAC,CAAC,EAE7EM,EAAS,KAAK,OAChB,CAAC,KAAK,OAAO,GAAG,CAAE,KAAM,CAAE,QAAS,EAAG,EAAG,CAAC,EAAI,SAAUH,EAAOnD,GAAc,OAAAgD,CAAM,CAAE,CAAC,EACtF,CAAA,EAEEO,EAAS9D,GAAW2D,EAAMC,EAAO,GAAGC,CAAM,EAEhD,OAAOJ,EACHzD,GACA8D,EAGA,KAAK,QACFC,IAAO,CAAE,aAAcA,CAAC,GACzBT,EAAW,IACXC,CAAM,CACP,EAEDO,CACN,CAkCA,gBACET,EAAiE,CAEjE,IAAMtB,EAASnB,EAAU,UAAUyC,EAAK,IAAI,EACtCC,EAAWD,EAAK,UAAY,GAC5BE,EAASF,EAAK,QAAUG,EAAO,EAC/BQ,EAAO,KAAK,aACZC,EAAW,KAAK,OAGtB,OAAIlC,EAAO,IAAM,QAAaA,EAAO,IAAM,OAClC,KAAK,YAAY,CAAE,SAAAuB,EAAU,OAAAC,CAAM,CAAE,EAI1CS,GAAQ,GAAK,CAAC,KAAK,WAAU,EACxB,KAAK,QACTD,IAAO,CAAE,OAAAhC,EAAQ,aAAciC,GAAQ,EAAIA,GAAQD,CAAC,GACrDT,EACAC,CAAM,EAQH,KAAK,QACTQ,GACCA,EAAI,GACA,CAAE,OAAQE,EAAU,aAAcD,GAAQ,EAAIT,EAAOQ,EAAI,CAAC,EAAE,EAC5D,CAAE,OAAAhC,EAAQ,aAAcwB,EAAOQ,EAAI,EAAI,CAAC,CAAC,EAC/CT,CAAQ,CAEZ,CAQA,YACED,EAAmE,CAAA,EAAE,CAErE,IAAMC,EAAWD,EAAK,UAAY,GAC5BE,EAASF,EAAK,QAAUG,EAAO,EAC/BQ,EAAO,KAAK,aAElB,OAAIA,IAAS,EAKJ,KAAK,QACV,KAAO,CAAE,OAAQ,KAAM,aAAc,CAAC,GACtCV,CAAQ,EAML,KAAK,QACTS,GAAOA,GAAK,EAAI,CAAE,OAAQ,KAAM,aAAc,CAAC,EAAK,CAAE,aAAcC,GAAQ,EAAID,EAAE,EACnFT,EACAC,CAAM,CAEV,CAWQ,KAA4D,KAO5D,OAAO,UAAUW,EAAgD,CACvE,IAAMnC,EAASmC,GAAM,OACrB,OAAOnC,GAAU,MAAQ,OAAOA,GAAW,SAAWA,EAAS,CAAA,CACjE,CAGQ,OAAO,KAAK,CAACoC,EAAGC,CAAC,EAAQ,CAC/B,OAAOD,GAAKC,EAAI,CAACD,EAAGC,CAAC,EAAI,CAACA,EAAGD,CAAC,CAChC,CAQQ,OAAO,QAAQE,EAAcC,EAAe,CAClD,IAAMC,EAAO3D,EAAU,KAAKyD,CAAK,EAC3BG,EAAQ,KAAK,IAAI,EAAGF,EAAM,MAAM,EACtC,MAAO,CAAE,MAAOC,EAAM,MAAOzD,EAAayD,EAAMC,EAAQ,CAAC,CAAC,CAC5D,CA0BA,OACEnB,EAAiE,CAEjE,IAAMtB,EAASnB,EAAU,UAAUyC,EAAK,IAAI,EACtCC,EAAWD,EAAK,UAAY,GAC5BE,EAASF,EAAK,QAAUG,EAAO,EACrC,GAAI,CAACzB,EAAO,GAAK,CAACA,EAAO,EAAG,OAAOhC,GAAKuD,CAAQ,EAKhD,KAAK,OAAS,CACZ,EAAG,CACD,MAAO,KAAK,MAAM,MAClB,MAAO,KAAK,OACZ,MAAO,KAAK,kBAEd,EAAG,CACD,MAAO,KAAK,MAAM,MAClB,MAAO,KAAK,OACZ,MAAO,KAAK,mBAIhB,IAAMmB,EAA+D,CAAA,EACrE,OAAI1C,EAAO,IAAG0C,EAAK,MAAQ7D,EAAU,QAAQmB,EAAO,EAAG,KAAK,gBAAgB,GACxEA,EAAO,IAAG0C,EAAK,MAAQ7D,EAAU,QAAQmB,EAAO,EAAG,KAAK,gBAAgB,GAErE,KAAK,GAAG,CAAE,KAAM0C,EAAM,SAAAnB,EAAU,OAAAC,CAAM,CAAE,CACjD,CAOA,UACEF,EAAmE,CAAA,EAAE,CAErE,IAAMC,EAAWD,EAAK,UAAY,GAC5BE,EAASF,EAAK,QAAUG,EAAO,EAC/BkB,EAAO,KAAK,KAClB,OAAKA,GAML,KAAK,KAAO,KACL,KAAK,GAAG,CAAE,KAAM,CAAE,MAAOA,EAAK,EAAG,MAAOA,EAAK,CAAC,EAAI,SAAApB,EAAU,OAAAC,CAAM,CAAE,GAPzDxD,GAAKuD,CAAQ,CAQjC,CAeA,WACED,EAAkE,CAElE,IAAMpC,EAAQoC,EAAK,MAAM,aAAe,EAClCC,EAAWD,EAAK,UAAY,IAC5BE,EAASF,EAAK,QAAUG,EAAO,EAC/BJ,EAAO,KAAK,MAAMnC,CAAK,EAC7B,OAAKmC,EAEEnD,GACLmD,EAAK,UAAU,EAAGE,EAAUC,CAAM,EAClC,GAAI,KAAK,OAAS,CAAC,KAAK,OAAO,WAAW,CAAE,KAAM,CAAE,MAAAtC,CAAK,EAAI,SAAAqC,EAAU,OAAAC,CAAM,CAAE,CAAC,EAAI,CAAA,CAAG,EAJvExD,GAAKuD,CAAQ,CAMjC,CAOA,WACED,EAAkE,CAElE,IAAMpC,EAAQoC,EAAK,MAAM,aAAe,EAClCC,EAAWD,EAAK,UAAY,IAC5BE,EAASF,EAAK,QAAUG,EAAO,EAC/BJ,EAAO,KAAK,MAAMnC,CAAK,EAC7B,OAAKmC,EAEEnD,GACLmD,EAAK,UAAU,EAAGE,EAAUC,EAAQ,CAAC,EACrC,GAAI,KAAK,OAAS,CAAC,KAAK,OAAO,WAAW,CAAE,KAAM,CAAE,MAAAtC,CAAK,EAAI,SAAAqC,EAAU,OAAAC,CAAM,CAAE,CAAC,EAAI,CAAA,CAAG,EAJvExD,GAAKuD,CAAQ,CAMjC,CAQA,gBACED,EAAkE,CAElE,IAAMpC,EAAQoC,EAAK,MAAM,aAAe,EAClCC,EAAWD,EAAK,UAAY,IAC5BE,EAASF,EAAK,QAAUG,EAAO,EACrC,OAAOvD,GACL,GAAG,KAAK,MAAM,IAAI,CAACmD,EAAMX,IACvBW,EAAK,GAAG,CAAE,KAAM,CAAE,QAASX,IAAMxB,EAAQ,EAAI0D,CAAa,EAAI,SAAArB,EAAU,OAAAC,CAAM,CAAE,CAAC,EAEnF,GAAI,KAAK,OACL,CAAC,KAAK,OAAO,gBAAgB,CAAE,KAAM,CAAE,MAAAtC,CAAK,EAAI,SAAAqC,EAAU,OAAAC,CAAM,CAAE,CAAC,EACnE,CAAA,CAAG,CAEX,GAltB0EqB,EAAA,CAAzEC,EAAS,CAAE,QAAS,IAAM,CAAA,EAAoB,KAAM,SAAS,CAAE,8BAC7BD,EAAA,CAAlCC,EAAS,CAAE,QAAS,EAAE,CAAE,gCACgDD,EAAA,CAAxEC,EAAS,CAAE,QAAS,IAAM,CAAA,EAAoB,KAAM,QAAQ,CAAE,gCAcvDD,EAAA,CALPC,EAAS,CACR,QAAS,CAAA,EACT,OAAQC,GACR,MAAOC,GACR,+BAOOH,EAAA,CALPC,EAAS,CACR,QAAS,CAAA,EACT,OAAQC,GACR,MAAOC,GACR,+BAcOH,EAAA,CALPC,EAAS,CACR,QAASG,EACT,OAAQC,GACR,MAAOC,GACR,+BAQoCN,EAAA,CAApCC,EAAS,CAAE,QAAS,IAAI,CAAE,gCAQOD,EAAA,CAAjCC,EAAS,CAAE,QAAS,CAAC,CAAE,sCAKgCD,EAAA,CAAvDC,EAAS,CAAE,QAAS,GAAM,WAAY,EAAK,CAAE,oCAqV9CD,EAAA,CADCO,EAAO,6BA4ERP,EAAA,CAZCO,EAAQ,CACP,KAAM,CAGJ,CACE,IAAK,SAAU,KAAM,SAAU,WAAY,CACzC,EAAG,CAAE,KAAM,QAAS,QAAS,GAAM,WAAY,EAAG,WAAY,GAAG,EACjE,EAAG,CAAE,KAAM,QAAS,QAAS,GAAO,WAAY,EAAG,WAAY,GAAG,KAIzE,uCA2CDP,EAAA,CADCO,EAAO,mCA0FRP,EAAA,CAZCO,EAAQ,CACP,KAAM,CAGJ,CACE,IAAK,SAAU,KAAM,SAAU,WAAY,CACzC,EAAG,CAAE,KAAM,QAAS,QAAS,GAAM,WAAY,EAAG,WAAY,GAAG,EACjE,EAAG,CAAE,KAAM,QAAS,QAAS,GAAO,WAAY,EAAG,WAAY,GAAG,KAIzE,8BAqCDP,EAAA,CADCO,EAAO,iCA8BRP,EAAA,CADCO,EAAQ,CAAE,KAAM,CAAChF,EAAU,CAAC,CAAE,kCAqB/ByE,EAAA,CADCO,EAAQ,CAAE,KAAM,CAAChF,EAAU,CAAC,CAAE,kCAsB/ByE,EAAA,CADCO,EAAQ,CAAE,KAAM,CAAChF,EAAU,CAAC,CAAE,uCApsBpBK,EAASI,EAAAgE,EAAA,CAfrBQ,GAAK,CACJ,IAAK,YACL,UAAW,OACX,SAAU,GACV,OAAQ,CACN,SAAU,WACV,iBAAkB,QAClB,kBAAmB,QACnB,gBAAiB,IAEnB,KAAM,CACJ,MAAO,IACP,OAAQ,KAEX,GACY5E,CAAS,EMzKtB,OACE,WAAA6E,GACA,QAAAC,GACA,UAAAC,GACA,QAAAC,GACA,WAAAC,GACA,YAAAC,MAOK,qBAgBP,OAAS,gBAAAC,OAAoB,+BAC7B,OACE,SAAAC,GACA,QAAAC,GACA,YAAYC,GACZ,YAAAC,OAEK,+BCpCP,OACE,WAAAC,GACA,UAAAC,GACA,QAAAC,GACA,QAAAC,GACA,WAAAC,GACA,YAAAC,GACA,aAAAC,GACA,WAAAC,OAIK,qBAQP,OAAS,MAAAC,GAAI,WAAAC,GAAS,QAAAC,OAA2B,0WAqB3CC,GAAQ,GAsBDC,EAAP,cAA4BC,EAAyB,CAOjD,aAAuB,CAAA,EAEvB,UAAyB,KAGzB,WAAWC,EAAa,CAC9B,IAAMC,EAAI,KAAK,WAAW,OAC1B,GAAIA,GAAK,EAAG,MAAO,GACnB,IAAMC,EAAQ,KAAK,aAAa,MAChC,MAAO,CAACA,EAAQ,EAAKA,EAAQD,GAAMD,EAAQ,GAC7C,CAEA,YAAYG,EAAmD,CAC7D,MAAMA,CAAK,EAEX,IAAMC,EAAUC,GAAU,QAAQ,KAAK,KAAK,WAAW,EAAE,IACnDC,EAAWD,GAAU,QAAQ,KAAK,KAAK,YAAY,EAAE,IAMrDE,EAAQ,KAAK,WAAW,IAAI,CAACC,EAASC,IAAK,CAC/C,IAAMC,EAAO,IAAIC,GAAK,CACpB,GAAGC,GAAQ,WAAW,KAAK,KAAK,UAAU,EAC1C,KAAMJ,EACP,EACD,YAAK,aAAa,KAAKE,CAAI,EACpB,IAAIG,GAAK,CACd,MAAO,MACP,OAAQ,MACR,KAAM,WACN,QAAS,IAAMC,EAAU,KAAK,KAAKL,CAAC,GAAK,CAAC,EAC1C,EAAG,IAAM,KAAK,WAAWA,CAAC,EAC1B,SAAU,CAACC,CAAI,EAChB,CACH,CAAC,EAEKK,EAAqB,CACzB,IAAIF,GAAK,CACP,MAAO,OACP,OAAQ,MACR,QAAS,CAAE,IAAKT,CAAO,EACvB,SAAUG,EACX,GAEC,KAAK,QAAU,KACjB,KAAK,UAAY,IAAII,GAAK,CACxB,GAAGC,GAAQ,WAAW,KAAK,KAAK,UAAU,EAC1C,KAAM,KAAK,MACZ,EACDG,EAAS,KAAK,KAAK,SAAS,GAG9B,KAAK,IAAI,IAAIF,GAAK,CAAE,KAAM,WAAY,IAAKP,EAAU,OAAQ,MAAO,MAAO,OAAQ,SAAAS,CAAQ,CAAE,CAAC,CAChG,CAOA,IAAIC,EAAQnB,GAAK,CACf,QAAWW,KAAW,KAAK,aAAcA,EAAQ,IAAI,CAAE,QAAS,EAAG,EAAGQ,CAAK,CAAE,EAC7E,KAAK,WAAW,IAAI,CAAE,QAAS,EAAG,EAAGA,CAAK,CAAE,CAC9C,CAcA,MAAMC,EAA8E,CAClF,GAAM,CAAE,QAAAC,EAAS,MAAAF,EAAQnB,EAAK,EAAKoB,EAAK,MAAQ,CAAA,EAC1CE,EAASF,EAAK,QAAUG,GAAO,EAC/BC,EAAQ,KAAK,aACnB,GAAIA,EAAM,SAAW,EAAG,OAAOzB,GAAKqB,EAAK,QAAQ,EACjD,GAAM,CAAE,KAAAK,EAAM,IAAAC,EAAK,QAAAf,CAAO,EAAKgB,GAC7BP,EAAK,SACLI,EAAM,OACN,KAAK,YAAc,KACnBH,CAAO,EAGT,KAAK,IAAIF,CAAK,EAKd,IAAMS,GAAmBJ,EAAM,OAAS,GAAKC,EAAOC,EACpD,OAAO5B,GAAQ,CACb,GAAG0B,EAAM,IAAI,CAACK,EAAMjB,IAClBf,GAAGe,EAAIa,EAAMI,EAAK,GAAG,CAAE,KAAM,CAAE,QAAS,EAAG,EAAG,CAAC,EAAI,SAAUH,EAAK,OAAAJ,CAAM,CAAE,CAAC,CAAC,EAE9E,GAAI,KAAK,UACL,CAACzB,GAAG+B,EAAiB,KAAK,UAAU,GAAG,CAAE,KAAM,CAAE,QAAS,EAAG,EAAG,CAAC,EAAI,SAAUjB,EAAS,OAAAW,CAAM,CAAE,CAAC,CAAC,EAClG,CAAA,EACL,CACH,GA/GqDQ,GAAA,CAApDC,GAAS,CAAE,QAAS,IAAM,CAAA,CAAc,CAAE,oCACID,GAAA,CAA9CC,GAAS,CAAE,QAASC,CAAc,CAAE,8BACFF,GAAA,CAAlCC,GAAS,CAAE,QAAS,EAAE,CAAE,+BAC4BD,GAAA,CAApDC,GAAS,CAAE,QAAS,IAAM,CAAA,CAAc,CAAE,8BAkF3CD,GAAA,CADCG,GAAO,6BCpJV,OACE,WAAAC,GAEA,cAAAC,GACA,UAAAC,GACA,QAAAC,GACA,WAAAC,GACA,WAAAC,GACA,aAAAC,GACA,aAAAC,GACA,YAAAC,EACA,WAAAC,OAYK,qBCTP,OACE,WAAAC,GACA,cAAAC,GACA,aAAAC,GACA,aAAAC,GACA,WAAAC,OAQK,qBAiED,SAAUC,GACdC,EACAC,EACAC,EAAgB,CAEhB,IAAIC,EAAU,EACd,OAAOF,EAAO,IAAKG,GAAK,CACtB,IAAMC,EAAQC,EAAIN,EAAKI,EAAE,KAAK,EAC9B,GAAI,CAACF,EAAS,MAAO,CAAE,KAAM,EAAG,GAAIG,EAAO,MAAAA,CAAK,EAChD,IAAME,EAAOJ,EACb,OAAAA,GAAWE,EACJ,CAAE,KAAAE,EAAM,GAAIJ,EAAS,MAAAE,CAAK,CACnC,CAAC,CACH,CAWM,SAAUG,GAAcR,EAAiBC,EAAmB,CAChE,IAAIQ,EAAO,GACX,QAAS,EAAI,EAAG,EAAIR,EAAO,OAAQ,IAC7BK,EAAIN,EAAKC,EAAO,CAAC,EAAE,KAAK,IAAM,IAAGQ,EAAO,GAE9C,OAAOA,CACT,CAGM,SAAUC,GAAcC,EAAoBC,EAAa,CAC7D,OAAOD,EAAK,IAAKX,GAAQa,GAAKb,EAAKY,CAAK,CAAC,CAC3C,CAUM,SAAUE,GACdH,EACAV,EACAC,EAAgB,CAEhB,IAAIa,EAAK,EACLC,EAAK,EACT,QAAWhB,KAAOW,EAChB,QAAWM,KAAQlB,GAASC,EAAKC,EAAQC,CAAO,EAC9Ca,EAAK,KAAK,IAAIA,EAAIE,EAAK,KAAMA,EAAK,EAAE,EACpCD,EAAK,KAAK,IAAIA,EAAIC,EAAK,KAAMA,EAAK,EAAE,EAGxC,OAAOF,EAAKC,EAAK,CAACD,EAAIC,CAAE,EAAI,CAAC,EAAG,CAAC,CACnC,CAUM,SAAUE,GACdP,EACAV,EACAC,EACAiB,EACAC,EAAiB,CAEjB,GAAID,EAAO,OAAOA,EAClB,GAAM,CAACJ,EAAIC,CAAE,EAAIF,GAAeH,EAAMV,EAAQC,CAAO,EAC/CmB,EAAW,KAAK,IAAI,EAAG,KAAK,MAAMD,CAAS,CAAC,EAC5CE,EAAOC,IAAUP,EAAKD,GAAMM,GAAY,CAAC,EAC/C,MAAO,CAAC,KAAK,MAAMN,EAAKO,CAAI,EAAIA,EAAM,KAAK,KAAKN,EAAKM,CAAI,EAAIA,CAAI,CACnE,CA0DO,IAAME,EAA0C,CACrD,YAAaC,GACb,IAAK,CACH,aAAc,EACd,SAAU,IACV,SAAU,EACV,OAAQ,CAAA,EACR,OAAQ,CAAA,GAKV,MAAO,CACL,GAAGC,EACH,WAAY,CAAE,GAAGA,EAAe,WAAY,WAAY,GAAG,EAC3D,YAAa,IAEf,MAAOC,GACP,SAAUC,GACV,OAAQC,EACR,WAAY,CACV,SAAU,GACV,WAAY,IACZ,UAAW,SACX,KAAMC,EAAc,aAEtB,UAAW,CAAE,WAAYC,CAAa,GAGxC,SAASC,GACPC,EACAC,EAAwB,CAExB,MAAO,CACL,aAAcD,GAAK,cAAgBC,EAAS,aAC5C,SAAUD,GAAK,UAAYC,EAAS,SACpC,SAAUD,GAAK,UAAYC,EAAS,SACpC,OAAQC,GAAU,QAAQF,GAAK,QAAUC,EAAS,MAAM,EACxD,OAAQE,GAAU,QAAQH,GAAK,QAAUC,EAAS,MAAM,EAE5D,CAEA,SAASG,GACP9B,EACA+B,EACA,EAAS,CAET,MAAO,CACL,aAAcC,GAAWhC,EAAK,aAAc+B,EAAG,aAAc,CAAC,EAC9D,SAAUC,GAAWhC,EAAK,SAAU+B,EAAG,SAAU,CAAC,EAClD,SAAUC,GAAWhC,EAAK,SAAU+B,EAAG,SAAU,CAAC,EAClD,OAAQH,GAAU,KAChB5B,EAAK,OACL+B,EAAG,OACH,CAAC,EAEH,OAAQF,GAAU,KAChB7B,EAAK,OACL+B,EAAG,OACH,CAAC,EAGP,CAQM,SAAUE,GACdC,EACAP,EAAyB,CAEzB,IAAMQ,EAAOR,GAAYV,EACzB,MAAO,CACL,YAAamB,GAAmBF,GAAO,YAAaC,EAAK,WAAW,EACpE,IAAKV,GAAgBS,GAAO,IAAKC,EAAK,GAAG,EACzC,MAAOE,GAAkBH,GAAO,MAAOC,EAAK,KAAK,EACjD,MAAOE,GAAkBH,GAAO,MAAOC,EAAK,KAAK,EACjD,SAAUG,GAAgBJ,GAAO,SAAUC,EAAK,QAAQ,EACxD,OAAQI,GAAoBL,GAAO,OAAQC,EAAK,MAAM,EACtD,WAAYK,GAAQ,aAAaN,GAAO,WAAYC,EAAK,UAAU,EACnE,UAAW,CACT,WAAYD,GAAO,WAAW,YAAcC,EAAK,UAAU,YAGjE,CAGM,SAAUM,GACdzC,EACA+B,EACA,EAAS,CAET,MAAO,CACL,YAAaW,GAAgB1C,EAAK,YAAa+B,EAAG,YAAa,CAAC,EAChE,IAAKD,GAAa9B,EAAK,IAAK+B,EAAG,IAAK,CAAC,EACrC,MAAOY,GAAe3C,EAAK,MAAO+B,EAAG,MAAO,CAAC,EAC7C,MAAOY,GAAe3C,EAAK,MAAO+B,EAAG,MAAO,CAAC,EAC7C,SAAUa,GAAa5C,EAAK,SAAU+B,EAAG,SAAU,CAAC,EACpD,OAAQc,GAAiB7C,EAAK,OAAQ+B,EAAG,OAAQ,CAAC,EAClD,WAAYS,GAAQ,UAAUxC,EAAK,WAAY+B,EAAG,WAAY,CAAC,EAC/D,UAAW,CACT,WAAYC,GACVhC,EAAK,UAAU,WACf+B,EAAG,UAAU,WACb,CAAC,GAIT,CAGM,SAAUe,GACdC,EACArD,EACAsD,EAAa,CAEb,OAAItD,EAAO,MAAQ,KAAauD,GAAQ,QAAQvD,EAAO,IAAI,EACvDqD,EAAQ,SAAW,EAAU,QAC1BA,EAAQC,EAAQD,EAAQ,MAAM,CACvC,CDrUA,OAAS,iBAAAG,OAAqB,yWA0BxBC,GAAY,GAEZC,GAAY,IAEZC,GAAa,GA4DNC,EAAP,cAAyBC,EAAsB,CAmCnD,SAAwBC,EAExB,YAAYC,EAA6C,CAMvD,GALA,MAAMA,CAAK,EAKP,EAAC,KAAK,WACV,QAASC,EAAI,EAAGA,EAAI,KAAK,KAAK,OAAQA,IACpC,KAAK,IACH,IAAIC,GAAK,CACP,GAAGC,GAAQ,WAAW,KAAK,UAAU,EACrC,KAAM,IAAM,KAAK,UAAUF,CAAC,EAC5B,EAAG,IAAM,KAAK,KAAKA,CAAC,GAAG,GAAK,EAC5B,EAAG,IAAM,KAAK,SAASA,CAAC,EACxB,QAAS,IAAM,KAAK,MAAMA,CAAC,EAC5B,CAAC,CAGR,CAUQ,MAAMG,EAAa,CACzB,OAAK,KAAK,QACHC,EAAU,KAAK,KAAKD,CAAK,GAAK,CAAC,EADZ,CAE5B,CAGQ,OAAOA,EAAa,CAC1B,IAAME,EAAM,KAAK,KAAKF,CAAK,EAC3B,GAAI,CAACE,EAAK,OAAO,KACjB,IAAMC,EAAIF,EAAU,KAAK,MAAM,EACzBG,EAAO,KAAK,UAAYF,EAAI,KAAO,KAAK,UAAYC,EACpDE,EAAM,KAAK,UAAYH,EAAI,GAAK,KAAK,UAAYC,EACvD,MAAO,CAAE,KAAAC,EAAM,IAAAC,CAAG,CACpB,CAGA,IAAY,WAAS,CACnB,IAAMC,EAAO,KAAK,WAAW,SAC7B,OAAO,OAAOA,GAAS,SAAWA,EAAOd,EAC3C,CASQ,UAAUQ,EAAa,CAC7B,IAAME,EAAM,KAAK,KAAKF,CAAK,EAC3B,GAAI,CAACE,GAAOA,EAAI,QAAU,EAAG,MAAO,GAIpC,GAAI,KAAK,SAASF,CAAK,EAAG,CACxB,IAAMO,EAAO,KAAK,OAAOP,CAAK,EAE9B,GADI,CAACO,GACD,KAAK,IAAIA,EAAK,IAAMA,EAAK,IAAI,EAAI,KAAK,UAAYhB,GAAW,MAAO,EAC1E,CACA,OAAO,KAAK,SAASW,EAAI,KAAK,CAChC,CAGQ,SAASF,EAAa,CAC5B,IAAME,EAAM,KAAK,KAAKF,CAAK,EAC3B,OAAOE,IAAQ,QAAa,KAAK,IAAIA,EAAI,KAAO,KAAK,QAAQ,EAAI,EACnE,CAGQ,SAASF,EAAa,CAC5B,IAAMO,EAAO,KAAK,OAAOP,CAAK,EAC9B,GAAI,CAACO,EAAM,MAAO,GAClB,GAAI,KAAK,SAASP,CAAK,EAAG,OAAQO,EAAK,KAAOA,EAAK,KAAO,EAG1D,IAAMC,EAAYD,EAAK,KAAOA,EAAK,KAAO,EAAI,GAC9C,OAAOA,EAAK,IAAMC,GAAalB,GAAY,KAAK,UAAY,EAC9D,CAOS,cAAcmB,EAAkB,CACvC,MAAM,cAAcA,CAAM,EAC1BpB,GAAc,KAAMoB,EAAQ,CAC1B,MAAQ,KAAK,UAA+B,IAAKC,GAAWA,EAAO,IAAI,EACvE,QAAS,CAAC,KAAK,SAA6B,EAC7C,CACH,CAEU,WAAWC,EAAoB,CACvC,GAAK,KAAK,QACV,QAASd,EAAI,EAAGA,EAAI,KAAK,KAAK,OAAQA,IAAK,CACzC,IAAMK,EAAM,KAAK,KAAKL,CAAC,EACjBU,EAAO,KAAK,OAAOV,CAAC,EAC1B,GAAI,CAACU,GAAQL,EAAI,OAAS,EAAG,SAC7B,IAAMU,EAAS,KAAK,IAAIL,EAAK,IAAMA,EAAK,IAAI,EAC5C,GAAIK,GAAU,EAAG,SAEjB,IAAMC,EAASX,EAAI,MAAQ,KAAK,IAAI,KAAK,aAAcU,CAAM,EAAI,EAI3DT,EAAI,IAAIW,GACRC,EAAOZ,EAAE,KAAK,CAClB,EAAGD,EAAI,EACP,GAAIK,EAAK,KAAOA,EAAK,KAAO,EAC5B,MAAOL,EAAI,MACX,OAAAU,EAMA,aACEC,EAAS,EACLN,EAAK,KAAOA,EAAK,KACf,CAAE,WAAYM,EAAQ,YAAaA,EAAQ,QAAS,EAAG,SAAU,CAAC,EAClE,CAAE,QAASA,EAAQ,SAAUA,EAAQ,WAAY,EAAG,YAAa,CAAC,EACpE,EACP,EACDE,EAAK,OAAO,KAAK,SAAS,EAC1BA,EAAK,KAAK,KAAK,IAAI,EACnBA,EAAK,OAAO,KAAK,SAAS,EAC1BJ,EAAI,KAAKR,EAAE,QAAQ,KAAK,MAAMN,CAAC,CAAC,CAAC,CACnC,CACF,CAUA,MAAMmB,EAA+D,CAGnE,OAAO,KAAK,QAA0B,IAAO,CAAE,OAAQ,CAAC,GAAKA,EAAK,SAAUA,EAAK,QAAUC,GAAO,CAAE,CACtG,GA1L0DC,EAAA,CAAzDC,EAAS,CAAE,QAAS,IAAM,CAAA,CAAmB,CAAE,8BACdD,EAAA,CAAjCC,EAAS,CAAE,QAAS,CAAC,CAAE,kCAMhBD,EAAA,CALPC,EAAS,CACR,QAAS,CAAA,EACT,OAAQC,GAAQ,QAChB,MAAOA,GAAQ,KAChB,8BAOOF,EAAA,CALPC,EAAS,CACR,QAASE,EAAwB,IAAI,OACrC,OAAQC,GAAU,QAClB,MAAOA,GAAU,KAClB,mCAOOJ,EAAA,CALPC,EAAS,CACR,QAASE,EAAwB,IAAI,OACrC,OAAQE,GAAU,QAClB,MAAOA,GAAU,KAClB,mCAGOL,EAAA,CADPC,EAAS,CAAE,QAASE,EAAwB,IAAI,YAAY,CAAE,sCAE7BH,EAAA,CAAjCC,EAAS,CAAE,QAAS,CAAC,CAAE,gCAC6BD,EAAA,CAApDC,EAAS,CAAE,QAAS,IAAM,CAAA,CAAc,CAAE,8BACLD,EAAA,CAArCC,EAAS,CAAE,QAAS,EAAK,CAAE,oCAEpBD,EAAA,CADPC,EAAS,CAAE,QAASE,EAAwB,UAAU,CAAE,oCAEpBH,EAAA,CAApCC,EAAS,CAAE,QAAS,EAAI,CAAE,iCA2J3BD,EAAA,CADCM,GAAO,0WFhNJC,GAAgB,GAEhBC,GAAgB,GAEhBC,GAAgB,GAEhBC,GAAiB,GACjBC,GAAiB,GAEjBC,GAAe,GAEfC,GAAe,GAGfC,GAAe,GAmBfC,GAAa,IACbC,GAAa,GACbC,GAAe,IAqDRC,EAAN,cAAuBC,EAAqB,gBA6DzC,MACA,MACA,OAA6B,KAC7B,QAAuB,CAAA,EAO/B,IAAY,eAAa,CACvB,OAAO,KAAK,KACd,CAKQ,OAAgB,gBAAkB,EAE1C,IAAY,YAAU,CACpB,IAAMC,GACH,KAAK,UAAU,OAAO,QAAUC,GAAS,iBAAmB,EAC/D,OAAOC,GACL,KAAK,KACL,KAAK,OACL,KAAK,QACL,KAAK,UAAU,MACfF,CAAS,CAEb,CAOQ,WAAWG,EAAa,CAC9B,IAAMC,EAAU,KAAK,UAAU,OAC/B,OAAOA,IAAY,OACfC,EAAaF,CAAK,EAClBG,GAAaH,EAAOC,CAAO,CACjC,CAGA,IAAY,iBAAe,CACzB,OACE,KAAK,UAAU,OACfG,EAAa,KAAK,WAAYN,GAAS,gBAAkB,CAAC,CAE9D,CAIA,IAAY,eAAa,CACvB,OAAO,KAAK,UAAU,QAAU,EAClC,CACA,IAAY,kBAAgB,CAC1B,OAAO,KAAK,aAAa,QAAU,EACrC,CAEA,IAAY,WAAS,CACnB,OAAOd,IAAiB,KAAK,cAAgBG,GAAiB,EAChE,CACA,IAAY,aAAW,CACrB,OAAOF,IAAiB,KAAK,iBAAmBG,GAAiB,EACnE,CAGQ,SAAO,CAMb,IAAMiB,EAAI,KAAK,aAAa,MACtBC,EAAI,KAAK,aAAa,OACtBC,EAAUD,EAAI,EACdE,EAAa,CAACF,EAAI,GAAK,KAAK,OAASpB,GAAgB,GACrDuB,EAAO,CAACJ,EAAI,EAAI,KAAK,UACrBK,EAAQL,EAAI,EAAIf,GAChBqB,EAAMJ,EAAUlB,GAChBuB,EAASJ,EAAa,KAAK,YACjC,MAAO,CAAE,KAAAC,EAAM,MAAAC,EAAO,IAAAC,EAAK,OAAAC,CAAM,CACnC,CAGQ,UAAQ,CACd,GAAM,CAAE,KAAAH,EAAM,MAAAC,EAAO,IAAAC,EAAK,OAAAC,CAAM,EAAK,KAAK,QAAO,EACjD,MAAO,CAAE,EAAGF,EAAQD,EAAM,EAAGE,EAAMC,CAAM,CAC3C,CAGQ,SAASZ,EAAa,CAC5B,GAAM,CAACa,EAAKC,CAAG,EAAI,KAAK,WAClB,CAAE,EAAAC,CAAC,EAAK,KAAK,SAAQ,EAC3B,OAAID,GAAOD,EAAY,CAACE,EAAI,IACnBf,EAAQa,IAAQC,EAAMD,GAAO,IAAOE,CAC/C,CASA,IAAY,YAAU,CACpB,GAAM,CAACF,EAAKC,CAAG,EAAI,KAAK,WACxB,OAAO,KAAK,SAAS,KAAK,IAAI,KAAK,IAAI,EAAGD,CAAG,EAAGC,CAAG,CAAC,CACtD,CAKA,IAAY,WAAS,CACnB,IAAME,EAAI,KAAK,KAAK,OACpB,OAAOA,EAAI,EAAI,KAAK,SAAQ,EAAG,EAAIA,EAAI,CACzC,CAGQ,WAAWC,EAAa,CAC9B,MAAO,CAAC,KAAK,SAAQ,EAAG,EAAI,EAAI,KAAK,WAAaA,EAAQ,GAC5D,CAGA,IAAY,WAAS,CACnB,OAAO,KAAK,UAAYC,EAAU,KAAK,cAAc,IAAI,QAAQ,CACnE,CAGQ,SAASD,EAAa,CAC5B,OAAO,KAAK,IAAI,EAAG,KAAK,QAAQA,CAAK,GAAK,CAAC,CAC7C,CAGA,IAAY,aAAW,CACrB,IAAIE,EAAM,EACV,QAASC,EAAI,EAAGA,EAAI,KAAK,OAAO,OAAQA,IAAKD,GAAO,KAAK,SAASC,CAAC,EACnE,OAAOD,EAAM,EAAIA,EAAM,CACzB,CAGQ,aAAaF,EAAa,CAChC,IAAIE,EAAM,EACV,QAAS,EAAI,EAAG,EAAIF,EAAO,IAAKE,GAAO,KAAK,SAAS,CAAC,EACtD,OAAOA,CACT,CAWQ,WAAWF,EAAa,CAC9B,IAAMI,EAAS,KAAK,OAAOJ,CAAK,EAChC,GAAI,CAACI,EAAQ,MAAO,CAAA,EACpB,IAAMC,EAAU,KAAK,QACfC,EAAQ,KAAK,UACbC,EAAM,KAAK,cAAc,IAAI,SAC7BC,EAAOF,EAAQ,KAAK,YACpBG,EAAS,KAAK,SAAST,CAAK,EAElC,OAAO,KAAK,KAAK,IAAI,CAACU,EAAKC,IAAY,CAUrC,IAAMC,EALQC,GACZR,EAAU,KAAK,YAAYK,CAAG,EAAIA,EAClC,KAAK,OACLL,CAAO,EAEUL,CAAK,GAAK,CAAE,KAAM,EAAG,GAAI,EAAG,MAAO,CAAC,EACjDc,EAAMT,EAAUU,GAAcL,EAAK,KAAK,MAAM,EAAIV,EAElDgB,EAAW,KAAK,WAAWL,CAAQ,EAAIL,EAAQ,EAC/CW,EAAIZ,EACN,KAAK,WAAWM,CAAQ,EACxBK,EAAWR,EAAO,KAAK,aAAaR,CAAK,EAAKQ,EAAOC,EAAU,EAC7DS,EAAQb,EACVC,EACA,KAAK,IAAI,EAAGE,EAAOC,GAAU,KAAK,OAAO,OAAS,EAAIF,EAAM,EAAE,EAElE,MAAO,CACL,EAAAU,EACA,MAAAC,EACA,KAAM,KAAK,SAASN,EAAK,IAAI,EAC7B,GAAI,KAAK,SAASA,EAAK,EAAE,EACzB,MAAOE,IAAQd,EAGf,MAAOmB,EAAIT,EAAKN,EAAO,KAAK,EAEhC,CAAC,CACH,CAMQ,YAAYM,EAAe,CACjC,IAAMU,EAAqB,CAAE,GAAGV,CAAG,EACnC,YAAK,OAAO,QAAQ,CAACW,EAAGlB,IAAK,CAC3B,IAAMpB,EAAQ2B,EAAIW,EAAE,KAAK,EACrB,OAAOtC,GAAU,WAAUqC,EAAOC,EAAE,KAAK,EAAItC,EAAQ,KAAK,SAASoB,CAAC,EAC1E,CAAC,EACMiB,CACT,CAOQ,cAAY,CAClB,IAAME,EAAQrB,EAAU,KAAK,eAAe,EACtCsB,EAAM,KAAK,cAAc,UAAU,WACnCC,EAAS,KAAK,UACpB,OAAO,KAAK,KAAK,IAAI,CAACC,EAAGtB,IACvBmB,GAAS,GAAKE,IAAWrB,EAAI,EAAI,GAAK,EAAIoB,GAAOD,CAAK,CAE1D,CAIA,YAAYI,EAA2C,CACrD,MAAMA,CAAK,EAQX,IAAMC,EAAU,KAAK,OAAO,IAAKN,GAAMA,EAAE,SAAW,EAAI,EACxD,KAAK,QAAUM,EAAQ,IAAKC,GAAQA,EAAK,EAAI,CAAE,EAC/C,IAAMC,EAAaH,GAAO,aAAe,GACnCI,EAAaJ,GAAO,aAAe,GAEzC,KAAK,MAAQ,IAAIK,GAAa,CAC5B,MAAO,IAAM,KAAK,WAClB,QAAS,IAAM,KAAK,gBACpB,KAAM,IAAM,KAAK,cAAc,MAC/B,MAAO,KAAK,UAAU,OAAS,GAC/B,MAAO,MACP,OAAQ,OACT,EACD,KAAK,MAAM,SAAYhD,GAAU,KAAK,WAAWA,CAAK,EAEtD,KAAK,MAAQ,IAAIiD,EAAa,CAC5B,WAAYC,GAAc,KAAK,KAAM,KAAK,aAAa,EACvD,KAAM,IAAM,KAAK,cAAc,MAC/B,MAAO,KAAK,aAAa,OAAS,GAClC,KAAM,IAAM,KAAK,aAAY,EAC7B,MAAO,OACP,OAAQ,MACT,EAED,KAAK,QAAU,KAAK,OAAO,IACzB,CAACZ,EAAGlB,IACF,IAAI+B,EAAU,CACZ,KAAM,IAAM,KAAK,WAAW/B,CAAC,EAC7B,SAAU,IAAM,KAAK,WACrB,KAAM,IAAMgC,GAAe,KAAK,cAAc,YAAad,EAAGlB,CAAC,EAC/D,UAAW,IAAM,KAAK,cAAc,IAAI,OACxC,UAAW,IAAM,KAAK,cAAc,IAAI,OACxC,aAAc,IAAM,KAAK,cAAc,IAAI,aAC3C,OAAQ,IAAM,KAAK,OACnB,KAAM,IAAM,KAAK,aAAY,EAC7B,WAAA2B,EACA,WAAY,KAAK,cAAc,WAC/B,QAASH,EAAQxB,CAAC,EACnB,CAAC,EAIN,QAAWiC,KAAO,KAAK,QAASA,EAAI,SAAYrD,GAAU,KAAK,WAAWA,CAAK,EAE3E8C,IACF,KAAK,OAAS,IAAIQ,GAAY,CAC5B,OAAQ,KAAK,OACb,SAAU,IACR,KAAK,OAAO,IAAI,CAAChB,EAAGlB,IAClBgC,GAAe,KAAK,cAAc,YAAad,EAAGlB,CAAC,CAAC,EAExD,OAAQ,IAAM,KAAK,cAAc,OAClC,GAGH,IAAMmC,EAAO,IAAIC,GAAK,CACpB,KAAM,GACN,SAAU,CACR,IAAIC,GAAY,CACd,KAAM,IAAM,KAAK,cAAc,SAAS,KACxC,OAAQ,IAAM,KAAK,cAAc,SAAS,OAC1C,YAAa,IAAM,KAAK,cAAc,MAAM,UAC5C,YAAa,IAAM,KAAK,cAAc,MAAM,UAI5C,SAAU,CAAA,EACV,SAAU,IAAM,KAAK,gBACrB,QAAS,IAAM,CAAC,EAAG,CAAC,EACpB,QAAS,IAAM,KAAK,WACrB,EACD,GAAG,KAAK,QACR,IAAIC,GAAY,CACd,OAAQ,IAAM,KAAK,cAAc,SAAS,OAC3C,GAEJ,EAKKC,EAAe,IAAIX,GAAa,CACpC,QAAS,EACT,MAAO,IAAM,KAAK,WAClB,QAAS,IAAM,KAAK,gBACpB,KAAM,IAAM,KAAK,cAAc,MAC/B,MAAO,KAAK,UAAU,OAAS,GAC/B,MAAO,MACP,OAAQ,EACT,EAEKY,EAAkB,CAAC,KAAK,KAAK,EAC/B,KAAK,QAAQA,EAAM,KAAK,KAAK,MAAM,EAEvC,KAAK,IACH,IAAIJ,GAAK,CAAE,KAAM,WACf,MAAO,OACP,OAAQ,OACR,SAAU,CACR,IAAIA,GAAK,CAAE,KAAM,aACf,OAAQ,OACR,MAAO,OACP,SAAU,CAAC,KAAK,MAAOD,CAAI,EAC5B,EACD,IAAIC,GAAK,CAAE,KAAM,aACf,MAAO,OACP,SAAU,CACRG,EACA,IAAIH,GAAK,CAAE,KAAM,WAAY,MAAO,OAAQ,IAAK,GAAI,SAAUI,CAAK,CAAE,GAEzE,GAEJ,CAAC,CAEN,CAaA,KAAG,CACD,KAAK,MAAM,IAAG,EACd,KAAK,MAAM,IAAG,EACd,KAAK,OAAS,EACd,KAAK,QAAQ,IAAI,CAAE,QAAS,EAAG,EAAG,CAACrE,EAAY,CAAE,CACnD,CAaA,MAAMsE,EAA+D,CACnE,GAAM,CAAE,SAAAC,CAAQ,EAAKD,EACfE,EAASF,EAAK,QAAUG,GAAO,EACrC,YAAK,IAAG,EAEDC,GACLC,GACE,KAAK,MAAM,MAAM,CAAE,KAAM,CAAE,MAAO,EAAE,EAAI,SAAUJ,EAAWtE,GAAY,OAAAuE,CAAM,CAAE,EACjF,KAAK,MAAM,MAAM,CAAE,KAAM,CAAE,MAAO,EAAE,EAAI,SAAUD,EAAWtE,GAAY,OAAAuE,CAAM,CAAE,CAAC,EAEpF,KAAK,SAASD,EAAWrE,GAAYsE,CAAM,EAC3C,GAAI,KAAK,OACL,CAAC,KAAK,OAAO,GAAG,CAAE,KAAM,CAAE,QAAS,EAAG,EAAG,CAAC,EAAI,SAAUD,EAAWpE,GAAc,OAAAqE,CAAM,CAAE,CAAC,EAC1F,CAAA,CAAG,CAEX,CAGQ,SACND,EACAC,EAAsB,CAItB,OAAO,KAAK,QAAoBI,IAAO,CAAE,OAAQA,CAAC,GAAKL,EAAUC,CAAM,CACzE,CAOA,WAAWF,EAA0C,CACnD,GAAM,CAAE,YAAAO,EAAc,CAAC,EAAKP,EAAK,MAAQ,CAAA,EACzC,OAAO,KAAK,aAAaO,EAAa,EAAGP,EAAK,UAAY,IAAMA,EAAK,QAAUG,GAAO,CAAE,CAC1F,CAQA,WAAWH,EAA0C,CACnD,GAAM,CAAE,YAAAO,EAAc,CAAC,EAAKP,EAAK,MAAQ,CAAA,EACzC,OAAO,KAAK,aAAaO,EAAa,EAAGP,EAAK,UAAY,IAAMA,EAAK,QAAUG,GAAO,CAAE,CAC1F,CAUQ,aACN/C,EACAoD,EACAP,EACAC,EAAsB,CAEtB,IAAMO,EAAO,KAAK,QAAQrD,CAAK,EAC/B,OAAKqD,EAEEJ,GACL,KAAK,YAAYjD,EAAOoD,EAAQP,EAAUC,CAAM,EAChDQ,GAAMT,EAAWK,GAAK,CACpB,IAAMK,EAAQL,GAAK,EAAIE,EAAS,KAAK,IAAIA,EAAQ,EAAIF,CAAC,EACtDG,EAAK,IAAI,CAAE,QAASD,EAAS,GAAKG,EAAQ,CAAC,CAAE,CAC/C,CAAC,EACD,GAAI,KAAK,OACL,CACAH,EAAS,EACL,KAAK,OAAO,WAAW,CAAE,KAAM,CAAE,MAAApD,CAAK,EAAI,SAAA6C,EAAU,OAAAC,CAAM,CAAE,EAC5D,KAAK,OAAO,WAAW,CAAE,KAAM,CAAE,MAAA9C,CAAK,EAAI,SAAA6C,EAAU,OAAAC,CAAM,CAAE,GAEhE,CAAA,CAAG,EAdSU,GAAKX,CAAQ,CAgBjC,CAQA,gBAAgBD,EAA0C,CACxD,GAAM,CAAE,YAAAO,EAAc,CAAC,EAAKP,EAAK,MAAQ,CAAA,EACnCC,EAAWD,EAAK,UAAY,IAC5BE,EAASF,EAAK,QAAUG,GAAO,EAC/BxB,EAAM,KAAK,cAAc,UAAU,WACzC,OAAO0B,GACL,GAAG,KAAK,QAAQ,IAAI,CAACI,EAAMlD,IACzBkD,EAAK,GAAG,CAAE,KAAM,CAAE,QAASlD,IAAMgD,EAAc,EAAI5B,CAAG,EAAI,SAAAsB,EAAU,OAAAC,CAAM,CAAE,CAAC,EAE/E,GAAI,KAAK,OACL,CAAC,KAAK,OAAO,gBAAgB,CAAE,KAAM,CAAE,MAAOK,CAAW,EAAI,SAAAN,EAAU,OAAAC,CAAM,CAAE,CAAC,EAChF,CAAA,CAAG,CAEX,CAGQ,YACN9C,EACAoD,EACAP,EACAC,EAAsB,CAItB,IAAMW,EAAO,KAAK,OAAO,IAAI,CAAChC,EAAGtB,IAAM,KAAK,SAASA,CAAC,CAAC,EACjDuD,EAAQD,EAAKzD,CAAK,GAAK,EAC7B,OAAI0D,IAAUN,EAAeI,GAAKX,CAAQ,EAEnCS,GAAMT,EAAWK,GAAK,CAC3B,IAAMS,EAAO,CAAC,GAAGF,CAAI,EACrBE,EAAK3D,CAAK,EAAI0D,GAASN,EAASM,GAASZ,EAAOI,CAAC,EACjD,KAAK,QAAUS,CACjB,CAAC,CACH,CAeA,kBAAkBf,EAA4C,CAC5D,GAAM,CAAE,cAAAgB,EAAgB,CAAC,EAAKhB,EAAK,MAAQ,CAAA,EACrCC,EAAWD,EAAK,UAAY,GAC5BE,EAASF,EAAK,QAAUG,GAAO,EACrC,GAAIa,EAAgB,GAAKA,GAAiB,KAAK,KAAK,OAAQ,OAAO,KAAK,cAAcf,CAAQ,EAC9F,IAAMY,EAAO,KAAK,gBAIlB,OAAO,KAAK,QACTP,IAAO,CAAE,UAAWU,EAAe,gBAAiBH,GAAQ,EAAIA,GAAQP,CAAC,GAC1EL,EACAC,CAAM,CAEV,CAQA,cAAcF,EAAwC,CACpD,IAAMC,EAAWD,EAAK,UAAY,GAC5BE,EAASF,EAAK,QAAUG,GAAO,EAC/BU,EAAO,KAAK,gBAClB,OAAIA,IAAS,EAAU,KAAK,cAAcZ,CAAQ,EAI3C,KAAK,QACTK,GACCA,GAAK,EACD,CAAE,UAAW,KAAM,gBAAiB,CAAC,EACrC,CAAE,gBAAiBO,GAAQ,EAAIP,EAAE,EACvCL,EACAC,CAAM,CAEV,CAGQ,cAAcD,EAAgB,CACpC,OAAO,KAAK,QAA2B,KAAO,CAAA,GAAKA,CAAQ,CAC7D,GA7mByDgB,EAAA,CAAxDC,EAAS,CAAE,QAAS,IAAM,CAAA,CAAkB,CAAE,8BACZD,EAAA,CAAlCC,EAAS,CAAE,QAAS,EAAE,CAAE,uCAC+BD,EAAA,CAAvDC,EAAS,CAAE,QAAS,IAAM,CAAA,CAAiB,CAAE,gCACRD,EAAA,CAArCC,EAAS,CAAE,QAAS,EAAK,CAAE,iCAapBD,EAAA,CALPC,EAAS,CACR,QAAS,CAAA,EACT,OAAQC,GACR,MAAOC,GACR,mCAOOH,EAAA,CALPC,EAAS,CACR,QAAS,CAAA,EACT,OAAQC,GACR,MAAOC,GACR,sCAOOH,EAAA,CALPC,EAAS,CACR,QAASG,EACT,OAAQC,GACR,MAAOC,GACR,+BASiCN,EAAA,CAAjCC,EAAS,CAAE,QAAS,CAAC,CAAE,gCAW6BD,EAAA,CAApDC,EAAS,CAAE,QAAS,IAAM,CAAA,CAAc,CAAE,iCAOND,EAAA,CAApCC,EAAS,CAAE,QAAS,IAAI,CAAE,mCAGOD,EAAA,CAAjCC,EAAS,CAAE,QAAS,CAAC,CAAE,yCAkYxBD,EAAA,CADCO,GAAO,6BAiCRP,EAAA,CADCO,GAAQ,CAAE,KAAM,CAAC,CAAE,IAAK,cAAe,KAAM,SAAU,QAAS,EAAG,IAAK,EAAG,KAAM,CAAC,CAAE,CAAC,CAAE,kCAYxFP,EAAA,CADCO,GAAQ,CAAE,KAAM,CAAC,CAAE,IAAK,cAAe,KAAM,SAAU,QAAS,EAAG,IAAK,EAAG,KAAM,CAAC,CAAE,CAAC,CAAE,kCA6CxFP,EAAA,CADCO,GAAQ,CAAE,KAAM,CAAC,CAAE,IAAK,cAAe,KAAM,SAAU,QAAS,EAAG,IAAK,EAAG,KAAM,CAAC,CAAE,CAAC,CAAE,uCAiDxFP,EAAA,CADCO,GAAQ,CAAE,KAAM,CAAC,CAAE,IAAK,gBAAiB,KAAM,SAAU,QAAS,EAAG,IAAK,EAAG,KAAM,CAAC,CAAE,CAAC,CAAE,yCAuB1FP,EAAA,CADCO,GAAO,qCAxlBG1F,EAAQG,GAAAgF,EAAA,CAfpBQ,GAAK,CACJ,IAAK,WACL,UAAW,OACX,SAAU,GACV,OAAQ,CACN,SAAU,WACV,iBAAkB,QAClB,kBAAmB,QACnB,gBAAiB,IAEnB,KAAM,CACJ,MAAO,IACP,OAAQ,KAEX,GACY3F,CAAQ,EIpLrB,OACE,QAAA4F,GACA,WAAAC,GACA,QAAAC,GACA,UAAAC,GACA,WAAAC,GACA,YAAAC,MAMK,qBCZP,OACE,WAAAC,GACA,QAAAC,GACA,QAAAC,GACA,WAAAC,GACA,YAAAC,GACA,WAAAC,OAIK,qBAOP,OAAS,MAAAC,GAAI,WAAAC,GAAS,QAAAC,OAA2B,0WAgB3CC,GAAQ,GAkBDC,EAAP,cAAyBC,EAAI,CAMzB,MAAgB,CAAA,EAExB,YAAYC,EAA6C,CAKvD,MAAM,CACJ,GAAIA,EACJ,KAAM,WACN,MAAO,aACP,MAAO,MACP,OAAQ,MACT,EAGD,KAAK,IAAI,CAAE,IAAK,KAAK,OAAO,QAAU,EAAG,QAAS,KAAK,OAAO,OAAO,CAAE,EAEvE,QAASC,EAAI,EAAGA,EAAI,KAAK,OAAO,OAAQA,IAAK,CAC3C,IAAMC,EAAO,KAAK,UAAU,KAAK,OAAOD,CAAC,CAAC,EAC1C,KAAK,MAAM,KAAKC,CAAI,EAKpB,KAAK,IACH,IAAIH,GAAK,CACP,MAAO,MACP,OAAQ,MACR,KAAM,WACN,QAAS,IAAMI,EAAU,KAAK,KAAKF,CAAC,GAAK,CAAC,EAC1C,SAAU,CAACC,CAAI,EAChB,CAAC,CAEN,CACF,CAGQ,UAAUE,EAAe,CAC/B,IAAMC,EAAS,KAAK,OAAO,YAC3B,OAAO,IAAIN,GAAK,CAAE,KAAM,aACtB,IAAK,GACL,MAAO,SACP,MAAO,MACP,OAAQ,MACR,SAAU,CACR,IAAIA,GAAK,CACP,MAAOM,EAAO,OACd,OAAQA,EAAO,OACf,aAAcA,EAAO,aACrB,KAAMD,EAAM,KACZ,OAAQC,EAAO,OACf,OAAQA,EAAO,OAChB,EACD,IAAIC,GAAK,CACP,GAAGC,GAAQ,WAAW,KAAK,OAAO,SAAS,EAC3C,KAAMH,EAAM,SACb,GAEJ,CACH,CASA,IAAII,EAAQX,GAAK,CACf,QAAWK,KAAQ,KAAK,MAAOA,EAAK,IAAI,CAAE,QAAS,EAAG,EAAGM,CAAK,CAAE,CAClE,CAUA,MACEC,EAA+D,CAE/D,IAAMC,EAAWD,EAAK,SAChBE,EAASF,EAAK,QAAUG,GAAO,EAC/BC,EAAQ,KAAK,MACnB,GAAIA,EAAM,SAAW,EAAG,OAAOjB,GAAKc,CAAQ,EAC5C,KAAK,IAAG,EAMR,IAAMI,EAAMJ,GAAY,EAAI,KAAK,IAAI,EAAGG,EAAM,OAAS,CAAC,EAAI,GACtDE,EAAOD,EAAM,EAEnB,OAAOnB,GACLkB,EAAM,IAAI,CAACX,EAAMD,IAAMP,GAAGO,EAAIc,EAAMb,EAAK,GAAG,CAAE,KAAM,CAAE,QAAS,EAAG,EAAG,CAAC,EAAI,SAAUY,EAAK,OAAAH,CAAM,CAAE,CAAC,CAAC,CAAC,CAExG,GAzGuDK,GAAA,CAAtDC,GAAS,CAAE,QAAS,IAAM,CAAA,CAAgB,CAAE,gCACED,GAAA,CAA9CC,GAAS,CAAE,QAASC,CAAc,CAAE,gCACgBF,GAAA,CAApDC,GAAS,CAAE,QAAS,IAAM,CAAA,CAAc,CAAE,8BAoF3CD,GAAA,CADCG,GAAO,6BCzIV,OACE,QAAAC,GACA,WAAAC,GAEA,cAAAC,GACA,UAAAC,GACA,eAAAC,GACA,QAAAC,GACA,WAAAC,GACA,WAAAC,GACA,YAAAC,EACA,aAAAC,GACA,WAAAC,OASK,qBCLP,OACE,cAAAC,GACA,aAAAC,GACA,aAAAC,GACA,WAAAC,MASK,qBA6CD,SAAUC,GACdC,EACAC,EACAC,EACAC,EAAe,CAEf,OAAOH,EAAK,IAAI,CAACI,EAAKC,KAAO,CAC3B,SAAUC,GAAKF,EAAKH,CAAQ,EAC5B,MAAOM,EAAIH,EAAKF,CAAK,EACrB,KACEC,EAAQ,OAAS,EAAIA,EAAQE,EAAIF,EAAQ,MAAM,EAAK,SACtD,CACJ,CA8BO,IAAMK,GAAM,KAAK,GAAK,IAGvB,SAAUC,GAAWC,EAAe,CACxC,OAAOA,EAAM,MAAQ,GAAK,OAAO,SAASA,EAAM,KAAK,EAAIA,EAAM,MAAQ,CACzE,CAGM,SAAUC,GAAWC,EAAkB,CAC3C,IAAIC,EAAM,EACV,QAAWH,KAASE,EAAQC,GAAOJ,GAAWC,CAAK,EACnD,OAAOG,CACT,CAOM,SAAUC,GAAUC,EAAWC,EAAS,CAC5C,MAAO,CAAE,EAAGD,EAAI,KAAK,IAAIC,CAAC,EAAG,EAAGD,EAAI,KAAK,IAAIC,CAAC,CAAC,CACjD,CA4BM,SAAUC,GAAQL,EAAoBM,EAAoB,CAC9D,IAAMC,EAAQR,GAAWC,CAAM,EAC/B,GAAIO,GAAS,EAAG,MAAO,CAAA,EAEvB,IAAMC,EAAiB,CAAA,EACnBC,EAASH,EAAeV,GAC5B,OAAAI,EAAO,QAAQ,CAACF,EAAOY,IAAS,CAC9B,IAAMpB,EAAQO,GAAWC,CAAK,EAC9B,GAAIR,GAAS,EAAG,OAChB,IAAMqB,EAAWrB,EAAQiB,EACnBK,EAAKH,EACLI,EAAKD,EAAKD,EAAW,KAAK,GAAK,EACrCF,EAASI,EACTL,EAAK,KAAK,CAAE,MAAAE,EAAO,GAAAE,EAAI,GAAAC,EAAI,KAAMD,EAAKC,GAAM,EAAG,SAAAF,CAAQ,CAAE,CAC3D,CAAC,EACMH,CACT,CAsBM,SAAUM,GACdC,EACAC,EACAC,EACAC,EAAc,CAEdH,EAAM,KAAK,CAACX,EAAGe,IAAMA,EAAE,MAAQf,EAAE,KAAK,EAEtC,IAAIgB,EAAUH,EACd,QAAWI,KAAQN,EACjBM,EAAK,EAAI,KAAK,IAAIA,EAAK,MAAOD,CAAO,EACrCA,EAAUC,EAAK,EAAIL,EAGrB,IAAIM,EAAQJ,EACZ,QAASzB,EAAIsB,EAAM,OAAS,EAAGtB,GAAK,EAAGA,IACrCsB,EAAMtB,CAAC,EAAE,EAAI,KAAK,IAAIsB,EAAMtB,CAAC,EAAE,EAAG6B,CAAK,EACvCA,EAAQP,EAAMtB,CAAC,EAAE,EAAIuB,EAGvB,OAAOD,CACT,CA6EA,IAAMQ,GAA8B,CAClC,SAAU,GACV,WAAY,IACZ,UAAW,SACX,KAAMC,EAAc,aAITC,EAA0C,CACrD,WAAYC,GACZ,KAAM,CACJ,IAAK,EAIL,WAAY,GACZ,WAAY,EACZ,aAAc,KACd,OAAQ,CAAA,EACR,OAAQ,CAAA,GAEV,MAAO,CACL,UAAWH,GACX,aAAc,CAAE,GAAGA,GAAkB,WAAY,GAAG,EAGpD,YAAa,CAAE,GAAGA,GAAkB,WAAY,IAAK,KAAM,SAAS,EACpE,aAAc,CAAE,OAAQ,EAAG,KAAMC,EAAc,WAAW,GAE5D,OAAQ,CACN,WAAY,CACV,SAAU,GACV,WAAY,IACZ,UAAW,SACX,KAAMA,EAAc,aAEtB,aAAc,CACZ,SAAU,GACV,WAAY,IACZ,UAAW,SACX,KAAMA,EAAc,cAGxB,OAAQG,EACR,UAAW,CAAE,WAAYC,CAAa,GAGxC,SAASC,GACPC,EACAC,EAAoB,CAEpB,MAAO,CACL,IAAKD,GAAM,KAAOC,EAAS,IAC3B,WAAYD,GAAM,YAAcC,EAAS,WACzC,WAAYD,GAAM,YAAcC,EAAS,WACzC,aAAcD,GAAM,cAAgBC,EAAS,aAC7C,OAAQC,GAAU,QAAQF,GAAM,QAAUC,EAAS,MAAM,EACzD,OAAQE,GAAU,QAAQH,GAAM,QAAUC,EAAS,MAAM,EAE7D,CAEA,SAASG,GACPC,EACAC,EACA,EAAS,CAET,MAAO,CACL,IAAKC,GAAWF,EAAK,IAAKC,EAAG,IAAK,CAAC,EACnC,WAAYC,GAAWF,EAAK,WAAYC,EAAG,WAAY,CAAC,EACxD,WAAYC,GAAWF,EAAK,WAAYC,EAAG,WAAY,CAAC,EACxD,aAAcC,GAAWF,EAAK,aAAcC,EAAG,aAAc,CAAC,EAC9D,OAAQJ,GAAU,KAChBG,EAAK,OACLC,EAAG,OACH,CAAC,EAEH,OAAQH,GAAU,KAChBE,EAAK,OACLC,EAAG,OACH,CAAC,EAGP,CAEA,SAASE,GACPC,EACAR,EAAqB,CAErB,MAAO,CACL,UAAWS,EAAQ,aAAaD,GAAO,UAAWR,EAAS,SAAS,EACpE,aAAcS,EAAQ,aACpBD,GAAO,aACPR,EAAS,YAAY,EAEvB,YAAaS,EAAQ,aAAaD,GAAO,YAAaR,EAAS,WAAW,EAC1E,aAAcC,GAAU,QACtBO,GAAO,cAAgBR,EAAS,YAAY,EAGlD,CAEA,SAASU,GACPN,EACAC,EACA,EAAS,CAET,MAAO,CACL,UAAWI,EAAQ,UAAUL,EAAK,UAAWC,EAAG,UAAW,CAAC,EAC5D,aAAcI,EAAQ,UAAUL,EAAK,aAAcC,EAAG,aAAc,CAAC,EACrE,YAAaI,EAAQ,UAAUL,EAAK,YAAaC,EAAG,YAAa,CAAC,EAClE,aAAcJ,GAAU,KACtBG,EAAK,aACLC,EAAG,aACH,CAAC,EAGP,CAEA,SAASM,GACPC,EACAZ,EAAsB,CAEtB,MAAO,CACL,WAAYS,EAAQ,aAAaG,GAAQ,WAAYZ,EAAS,UAAU,EACxE,aAAcS,EAAQ,aACpBG,GAAQ,aACRZ,EAAS,YAAY,EAG3B,CAEA,SAASa,GACPT,EACAC,EACA,EAAS,CAET,MAAO,CACL,WAAYI,EAAQ,UAAUL,EAAK,WAAYC,EAAG,WAAY,CAAC,EAC/D,aAAcI,EAAQ,UAAUL,EAAK,aAAcC,EAAG,aAAc,CAAC,EAEzE,CAQM,SAAUS,GACdC,EACAf,EAAyB,CAEzB,IAAMgB,EAAOhB,GAAYN,EACzB,MAAO,CACL,WAAYuB,GAAmBF,GAAO,WAAYC,EAAK,UAAU,EACjE,KAAMlB,GAAkBiB,GAAO,KAAMC,EAAK,IAAI,EAC9C,MAAOT,GAAmBQ,GAAO,MAAOC,EAAK,KAAK,EAClD,OAAQL,GAAoBI,GAAO,OAAQC,EAAK,MAAM,EACtD,OAAQE,GAAoBH,GAAO,OAAQC,EAAK,MAAM,EACtD,UAAW,CACT,WAAYD,GAAO,WAAW,YAAcC,EAAK,UAAU,YAGjE,CAGM,SAAUG,GACdf,EACAC,EACA,EAAS,CAET,MAAO,CAIL,WAAYe,GAAgBhB,EAAK,WAAYC,EAAG,WAAY,CAAC,EAC7D,KAAMF,GAAeC,EAAK,KAAMC,EAAG,KAAM,CAAC,EAC1C,MAAOK,GAAgBN,EAAK,MAAOC,EAAG,MAAO,CAAC,EAC9C,OAAQQ,GAAiBT,EAAK,OAAQC,EAAG,OAAQ,CAAC,EAClD,OAAQgB,GAAiBjB,EAAK,OAAQC,EAAG,OAAQ,CAAC,EAClD,UAAW,CACT,WAAYC,GACVF,EAAK,UAAU,WACfC,EAAG,UAAU,WACb,CAAC,GAIT,CDpcA,OAAS,iBAAAiB,OAAqB,yWAwDxBC,GAAY,GAEZC,GAAY,GAEZC,GAAc,IAEdC,GAAiB,EAEjBC,GAAgB,GAEhBC,GAAa,GAEbC,GAAa,EAMbC,GAAY,IAEZC,GAAY,IAEZC,GAAe,EAEfC,GAAW,EAEXC,GAAuB,IAMvBC,GAAmB,IAEnBC,GAAmB,IAEnBC,GAAmB,GAEnBC,GAAe,KAEfC,GAAgB,GAGtB,SAASC,EAAOC,EAAkBC,EAAWH,GAAa,CACxD,IAAMI,EAAOF,EAAM,SACnB,OAAO,OAAOE,GAAS,SAAWA,EAAOD,CAC3C,CA4BA,SAASE,GACPC,EACAC,EACAC,EACAC,EACAC,EACAC,EAAU,CAEV,IAAMC,EAAO,IAAIC,GACXC,EAAOP,EAAKD,EACZS,EAAQ,KAAK,IACjB,EACA,KAAK,KAAM,KAAK,IAAID,CAAI,GAAK,KAAK,GAAK,GAAMtB,EAAS,CAAC,EAGnDwB,EAAWC,GACfA,GAAK,EAAI,EAAI,KAAK,IAAIP,EAAUO,EAAGH,EAAO,GAAG,EACzCI,EAAQ,CAACD,EAAWE,IAAsB,CAC9C,IAAMC,EAAQJ,EAAQC,CAAC,EACjBI,EAAOf,EAAKc,EACZE,EAAKf,EAAKa,EACVG,EAAQC,GAAUP,EAAGI,GAASC,EAAKD,GAAQF,EAAKJ,CAAK,EAC3D,MAAO,CAAE,EAAGQ,EAAM,EAAIZ,EAAI,EAAGY,EAAM,CAAC,CACtC,EAEA,GAAIf,GAAU,EAAG,CAGfI,EAAK,OAAOD,EAAI,CAAC,EACjB,QAASQ,EAAI,EAAGA,GAAKJ,EAAOI,IAAK,CAC/B,IAAM,EAAID,EAAMT,EAAQU,CAAC,EACzBP,EAAK,OAAO,EAAE,EAAG,EAAE,CAAC,CACtB,CACA,OAAAA,EAAK,MAAK,EACHA,CACT,CAEA,QAASO,EAAI,EAAGA,GAAKJ,EAAOI,IAAK,CAC/B,IAAM,EAAID,EAAMT,EAAQU,CAAC,EACrBA,IAAM,EAAGP,EAAK,OAAO,EAAE,EAAG,EAAE,CAAC,EAC5BA,EAAK,OAAO,EAAE,EAAG,EAAE,CAAC,CAC3B,CAGA,QAASO,EAAIJ,EAAOI,GAAK,EAAGA,IAAK,CAC/B,IAAM,EAAID,EAAMV,EAAQW,CAAC,EACzBP,EAAK,OAAO,EAAE,EAAG,EAAE,CAAC,CACtB,CACA,OAAAA,EAAK,MAAK,EACHA,CACT,CAyCM,IAAOa,EAAP,cAAuBC,EAAoB,CAmBvC,MAA0B,CAAA,EAElC,YAAYC,EAAyC,CAanD,GAZA,MAAMA,CAAK,EAEX,KAAK,MAAQ,KAAK,OAAO,IAAKC,GAAUC,GAAQ,QAAQD,EAAM,IAAI,CAAC,EAU/D,KAAK,iBAAmB,QAAU,KAAK,YAAc,OACvD,QAAWE,KAAO,KAAK,KAAM,KAAK,SAASA,CAAG,EAK5C,KAAK,gBAAgB,KAAK,eAAc,CAC9C,CAGQ,SAASA,EAAW,CAC1B,IAAMF,EAAQ,KAAK,OAAOE,EAAI,KAAK,EAG7BC,EAAU,IACdC,EAAU,KAAK,WAAW,EAAI,KAAK,aAAaF,EAAI,KAAK,EACrDG,EACJ,KAAK,YAAc,QAAU,KAAK,YAAc,WAC5CC,EACJ,KAAK,YAAc,QAAU,KAAK,YAAc,UAElD,GAAI,KAAK,iBAAmB,SAAU,CAMpC,IAAMC,EAAmB,CAAA,EACrBD,GACFC,EAAO,KACL,IAAIC,GAAK,CACP,GAAGC,GAAQ,WAAW,KAAK,MAAM,WAAW,EAC5C,KAAMC,GAAcR,EAAI,QAAQ,EAChC,SAAU,IAAM,KAAK,WACtB,CAAC,EAGFG,GACFE,EAAO,KACL,IAAIC,GAAK,CACP,GAAGC,GAAQ,WAAW,KAAK,MAAM,WAAW,EAC5C,KAAMT,EAAM,SACZ,SAAU,IACR,KAAK,YAAc,WACf,KAAK,WACL,KAAK,WAAajC,GACzB,CAAC,EAGN,KAAK,IACH,IAAI4C,GAAK,CAAE,KAAM,WACf,MAAO,MACP,OAAQ,MACR,IAAK,EACL,MAAO,SACP,QAAAR,EACA,OAAQ,IAAM,KAAK,aAAaD,EAAI,KAAK,EACzC,SAAUK,EACX,CAAC,EAEJ,MACF,CAEA,IAAMK,EAAO,KAAK,OAAOV,EAAI,GAAG,EAC1BW,EAAK,IAAM,KAAK,aAAaX,EAAI,KAAK,GAAG,MAAQ,CAAE,EAAG,EAAG,EAAG,CAAC,EAC7DY,EAAqB,CAAA,EACvBT,GACFS,EAAS,KACP,IAAIN,GAAK,CACP,GAAGC,GAAQ,WAAW,KAAK,MAAM,SAAS,EAC1C,KAAMT,EAAM,SACZ,UAAWY,EAAO,EAAI,OAAS,QAChC,CAAC,EAGFN,GACFQ,EAAS,KACP,IAAIN,GAAK,CACP,GAAGC,GAAQ,WAAW,KAAK,MAAM,YAAY,EAC7C,KAAMC,GAAcR,EAAI,QAAQ,EAChC,UAAWU,EAAO,EAAI,OAAS,QAChC,CAAC,EAGN,KAAK,IACH,IAAID,GAAK,CAAE,KAAM,WACf,MAAO,MACP,OAAQ,MACR,IAAK,EACL,MAAOC,EAAO,EAAI,aAAe,cACjC,QAAAT,EAEA,WAAYS,EAAO,EAAIC,EAAK,OAC5B,YAAaD,EAAO,EAAI,OAAYC,EACpC,SAAAC,EACD,CAAC,CAEN,CAGQ,gBAAc,CACpB,IAAMA,EAAqB,CAAA,EACvB,KAAK,cAAgB,IACvBA,EAAS,KACP,IAAIN,GAAK,CACP,GAAGC,GAAQ,WAAW,KAAK,YAAY,UAAU,EACjD,KAAM,KAAK,YACX,SAAU,IAAM,KAAK,WACtB,CAAC,EAGF,KAAK,gBAAkB,IACzBK,EAAS,KACP,IAAIN,GAAK,CACP,GAAGC,GAAQ,WAAW,KAAK,YAAY,YAAY,EACnD,KAAM,KAAK,cACX,SAAU,IACR,KAAK,YACJpC,EAAO,KAAK,YAAY,aAAc,EAAE,EACvCA,EAAO,KAAK,YAAY,WAAY,EAAE,GAC3C,CAAC,EAGN,KAAK,IACH,IAAIsC,GAAK,CAAE,KAAM,WACf,MAAO,MACP,OAAQ,MACR,IAAK,EACL,MAAO,SACP,QAAS,IAAMP,EAAU,KAAK,YAAY,EAC1C,OAAQ,KAAO,CAAE,EAAG,KAAK,QAAS,EAAG,CAAC,GACtC,SAAAU,EACD,CAAC,CAEN,CAKA,IAAY,MAAI,CACd,OAAOC,GAAQ,KAAK,OAAQ,KAAK,KAAK,UAAU,CAClD,CAGQ,OAAOC,EAAW,CACxB,OAAO,KAAK,IAAIA,CAAG,GAAK,EAAI,EAAI,EAClC,CAGA,IAAY,iBAAe,CACzB,OACE,KAAK,iBAAmB,UACxB,KAAK,YAAc,QACnB,KAAK,KAAK,OAAS,CAEvB,CAoBQ,WAAWd,EAAW,CAC5B,IAAMe,EAAQ,KAAK,YACbC,EAAO,KAAK,WACZC,EAAOD,EAAO,EAAID,EAAQC,EAAOD,GAAS,EAAIjD,IAAoB,EAClEgD,EAAME,EAAO,GAAKA,EAAOD,GAAS,EAAIA,EAAQjD,GAC9CoD,EAAM,KAAK,IAAI,KAAK,IAAIlB,EAAI,GAAG,CAAC,EAChCmB,EAAM,KAAK,IAAI,KAAK,IAAInB,EAAI,GAAG,CAAC,EAChCoB,EAAaF,EAAMjD,GAAegD,EAAOC,EAAM,IAC/CG,EAAWF,EAAMlD,IAAiB+B,EAAI,GAAKA,EAAI,IAAMc,EAAOK,EAAM,IACxE,OAAO,KAAK,IAAI,EAAG,KAAK,IAAIC,EAAYC,CAAQ,CAAC,CACnD,CAGQ,aAAarB,EAAW,CAC9B,IAAMsB,EAAW,KAAK,OAAOtB,EAAI,KAAK,GAAG,SAAS,QAAU,EACtDuB,EACJ,KAAK,YAAc,QAAU,KAAK,YAAc,UAC5Cf,GAAcR,EAAI,QAAQ,EAAE,OAC5B,EAGAwB,EACJ,KAAK,YAAc,WACfF,EACA,KAAK,YAAc,OACjBA,EAAWzD,GACX,EACR,OAAO,KAAK,IAAI,EAAG0D,EAASC,CAAO,CACrC,CAWA,IAAY,YAAU,CACpB,IAAIlD,EAAOH,EAAO,KAAK,MAAM,YAAaD,GAAgB,GAAG,EAC7D,QAAW8B,KAAO,KAAK,KAAM,CAC3B,IAAMyB,EACH,KAAK,WAAWzB,CAAG,EAAIhC,IACvB,KAAK,aAAagC,CAAG,EAAI5C,IAC5BkB,EAAO,KAAK,IAAIA,EAAMmD,CAAI,CAC5B,CACA,OAAO,KAAK,IAAI,EAAGnD,CAAI,CACzB,CAGA,IAAY,gBAAc,CACxB,OAAO,KAAK,cAAgB,IAAM,KAAK,gBAAkB,EAC3D,CAQA,IAAY,YAAU,CACpB,IAAMoD,EAASvD,EAAO,KAAK,YAAY,WAAY,EAAE,EAC/C6C,EAAO,KAAK,WAClB,GAAIA,GAAQ,EAAG,OAAOU,EACtB,IAAMC,EACJxD,EAAO,KAAK,YAAY,aAAc,EAAE,EAAI,KAAK,IAAI,EAAGuD,CAAM,EAI1DE,EAAS,KAAK,IAClB,EACA,KAAK,YAAY,OACjB,KAAK,cAAc,OAASD,CAAY,EAE1C,OAAO,KAAK,IAAID,EAASV,EAAO,EAAIjD,IAAqB6D,EAASxE,GAAY,CAChF,CAKA,IAAY,YAAU,CACpB,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,IAAK,KAAK,KAAK,YAAY,CAAC,CAC1D,CAGA,IAAY,QAAM,CAChB,OAAO,KAAK,aAAe,KAAK,WAAa,EAC/C,CAGA,IAAY,gBAAc,CAGxB,GAAI,CAAC,KAAK,gBAAiB,MAAO,GAClC,IAAMoE,EAAUrD,EAAO,KAAK,MAAM,SAAS,EAAI,KACzCoD,EAAUpD,EAAO,KAAK,MAAM,YAAY,EAAI,KAClD,OAAI,KAAK,YAAc,OAAeqD,EAAUD,EAAU,EACnD,KAAK,YAAc,WAAaC,EAAUD,CACnD,CAGQ,cAAcM,EAAa,CAEjC,IAAMN,EACJ,KAAK,YAAc,WAAa,KAAK,YAAc,OAAS,EAAI,EAC5DD,EACJ,KAAK,YAAc,YAAc,KAAK,YAAc,OAC/C,KAAK,OAAOO,CAAK,GAAG,SAAS,QAAU,EACxC,EACN,OAAO,KAAK,IAAIN,EAASD,CAAQ,CACnC,CAUA,IAAY,cAAY,CACtB,IAAMQ,EAAU,CAAE,KAAMlE,GAAU,MAAOA,EAAQ,EACjD,GAAI,CAAC,KAAK,gBAAiB,OAAOkE,EAClC,IAAMC,EAAQ5D,EAAO,KAAK,MAAM,SAAS,EAAIf,GAC7C,QAAW4C,KAAO,KAAK,KAAM,CAC3B,IAAMgC,EACJ1E,GACAC,GACAC,GACA,KAAK,cAAcwC,EAAI,KAAK,EAAI+B,EAChC5E,GACI8E,EAAM,KAAK,OAAOjC,EAAI,GAAG,EAAI,EAAI,QAAU,OACjD8B,EAAQG,CAAG,EAAI,KAAK,IAAIH,EAAQG,CAAG,EAAGD,CAAI,CAC5C,CACA,OAAOF,CACT,CAQA,IAAY,SAAO,CACjB,GAAM,CAAE,KAAAI,EAAM,MAAAC,CAAK,EAAK,KAAK,aAC7B,OAAQD,EAAOC,GAAS,CAC1B,CAQA,IAAY,aAAW,CACrB,GAAM,CAAE,KAAAD,EAAM,MAAAC,CAAK,EAAK,KAAK,aACvBH,EAAO,KAAK,KACf,KAAK,aAAa,MAAQE,EAAOC,GAAS,EAC3C,KAAK,aAAa,OAAS,EAAI,KAAK,eAAiB,CAAC,EAExD,OAAO,KAAK,IAAI,EAAGH,EAAO,KAAK,UAAU,CAC3C,CAGA,IAAY,YAAU,CACpB,IAAMI,EAAa,KAAK,KAAK,WAC7B,GAAIA,GAAc,EAAG,MAAO,GAC5B,IAAMC,EAAKD,EAAa,EAAIA,EAAa,KAAK,YAAcA,EAC5D,OAAO,KAAK,IAAIC,EAAI,KAAK,YAAc,GAAI,CAC7C,CAGA,IAAY,aAAW,CACrB,OAAO,KAAK,YAAc,KAAK,OAAS/E,EAC1C,CAGQ,MAAMuE,EAAa,CACzB,OAAO3B,EAAU,KAAK,KAAK2B,CAAK,GAAK,CAAC,CACxC,CAGQ,aAAaA,EAAa,CAChC,OAAO,KAAK,YAAc,KAAK,OAAS,KAAK,MAAMA,CAAK,CAC1D,CAQQ,cAAcA,EAAa,CACjC,IAAMS,EAAM,KAAK,aAAaT,CAAK,EAC7Bb,EAAO,KAAK,WAClB,OAAOA,EAAO,GAAKA,EAAOsB,GAAO,EAAIA,EAAMxE,EAC7C,CAGQ,aAAa+D,EAAa,CAChC,IAAM7B,EAAM,KAAK,KAAK,KAAMuC,GAAMA,EAAE,QAAUV,CAAK,EACnD,GAAI7B,IAAQ,OAAW,MAAO,CAAE,EAAG,KAAK,QAAS,EAAG,CAAC,EACrD,IAAMP,EAAQC,GAAU,KAAK,cAAcmC,CAAK,EAAG7B,EAAI,GAAG,EAC1D,MAAO,CAAE,EAAGP,EAAM,EAAI,KAAK,QAAS,EAAGA,EAAM,CAAC,CAChD,CAOA,IAAY,mBAAiB,CAC3B,IAAI+C,EAAY,EAChB,QAASnD,EAAI,EAAGA,EAAI,KAAK,OAAO,OAAQA,IACtCmD,EAAY,KAAK,IAAIA,EAAW,KAAK,MAAMnD,CAAC,CAAC,EAE/C,OAAOmD,CACT,CAQQ,aAAaX,EAAa,CAChC,IAAMY,EAAQ,KAAK,kBACnB,OAAIA,GAAS,EAAU,EAChB,GAAK,EAAIvC,EAAU,KAAK,UAAU,GAAKuC,GAAS,EAAI,KAAK,MAAMZ,CAAK,EAC7E,CAYQ,YAAU,CAChB,GAAI,CAAC,KAAK,gBAAiB,MAAO,CAAA,EAClC,IAAMa,EAAO,KAAK,KAClB,GAAIA,EAAK,SAAW,GAAK,KAAK,aAAe,EAAG,MAAO,CAAA,EAEvD,IAAMC,EAAS,KAAK,YACdC,EAAY,KAAK,eACjBC,EAAM,KAAK,aAAa,OAAS,EAAID,EAAY,EACjDE,EAAS,CAACD,EACVhE,EAAK,KAAK,QASVsD,EAAgB,CAAA,EAChBD,EAAe,CAAA,EACrB,QAAWlC,KAAO0C,EAAM,CACtB,IAAMhC,EAAO,KAAK,OAAOV,EAAI,GAAG,EAC1B+C,EAAQrD,GAAUiD,EAAQ3C,EAAI,GAAG,EAAE,GACvCU,EAAO,EAAIyB,EAAQD,GAAM,KAAK,CAC9B,MAAOlC,EAAI,MACX,KAAAU,EACA,IAAKV,EAAI,IACT,MAAA+C,EACA,EAAGA,EACJ,CACH,CACA,OAAAC,GAAYb,EAAOS,EAAY1F,GAAW2F,EAAKC,CAAM,EACrDE,GAAYd,EAAMU,EAAY1F,GAAW2F,EAAKC,CAAM,EAE7C,CAAC,GAAGX,EAAO,GAAGD,CAAI,EAAE,IAAKe,GAAQ,CACtC,IAAMC,EAAQxD,GACZ,KAAK,aAAauD,EAAK,KAAK,EAAI5F,GAChC4F,EAAK,GAAG,EAMJE,EAAW,KAAK,KACpB,KAAK,IAAI,EAAGR,EAASA,EAASM,EAAK,EAAIA,EAAK,CAAC,CAAC,EAE1CG,EAASvE,EAAKoE,EAAK,KAAO,KAAK,IAAIE,EAAUR,EAASlF,EAAS,EAC/D4F,EAAOxE,EAAKoE,EAAK,MAAQN,EAASpF,IAExC,MAAO,CACL,MAAO0F,EAAK,MACZ,KAAMA,EAAK,KACX,OAAQ,CACN,CAAE,EAAGC,EAAM,EAAIrE,EAAI,EAAGqE,EAAM,CAAC,EAC7B,CAAE,EAAGE,EAAQ,EAAGH,EAAK,CAAC,EACtB,CAAE,EAAGI,EAAM,EAAGJ,EAAK,CAAC,GAEtB,KAAM,CAAE,EAAGI,EAAOJ,EAAK,KAAOzF,GAAY,EAAGyF,EAAK,CAAC,EAEvD,CAAC,CACH,CAGQ,aAAapB,EAAa,CAChC,OAAO,KAAK,WAAU,EAAG,KAAMyB,GAAcA,EAAU,QAAUzB,CAAK,CACxE,CASS,cAAc0B,EAAkB,CACvC,MAAM,cAAcA,CAAM,EAC1BtG,GAAc,KAAMsG,EAAQ,CAC1B,MAAO,KAAK,MACZ,QAAS,CAACC,GAAU,QAAQ,KAAK,MAAM,YAAY,CAAC,EACrD,CACH,CAEU,WAAWC,EAAoB,CACvC,IAAM9E,EAAS,KAAK,YACpB,GAAIA,GAAU,EAAG,OAEjB,IAAM+D,EAAO,KAAK,KAClB,GAAIA,EAAK,SAAW,EAAG,OAEvB,IAAMgB,EAAQxD,EAAU,KAAK,MAAM,EAC7ByD,EAAY,KAAK,KAAK,YAAc,KAAK,GAAK,KAAO,KAAK,GAAK,EAAID,EACnE1C,EAAO,KAAK,WACZpC,EAAU,KAAK,IAAI,EAAG,KAAK,KAAK,GAAG,EAAI,EAQvCC,EAAK,KAAK,QACV+E,EAAOjF,EAAS,KAAK,OAAS,EAAI,KAAK,IAAIE,CAAE,EAC7CgF,EAAiD,CACrD,CAACD,EACD,CAACA,EACDA,EACAA,GAKI5E,EAAO4E,EAAO,EAEpB,QAAW5D,KAAO0C,EAAM,CAGtB,IAAMjE,EAAK,KAAK,IAAIuB,EAAI,GAAI2D,CAAS,EACrC,GAAIlF,GAAMuB,EAAI,GAAI,SAElB,IAAMlB,EAAOP,GACXyB,EAAI,GACJvB,EACAuC,EACA,KAAK,aAAahB,EAAI,KAAK,EAC3BpB,EACAC,CAAE,EAEEiF,EAAI,IAAIC,GAAU,EAAG,KACzBjF,EAAK,YAAY,CAAE,aAAA+E,EAAc,MAAO7E,EAAM,OAAQA,CAAI,CAAE,CAAC,EAE/D8E,EAAE,OAAO,KAAK,KAAK,MAAM,EACzBA,EAAE,KAAK,KAAK,MAAM9D,EAAI,KAAK,GAAK,CAAA,CAAE,EAClC8D,EAAE,OAAO,KAAK,KAAK,MAAM,EACzBL,EAAI,KAAKK,EAAE,QAAQ,KAAK,aAAa9D,EAAI,KAAK,CAAC,CAAC,CAClD,CAMA,IAAMgE,EAAS9D,EAAU,KAAK,WAAW,EACzC,GAAI,EAAA8D,GAAU,MACd,QAAWV,KAAa,KAAK,WAAU,EACrCG,EAAI,KACF,IAAIM,GAAU,EACX,KAAK,CAAE,OAAQT,EAAU,OAAQ,OAAQ3F,EAAY,CAAE,EACvD,OAAO,KAAK,MAAM,YAAY,EAC9B,QAAQqG,EAAS,KAAK,aAAaV,EAAU,KAAK,CAAC,CAAC,CAG7D,CAMA,MACEW,EAA+D,CAI/D,OAAO,KAAK,QAAoB,IAAO,CAAE,OAAQ,CAAC,GAAKA,EAAK,SAAUA,EAAK,QAAUC,GAAO,CAAE,CAChG,CAIA,aACED,EAA+D,CAE/D,OAAO,KAAK,QAAqB,IAAO,CAAE,YAAa,CAAC,GAAKA,EAAK,SAAUA,EAAK,QAAUC,GAAO,CAAE,CACtG,CAQA,aACED,EAAmE,CAAA,EAAE,CAErE,IAAM1E,EAAOW,EAAU,KAAK,YAAY,EACxC,OAAO,KAAK,QACTiE,IAAO,CAAE,aAAc5E,GAAQ,EAAIA,GAAQ4E,CAAC,GAC7CF,EAAK,UAAY,GACjBA,EAAK,QAAUC,GAAO,CAAE,CAE5B,GA7nBuDE,EAAA,CAAtDC,EAAS,CAAE,QAAS,IAAM,CAAA,CAAgB,CAAE,gCACgBD,EAAA,CAA5DC,EAAS,CAAE,QAASC,EAAwB,IAAI,CAAE,8BAE3CF,EAAA,CADPC,EAAS,CAAE,QAASC,EAAwB,KAAK,CAAE,+BAG5CF,EAAA,CADPC,EAAS,CAAE,QAASC,EAAwB,MAAM,CAAE,qCAEXF,EAAA,CAAzCC,EAAS,CAAE,QAAS,SAAS,CAAE,mCACSD,EAAA,CAAxCC,EAAS,CAAE,QAAS,QAAQ,CAAE,wCACGD,EAAA,CAAjCC,EAAS,CAAE,QAAS,CAAC,CAAE,gCACUD,EAAA,CAAjCC,EAAS,CAAE,QAAS,CAAC,CAAE,qCAC6BD,EAAA,CAApDC,EAAS,CAAE,QAAS,IAAM,CAAA,CAAc,CAAE,8BAEnCD,EAAA,CADPC,EAAS,CAAE,QAASC,EAAwB,UAAU,UAAU,CAAE,oCAEhCF,EAAA,CAAlCC,EAAS,CAAE,QAAS,EAAE,CAAE,qCACUD,EAAA,CAAlCC,EAAS,CAAE,QAAS,EAAE,CAAE,uCACSD,EAAA,CAAjCC,EAAS,CAAE,QAAS,CAAC,CAAE,sCA+kBxBD,EAAA,CADCG,GAAO,6BAWRH,EAAA,CADCG,GAAO,oCAaRH,EAAA,CADCG,GAAO,oCF11BV,OACE,SAAAC,GACA,QAAAC,GACA,YAAYC,GACZ,YAAAC,OAEK,yWAyCDC,GAAc,IACdC,GAAc,IA8CPC,EAAN,cAAuBC,EAAqB,CA0BzC,KACA,OAA2B,KAGnC,IAAY,eAAa,CACvB,OAAO,KAAK,KACd,CAGA,IAAY,QAAM,CAChB,OAAOC,GACL,KAAK,KACL,KAAK,cACL,KAAK,WACL,KAAK,cAAc,UAAU,CAEjC,CAQQ,WAAS,CACf,IAAIC,EAAQ,EACZ,QAAS,EAAI,EAAG,EAAI,KAAK,KAAK,OAAQ,IACpCA,EAAQ,KAAK,IAAIA,EAAO,KAAK,MAAM,CAAC,CAAC,EAEvC,IAAMC,EAAM,KAAK,cAAc,UAAU,WACzC,OAAID,GAAS,EAAU,KAAK,KAAK,IAAI,IAAM,CAAC,EACrC,KAAK,KAAK,IACf,CAACE,EAAGC,IAAM,GAAK,EAAIC,EAAUH,CAAG,GAAKD,GAAS,EAAI,KAAK,MAAMG,CAAC,EAAE,CAEpE,CAGQ,MAAME,EAAa,CACzB,OAAOD,EAAU,KAAK,KAAKC,CAAK,GAAK,CAAC,CACxC,CAEA,YAAYC,EAA2C,CACrD,MAAMA,CAAK,EAOX,IAAMC,EAAS,KAAK,OACdC,EAAaF,GAAO,aAAe,GACzC,KAAK,KAAOC,EAAO,IAAI,IAAM,CAAC,EAE9B,KAAK,KAAO,IAAIE,EAAQ,CACtB,MAAO,OACP,OAAQ,OACR,OAAAF,EACA,KAAM,IAAM,KAAK,cAAc,KAC/B,MAAO,IAAM,KAAK,cAAc,MAChC,YAAa,IAAM,KAAK,cAAc,OACtC,UAAW,KAAK,UAChB,eAAgB,KAAK,eACrB,KAAM,IAAM,KAAK,KACjB,WAAY,IAAM,KAAK,cAAc,UAAU,WAC/C,YAAa,KAAK,YAClB,cAAe,KAAK,cACpB,aAAcD,GAAO,aAAe,GAAQ,EAAI,EACjD,EAED,IAAMI,EAAqB,CAAC,KAAK,IAAI,EACjCF,IACF,KAAK,OAAS,IAAIG,EAAU,CAC1B,OAAAJ,EACA,OAAQ,IAAM,KAAK,cAAc,OACjC,KAAM,IAAM,KAAK,UAAS,EAC3B,EACDG,EAAS,KAAK,KAAK,MAAM,GAG3B,KAAK,IACH,IAAIE,GAAK,CAAE,KAAM,aACf,MAAO,OACP,OAAQ,OACR,IAAK,KAAK,IACV,MAAO,SACP,SAAAF,EACD,CAAC,CAEN,CAaA,KAAG,CACD,KAAK,KAAK,IAAI,CAAE,OAAQ,EAAG,YAAa,CAAC,CAAE,EAC3C,KAAK,QAAQ,IAAG,CAClB,CASA,MACEG,EAA+D,CAE/D,IAAMC,EAAWD,EAAK,SAChBE,EAASF,EAAK,QAAUG,GAAO,EACrC,YAAK,IAAG,EAEDvB,GACL,KAAK,KAAK,MAAM,CAAE,SAAUqB,EAAWnB,GAAa,OAAAoB,CAAM,CAAE,EAC5DrB,GACE,KAAK,KAAK,aAAa,CAAE,SAAUoB,EAAWlB,GAAa,OAAAmB,CAAM,CAAE,EACnE,KAAK,OACD,KAAK,OAAO,MAAM,CAAE,SAAUD,EAAWlB,GAAa,OAAAmB,CAAM,CAAE,EAC9DvB,GAAKsB,EAAWlB,EAAW,CAAC,CACjC,CAEL,CAWA,aACEiB,EAAmE,CAAA,EAAE,CAErE,OAAO,KAAK,KAAK,aAAa,CAAE,SAAUA,EAAK,UAAY,GAAK,OAAQA,EAAK,QAAUG,GAAO,CAAE,CAAE,CACpG,CAKA,WAAWX,EAAa,CACtB,OAAO,KAAK,MAAMA,CAAK,EAAI,EAC7B,CAOA,YACEQ,EAAiE,CAEjE,GAAM,CAAE,WAAYR,CAAK,EAAKQ,EAAK,KAC7BC,EAAWD,EAAK,UAAY,IAC5BE,EAASF,EAAK,QAAUG,GAAO,EACrC,OAAO,KAAK,UAAWb,GAAOA,IAAME,EAAQ,EAAI,KAAK,MAAMF,CAAC,EAAIW,EAAUC,CAAM,CAClF,CAIA,cACEF,EAAiE,CAEjE,GAAM,CAAE,WAAYR,CAAK,EAAKQ,EAAK,KAC7BC,EAAWD,EAAK,UAAY,IAC5BE,EAASF,EAAK,QAAUG,GAAO,EACrC,OAAO,KAAK,UAAWb,GAAOA,IAAME,EAAQ,EAAI,KAAK,MAAMF,CAAC,EAAIW,EAAUC,CAAM,CAClF,CAIA,gBACEF,EAAiE,CAEjE,GAAM,CAAE,WAAYR,CAAK,EAAKQ,EAAK,KAC7BC,EAAWD,EAAK,UAAY,IAC5BE,EAASF,EAAK,QAAUG,GAAO,EACrC,OAAO,KAAK,UAAWb,GAAOA,IAAME,EAAQ,EAAI,EAAIS,EAAUC,CAAM,CACtE,CAIA,eACEF,EAAmE,CAAA,EAAE,CAErE,IAAMC,EAAWD,EAAK,UAAY,IAC5BE,EAASF,EAAK,QAAUG,GAAO,EACrC,OAAO,KAAK,UAAU,IAAM,EAAGF,EAAUC,CAAM,CACjD,CAcQ,UACNE,EACAH,EACAC,EAAsB,CAEtB,IAAMG,EAAO,KAAK,KAAK,IAAI,CAAChB,EAAGC,IAAM,KAAK,MAAMA,CAAC,CAAC,EAC5CgB,EAAU,KAAK,KAAK,IAAI,CAACjB,EAAGC,IAAMC,EAAUa,EAAId,CAAC,CAAC,CAAC,EACzD,OAAIe,EAAK,MAAM,CAACE,EAAOjB,IAAMiB,IAAUD,EAAQhB,CAAC,CAAC,EAAUX,GAAKsB,CAAQ,EAEjEvB,GAAMuB,EAAWO,GAAK,CAC3B,IAAMC,EAAIP,EAAOM,CAAC,EAClB,KAAK,KAAOH,EAAK,IAAI,CAACE,EAAOjB,IAAMiB,GAASD,EAAQhB,CAAC,EAAIiB,GAASE,CAAC,CACrE,CAAC,CACH,GAzPyDC,EAAA,CAAxDC,EAAS,CAAE,QAAS,IAAM,CAAA,CAAkB,CAAE,8BACZD,EAAA,CAAlCC,EAAS,CAAE,QAAS,EAAE,CAAE,uCACUD,EAAA,CAAlCC,EAAS,CAAE,QAAS,EAAE,CAAE,oCAMjBD,EAAA,CALPC,EAAS,CACR,QAASC,EACT,OAAQC,GACR,MAAOC,GACR,+BAEyCJ,EAAA,CAAzCC,EAAS,CAAE,QAAS,SAAS,CAAE,mCACSD,EAAA,CAAxCC,EAAS,CAAE,QAAS,QAAQ,CAAE,wCACID,EAAA,CAAlCC,EAAS,CAAE,QAAS,EAAE,CAAE,6BACUD,EAAA,CAAlCC,EAAS,CAAE,QAAS,EAAE,CAAE,qCACUD,EAAA,CAAlCC,EAAS,CAAE,QAAS,EAAE,CAAE,uCAS4BD,EAAA,CAApDC,EAAS,CAAE,QAAS,IAAM,CAAA,CAAc,CAAE,8BAoH3CD,EAAA,CADCK,GAAO,6BA4BRL,EAAA,CADCK,GAAO,oCAmBRL,EAAA,CADCK,GAAQ,CAAE,KAAM,CAAC,CAAE,IAAK,aAAc,KAAM,SAAU,QAAS,EAAG,IAAK,EAAG,KAAM,CAAC,CAAE,CAAC,CAAE,mCAYvFL,EAAA,CADCK,GAAQ,CAAE,KAAM,CAAC,CAAE,IAAK,aAAc,KAAM,SAAU,QAAS,EAAG,IAAK,EAAG,KAAM,CAAC,CAAE,CAAC,CAAE,qCAYvFL,EAAA,CADCK,GAAQ,CAAE,KAAM,CAAC,CAAE,IAAK,aAAc,KAAM,SAAU,QAAS,EAAG,IAAK,EAAG,KAAM,CAAC,CAAE,CAAC,CAAE,uCAYvFL,EAAA,CADCK,GAAO,sCAxNG/B,EAAQ0B,EAAA,CAfpBM,GAAK,CACJ,IAAK,WACL,UAAW,OACX,SAAU,GACV,OAAQ,CACN,SAAU,WACV,iBAAkB,QAClB,kBAAmB,QACnB,gBAAiB,IAEnB,KAAM,CACJ,MAAO,IACP,OAAQ,KAEX,GACYhC,CAAQ,EIvGd,IAAMiC,GAAQ,CAACC,EAAWC,EAAUC,CAAQ",
6
+ "names": ["command", "node", "Node2D", "Rect", "easeOut", "property", "formatNumber", "Graphics2D", "Node2D", "command", "easeOut", "strokeOps", "property", "Fills", "fillOps", "lerpNumber", "strokeOps", "EMPTY_REGION_BOX", "hasRegion", "box", "resolveRegionBox", "region", "plot", "xScale", "yScale", "span", "range", "min", "max", "lo", "hi", "a", "b", "toPx", "v", "left", "right", "xBounded", "bottom", "top", "yBounded", "clipPolylineToBox", "points", "runs", "run", "i", "seg", "clipSegment", "samePoint", "dx", "dy", "t0", "t1", "edges", "p", "q", "r", "LINE_CHART_LEGEND", "DEFAULT_LEGEND", "DEFAULT_CHART_THEME", "DEFAULT_SERIES_STROKES", "DEFAULT_X_AXIS", "DEFAULT_Y_AXIS", "DEFAULT_PLOT_AREA", "CHART_PALETTE", "Fills", "SPOTLIGHT_DIM", "resolveRegionConfig", "previous", "strokeOps", "fillOps", "lerpRegionConfig", "from", "to", "lerpNumber", "resolveChartTheme", "theme", "base", "resolveSeriesStrokes", "resolveAxisConfig", "resolvePlotArea", "resolveLegendConfig", "lerpChartTheme", "lerpSeriesStrokes", "lerpAxisConfig", "lerpPlotArea", "lerpLegendConfig", "seriesPoints", "data", "x", "series", "row", "num", "yExtent", "resolveYScale", "divisions", "segments", "step", "niceStep", "declarePaints", "ChartLine", "Node2D", "assets", "ctx", "pts", "p", "drawn", "stroke", "radius", "box", "hasRegion", "Graphics2D", "run", "clipPolylineToBox", "segLen", "total", "i", "d", "target", "out", "t", "args", "easeOut", "from", "duration", "easing", "to", "progress", "__decorate", "property", "strokeOps", "EMPTY_REGION_BOX", "SPOTLIGHT_DIM", "command", "Graphics2D", "Node2D", "fillOps", "strokeOps", "property", "declarePaints", "ChartRegion", "Node2D", "assets", "ctx", "box", "hasRegion", "r", "width", "height", "cx", "cy", "Graphics2D", "rule", "points", "half", "x", "y", "__decorate", "property", "EMPTY_REGION_BOX", "DEFAULT_CHART_THEME", "strokeOps", "fillOps", "command", "Node2D", "Rect", "Text", "easeOut", "property", "insetsOps", "textOps", "at", "compose", "SLIDE", "HorizontalAxis", "Node2D", "formatMarker", "a", "b", "index", "v", "min", "max", "props", "tickPad", "insetsOps", "titlePad", "_", "i", "marker", "Text", "textOps", "width", "centred", "limit", "children", "Rect", "slide", "args", "stagger", "duration", "easing", "easeOut", "nodes", "step", "per", "caption", "enterBudget", "markers", "node", "lastMarkerEnds", "__decorate", "property", "DEFAULT_X_AXIS", "command", "formatNumber", "DAY", "MONTHS", "NOMINAL_YEAR", "monthIndex", "word", "month", "isoWeekStart", "year", "week", "jan4", "weekday", "parseInstant", "raw", "s", "ymd", "day", "named", "y", "dayNamed", "formatInstant", "value", "span", "date", "label", "isNumericColumn", "data", "field", "sawValue", "row", "v", "instantColumn", "parsed", "t", "last", "extentOf", "values", "lo", "hi", "sampledTicks", "positions", "min", "max", "divisions", "inside", "unique", "b", "markerValues", "nearest", "target", "best", "out", "tick", "labelBySample", "captions", "fallback", "i", "resolveXScale", "pattern", "num", "formatMarker", "text", "instants", "extent", "range", "_", "hold", "sequenceOf", "together", "TOP_HEADROOM", "SERIES_ARG", "LEGEND_SLIDE", "AXES_SHARE", "LINES_SHARE", "LEGEND_SHARE", "LineChart", "Node2D", "resolveXScale", "divisions", "LineChart_1", "resolveYScale", "markerValues", "width", "height", "index", "s", "points", "seriesPoints", "w", "h", "xMin", "xMax", "yMin", "yMax", "xToPx", "x", "yToPx", "y", "region", "resolveRegionBox", "props", "enabled", "showLegend", "VerticalAxis", "value", "formatMarker", "formatNumber", "HorizontalAxis", "i", "ChartLine", "resolveSeriesStroke", "ChartLegend", "resolveSeriesFill", "Rect", "ChartCanvas", "ChartRegion", "ChartBorder", "gutterSpacer", "below", "line", "args", "duration", "easing", "easeOut", "revealing", "body", "axes", "lines", "legend", "phases", "t", "from", "previous", "data", "a", "b", "range", "ticks", "span", "count", "next", "home", "SPOTLIGHT_DIM", "__decorate", "property", "resolveAxisDomain", "lerpAxisDomain", "DEFAULT_CHART_THEME", "resolveChartTheme", "lerpChartTheme", "command", "node", "command", "node", "Node2D", "Rect", "easeOut", "property", "formatNumber", "drive", "hold", "sequenceOf", "together", "command", "Node2D", "Rect", "Text", "easeOut", "property", "insetsOps", "textOps", "at", "compose", "hold", "SLIDE", "CategoryAxis", "Node2D", "index", "n", "width", "props", "tickPad", "insetsOps", "titlePad", "slots", "caption", "i", "text", "Text", "textOps", "Rect", "clampUnit", "children", "slide", "args", "stagger", "easing", "easeOut", "nodes", "step", "per", "enterBudget", "lastCaptionEnds", "node", "__decorate", "property", "DEFAULT_X_AXIS", "command", "command", "Graphics2D", "Node2D", "Text", "easeOut", "fillOps", "shadowOps", "strokeOps", "property", "textOps", "fillOps", "lerpNumber", "shadowOps", "strokeOps", "textOps", "barSpans", "row", "series", "stacked", "running", "s", "value", "num", "from", "stackCapIndex", "last", "barCategories", "data", "field", "text", "barValueExtent", "lo", "hi", "span", "resolveBarScale", "range", "divisions", "segments", "step", "niceStep", "DEFAULT_BAR_CHART_THEME", "DEFAULT_SERIES_FILLS", "DEFAULT_X_AXIS", "DEFAULT_Y_AXIS", "DEFAULT_PLOT_AREA", "DEFAULT_LEGEND", "CHART_PALETTE", "SPOTLIGHT_DIM", "resolveBarStyle", "bar", "previous", "strokeOps", "shadowOps", "lerpBarStyle", "to", "lerpNumber", "resolveBarChartTheme", "theme", "base", "resolveSeriesFills", "resolveAxisConfig", "resolvePlotArea", "resolveLegendConfig", "textOps", "lerpBarChartTheme", "lerpSeriesFills", "lerpAxisConfig", "lerpPlotArea", "lerpLegendConfig", "resolveBarFill", "palette", "index", "fillOps", "declarePaints", "VALUE_GAP", "VALUE_FIT", "VALUE_SIZE", "ChartBars", "Node2D", "formatMarker", "props", "i", "Text", "textOps", "index", "clampUnit", "bar", "g", "base", "end", "size", "ends", "direction", "assets", "shadow", "ctx", "height", "radius", "Graphics2D", "rect", "args", "easeOut", "__decorate", "property", "fillOps", "DEFAULT_BAR_CHART_THEME", "strokeOps", "shadowOps", "command", "Y_AXIS_GUTTER", "X_AXIS_GUTTER", "LEGEND_GUTTER", "Y_LABEL_GUTTER", "X_LABEL_GUTTER", "TOP_HEADROOM", "RIGHT_GUTTER", "LEGEND_SLIDE", "AXES_SHARE", "BARS_SHARE", "LEGEND_SHARE", "BarChart", "Node2D", "divisions", "BarChart_1", "resolveBarScale", "value", "pattern", "formatMarker", "formatNumber", "markerValues", "W", "H", "areaTop", "areaBottom", "left", "right", "top", "bottom", "min", "max", "h", "n", "index", "clampUnit", "sum", "i", "series", "stacked", "inner", "gap", "unit", "weight", "row", "rowIndex", "span", "barSpans", "cap", "stackCapIndex", "slotLeft", "x", "width", "num", "scaled", "s", "focus", "dim", "chosen", "_", "props", "enabled", "on", "showLegend", "showValues", "VerticalAxis", "CategoryAxis", "barCategories", "ChartBars", "resolveBarFill", "set", "ChartLegend", "plot", "Rect", "ChartCanvas", "ChartBorder", "gutterSpacer", "below", "args", "duration", "easing", "easeOut", "sequenceOf", "together", "t", "seriesIndex", "target", "bars", "drive", "share", "hold", "from", "start", "next", "categoryIndex", "__decorate", "property", "resolveAxisDomain", "lerpAxisDomain", "DEFAULT_BAR_CHART_THEME", "resolveBarChartTheme", "lerpBarChartTheme", "command", "node", "Rect", "command", "node", "Node2D", "easeOut", "property", "command", "Rect", "Text", "easeOut", "property", "textOps", "at", "compose", "hold", "SLIDE", "PieLegend", "Rect", "props", "i", "item", "clampUnit", "slice", "marker", "Text", "textOps", "slide", "args", "duration", "easing", "easeOut", "items", "per", "step", "__decorate", "property", "DEFAULT_LEGEND", "command", "Rect", "command", "Graphics2D", "Node2D", "PathBuilder", "Text", "easeOut", "fillOps", "property", "strokeOps", "textOps", "lerpNumber", "shadowOps", "strokeOps", "textOps", "resolvePieSlices", "data", "category", "value", "palette", "row", "i", "text", "num", "DEG", "sliceValue", "slice", "sliceTotal", "slices", "sum", "ringPoint", "r", "a", "pieArcs", "startDegrees", "total", "arcs", "cursor", "index", "fraction", "a0", "a1", "relaxLabels", "slots", "minGap", "top", "bottom", "b", "ceiling", "slot", "floor", "LABEL_TEXT_STYLE", "CHART_PALETTE", "DEFAULT_PIE_CHART_THEME", "DEFAULT_SERIES_FILLS", "DEFAULT_LEGEND", "SPOTLIGHT_DIM", "resolveRingConfig", "ring", "previous", "strokeOps", "shadowOps", "lerpRingConfig", "from", "to", "lerpNumber", "resolveLabelConfig", "label", "textOps", "lerpLabelConfig", "resolveCenterConfig", "center", "lerpCenterConfig", "resolvePieChartTheme", "theme", "base", "resolveSeriesFills", "resolveLegendConfig", "lerpPieChartTheme", "lerpSeriesFills", "lerpLegendConfig", "declarePaints", "LABEL_GAP", "LABEL_PAD", "GLYPH_RATIO", "LEADER_LIFTOFF", "LEADER_LENGTH", "LABEL_STUB", "TEXT_INSET", "MIN_ELBOW", "ARC_STEPS", "ELBOW_RADIUS", "BARE_PAD", "INSIDE_CAPTION_RATIO", "PIE_LABEL_RADIUS", "CENTER_LABEL_FIT", "INSIDE_LABEL_FIT", "AXIS_EPSILON", "FALLBACK_SIZE", "sizeOf", "style", "fallback", "size", "wedgePath", "a0", "a1", "rInner", "rOuter", "halfGap", "cx", "path", "PathBuilder", "span", "steps", "insetAt", "r", "arcTo", "i", "inset", "from", "to", "point", "ringPoint", "PieRing", "Node2D", "props", "slice", "fillOps", "arc", "opacity", "clampUnit", "wantsCategory", "wantsPercent", "inside", "Text", "textOps", "formatPercent", "Rect", "side", "at", "children", "pieArcs", "mid", "outer", "hole", "band", "sin", "cos", "acrossBand", "alongArc", "category", "percent", "caption", "fits", "wanted", "captionRatio", "glyphs", "index", "gutters", "glyph", "room", "key", "left", "right", "configured", "px", "rim", "a", "strongest", "focus", "arcs", "radius", "rowHeight", "top", "bottom", "ideal", "relaxLabels", "slot", "start", "onCircle", "elbowX", "endX", "placement", "assets", "strokeOps", "ctx", "sweep", "revealEnd", "half", "centerBounds", "g", "Graphics2D", "reveal", "args", "easeOut", "t", "__decorate", "property", "DEFAULT_PIE_CHART_THEME", "command", "drive", "hold", "sequenceOf", "together", "SWEEP_SHARE", "LABEL_SHARE", "PieChart", "Node2D", "resolvePieSlices", "focus", "dim", "_", "i", "clampUnit", "index", "props", "slices", "showLegend", "PieRing", "children", "PieLegend", "Rect", "args", "duration", "easing", "easeOut", "pop", "from", "targets", "value", "t", "e", "__decorate", "property", "DEFAULT_PIE_CHART_THEME", "resolvePieChartTheme", "lerpPieChartTheme", "command", "node", "NODES", "LineChart", "BarChart", "PieChart"]
7
+ }