@pixerate/editor 0.165.0 → 0.166.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 (46) hide show
  1. package/dist/agent/index.cjs +34 -34
  2. package/dist/agent/index.js +5 -5
  3. package/dist/canvas/index.cjs +6 -6
  4. package/dist/canvas/index.js +2 -2
  5. package/dist/chart/controller.d.cts +29 -0
  6. package/dist/chart/controller.d.ts +29 -0
  7. package/dist/chart/index.cjs +301 -179
  8. package/dist/chart/index.d.cts +1 -0
  9. package/dist/chart/index.d.ts +1 -0
  10. package/dist/chart/index.js +132 -10
  11. package/dist/chart/layout-families.d.cts +7 -0
  12. package/dist/chart/layout-families.d.ts +7 -0
  13. package/dist/chart/layout-shared.d.cts +5 -0
  14. package/dist/chart/layout-shared.d.ts +5 -0
  15. package/dist/chart/race.d.cts +26 -0
  16. package/dist/chart/race.d.ts +26 -0
  17. package/dist/chart/schemas.d.cts +2 -0
  18. package/dist/chart/schemas.d.ts +2 -0
  19. package/dist/chart/spec.d.cts +1 -1
  20. package/dist/chart/spec.d.ts +1 -1
  21. package/dist/chart/types.d.cts +6 -2
  22. package/dist/chart/types.d.ts +6 -2
  23. package/dist/{chunk-FEU2XVQH.js → chunk-5UNQDC3Q.js} +5 -2
  24. package/dist/{chunk-64VWWR5U.cjs → chunk-6FYIFGBW.cjs} +16 -16
  25. package/dist/{chunk-UVXTOZ2B.cjs → chunk-BPX4Z3N3.cjs} +12 -3
  26. package/dist/{chunk-MKXK6E4O.cjs → chunk-CHLV7UFP.cjs} +51 -51
  27. package/dist/{chunk-X5W645RI.js → chunk-DFCAR7AW.js} +3 -3
  28. package/dist/{chunk-2ZRN43IN.js → chunk-FR432ROA.js} +12 -3
  29. package/dist/{chunk-ODFCS7HF.cjs → chunk-HXOBOHH2.cjs} +8 -8
  30. package/dist/{chunk-V5GSY3NC.js → chunk-ITANXMZD.js} +78 -2
  31. package/dist/{chunk-TVQW26AV.cjs → chunk-MZFZSVUJ.cjs} +26 -23
  32. package/dist/{chunk-N7NBGT2Y.js → chunk-NQQDGGXY.js} +2 -2
  33. package/dist/{chunk-Q25WAFTD.cjs → chunk-SZWVMB3B.cjs} +21 -18
  34. package/dist/{chunk-QO2T6KA5.cjs → chunk-UUUTEVX6.cjs} +116 -40
  35. package/dist/{chunk-IMRXC7OA.cjs → chunk-V5L2G22R.cjs} +153 -77
  36. package/dist/{chunk-2J5VLJHC.js → chunk-VB2CY6EG.js} +3 -3
  37. package/dist/{chunk-CRFSQWKQ.js → chunk-XNU45TYA.js} +4 -1
  38. package/dist/{chunk-FRBRLKNQ.js → chunk-YUFTVNLX.js} +78 -2
  39. package/dist/export/index.cjs +4 -4
  40. package/dist/export/index.js +3 -3
  41. package/dist/extensions/index.cjs +7 -7
  42. package/dist/extensions/index.js +6 -6
  43. package/dist/index.cjs +13 -13
  44. package/dist/index.js +7 -7
  45. package/package.json +1 -1
  46. package/skills/pixerate-chart/SKILL.md +20 -2
@@ -11,8 +11,8 @@ var _chunkTTCJPX4Ucjs = require('./chunk-TTCJPX4U.cjs');
11
11
 
12
12
 
13
13
 
14
- var _chunkQO2T6KA5cjs = require('./chunk-QO2T6KA5.cjs');
15
14
 
15
+ var _chunkUUUTEVX6cjs = require('./chunk-UUUTEVX6.cjs');
16
16
 
17
17
 
18
18
 
@@ -44,7 +44,8 @@ var _chunkQO2T6KA5cjs = require('./chunk-QO2T6KA5.cjs');
44
44
 
45
45
 
46
46
 
47
- var _chunkUVXTOZ2Bcjs = require('./chunk-UVXTOZ2B.cjs');
47
+
48
+ var _chunkBPX4Z3N3cjs = require('./chunk-BPX4Z3N3.cjs');
48
49
 
49
50
 
50
51
 
@@ -83,7 +84,7 @@ var csvField = (text) => /[",\n]|^\s|\s$/.test(text) ? `"${text.replace(/"/g, '"
83
84
  var treemapName = (name) => !name.includes('"') ? `"${name}"` : !name.includes("'") ? `'${name}'` : `"${name.replace(/"/g, "'")}"`;
84
85
  var yamlText = (text) => /^[\w][\w .,!?()/&+-]*$/.test(text) && !/:\s/.test(text) ? text : JSON.stringify(text);
85
86
  function chartMermaidFamily(spec) {
86
- const family = _chunkUVXTOZ2Bcjs.chartFamily.call(void 0, spec);
87
+ const family = _chunkBPX4Z3N3cjs.chartFamily.call(void 0, spec);
87
88
  if (family === "sankey" && _optionalChain([spec, 'access', _2 => _2.links, 'optionalAccess', _3 => _3.length])) return "sankey";
88
89
  if (family === "treemap" || family === "sunburst") return "treemap";
89
90
  if (family === "pie" || family === "funnel" || family === "parliament") return "pie";
@@ -136,7 +137,7 @@ function chartToMermaid(spec, options = {}) {
136
137
  lines.push("treemap-beta");
137
138
  if (spec.title) lines.push(`title ${spec.title}`);
138
139
  const first = _nullishCoalesce(visible[0], () => ( spec.series[0]));
139
- const tree = spec.tree.length ? spec.tree : _chunkUVXTOZ2Bcjs.chartTreeFromSeries.call(void 0, spec.categories, _nullishCoalesce(_optionalChain([first, 'optionalAccess', _4 => _4.values]), () => ( [])));
140
+ const tree = spec.tree.length ? spec.tree : _chunkBPX4Z3N3cjs.chartTreeFromSeries.call(void 0, spec.categories, _nullishCoalesce(_optionalChain([first, 'optionalAccess', _4 => _4.values]), () => ( [])));
140
141
  const write = (nodes, depth) => {
141
142
  for (const n of nodes) {
142
143
  const pad = ind.repeat(depth);
@@ -271,8 +272,8 @@ function chartFromMermaid(text, options = {}) {
271
272
  let title = _nullishCoalesce(frontTitle, () => ( ""));
272
273
  const categories = [];
273
274
  const series = [];
274
- const xAxis = _chunkUVXTOZ2Bcjs.defaultChartAxis.call(void 0, false);
275
- const yAxis = _chunkUVXTOZ2Bcjs.defaultChartAxis.call(void 0, true);
275
+ const xAxis = _chunkBPX4Z3N3cjs.defaultChartAxis.call(void 0, false);
276
+ const yAxis = _chunkBPX4Z3N3cjs.defaultChartAxis.call(void 0, true);
276
277
  const radarAxes = [];
277
278
  const pieSlices = [];
278
279
  let legendShow = true;
@@ -434,7 +435,7 @@ function chartFromMermaid(text, options = {}) {
434
435
  input.series = metaSeries.map((extra, i) => ({ ..._nullishCoalesce(base[i], () => ( {})), ..._nullishCoalesce(extra, () => ( {})) }));
435
436
  }
436
437
  }
437
- const { spec, issues: specIssues } = _chunkUVXTOZ2Bcjs.normalizeChartSpec.call(void 0, input);
438
+ const { spec, issues: specIssues } = _chunkBPX4Z3N3cjs.normalizeChartSpec.call(void 0, input);
438
439
  if (!family && !partial && !issues.length) issues.push({ path: "header", message: "The text is empty: expected a chart header (xychart-beta, pie, radar-beta, sankey-beta or treemap-beta)." });
439
440
  return { spec, issues: [...issues, ...specIssues], family, hasMeta: !!meta };
440
441
  }
@@ -464,7 +465,7 @@ function readWholeMermaid(family, all, headerLine, partial, title, issues) {
464
465
  const tree = items.map(toNode);
465
466
  const leaf = (n) => !n.children;
466
467
  const flatOrOneLevel = tree.every(leaf) || tree.every((n) => _optionalChain([n, 'access', _18 => _18.children, 'optionalAccess', _19 => _19.every, 'call', _20 => _20(leaf)]));
467
- return { type: flatOrOneLevel ? "treemap" : "sunburst", title: name, tree, categories: tree.map((n) => n.name), series: [{ id: "series-1", name: "Values", values: tree.map(_chunkUVXTOZ2Bcjs.chartTreeTotal) }] };
468
+ return { type: flatOrOneLevel ? "treemap" : "sunburst", title: name, tree, categories: tree.map((n) => n.name), series: [{ id: "series-1", name: "Values", values: tree.map(_chunkBPX4Z3N3cjs.chartTreeTotal) }] };
468
469
  }
469
470
  function findMermaidCharts(text) {
470
471
  const lines = text.replace(/^/, "").split(/\r?\n/);
@@ -509,11 +510,11 @@ function findMermaidCharts(text) {
509
510
  }
510
511
 
511
512
  // src/chart/summary.ts
512
- function summarizeChart(spec, size = { width: 640, height: 400 }, scene = _chunkQO2T6KA5cjs.layoutChart.call(void 0, spec, { ...size, selection: null })) {
513
+ function summarizeChart(spec, size = { width: 640, height: 400 }, scene = _chunkUUUTEVX6cjs.layoutChart.call(void 0, spec, { ...size, selection: null })) {
513
514
  const visible = spec.series.filter((s) => !s.hidden);
514
- const series = visible.map((s) => _chunkQO2T6KA5cjs.summarizeChartSeries.call(void 0, s, spec.categories));
515
- const colors = _chunkUVXTOZ2Bcjs.chartSpecColorsByCategory.call(void 0, spec) ? spec.categories.map((_, i) => _optionalChain([visible, 'access', _24 => _24[0], 'optionalAccess', _25 => _25.pointColors, 'optionalAccess', _26 => _26[i]]) || _chunkUVXTOZ2Bcjs.chartSliceColor.call(void 0, spec, i)) : visible.map((s) => _chunkUVXTOZ2Bcjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s)));
516
- const check = _chunkUVXTOZ2Bcjs.checkChartColors.call(void 0, colors, { surface: scene.theme.surface.startsWith("var(") ? "#ffffff" : scene.theme.surface });
515
+ const series = visible.map((s) => _chunkUUUTEVX6cjs.summarizeChartSeries.call(void 0, s, spec.categories));
516
+ const colors = _chunkBPX4Z3N3cjs.chartSpecColorsByCategory.call(void 0, spec) ? spec.categories.map((_, i) => _optionalChain([visible, 'access', _24 => _24[0], 'optionalAccess', _25 => _25.pointColors, 'optionalAccess', _26 => _26[i]]) || _chunkBPX4Z3N3cjs.chartSliceColor.call(void 0, spec, i)) : visible.map((s) => _chunkBPX4Z3N3cjs.chartSeriesColor.call(void 0, spec, spec.series.indexOf(s)));
517
+ const check = _chunkBPX4Z3N3cjs.checkChartColors.call(void 0, colors, { surface: scene.theme.surface.startsWith("var(") ? "#ffffff" : scene.theme.surface });
517
518
  const axis = (s, a) => s ? { min: s.domain[0], max: s.domain[1], ticks: [...s.ticks], ...s.logBase ? { logBase: s.logBase } : {}, ...a.reverse ? { reversed: true } : {} } : void 0;
518
519
  return {
519
520
  type: spec.type,
@@ -528,12 +529,12 @@ function summarizeChart(spec, size = { width: 640, height: 400 }, scene = _chunk
528
529
  description: describeChart(spec, series)
529
530
  };
530
531
  }
531
- function describeChart(spec, series = spec.series.filter((s) => !s.hidden).map((s) => _chunkQO2T6KA5cjs.summarizeChartSeries.call(void 0, s, spec.categories))) {
532
- const kind = _chunkUVXTOZ2Bcjs.CHART_TYPE_LABELS[spec.type].toLowerCase();
532
+ function describeChart(spec, series = spec.series.filter((s) => !s.hidden).map((s) => _chunkUUUTEVX6cjs.summarizeChartSeries.call(void 0, s, spec.categories))) {
533
+ const kind = _chunkBPX4Z3N3cjs.CHART_TYPE_LABELS[spec.type].toLowerCase();
533
534
  const parts = [];
534
- const family = _chunkUVXTOZ2Bcjs.chartFamily.call(void 0, spec);
535
+ const family = _chunkBPX4Z3N3cjs.chartFamily.call(void 0, spec);
535
536
  const format = spec.yAxis.format;
536
- const fmt = (v) => v === null ? "blank" : _chunkQO2T6KA5cjs.formatChartNumber.call(void 0, v, format);
537
+ const fmt = (v) => v === null ? "blank" : _chunkUUUTEVX6cjs.formatChartNumber.call(void 0, v, format);
537
538
  const head = spec.title ? `${spec.title}: a ${kind} chart` : `A ${kind} chart`;
538
539
  if (!series.length || series.every((s) => s.count === 0)) return `${head} with no data.`;
539
540
  if (family === "pie" || family === "funnel" || family === "treemap" || family === "sunburst") {
@@ -543,12 +544,19 @@ function describeChart(spec, series = spec.series.filter((s) => !s.hidden).map((
543
544
  parts.push(`${head} of ${s.name} across ${spec.categories.length} categories, totalling ${fmt(s.sum)}.`);
544
545
  parts.push(`The largest is ${s.maxAt} at ${fmt(s.max)}${share(s.max)}; the smallest is ${s.minAt} at ${fmt(s.min)}${share(s.min)}.`);
545
546
  if (spec.tree.length) {
546
- const depth = _chunkUVXTOZ2Bcjs.chartTreeDepth.call(void 0, spec.tree);
547
- const items = _chunkUVXTOZ2Bcjs.chartTreeToRows.call(void 0, spec.tree).length;
547
+ const depth = _chunkBPX4Z3N3cjs.chartTreeDepth.call(void 0, spec.tree);
548
+ const items = _chunkBPX4Z3N3cjs.chartTreeToRows.call(void 0, spec.tree).length;
548
549
  parts.push(`The data is grouped ${depth} level${depth === 1 ? "" : "s"} deep with ${items} item${items === 1 ? "" : "s"}.`);
549
550
  }
550
551
  return parts.join(" ");
551
552
  }
553
+ if (family === "barrace") {
554
+ const n = spec.categories.length;
555
+ const first = _chunkUUUTEVX6cjs.chartRaceRanking.call(void 0, spec, 0)[0];
556
+ const last = _chunkUUUTEVX6cjs.chartRaceRanking.call(void 0, spec, n - 1)[0];
557
+ const lead = (e, at) => e ? `${e.name} leads at ${at} with ${fmt(e.value)}` : "";
558
+ return `${head} of ${series.length} names over ${n} time points (${spec.categories[0]} to ${spec.categories[n - 1]}), showing the top ${Math.min(spec.raceTopN, series.length)}. ${lead(first, spec.categories[0])}; ${lead(last, spec.categories[n - 1])}.`;
559
+ }
552
560
  if (family === "sankey") {
553
561
  if (!spec.links.length) return `${head} with no flows yet.`;
554
562
  const total = spec.links.reduce((t, l) => t + l.value, 0);
@@ -625,7 +633,7 @@ function describeChart(spec, series = spec.series.filter((s) => !s.hidden).map((
625
633
  }
626
634
  parts.push(...axes);
627
635
  for (const s of spec.series.filter((x) => !x.hidden && (x.errorBars || x.xErrorBars)).slice(0, 8)) {
628
- const bars = [s.errorBars ? _chunkUVXTOZ2Bcjs.describeChartErrorBars.call(void 0, s.errorBars) : "", s.xErrorBars ? `x ${_chunkUVXTOZ2Bcjs.describeChartErrorBars.call(void 0, s.xErrorBars)}` : ""].filter(Boolean).join(" and ");
636
+ const bars = [s.errorBars ? _chunkBPX4Z3N3cjs.describeChartErrorBars.call(void 0, s.errorBars) : "", s.xErrorBars ? `x ${_chunkBPX4Z3N3cjs.describeChartErrorBars.call(void 0, s.xErrorBars)}` : ""].filter(Boolean).join(" and ");
629
637
  parts.push(`${s.name} has ${bars}.`);
630
638
  }
631
639
  return parts.join(" ");
@@ -690,7 +698,7 @@ function chartSceneToSvg(scene, options = {}) {
690
698
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${scene.width}" height="${scene.height}" viewBox="0 0 ${scene.width} ${scene.height}" role="img"${label ? ` aria-label="${escape(label)}"` : ""}${labelled ? ` aria-labelledby="${labelled}"` : ""}>${head}${body}</svg>`;
691
699
  }
692
700
  function chartToSvg(spec, options) {
693
- const scene = _chunkQO2T6KA5cjs.layoutChart.call(void 0, spec, { ...options, selection: null });
701
+ const scene = _chunkUUUTEVX6cjs.layoutChart.call(void 0, spec, { ...options, selection: null });
694
702
  return chartSceneToSvg(scene, { ...options, ariaLabel: _nullishCoalesce(options.ariaLabel, () => ( (spec.title || void 0))), description: _nullishCoalesce(options.description, () => ( describeChart(spec))) });
695
703
  }
696
704
  var SVG_NS = "http://www.w3.org/2000/svg";
@@ -728,10 +736,10 @@ var CHART_LAYOUT_PRESETS = [
728
736
  ];
729
737
  function monochromeChartPalette(base, count = 6) {
730
738
  const steps = count <= 1 ? [0] : Array.from({ length: count }, (_, i) => -0.45 + 1.05 * i / (count - 1));
731
- return steps.map((t) => _chunkUVXTOZ2Bcjs.shadeChartColor.call(void 0, base, t));
739
+ return steps.map((t) => _chunkBPX4Z3N3cjs.shadeChartColor.call(void 0, base, t));
732
740
  }
733
741
  var CHART_COLOR_SCHEMES = [
734
- { id: "default", label: "Default", description: "Ten distinct colours, readable on light and dark backgrounds.", palette: _chunkUVXTOZ2Bcjs.DEFAULT_CHART_PALETTE },
742
+ { id: "default", label: "Default", description: "Ten distinct colours, readable on light and dark backgrounds.", palette: _chunkBPX4Z3N3cjs.DEFAULT_CHART_PALETTE },
735
743
  { id: "okabe-ito", label: "Okabe\u2013Ito", description: "Eight colours chosen to stay distinct with colour-blindness.", palette: ["#e69f00", "#56b4e9", "#009e73", "#f0e442", "#0072b2", "#d55e00", "#cc79a7", "#000000"] },
736
744
  { id: "office", label: "Office", description: "The six accent colours of the default Office theme.", palette: ["#4472c4", "#ed7d31", "#a5a5a5", "#ffc000", "#5b9bd5", "#70ad47"] },
737
745
  { id: "warm", label: "Warm", description: "Reds, oranges and yellows.", palette: ["#b2182b", "#d6604d", "#f4a582", "#e08214", "#fdb863", "#8c510a"] },
@@ -776,6 +784,17 @@ function sameChartJson(a, b) {
776
784
  if (keys.length !== Object.keys(bo).filter((k) => bo[k] !== void 0).length) return false;
777
785
  return keys.every((k) => Object.prototype.hasOwnProperty.call(bo, k) && sameChartJson(ao[k], bo[k]));
778
786
  }
787
+ var defaultRaceTicker = {
788
+ now: () => typeof performance !== "undefined" ? performance.now() : Date.now(),
789
+ next: (step) => {
790
+ if (typeof requestAnimationFrame === "function") {
791
+ const id2 = requestAnimationFrame(() => step());
792
+ return () => cancelAnimationFrame(id2);
793
+ }
794
+ const id = setTimeout(step, 16);
795
+ return () => clearTimeout(id);
796
+ }
797
+ };
779
798
  var fail = (message, issues) => ({ ok: false, message, ...issues ? { issues } : {} });
780
799
  var ChartController = (_class = class {
781
800
 
@@ -788,13 +807,17 @@ var ChartController = (_class = class {
788
807
  __init6() {this.revision = 0}
789
808
  __init7() {this.applying = false}
790
809
 
791
- __init8() {this.listeners = /* @__PURE__ */ new Set()}
810
+
811
+ __init8() {this.raceFrame = null}
812
+ __init9() {this.raceStop = null}
813
+ __init10() {this.listeners = /* @__PURE__ */ new Set()}
792
814
 
793
815
 
794
- constructor(options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);
816
+ constructor(options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);_class.prototype.__init11.call(this);_class.prototype.__init12.call(this);
795
817
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkFJ2MSI43cjs.HistoryManager)()));
796
818
  this.onChange = options.onChange;
797
- const { spec, issues } = _chunkUVXTOZ2Bcjs.normalizeChartSpec.call(void 0, _nullishCoalesce(options.spec, () => ( {})));
819
+ this.raceTicker = _nullishCoalesce(options.raceTicker, () => ( defaultRaceTicker));
820
+ const { spec, issues } = _chunkBPX4Z3N3cjs.normalizeChartSpec.call(void 0, _nullishCoalesce(options.spec, () => ( {})));
798
821
  this.spec = spec;
799
822
  this.issues = issues;
800
823
  this.state = this.computeState();
@@ -812,9 +835,9 @@ var ChartController = (_class = class {
812
835
  // State
813
836
  // -------------------------------------------------------------------------
814
837
  /** The current snapshot (stable until the next change). */
815
- __init9() {this.getState = () => this.state}
838
+ __init11() {this.getState = () => this.state}
816
839
  /** Calls `listener` after every change. Returns an unsubscribe function. */
817
- __init10() {this.subscribe = (listener) => {
840
+ __init12() {this.subscribe = (listener) => {
818
841
  if (!this.unsubscribeHistory) this.connect();
819
842
  this.listeners.add(listener);
820
843
  return () => {
@@ -843,6 +866,8 @@ var ChartController = (_class = class {
843
866
  canRedo: h.canRedo,
844
867
  undoLabel: h.undoName,
845
868
  redoLabel: h.redoName,
869
+ raceFrame: this.raceFrame === null ? null : Math.min(this.raceFrame, Math.max(0, this.spec.categories.length - 1)),
870
+ racePlaying: this.raceStop !== null,
846
871
  revision: this.revision
847
872
  };
848
873
  }
@@ -897,7 +922,7 @@ var ChartController = (_class = class {
897
922
  const hadIssues = this.issues.length > 0;
898
923
  this.issues = [];
899
924
  if (sameChartJson(before, next)) {
900
- const reselect = select !== void 0 && !_chunkQO2T6KA5cjs.sameChartSelection.call(void 0, this.selection, select) && !(select === null && this.selection === null);
925
+ const reselect = select !== void 0 && !_chunkUUUTEVX6cjs.sameChartSelection.call(void 0, this.selection, select) && !(select === null && this.selection === null);
901
926
  if (reselect) this.selection = select;
902
927
  if (reselect || hadIssues) this.notify(false);
903
928
  return;
@@ -920,7 +945,7 @@ var ChartController = (_class = class {
920
945
  }
921
946
  /** A copy of the spec with `edit` applied (series and categories copied as needed). */
922
947
  edited(edit) {
923
- const draft = _chunkUVXTOZ2Bcjs.cloneChartSpec.call(void 0, this.spec);
948
+ const draft = _chunkBPX4Z3N3cjs.cloneChartSpec.call(void 0, this.spec);
924
949
  edit(draft);
925
950
  return draft;
926
951
  }
@@ -950,7 +975,7 @@ var ChartController = (_class = class {
950
975
  legend: patch.legend ? { ...s.legend, ...patch.legend } : s.legend,
951
976
  version: 1
952
977
  };
953
- const { spec, issues } = _chunkUVXTOZ2Bcjs.normalizeChartSpec.call(void 0, merged);
978
+ const { spec, issues } = _chunkBPX4Z3N3cjs.normalizeChartSpec.call(void 0, merged);
954
979
  if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
955
980
  if (JSON.stringify(spec) === JSON.stringify(s)) return { ok: true };
956
981
  this.commit(_nullishCoalesce(options.label, () => ( updateLabel(patch))), spec);
@@ -1004,7 +1029,7 @@ var ChartController = (_class = class {
1004
1029
  const series = this.getSeries(seriesId);
1005
1030
  if (!series) return this.unknownSeries(seriesId);
1006
1031
  if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
1007
- const parsed = _chunkUVXTOZ2Bcjs.parseChartValue.call(void 0, value);
1032
+ const parsed = _chunkBPX4Z3N3cjs.parseChartValue.call(void 0, value);
1008
1033
  if (parsed === void 0) return this.reject(`"${String(value)}" is not a number; enter a number or leave the cell blank.`, [{ path: `${seriesId}[${index}]`, message: `"${String(value)}" is not a number.` }]);
1009
1034
  if (series.values[index] === parsed) {
1010
1035
  if (this.issues.length) {
@@ -1029,7 +1054,7 @@ var ChartController = (_class = class {
1029
1054
  if (!this.getSeries(cell.seriesId)) issues.push({ path: cell.seriesId, message: this.unknownSeries(cell.seriesId).message });
1030
1055
  else if (!Number.isInteger(cell.index) || cell.index < 0 || cell.index >= this.spec.categories.length) issues.push({ path: `${cell.seriesId}[${cell.index}]`, message: this.badIndex(cell.index).message });
1031
1056
  else {
1032
- const value = _chunkUVXTOZ2Bcjs.parseChartValue.call(void 0, cell.value);
1057
+ const value = _chunkBPX4Z3N3cjs.parseChartValue.call(void 0, cell.value);
1033
1058
  if (value === void 0) issues.push({ path: `${cell.seriesId}[${cell.index}]`, message: `"${String(cell.value)}" is not a number.` });
1034
1059
  else parsed.push({ seriesId: cell.seriesId, index: cell.index, value });
1035
1060
  }
@@ -1043,12 +1068,12 @@ var ChartController = (_class = class {
1043
1068
  }
1044
1069
  /** Adds a series (values padded to the categories) and selects it. Returns its id. */
1045
1070
  addSeries(input = {}, options = {}) {
1046
- const id = input.id && !this.getSeries(input.id) ? input.id : _chunkUVXTOZ2Bcjs.uniqueChartSeriesId.call(void 0, this.spec.series);
1071
+ const id = input.id && !this.getSeries(input.id) ? input.id : _chunkBPX4Z3N3cjs.uniqueChartSeriesId.call(void 0, this.spec.series);
1047
1072
  const name = _nullishCoalesce(input.name, () => ( `Series ${this.spec.series.length + 1}`));
1048
1073
  const at = Math.max(0, Math.min(this.spec.series.length, _nullishCoalesce(options.at, () => ( this.spec.series.length))));
1049
1074
  const series = [...this.spec.series];
1050
1075
  series.splice(at, 0, { ...input, id, name, values: _nullishCoalesce(input.values, () => ( [])) });
1051
- const { spec, issues } = _chunkUVXTOZ2Bcjs.normalizeChartSpec.call(void 0, { ...this.spec, series });
1076
+ const { spec, issues } = _chunkBPX4Z3N3cjs.normalizeChartSpec.call(void 0, { ...this.spec, series });
1052
1077
  if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1053
1078
  this.commit(`Add series "${name}"`, spec, { kind: "series", seriesId: id });
1054
1079
  return { ok: true, id };
@@ -1068,7 +1093,7 @@ var ChartController = (_class = class {
1068
1093
  updateSeries(id, patch) {
1069
1094
  const series = this.getSeries(id);
1070
1095
  if (!series) return this.unknownSeries(id);
1071
- const next = _chunkUVXTOZ2Bcjs.cloneChartSeries.call(void 0, series);
1096
+ const next = _chunkBPX4Z3N3cjs.cloneChartSeries.call(void 0, series);
1072
1097
  if (patch.name !== void 0) next.name = String(patch.name);
1073
1098
  if (patch.color === null || patch.color === "") delete next.color;
1074
1099
  else if (patch.color !== void 0) next.color = patch.color;
@@ -1088,7 +1113,7 @@ var ChartController = (_class = class {
1088
1113
  const series2 = this.spec.series.map((s) => s.id === id ? next : s);
1089
1114
  const renamed = next.name !== series.name && this.spec.highlight.includes(series.name) && !this.spec.categories.includes(series.name);
1090
1115
  const highlight = renamed ? this.spec.highlight.map((h) => h === series.name ? next.name : h) : this.spec.highlight;
1091
- const { spec, issues } = _chunkUVXTOZ2Bcjs.normalizeChartSpec.call(void 0, { ...this.spec, series: series2, highlight });
1116
+ const { spec, issues } = _chunkBPX4Z3N3cjs.normalizeChartSpec.call(void 0, { ...this.spec, series: series2, highlight });
1092
1117
  if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1093
1118
  if (JSON.stringify(spec.series) === JSON.stringify(this.spec.series)) return { ok: true };
1094
1119
  const label = patch.hidden !== void 0 && Object.keys(patch).length === 1 ? `${patch.hidden ? "Hide" : "Show"} "${series.name}"` : patch.name !== void 0 && Object.keys(patch).length === 1 ? "Rename series" : `Change series "${series.name}"`;
@@ -1194,7 +1219,7 @@ var ChartController = (_class = class {
1194
1219
  const width = Math.max(...rows.map((r) => r.length));
1195
1220
  const lastSeries = column + width - 1;
1196
1221
  while (d.series.length <= lastSeries) {
1197
- const id = _chunkUVXTOZ2Bcjs.uniqueChartSeriesId.call(void 0, d.series);
1222
+ const id = _chunkBPX4Z3N3cjs.uniqueChartSeriesId.call(void 0, d.series);
1198
1223
  d.series.push({ id, name: `Series ${d.series.length + 1}`, values: d.categories.map(() => null) });
1199
1224
  }
1200
1225
  const lastRow = row + rows.length - 1;
@@ -1208,7 +1233,7 @@ var ChartController = (_class = class {
1208
1233
  const index = row + r;
1209
1234
  if (col < 0) d.categories[index] = cell === null || cell === void 0 ? "" : String(cell).trim();
1210
1235
  else {
1211
- const parsed = _chunkUVXTOZ2Bcjs.parseChartValue.call(void 0, cell);
1236
+ const parsed = _chunkBPX4Z3N3cjs.parseChartValue.call(void 0, cell);
1212
1237
  if (parsed === void 0) issues.push({ path: `${d.series[col].id}[${index}]`, message: `"${String(cell)}" is not a number; it was left blank.` });
1213
1238
  d.series[col].values[index] = _nullishCoalesce(parsed, () => ( null));
1214
1239
  }
@@ -1227,14 +1252,14 @@ var ChartController = (_class = class {
1227
1252
  * colours and types are kept by name.
1228
1253
  */
1229
1254
  importTable(rows, options = {}) {
1230
- const data = _chunkUVXTOZ2Bcjs.chartDataFromTable.call(void 0, rows, options);
1255
+ const data = _chunkBPX4Z3N3cjs.chartDataFromTable.call(void 0, rows, options);
1231
1256
  if (!data.categories.length || !data.series.length) return this.reject("There is no data to import: expected a header row and at least one row of values.");
1232
1257
  const byName = new Map(this.spec.series.map((s) => [s.name, s]));
1233
1258
  const series = data.series.map((s) => {
1234
1259
  const old = byName.get(s.name);
1235
1260
  return { ...s, ..._optionalChain([old, 'optionalAccess', _63 => _63.color]) ? { color: old.color } : {}, ..._optionalChain([old, 'optionalAccess', _64 => _64.type]) ? { type: old.type } : {}, ...old ? { id: old.id } : {} };
1236
1261
  });
1237
- const { spec } = _chunkUVXTOZ2Bcjs.normalizeChartSpec.call(void 0, { ...this.spec, categories: data.categories, series, tree: [], links: [] });
1262
+ const { spec } = _chunkBPX4Z3N3cjs.normalizeChartSpec.call(void 0, { ...this.spec, categories: data.categories, series, tree: [], links: [] });
1238
1263
  this.commit("Import data", spec);
1239
1264
  if (data.issues.length) {
1240
1265
  this.issues = data.issues;
@@ -1265,7 +1290,7 @@ var ChartController = (_class = class {
1265
1290
  /** Replaces the grouped data (an empty list goes back to the table, keeping the totals as values). One undo step. */
1266
1291
  setTree(tree, options = {}) {
1267
1292
  const issues = [];
1268
- const next = _chunkUVXTOZ2Bcjs.normalizeChartTree.call(void 0, tree, issues);
1293
+ const next = _chunkBPX4Z3N3cjs.normalizeChartTree.call(void 0, tree, issues);
1269
1294
  if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1270
1295
  return this.update({ tree: next, links: [] }, { label: _nullishCoalesce(options.label, () => ( (next.length ? "Replace grouped data" : "Ungroup data"))) });
1271
1296
  }
@@ -1275,11 +1300,11 @@ var ChartController = (_class = class {
1275
1300
  */
1276
1301
  setTreeRows(rows, options = {}) {
1277
1302
  const issues = [];
1278
- const next = _chunkUVXTOZ2Bcjs.chartTreeFromRows.call(void 0, rows, issues);
1303
+ const next = _chunkBPX4Z3N3cjs.chartTreeFromRows.call(void 0, rows, issues);
1279
1304
  if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1280
1305
  const keepColors = (list, prefix) => {
1281
1306
  for (const n of list) {
1282
- const old = _chunkUVXTOZ2Bcjs.findChartTreeNode.call(void 0, this.spec.tree, [...prefix, n.name]);
1307
+ const old = _chunkBPX4Z3N3cjs.findChartTreeNode.call(void 0, this.spec.tree, [...prefix, n.name]);
1283
1308
  if (_optionalChain([old, 'optionalAccess', _65 => _65.color])) n.color = old.color;
1284
1309
  if (n.children) keepColors(n.children, [...prefix, n.name]);
1285
1310
  }
@@ -1290,15 +1315,15 @@ var ChartController = (_class = class {
1290
1315
  /** Replaces the data with a table of names and values (group, …, item, value); see `chartTreeFromTable`. */
1291
1316
  importTreeTable(rows, options = {}) {
1292
1317
  const issues = [];
1293
- const next = _chunkUVXTOZ2Bcjs.chartTreeFromTable.call(void 0, rows, options, issues);
1318
+ const next = _chunkBPX4Z3N3cjs.chartTreeFromTable.call(void 0, rows, options, issues);
1294
1319
  if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1295
1320
  if (!next.length) return this.reject('There is no grouped data to import: expected rows of names followed by a value, e.g. "Europe,France,40".');
1296
1321
  return this.update({ tree: next, links: [] }, { label: "Import grouped data" });
1297
1322
  }
1298
1323
  nodeAt(path) {
1299
- const node = _chunkUVXTOZ2Bcjs.findChartTreeNode.call(void 0, this.spec.tree, path);
1324
+ const node = _chunkBPX4Z3N3cjs.findChartTreeNode.call(void 0, this.spec.tree, path);
1300
1325
  if (node) return node;
1301
- const known = path.length > 1 ? _chunkUVXTOZ2Bcjs.findChartTreeNode.call(void 0, this.spec.tree, path.slice(0, -1)) : null;
1326
+ const known = path.length > 1 ? _chunkBPX4Z3N3cjs.findChartTreeNode.call(void 0, this.spec.tree, path.slice(0, -1)) : null;
1302
1327
  const siblings = _nullishCoalesce((path.length > 1 ? _optionalChain([known, 'optionalAccess', _66 => _66.children]) : this.spec.tree), () => ( []));
1303
1328
  return fail(
1304
1329
  `There is no node ${JSON.stringify(path)}. ${siblings.length ? `${path.length > 1 && known ? `"${known.name}" contains` : "The top level has"} ${siblings.map((n) => `"${n.name}"`).slice(0, 20).join(", ")}.` : "The chart has no grouped data yet; add nodes with add_chart_tree_node or set_chart_tree."}`
@@ -1314,12 +1339,12 @@ var ChartController = (_class = class {
1314
1339
  siblings = group.children;
1315
1340
  }
1316
1341
  const issues = [];
1317
- const [node] = _chunkUVXTOZ2Bcjs.normalizeChartTree.call(void 0, [input], issues, "node");
1342
+ const [node] = _chunkBPX4Z3N3cjs.normalizeChartTree.call(void 0, [input], issues, "node");
1318
1343
  if (!node || issues.length) return this.reject(issues.map((i) => i.message).join(" ") || "The node is not valid.", issues);
1319
1344
  if (siblings.some((n) => n.name === node.name)) return this.reject(`"${node.name}" is already there; names under one group must be unique.`);
1320
1345
  const at = Math.max(0, Math.min(siblings.length, _nullishCoalesce(options.at, () => ( siblings.length))));
1321
- const tree = _chunkUVXTOZ2Bcjs.cloneChartTree.call(void 0, this.spec.tree);
1322
- const list = parent.length ? _chunkUVXTOZ2Bcjs.findChartTreeNode.call(void 0, tree, parent).children : tree;
1346
+ const tree = _chunkBPX4Z3N3cjs.cloneChartTree.call(void 0, this.spec.tree);
1347
+ const list = parent.length ? _chunkBPX4Z3N3cjs.findChartTreeNode.call(void 0, tree, parent).children : tree;
1323
1348
  list.splice(at, 0, node);
1324
1349
  const result = this.update({ tree }, { label: `Add "${node.name}"` });
1325
1350
  if (!result.ok) return result;
@@ -1331,16 +1356,16 @@ var ChartController = (_class = class {
1331
1356
  if ("ok" in node) return node;
1332
1357
  if (patch.value !== void 0 && node.children) return this.reject(`"${node.name}" is a group: its value is the sum of its items. Change the items instead.`);
1333
1358
  const name = patch.name !== void 0 ? String(patch.name).trim() : node.name;
1334
- const siblings = (path.length > 1 ? _chunkUVXTOZ2Bcjs.findChartTreeNode.call(void 0, this.spec.tree, path.slice(0, -1)).children : this.spec.tree).filter((n) => n !== node);
1359
+ const siblings = (path.length > 1 ? _chunkBPX4Z3N3cjs.findChartTreeNode.call(void 0, this.spec.tree, path.slice(0, -1)).children : this.spec.tree).filter((n) => n !== node);
1335
1360
  if (siblings.some((n) => n.name === name)) return this.reject(`There is already a "${name}" next to "${node.name}"; names under one group must be unique.`);
1336
- const tree = _chunkUVXTOZ2Bcjs.cloneChartTree.call(void 0, this.spec.tree);
1337
- const target = _chunkUVXTOZ2Bcjs.findChartTreeNode.call(void 0, tree, path);
1361
+ const tree = _chunkBPX4Z3N3cjs.cloneChartTree.call(void 0, this.spec.tree);
1362
+ const target = _chunkBPX4Z3N3cjs.findChartTreeNode.call(void 0, tree, path);
1338
1363
  const merged = { ...target, name };
1339
1364
  if (patch.value !== void 0) merged.value = patch.value;
1340
1365
  if (patch.color === null) delete merged.color;
1341
1366
  else if (patch.color !== void 0) merged.color = patch.color;
1342
1367
  const issues = [];
1343
- const [checked] = _chunkUVXTOZ2Bcjs.normalizeChartTree.call(void 0, [merged], issues, "node");
1368
+ const [checked] = _chunkBPX4Z3N3cjs.normalizeChartTree.call(void 0, [merged], issues, "node");
1344
1369
  if (!checked || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1345
1370
  Object.assign(target, checked);
1346
1371
  if (patch.color === null) delete target.color;
@@ -1352,8 +1377,8 @@ var ChartController = (_class = class {
1352
1377
  removeTreeNode(path) {
1353
1378
  const node = this.nodeAt(path);
1354
1379
  if ("ok" in node) return node;
1355
- const tree = _chunkUVXTOZ2Bcjs.cloneChartTree.call(void 0, this.spec.tree);
1356
- const parent = path.length > 1 ? _chunkUVXTOZ2Bcjs.findChartTreeNode.call(void 0, tree, path.slice(0, -1)) : null;
1380
+ const tree = _chunkBPX4Z3N3cjs.cloneChartTree.call(void 0, this.spec.tree);
1381
+ const parent = path.length > 1 ? _chunkBPX4Z3N3cjs.findChartTreeNode.call(void 0, tree, path.slice(0, -1)) : null;
1357
1382
  const list = parent ? parent.children : tree;
1358
1383
  list.splice(
1359
1384
  list.findIndex((n) => n.name === node.name),
@@ -1374,7 +1399,7 @@ var ChartController = (_class = class {
1374
1399
  */
1375
1400
  setLinks(links, options = {}) {
1376
1401
  const issues = [];
1377
- const next = _chunkUVXTOZ2Bcjs.normalizeChartLinks.call(void 0, links, issues);
1402
+ const next = _chunkBPX4Z3N3cjs.normalizeChartLinks.call(void 0, links, issues);
1378
1403
  if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1379
1404
  const used = new Set(next.flatMap((l) => [l.source, l.target]));
1380
1405
  const sankeyNodes = options.nodes ? [...options.nodes] : this.spec.sankeyNodes.filter((n) => used.has(n));
@@ -1382,7 +1407,7 @@ var ChartController = (_class = class {
1382
1407
  }
1383
1408
  /** Replaces the data with flows from a table of source, target and value columns. */
1384
1409
  importLinksTable(rows, options = {}) {
1385
- const raw = _chunkUVXTOZ2Bcjs.chartLinksFromTable.call(void 0, rows, options);
1410
+ const raw = _chunkBPX4Z3N3cjs.chartLinksFromTable.call(void 0, rows, options);
1386
1411
  if (!raw.length) return this.reject('There are no flows to import: expected rows of source, target and value, e.g. "Website,Signup,120".');
1387
1412
  return this.setLinks(raw, { label: "Import flows" });
1388
1413
  }
@@ -1446,7 +1471,7 @@ var ChartController = (_class = class {
1446
1471
  /** Why a reference on `axis` at `values` cannot be drawn on this chart, or null. */
1447
1472
  referenceProblem(axis, values) {
1448
1473
  const s = this.spec;
1449
- const family = _chunkUVXTOZ2Bcjs.chartFamily.call(void 0, s);
1474
+ const family = _chunkBPX4Z3N3cjs.chartFamily.call(void 0, s);
1450
1475
  if (family !== "cartesian" && family !== "xy") return `Reference lines and bands are drawn on bar, line, area, scatter and bubble charts; this is a ${s.type} chart.`;
1451
1476
  if (axis === "x" && family !== "xy") return 'axis "x" needs a scatter or bubble chart with numeric x values; use "y" for the value axis.';
1452
1477
  if (axis === "y2" && (family !== "cartesian" || !s.series.some((x) => !x.hidden && x.axis === "secondary")))
@@ -1465,7 +1490,7 @@ var ChartController = (_class = class {
1465
1490
  /** Adds a reference line (a target, floor or average). Returns its index. */
1466
1491
  addReferenceLine(input) {
1467
1492
  const issues = [];
1468
- const line = _chunkUVXTOZ2Bcjs.normalizeChartReferenceLine.call(void 0, input, "referenceLine", issues);
1493
+ const line = _chunkBPX4Z3N3cjs.normalizeChartReferenceLine.call(void 0, input, "referenceLine", issues);
1469
1494
  if (!line || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1470
1495
  const problem = this.referenceProblem(line.axis, [line.value]);
1471
1496
  if (problem) return this.reject(problem);
@@ -1480,7 +1505,7 @@ var ChartController = (_class = class {
1480
1505
  const merged = { ...current, ...patch };
1481
1506
  for (const [k, v] of Object.entries(patch)) if (v === null) delete merged[k];
1482
1507
  const issues = [];
1483
- const line = _chunkUVXTOZ2Bcjs.normalizeChartReferenceLine.call(void 0, merged, "referenceLine", issues);
1508
+ const line = _chunkBPX4Z3N3cjs.normalizeChartReferenceLine.call(void 0, merged, "referenceLine", issues);
1484
1509
  if (!line || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1485
1510
  const problem = this.referenceProblem(line.axis, [line.value]);
1486
1511
  if (problem) return this.reject(problem);
@@ -1495,7 +1520,7 @@ var ChartController = (_class = class {
1495
1520
  /** Adds a reference band (a target range). Returns its index. */
1496
1521
  addReferenceBand(input) {
1497
1522
  const issues = [];
1498
- const band = _chunkUVXTOZ2Bcjs.normalizeChartReferenceBand.call(void 0, input, "referenceBand", issues);
1523
+ const band = _chunkBPX4Z3N3cjs.normalizeChartReferenceBand.call(void 0, input, "referenceBand", issues);
1499
1524
  if (!band || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1500
1525
  const problem = this.referenceProblem(band.axis, [band.from, band.to]);
1501
1526
  if (problem) return this.reject(problem);
@@ -1510,7 +1535,7 @@ var ChartController = (_class = class {
1510
1535
  const merged = { ...current, ...patch };
1511
1536
  for (const [k, v] of Object.entries(patch)) if (v === null) delete merged[k];
1512
1537
  const issues = [];
1513
- const band = _chunkUVXTOZ2Bcjs.normalizeChartReferenceBand.call(void 0, merged, "referenceBand", issues);
1538
+ const band = _chunkBPX4Z3N3cjs.normalizeChartReferenceBand.call(void 0, merged, "referenceBand", issues);
1514
1539
  if (!band || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1515
1540
  const problem = this.referenceProblem(band.axis, [band.from, band.to]);
1516
1541
  if (problem) return this.reject(problem);
@@ -1533,7 +1558,7 @@ var ChartController = (_class = class {
1533
1558
  const series = this.getSeries(seriesId);
1534
1559
  if (!series) return this.unknownSeries(seriesId);
1535
1560
  if (!Number.isInteger(index) || index < 0 || index >= this.spec.categories.length) return this.badIndex(index);
1536
- const family = _chunkUVXTOZ2Bcjs.chartFamily.call(void 0, this.spec);
1561
+ const family = _chunkBPX4Z3N3cjs.chartFamily.call(void 0, this.spec);
1537
1562
  const text = label === null ? "" : String(label).trim();
1538
1563
  if (text && family !== "cartesian" && family !== "xy") return this.reject(`Callouts are drawn on bar, line, area, scatter and bubble charts; this is a ${this.spec.type} chart.`);
1539
1564
  if (text && (series.values[index] === null || series.values[index] === void 0))
@@ -1555,10 +1580,10 @@ var ChartController = (_class = class {
1555
1580
  addTrendline(seriesId, input) {
1556
1581
  const series = this.getSeries(seriesId);
1557
1582
  if (!series) return this.unknownSeries(seriesId);
1558
- if (!_chunkQO2T6KA5cjs.chartSupportsTrendlines.call(void 0, this.spec.type, this.spec.stacking))
1583
+ if (!_chunkUUUTEVX6cjs.chartSupportsTrendlines.call(void 0, this.spec.type, this.spec.stacking))
1559
1584
  return this.reject(`Trendlines need an unstacked bar, line, area, scatter or bubble chart; this is a ${this.spec.stacking !== "none" ? `${this.spec.stacking} ` : ""}${this.spec.type} chart.`);
1560
1585
  const issues = [];
1561
- const line = _chunkUVXTOZ2Bcjs.normalizeChartTrendline.call(void 0, input, "trendline", issues);
1586
+ const line = _chunkBPX4Z3N3cjs.normalizeChartTrendline.call(void 0, input, "trendline", issues);
1562
1587
  if (!line || issues.length) return this.reject(issues.map((i) => i.message).join(" ") || "The trendline is not valid.", issues);
1563
1588
  const index = _nullishCoalesce(_optionalChain([series, 'access', _69 => _69.trendlines, 'optionalAccess', _70 => _70.length]), () => ( 0));
1564
1589
  const next = this.edited((d) => {
@@ -1577,7 +1602,7 @@ var ChartController = (_class = class {
1577
1602
  const issues = [];
1578
1603
  const merged = { ...current, ...patch };
1579
1604
  for (const [k, v] of Object.entries(patch)) if (v === null) delete merged[k];
1580
- const line = _chunkUVXTOZ2Bcjs.normalizeChartTrendline.call(void 0, merged, "trendline", issues);
1605
+ const line = _chunkBPX4Z3N3cjs.normalizeChartTrendline.call(void 0, merged, "trendline", issues);
1581
1606
  if (!line || issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1582
1607
  this.commit(
1583
1608
  "Change trendline",
@@ -1605,7 +1630,7 @@ var ChartController = (_class = class {
1605
1630
  trendlinePoints(seriesId) {
1606
1631
  const series = this.getSeries(seriesId);
1607
1632
  if (!series) return [];
1608
- const xy = _chunkUVXTOZ2Bcjs.chartFamily.call(void 0, this.spec) === "xy";
1633
+ const xy = _chunkBPX4Z3N3cjs.chartFamily.call(void 0, this.spec) === "xy";
1609
1634
  const out = [];
1610
1635
  series.values.forEach((v, i) => {
1611
1636
  const x = xy ? _nullishCoalesce(_optionalChain([series, 'access', _77 => _77.x, 'optionalAccess', _78 => _78[i]]), () => ( (this.spec.type === "bubble" ? i + 1 : null))) : i + 1;
@@ -1617,13 +1642,13 @@ var ChartController = (_class = class {
1617
1642
  trendlineFit(seriesId, index) {
1618
1643
  const line = _optionalChain([this, 'access', _79 => _79.getSeries, 'call', _80 => _80(seriesId), 'optionalAccess', _81 => _81.trendlines, 'optionalAccess', _82 => _82[index]]);
1619
1644
  if (!line) return null;
1620
- return _chunkQO2T6KA5cjs.fitChartTrendline.call(void 0, line, this.trendlinePoints(seriesId));
1645
+ return _chunkUUUTEVX6cjs.fitChartTrendline.call(void 0, line, this.trendlinePoints(seriesId));
1621
1646
  }
1622
1647
  /** The moving-average points of a trendline (empty for other kinds). */
1623
1648
  trendlineMovingAverage(seriesId, index) {
1624
1649
  const line = _optionalChain([this, 'access', _83 => _83.getSeries, 'call', _84 => _84(seriesId), 'optionalAccess', _85 => _85.trendlines, 'optionalAccess', _86 => _86[index]]);
1625
1650
  if (!line || line.type !== "moving-average") return [];
1626
- return _chunkQO2T6KA5cjs.chartMovingAverage.call(void 0, this.trendlinePoints(seriesId), _nullishCoalesce(line.period, () => ( 2)));
1651
+ return _chunkUUUTEVX6cjs.chartMovingAverage.call(void 0, this.trendlinePoints(seriesId), _nullishCoalesce(line.period, () => ( 2)));
1627
1652
  }
1628
1653
  // -------------------------------------------------------------------------
1629
1654
  // Reshaping
@@ -1644,17 +1669,66 @@ var ChartController = (_class = class {
1644
1669
  used.add(id);
1645
1670
  return { id, name, values: s.series.map((x) => _nullishCoalesce(x.values[i], () => ( null))) };
1646
1671
  });
1647
- const { spec } = _chunkUVXTOZ2Bcjs.normalizeChartSpec.call(void 0, { ...s, categories: s.series.map((x) => x.name), series });
1672
+ const { spec } = _chunkBPX4Z3N3cjs.normalizeChartSpec.call(void 0, { ...s, categories: s.series.map((x) => x.name), series });
1648
1673
  this.commit("Switch rows and columns", spec, null);
1649
1674
  return { ok: true };
1650
1675
  }
1651
1676
  // -------------------------------------------------------------------------
1652
1677
  // Selection and gestures
1653
1678
  // -------------------------------------------------------------------------
1679
+ /**
1680
+ * Shows a bar race at a moment: a category index, fractional between time points (`null`:
1681
+ * the last one). View state like the selection, so not an undo step. Stops playback.
1682
+ */
1683
+ setRaceFrame(frame) {
1684
+ const n = this.spec.categories.length;
1685
+ if (frame !== null && !(Number.isFinite(frame) && frame >= 0 && frame <= Math.max(0, n - 1)))
1686
+ return fail(n ? `The race has time points 0 to ${n - 1}${n > 1 ? ` ("${this.spec.categories[0]}" to "${this.spec.categories[n - 1]}")` : ""}; got ${frame}.` : "The chart has no time points (categories) yet.");
1687
+ this.stopRace();
1688
+ if (frame === this.raceFrame) return { ok: true };
1689
+ this.raceFrame = frame;
1690
+ this.notify(false);
1691
+ return { ok: true };
1692
+ }
1693
+ /**
1694
+ * Plays the bar race from the moment shown (from the start when it is at the end), moving
1695
+ * one time point every `msPerStep` (default 1000) until the last. Not an undo step.
1696
+ */
1697
+ playRace(options = {}) {
1698
+ const last = this.spec.categories.length - 1;
1699
+ if (last < 1) return fail("A race needs at least two time points (categories) to play.");
1700
+ this.stopRace();
1701
+ const per = Math.max(50, _nullishCoalesce(options.msPerStep, () => ( 1e3)));
1702
+ const from = this.raceFrame === null || this.raceFrame >= last ? 0 : this.raceFrame;
1703
+ const start = this.raceTicker.now();
1704
+ const step = () => {
1705
+ const at = Math.min(last, from + (this.raceTicker.now() - start) / per);
1706
+ this.raceFrame = at;
1707
+ if (at >= last) {
1708
+ this.raceStop = null;
1709
+ this.raceFrame = null;
1710
+ } else this.raceStop = this.raceTicker.next(step);
1711
+ this.notify(false);
1712
+ };
1713
+ this.raceFrame = from;
1714
+ this.raceStop = this.raceTicker.next(step);
1715
+ this.notify(false);
1716
+ return { ok: true };
1717
+ }
1718
+ /** Pauses bar race playback where it is. */
1719
+ pauseRace() {
1720
+ if (!this.raceStop) return;
1721
+ this.stopRace();
1722
+ this.notify(false);
1723
+ }
1724
+ stopRace() {
1725
+ _optionalChain([this, 'access', _87 => _87.raceStop, 'optionalCall', _88 => _88()]);
1726
+ this.raceStop = null;
1727
+ }
1654
1728
  /** Selects a chart element (view state; not an undo step). */
1655
1729
  select(selection) {
1656
1730
  const next = this.validSelection(selection, this.spec);
1657
- if (_chunkQO2T6KA5cjs.sameChartSelection.call(void 0, next, this.selection) || next === null && this.selection === null) return;
1731
+ if (_chunkUUUTEVX6cjs.sameChartSelection.call(void 0, next, this.selection) || next === null && this.selection === null) return;
1658
1732
  this.selection = next;
1659
1733
  this.notify(false);
1660
1734
  }
@@ -1731,14 +1805,14 @@ var ChartController = (_class = class {
1731
1805
  // -------------------------------------------------------------------------
1732
1806
  /** The spec, for saving (a copy). */
1733
1807
  toJSON() {
1734
- return _chunkUVXTOZ2Bcjs.cloneChartSpec.call(void 0, this.spec);
1808
+ return _chunkBPX4Z3N3cjs.cloneChartSpec.call(void 0, this.spec);
1735
1809
  }
1736
1810
  /**
1737
1811
  * Replaces the chart without an undo step (e.g. when opening a document). Problems are
1738
1812
  * fixed with defaults and reported in `state.issues` (and returned).
1739
1813
  */
1740
1814
  load(input, options = {}) {
1741
- const { spec, issues } = _chunkUVXTOZ2Bcjs.normalizeChartSpec.call(void 0, input);
1815
+ const { spec, issues } = _chunkBPX4Z3N3cjs.normalizeChartSpec.call(void 0, input);
1742
1816
  this.spec = spec;
1743
1817
  this.issues = issues;
1744
1818
  this.selection = this.validSelection(this.selection, spec);
@@ -1758,7 +1832,7 @@ var ChartController = (_class = class {
1758
1832
  * nothing changes and every problem is returned (and shown in `state.issues`).
1759
1833
  */
1760
1834
  replace(input, options = {}) {
1761
- const { spec, issues } = _chunkUVXTOZ2Bcjs.normalizeChartSpec.call(void 0, input);
1835
+ const { spec, issues } = _chunkBPX4Z3N3cjs.normalizeChartSpec.call(void 0, input);
1762
1836
  if (issues.length) return this.reject(issues.map((i) => i.message).join(" "), issues);
1763
1837
  this.commit(_nullishCoalesce(options.label, () => ( "Replace chart")), spec, this.validSelection(this.selection, spec));
1764
1838
  return { ok: true };
@@ -1773,7 +1847,8 @@ var ChartController = (_class = class {
1773
1847
  }
1774
1848
  /** Stops listening to the history (reconnected by the next `subscribe`). */
1775
1849
  destroy() {
1776
- _optionalChain([this, 'access', _87 => _87.unsubscribeHistory, 'optionalCall', _88 => _88()]);
1850
+ this.stopRace();
1851
+ _optionalChain([this, 'access', _89 => _89.unsubscribeHistory, 'optionalCall', _90 => _90()]);
1777
1852
  this.unsubscribeHistory = void 0;
1778
1853
  this.listeners.clear();
1779
1854
  }
@@ -1808,6 +1883,7 @@ function updateLabel(patch) {
1808
1883
  parliamentArc: "Change hemicycle sweep",
1809
1884
  parliamentMajority: "Change majority",
1810
1885
  parliamentLabel: "Edit seat caption",
1886
+ raceTopN: "Change bars shown",
1811
1887
  sunburstLabel: "Edit centre caption",
1812
1888
  tree: "Replace grouped data",
1813
1889
  links: "Replace flows",
@@ -1835,7 +1911,7 @@ function layoutSparkline(values, options) {
1835
1911
  const h = Number.isFinite(options.height) ? Math.max(1, options.height) : 1;
1836
1912
  const color = _nullishCoalesce(options.color, () => ( "#4269d0"));
1837
1913
  const negative = _nullishCoalesce(options.negativeColor, () => ( "#e5484d"));
1838
- const theme = { ..._chunkUVXTOZ2Bcjs.DEFAULT_CHART_THEME, background: "transparent" };
1914
+ const theme = { ..._chunkBPX4Z3N3cjs.DEFAULT_CHART_THEME, background: "transparent" };
1839
1915
  const marks = [];
1840
1916
  const scene = { width: w, height: h, plot: { x: 0, y: 0, width: w, height: h }, marks, percent: false, theme, warnings: [] };
1841
1917
  const vals = values.slice(0, MAX_POINTS).map((v) => v !== null && Number.isFinite(v) ? Math.max(-1e300, Math.min(1e300, v)) : null);
@@ -1868,7 +1944,7 @@ function layoutSparkline(values, options) {
1868
1944
  const flush = () => {
1869
1945
  if (run.length >= 2) {
1870
1946
  const pts = run.length > 4e3 ? run.filter((_, i) => i % (Math.floor(run.length / 2e3) + 1) === 0) : run;
1871
- d += pts.map(([x, y], i) => `${i ? "L" : "M"}${_chunkQO2T6KA5cjs.r2.call(void 0, x)},${_chunkQO2T6KA5cjs.r2.call(void 0, y)}`).join("");
1947
+ d += pts.map(([x, y], i) => `${i ? "L" : "M"}${_chunkUUUTEVX6cjs.r2.call(void 0, x)},${_chunkUUUTEVX6cjs.r2.call(void 0, y)}`).join("");
1872
1948
  }
1873
1949
  run = [];
1874
1950
  };