@pixerate/editor 0.164.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 (146) hide show
  1. package/dist/agent/index.cjs +54 -39
  2. package/dist/agent/index.js +24 -9
  3. package/dist/animation/index.cjs +6 -5
  4. package/dist/animation/index.js +5 -4
  5. package/dist/animation-editor/index.cjs +65 -64
  6. package/dist/animation-editor/index.js +8 -7
  7. package/dist/bridge/cli.cjs +3 -3
  8. package/dist/bridge/cli.js +2 -2
  9. package/dist/bridge/index.cjs +3 -3
  10. package/dist/bridge/index.js +2 -2
  11. package/dist/canvas/index.cjs +89 -87
  12. package/dist/canvas/index.js +11 -9
  13. package/dist/chart/controller.d.cts +60 -2
  14. package/dist/chart/controller.d.ts +60 -2
  15. package/dist/chart/index.cjs +657 -163
  16. package/dist/chart/index.d.cts +3 -0
  17. package/dist/chart/index.d.ts +3 -0
  18. package/dist/chart/index.js +512 -18
  19. package/dist/chart/layout-families.d.cts +14 -0
  20. package/dist/chart/layout-families.d.ts +14 -0
  21. package/dist/chart/layout-shared.d.cts +5 -0
  22. package/dist/chart/layout-shared.d.ts +5 -0
  23. package/dist/chart/links.d.cts +34 -0
  24. package/dist/chart/links.d.ts +34 -0
  25. package/dist/chart/mermaid.d.cts +9 -6
  26. package/dist/chart/mermaid.d.ts +9 -6
  27. package/dist/chart/race.d.cts +26 -0
  28. package/dist/chart/race.d.ts +26 -0
  29. package/dist/chart/schemas.d.cts +5 -0
  30. package/dist/chart/schemas.d.ts +5 -0
  31. package/dist/chart/spec.d.cts +3 -2
  32. package/dist/chart/spec.d.ts +3 -2
  33. package/dist/chart/style.d.cts +33 -0
  34. package/dist/chart/style.d.ts +33 -0
  35. package/dist/chart/types.d.cts +28 -2
  36. package/dist/chart/types.d.ts +28 -2
  37. package/dist/{chunk-ZVNAMTFO.cjs → chunk-3FSUJIXN.cjs} +18 -174
  38. package/dist/{chunk-RQCMSEM5.cjs → chunk-4QUUHTGJ.cjs} +2 -2
  39. package/dist/{chunk-CIPIESX4.cjs → chunk-4ZC5QJE2.cjs} +3 -3
  40. package/dist/{chunk-S6X2QRVU.cjs → chunk-5UEFBM2M.cjs} +1 -138
  41. package/dist/{chunk-5FNGLV2D.js → chunk-5UNQDC3Q.js} +18 -3
  42. package/dist/{chunk-YSAGBIRW.cjs → chunk-6FYIFGBW.cjs} +16 -16
  43. package/dist/{chunk-QYZGBVNC.js → chunk-7272D3K2.js} +10 -8
  44. package/dist/{chunk-462WGGXX.cjs → chunk-AMX23CSN.cjs} +27 -16
  45. package/dist/{chunk-3VLOOO3G.js → chunk-BDIM6NVW.js} +1 -1
  46. package/dist/{chunk-R3P2M7HU.cjs → chunk-BPX4Z3N3.cjs} +355 -207
  47. package/dist/{chunk-D54JHQK2.cjs → chunk-CHLV7UFP.cjs} +51 -51
  48. package/dist/{chunk-OXERWBKE.js → chunk-DFCAR7AW.js} +3 -3
  49. package/dist/{chunk-GZ2FM3ZB.js → chunk-DKNEQX7T.js} +6 -162
  50. package/dist/{chunk-JCXMJUQO.cjs → chunk-EGZM52EO.cjs} +3 -3
  51. package/dist/{chunk-ES3UEVUT.cjs → chunk-EUIDTZNQ.cjs} +112 -110
  52. package/dist/{chunk-H3OEM3Q2.js → chunk-F6R67I3A.js} +1 -1
  53. package/dist/{chunk-3NEZJCBY.js → chunk-FR432ROA.js} +349 -201
  54. package/dist/chunk-HIDFL5LQ.js +484 -0
  55. package/dist/{chunk-22KLZZGH.cjs → chunk-HXOBOHH2.cjs} +8 -8
  56. package/dist/{chunk-MKTDELCD.js → chunk-I5MRJW7B.js} +1 -1
  57. package/dist/{chunk-SDHZG5TF.js → chunk-ILP77UD6.js} +160 -2
  58. package/dist/{chunk-Z4P2RBMJ.js → chunk-ITANXMZD.js} +177 -3
  59. package/dist/chunk-KGRZMHZB.js +204 -0
  60. package/dist/{chunk-SET6KJOP.js → chunk-LPIKXPBM.js} +1 -1
  61. package/dist/{chunk-BQWGN5WH.js → chunk-M2TPMYJE.js} +1 -1
  62. package/dist/{chunk-GTELFJRE.cjs → chunk-M5GLQQWG.cjs} +2 -2
  63. package/dist/{chunk-4VU4CP7R.js → chunk-MHLU6GRJ.js} +1 -138
  64. package/dist/{chunk-NZD2MZJC.cjs → chunk-MZFZSVUJ.cjs} +38 -23
  65. package/dist/{chunk-DAPQGA55.js → chunk-NQQDGGXY.js} +2 -2
  66. package/dist/chunk-O6OGNDND.js +164 -0
  67. package/dist/{chunk-PA3IMPPS.cjs → chunk-OVYQAYGH.cjs} +81 -81
  68. package/dist/{chunk-YXOW456B.js → chunk-PPEDZ5WR.js} +1 -1
  69. package/dist/{chunk-24M7VD6M.cjs → chunk-S4EIAIEW.cjs} +7 -7
  70. package/dist/{chunk-M4MIZPQI.cjs → chunk-SJ2CPTQC.cjs} +189 -15
  71. package/dist/{chunk-YGG7WDWT.cjs → chunk-SZWVMB3B.cjs} +24 -18
  72. package/dist/chunk-TFF2GTGN.cjs +153 -0
  73. package/dist/chunk-TTCJPX4U.cjs +204 -0
  74. package/dist/{chunk-SBQVEBSU.cjs → chunk-UUUTEVX6.cjs} +242 -68
  75. package/dist/{chunk-WI7QJA7A.cjs → chunk-UXETV2NC.cjs} +161 -3
  76. package/dist/{chunk-GKNEJ6EN.cjs → chunk-V5L2G22R.cjs} +321 -107
  77. package/dist/{chunk-TALQONZ5.js → chunk-VB2CY6EG.js} +3 -3
  78. package/dist/{chunk-QY4KUDMY.js → chunk-VRXXAXUT.js} +2 -2
  79. package/dist/chunk-VU6WQSMB.cjs +164 -0
  80. package/dist/{chunk-NHLYK3AX.js → chunk-XBQX76CT.js} +182 -8
  81. package/dist/{chunk-5AARIS3R.cjs → chunk-XCDGM7TD.cjs} +37 -37
  82. package/dist/{chunk-POR54B55.js → chunk-XNU45TYA.js} +7 -1
  83. package/dist/{chunk-EVXQNQND.js → chunk-XZB3TJ6K.js} +16 -5
  84. package/dist/chunk-YBAPT7NL.js +153 -0
  85. package/dist/{chunk-SZA42R4N.js → chunk-YUFTVNLX.js} +230 -16
  86. package/dist/chunk-Z2UQYMPS.cjs +484 -0
  87. package/dist/color/index.cjs +5 -3
  88. package/dist/color/index.js +12 -10
  89. package/dist/diagram/index.cjs +55 -243
  90. package/dist/diagram/index.js +12 -200
  91. package/dist/export/index.cjs +7 -6
  92. package/dist/export/index.js +6 -5
  93. package/dist/extensions/index.cjs +12 -9
  94. package/dist/extensions/index.js +11 -8
  95. package/dist/gradient/index.cjs +29 -27
  96. package/dist/gradient/index.js +6 -4
  97. package/dist/image-editor/index.cjs +644 -643
  98. package/dist/image-editor/index.js +7 -6
  99. package/dist/index.cjs +21 -18
  100. package/dist/index.js +14 -11
  101. package/dist/paint-editor/index.cjs +19 -18
  102. package/dist/paint-editor/index.js +7 -6
  103. package/dist/parameters/index.cjs +2 -2
  104. package/dist/parameters/index.js +1 -1
  105. package/dist/persistence/assetWrites.d.cts +22 -0
  106. package/dist/persistence/assetWrites.d.ts +22 -0
  107. package/dist/persistence/assets.d.cts +71 -0
  108. package/dist/persistence/assets.d.ts +71 -0
  109. package/dist/persistence/documentStore.d.cts +17 -1
  110. package/dist/persistence/documentStore.d.ts +17 -1
  111. package/dist/persistence/documentTools.d.cts +5 -1
  112. package/dist/persistence/documentTools.d.ts +5 -1
  113. package/dist/persistence/editorAutosave.d.cts +7 -0
  114. package/dist/persistence/editorAutosave.d.ts +7 -0
  115. package/dist/persistence/index.cjs +141 -21
  116. package/dist/persistence/index.js +130 -10
  117. package/dist/persistence/storage.d.cts +13 -1
  118. package/dist/persistence/storage.d.ts +13 -1
  119. package/dist/spreadsheet/index.cjs +3 -3
  120. package/dist/spreadsheet/index.js +2 -2
  121. package/dist/tokens/index.cjs +11 -3
  122. package/dist/tokens/index.d.cts +1 -0
  123. package/dist/tokens/index.d.ts +1 -0
  124. package/dist/tokens/index.js +10 -2
  125. package/dist/tokens/style.d.cts +36 -0
  126. package/dist/tokens/style.d.ts +36 -0
  127. package/dist/tools/index.cjs +15 -3
  128. package/dist/tools/index.d.cts +2 -0
  129. package/dist/tools/index.d.ts +2 -0
  130. package/dist/tools/index.js +14 -2
  131. package/dist/tools/style-tools.d.cts +19 -0
  132. package/dist/tools/style-tools.d.ts +19 -0
  133. package/dist/tools/style.d.cts +212 -0
  134. package/dist/tools/style.d.ts +212 -0
  135. package/dist/tools/view.d.cts +6 -0
  136. package/dist/tools/view.d.ts +6 -0
  137. package/dist/typography/index.cjs +8 -6
  138. package/dist/typography/index.js +5 -3
  139. package/dist/vector/index.cjs +953 -952
  140. package/dist/vector/index.js +7 -6
  141. package/dist/video-editor/index.cjs +8 -7
  142. package/dist/video-editor/index.js +6 -5
  143. package/package.json +1 -1
  144. package/skills/pixerate-chart/SKILL.md +20 -2
  145. package/dist/chunk-ERAGQL2N.cjs +0 -68
  146. package/dist/chunk-KMU6QJB7.js +0 -68
@@ -1,10 +1,12 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunkES3UEVUTcjs = require('./chunk-ES3UEVUT.cjs');
3
+ var _chunkEUIDTZNQcjs = require('./chunk-EUIDTZNQ.cjs');
4
4
 
5
5
 
6
+ var _chunkTFF2GTGNcjs = require('./chunk-TFF2GTGN.cjs');
6
7
 
7
- var _chunkS6X2QRVUcjs = require('./chunk-S6X2QRVU.cjs');
8
+
9
+ var _chunk5UEFBM2Mcjs = require('./chunk-5UEFBM2M.cjs');
8
10
 
9
11
  // src/chart/color.ts
10
12
  var NAMED = {
@@ -81,18 +83,18 @@ function chartTextOn(fill, fallback, dark = "#0f172a", light = "#ffffff") {
81
83
  return darkRatio >= lightRatio ? dark : light;
82
84
  }
83
85
  function chartRgbToOklab(rgb) {
84
- const lab = _chunkS6X2QRVUcjs.rgbToOklab.call(void 0, { r: rgb[0], g: rgb[1], b: rgb[2], a: 1 });
86
+ const lab = _chunk5UEFBM2Mcjs.rgbToOklab.call(void 0, { r: rgb[0], g: rgb[1], b: rgb[2], a: 1 });
85
87
  return [lab.l, lab.a, lab.b];
86
88
  }
87
89
  function chartColorDistance(a, b) {
88
- return _chunkS6X2QRVUcjs.deltaEOK.call(void 0,
90
+ return _chunkTFF2GTGNcjs.deltaEOK.call(void 0,
89
91
  { r: a[0], g: a[1], b: a[2], a: 1 },
90
92
  { r: b[0], g: b[1], b: b[2], a: 1 }
91
93
  );
92
94
  }
93
95
  function simulateChartVision(rgb, vision) {
94
96
  if (vision === "normal") return rgb;
95
- const c = _chunkES3UEVUTcjs.simulateColorVision.call(void 0, { r: rgb[0], g: rgb[1], b: rgb[2], a: 1 }, vision);
97
+ const c = _chunkEUIDTZNQcjs.simulateColorVision.call(void 0, { r: rgb[0], g: rgb[1], b: rgb[2], a: 1 }, vision);
96
98
  return [c.r, c.g, c.b];
97
99
  }
98
100
  function checkChartColors(colors, options = {}) {
@@ -223,8 +225,208 @@ function parseChartAmountList(text2) {
223
225
  return text2.split(/[;,\s]+/).filter((t) => t !== "").map((t) => t === "-" ? null : t);
224
226
  }
225
227
 
228
+ // src/chart/tree.ts
229
+ var MAX_CHART_TREE_DEPTH = 6;
230
+ var MAX_CHART_TREE_NODES = 5e3;
231
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
232
+ function normalizeChartTree(value, issues = [], path = "tree") {
233
+ if (value === void 0 || value === null) return [];
234
+ if (!Array.isArray(value)) {
235
+ issues.push({ path, message: `${path} must be an array of nodes such as {"name": "Europe", "children": [{"name": "France", "value": 40}]}.` });
236
+ return [];
237
+ }
238
+ let count = 0;
239
+ const read = (list, at, depth) => {
240
+ const out = [];
241
+ const names = /* @__PURE__ */ new Set();
242
+ list.forEach((raw, i) => {
243
+ const where = `${at}[${i}]`;
244
+ if (!isRecord(raw)) {
245
+ issues.push({ path: where, message: `${where} must be an object with a name and a value or children.` });
246
+ return;
247
+ }
248
+ const name = typeof raw.name === "number" ? String(raw.name) : typeof raw.name === "string" ? raw.name.trim() : "";
249
+ if (!name) {
250
+ issues.push({ path: `${where}.name`, message: `${where} needs a name.` });
251
+ return;
252
+ }
253
+ if (names.has(name)) {
254
+ issues.push({ path: `${where}.name`, message: `"${name}" appears twice under the same group in ${at}; sibling names must be unique, so the second was left out.` });
255
+ return;
256
+ }
257
+ if (++count > MAX_CHART_TREE_NODES) {
258
+ if (count === MAX_CHART_TREE_NODES + 1) issues.push({ path, message: `The tree has more than ${MAX_CHART_TREE_NODES} nodes; the rest were left out.` });
259
+ return;
260
+ }
261
+ names.add(name);
262
+ for (const k of Object.keys(raw)) if (!["name", "value", "children", "color"].includes(k)) issues.push({ path: `${where}.${k}`, message: `${where} has no "${k}"; a node takes name, value, children and color.` });
263
+ const node = { name };
264
+ if (typeof raw.color === "string" && raw.color.trim()) node.color = raw.color.trim();
265
+ if (Array.isArray(raw.children) && raw.children.length) {
266
+ if (depth >= MAX_CHART_TREE_DEPTH) {
267
+ issues.push({ path: `${where}.children`, message: `${where} is more than ${MAX_CHART_TREE_DEPTH} levels deep; its children were left out.` });
268
+ node.value = 0;
269
+ } else {
270
+ if (raw.value !== void 0 && raw.value !== null) issues.push({ path: `${where}.value`, message: `"${name}" is a group: its value is the sum of its children, so the value given was ignored.` });
271
+ const children = read(raw.children, `${where}.children`, depth + 1);
272
+ if (children.length) node.children = children;
273
+ else node.value = 0;
274
+ }
275
+ } else {
276
+ if (raw.children !== void 0 && raw.children !== null && !Array.isArray(raw.children)) issues.push({ path: `${where}.children`, message: `${where}.children must be an array of nodes.` });
277
+ const v = parseChartValue(raw.value);
278
+ if (raw.value === void 0 || raw.value === null) node.value = 0;
279
+ else if (typeof v === "number" && v >= 0) node.value = v;
280
+ else {
281
+ issues.push({ path: `${where}.value`, message: `"${name}" needs a value of 0 or more; got ${JSON.stringify(raw.value)}.` });
282
+ node.value = 0;
283
+ }
284
+ }
285
+ out.push(node);
286
+ });
287
+ return out;
288
+ };
289
+ return read(value, path, 1);
290
+ }
291
+ function chartTreeTotal(node) {
292
+ return node.children ? node.children.reduce((t, c) => t + chartTreeTotal(c), 0) : _nullishCoalesce(node.value, () => ( 0));
293
+ }
294
+ function chartTreeDepth(tree) {
295
+ return tree.reduce((d, n) => Math.max(d, 1 + (n.children ? chartTreeDepth(n.children) : 0)), 0);
296
+ }
297
+ function findChartTreeNode(tree, path) {
298
+ let list = tree;
299
+ let node;
300
+ for (const name of path) {
301
+ node = _optionalChain([list, 'optionalAccess', _10 => _10.find, 'call', _11 => _11((n) => n.name === name)]);
302
+ if (!node) return void 0;
303
+ list = node.children;
304
+ }
305
+ return node;
306
+ }
307
+ function chartTreeToRows(tree) {
308
+ const out = [];
309
+ const walk = (list, prefix) => {
310
+ for (const n of list) {
311
+ if (n.children) walk(n.children, [...prefix, n.name]);
312
+ else out.push({ path: [...prefix, n.name], value: _nullishCoalesce(n.value, () => ( 0)) });
313
+ }
314
+ };
315
+ walk(tree, []);
316
+ return out;
317
+ }
318
+ function chartTreeFromRows(rows, issues = []) {
319
+ const tree = [];
320
+ rows.forEach((row, r) => {
321
+ const path = [];
322
+ for (const cell of row.path) {
323
+ const name = cell === null || cell === void 0 ? "" : String(cell).trim();
324
+ if (!name) break;
325
+ path.push(name);
326
+ }
327
+ if (!path.length) return;
328
+ if (path.length > MAX_CHART_TREE_DEPTH) {
329
+ issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} is ${path.length} levels deep; the tree keeps at most ${MAX_CHART_TREE_DEPTH}, so the row was left out.` });
330
+ return;
331
+ }
332
+ const v = parseChartValue(row.value);
333
+ if (row.value !== null && row.value !== void 0 && String(row.value).trim() !== "" && !(typeof v === "number" && v >= 0)) {
334
+ issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} (${path.join(" / ")}) needs a value of 0 or more; got ${JSON.stringify(row.value)}. It was counted as 0.` });
335
+ }
336
+ const value = typeof v === "number" && v >= 0 ? v : 0;
337
+ let list = tree;
338
+ for (let k = 0; k < path.length; k++) {
339
+ const last = k === path.length - 1;
340
+ let node = list.find((n) => n.name === path[k]);
341
+ if (!node) {
342
+ node = last ? { name: path[k], value } : { name: path[k], children: [] };
343
+ list.push(node);
344
+ if (last) return;
345
+ } else if (last) {
346
+ issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} (${path.join(" / ")}) ${node.children ? "names a group that already has items" : "repeats an earlier row"}; it was left out.` });
347
+ return;
348
+ } else if (!node.children) {
349
+ issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} puts items under "${path.slice(0, k + 1).join(" / ")}", which an earlier row made an item with its own value; it was left out.` });
350
+ return;
351
+ }
352
+ list = node.children;
353
+ }
354
+ });
355
+ return tree;
356
+ }
357
+ function chartTreeFromTable(rows, options = {}, issues = []) {
358
+ const grid = rows.filter((r) => r.some((c) => c !== null && c !== void 0 && String(c).trim() !== ""));
359
+ if (!grid.length) return [];
360
+ const width = Math.max(...grid.map((r) => r.length));
361
+ if (width < 2) {
362
+ issues.push({ path: "rows", message: "A tree table needs at least two columns: names, then values." });
363
+ return [];
364
+ }
365
+ const header = _nullishCoalesce(options.header, () => ( "auto"));
366
+ const skip = header === true || header === "auto" && typeof parseChartValue(grid[0][width - 1]) !== "number";
367
+ const body = skip ? grid.slice(1) : grid;
368
+ let above = [];
369
+ return chartTreeFromRows(
370
+ body.map((r) => {
371
+ const cells = Array.from({ length: width - 1 }, (_, k) => r[k] === null || r[k] === void 0 ? "" : String(r[k]).trim());
372
+ const first = cells.findIndex((c) => c !== "");
373
+ if (first < 0) return { path: [], value: r[width - 1] };
374
+ const own = [];
375
+ for (const c of cells.slice(first)) {
376
+ if (!c) break;
377
+ own.push(c);
378
+ }
379
+ const path = [...above.slice(0, first), ...own];
380
+ above = path;
381
+ return { path, value: r[width - 1] };
382
+ }),
383
+ issues
384
+ );
385
+ }
386
+ function chartTreeFromSeries(categories, values) {
387
+ const seen = /* @__PURE__ */ new Set();
388
+ const out = [];
389
+ categories.forEach((c, i) => {
390
+ const name = c.trim() || `${i + 1}`;
391
+ if (seen.has(name)) return;
392
+ seen.add(name);
393
+ const v = values[i];
394
+ out.push({ name, value: v !== null && v !== void 0 && Number.isFinite(v) && v > 0 ? v : 0 });
395
+ });
396
+ return out;
397
+ }
398
+ function cloneChartTree(tree) {
399
+ return tree.map((n) => ({ ...n, ...n.children ? { children: cloneChartTree(n.children) } : {} }));
400
+ }
401
+ function chartTreeColumnLabels(depth) {
402
+ if (depth <= 1) return ["Item"];
403
+ return Array.from({ length: depth }, (_, k) => k === 0 ? "Group" : k === depth - 1 ? "Item" : `Level ${k + 1}`);
404
+ }
405
+ function setChartTreeRowName(rows, row, level, name) {
406
+ return rows.map((r, i) => {
407
+ if (i !== row) return { path: [...r.path], value: r.value };
408
+ const path = [...r.path];
409
+ while (path.length <= level) path.push("");
410
+ path[level] = name.trim();
411
+ return { path, value: r.value };
412
+ });
413
+ }
414
+ function setChartTreeRowValue(rows, row, value) {
415
+ return rows.map((r, i) => ({ path: [...r.path], value: i === row ? value : r.value }));
416
+ }
417
+ function addChartTreeRow(rows) {
418
+ const parent = rows.length ? rows[rows.length - 1].path.slice(0, -1) : [];
419
+ const taken = new Set(rows.filter((r) => r.path.length === parent.length + 1 && parent.every((p, k) => r.path[k] === p)).map((r) => r.path[parent.length]));
420
+ let n = rows.length + 1;
421
+ while (taken.has(`Item ${n}`)) n++;
422
+ return [...rows.map((r) => ({ path: [...r.path], value: r.value })), { path: [...parent, `Item ${n}`], value: 0 }];
423
+ }
424
+ function removeChartTreeRow(rows, row) {
425
+ return rows.filter((_, i) => i !== row).map((r) => ({ path: [...r.path], value: r.value }));
426
+ }
427
+
226
428
  // src/chart/spec.ts
227
- var CHART_TYPES = ["bar", "line", "area", "scatter", "bubble", "pie", "donut", "radar", "waterfall", "histogram", "box", "funnel", "treemap", "sunburst", "slope", "likert", "parliament"];
429
+ var CHART_TYPES = ["bar", "line", "area", "scatter", "bubble", "pie", "donut", "radar", "waterfall", "histogram", "box", "funnel", "treemap", "sunburst", "sankey", "slope", "likert", "parliament", "barrace"];
228
430
  var CHART_MARKERS = ["circle", "square", "diamond", "triangle", "cross", "none"];
229
431
  var CHART_TRENDLINE_TYPES = ["linear", "exponential", "logarithmic", "power", "polynomial", "moving-average"];
230
432
  var CHART_LABEL_POSITIONS = ["auto", "inside", "outside", "center"];
@@ -236,6 +438,7 @@ var CHART_CURVES = ["linear", "smooth", "step"];
236
438
  var CHART_RADAR_GRATICULES = ["polygon", "circle"];
237
439
  var CHART_LIKERT_NEUTRALS = ["split", "separate"];
238
440
  var CHART_LIKERT_SORTS = ["none", "positive", "negative"];
441
+ var CHART_SANKEY_LINK_COLORS = ["source", "target", "gray"];
239
442
  var CHART_REFERENCE_AXES = ["y", "y2", "x"];
240
443
  var CHART_ORIENTATIONS = ["vertical", "horizontal"];
241
444
  var CHART_LEGEND_POSITIONS = ["top", "bottom", "left", "right"];
@@ -257,7 +460,9 @@ var CHART_TYPE_LABELS = {
257
460
  slope: "Slope",
258
461
  likert: "Likert",
259
462
  parliament: "Parliament",
260
- sunburst: "Sunburst"
463
+ sunburst: "Sunburst",
464
+ sankey: "Sankey",
465
+ barrace: "Bar race"
261
466
  };
262
467
  var DEFAULT_CHART_PALETTE = ["#4269d0", "#efb118", "#ff725c", "#6cc5b0", "#3ca951", "#ff8ab7", "#a463f2", "#97bbf5", "#9c6b4e", "#9498a0"];
263
468
  var DEFAULT_CHART_THEME = {
@@ -308,6 +513,8 @@ function chartFamily(spec) {
308
513
  case "likert":
309
514
  case "parliament":
310
515
  case "sunburst":
516
+ case "sankey":
517
+ case "barrace":
311
518
  return spec.type;
312
519
  case "bubble":
313
520
  return "xy";
@@ -316,7 +523,7 @@ function chartFamily(spec) {
316
523
  }
317
524
  }
318
525
  function chartColorsByCategory(type) {
319
- return type === "pie" || type === "donut" || type === "funnel" || type === "treemap" || type === "slope" || type === "parliament" || type === "sunburst";
526
+ return type === "pie" || type === "donut" || type === "funnel" || type === "treemap" || type === "slope" || type === "parliament" || type === "sunburst" || type === "sankey";
320
527
  }
321
528
  function chartSpecColorsByCategory(spec) {
322
529
  return chartColorsByCategory(spec.type) || chartVariesColors(spec);
@@ -329,7 +536,7 @@ function createChartSpec(partial = {}) {
329
536
  }
330
537
  function chartSeriesColor(spec, seriesIndex) {
331
538
  const series = spec.series[seriesIndex];
332
- if (_optionalChain([series, 'optionalAccess', _10 => _10.color])) return series.color;
539
+ if (_optionalChain([series, 'optionalAccess', _12 => _12.color])) return series.color;
333
540
  if (spec.type === "likert") return chartLikertColor(spec.series.length, seriesIndex);
334
541
  return cyclePaletteColor(spec.palette.length ? spec.palette : DEFAULT_CHART_PALETTE, seriesIndex);
335
542
  }
@@ -352,7 +559,7 @@ function chartVariesColors(spec) {
352
559
  return !!spec.varyColors && isCartesianChart(spec.type) && spec.series.filter((s) => !s.hidden).length === 1;
353
560
  }
354
561
  function chartPointColor(spec, seriesIndex, index) {
355
- const own = _optionalChain([spec, 'access', _11 => _11.series, 'access', _12 => _12[seriesIndex], 'optionalAccess', _13 => _13.pointColors, 'optionalAccess', _14 => _14[index]]);
562
+ const own = _optionalChain([spec, 'access', _13 => _13.series, 'access', _14 => _14[seriesIndex], 'optionalAccess', _15 => _15.pointColors, 'optionalAccess', _16 => _16[index]]);
356
563
  if (own) return own;
357
564
  if (spec.type && chartVariesColors(spec)) return chartSliceColor(spec, index);
358
565
  return chartSeriesColor(spec, seriesIndex);
@@ -390,7 +597,7 @@ function parseChartValue(input) {
390
597
  if (negative) value = -value;
391
598
  return Number.isFinite(value) ? value : void 0;
392
599
  }
393
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
600
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
394
601
  function pick(value, options, fallback, path, issues) {
395
602
  if (value === void 0) return fallback;
396
603
  if (typeof value === "string" && options.includes(value)) return value;
@@ -419,7 +626,7 @@ function bound(value, path, issues) {
419
626
  }
420
627
  function normalizeFormat(value, path, issues) {
421
628
  if (value === void 0 || value === null) return {};
422
- if (!isRecord(value)) {
629
+ if (!isRecord2(value)) {
423
630
  issues.push({ path, message: `${path} must be an object.` });
424
631
  return {};
425
632
  }
@@ -437,7 +644,7 @@ function normalizeFormat(value, path, issues) {
437
644
  }
438
645
  function normalizeChartAxis(value, fallback, path, issues = []) {
439
646
  if (value === void 0) return { ...fallback, format: { ...fallback.format } };
440
- if (!isRecord(value)) {
647
+ if (!isRecord2(value)) {
441
648
  issues.push({ path, message: `${path} must be an object.` });
442
649
  return { ...fallback, format: { ...fallback.format } };
443
650
  }
@@ -487,7 +694,7 @@ function normalizeChartAxis(value, fallback, path, issues = []) {
487
694
  function normalizeLegend(value, issues) {
488
695
  const fallback = { show: true, position: "bottom" };
489
696
  if (value === void 0) return fallback;
490
- if (!isRecord(value)) {
697
+ if (!isRecord2(value)) {
491
698
  issues.push({ path: "legend", message: "legend must be an object." });
492
699
  return fallback;
493
700
  }
@@ -517,7 +724,7 @@ function normalizeChartValues(value, length, path, issues = []) {
517
724
  }
518
725
  function normalizeSeries(value, length, used, index, issues) {
519
726
  const path = `series[${index}]`;
520
- if (!isRecord(value)) {
727
+ if (!isRecord2(value)) {
521
728
  issues.push({ path, message: `${path} must be an object with a name and values.` });
522
729
  return null;
523
730
  }
@@ -544,7 +751,7 @@ function normalizeSeries(value, length, used, index, issues) {
544
751
  const list = normalizeChartValues(value[key2], length, `${path}.${key2}`, issues);
545
752
  if (list.some((v) => v !== null)) series[key2] = list;
546
753
  }
547
- if (key(value, "sizes") && _optionalChain([series, 'access', _15 => _15.sizes, 'optionalAccess', _16 => _16.some, 'call', _17 => _17((v) => v !== null && v < 0)])) {
754
+ if (key(value, "sizes") && _optionalChain([series, 'access', _17 => _17.sizes, 'optionalAccess', _18 => _18.some, 'call', _19 => _19((v) => v !== null && v < 0)])) {
548
755
  issues.push({ path: `${path}.sizes`, message: `${path}.sizes must not be negative.` });
549
756
  series.sizes = series.sizes.map((v) => v !== null && v < 0 ? null : v);
550
757
  }
@@ -607,7 +814,7 @@ var finiteNumber = (value) => {
607
814
  return typeof n === "number" && Number.isFinite(n) ? n : null;
608
815
  };
609
816
  function normalizeChartReferenceLine(value, path, issues = []) {
610
- if (!isRecord(value)) {
817
+ if (!isRecord2(value)) {
611
818
  issues.push({ path, message: `${path} must be an object with a value, e.g. {"value": 100, "label": "Target"}.` });
612
819
  return null;
613
820
  }
@@ -620,7 +827,7 @@ function normalizeChartReferenceLine(value, path, issues = []) {
620
827
  return { value: at, ...referenceCommon(value, path, issues) };
621
828
  }
622
829
  function normalizeChartReferenceBand(value, path, issues = []) {
623
- if (!isRecord(value)) {
830
+ if (!isRecord2(value)) {
624
831
  issues.push({ path, message: `${path} must be an object with from and to, e.g. {"from": 80, "to": 120, "label": "Target range"}.` });
625
832
  return null;
626
833
  }
@@ -634,7 +841,7 @@ function normalizeChartReferenceBand(value, path, issues = []) {
634
841
  return { from: Math.min(from, to), to: Math.max(from, to), ...referenceCommon(value, path, issues) };
635
842
  }
636
843
  function normalizeChartErrorBars(value, length, path, issues = []) {
637
- if (!isRecord(value)) {
844
+ if (!isRecord2(value)) {
638
845
  issues.push({ path, message: `${path} must be an object with a type (${CHART_ERROR_BAR_TYPES.join(", ")}), or null for none.` });
639
846
  return null;
640
847
  }
@@ -667,7 +874,7 @@ function normalizeChartErrorBars(value, length, path, issues = []) {
667
874
  return issues.length > before ? null : bars;
668
875
  }
669
876
  function normalizeChartTrendline(value, path, issues = []) {
670
- if (!isRecord(value)) {
877
+ if (!isRecord2(value)) {
671
878
  issues.push({ path, message: `${path} must be an object with a type.` });
672
879
  return null;
673
880
  }
@@ -706,8 +913,8 @@ function normalizeChartTrendline(value, path, issues = []) {
706
913
  }
707
914
  function normalizeChartSpec(input) {
708
915
  const issues = [];
709
- const value = isRecord(input) ? input : {};
710
- if (input !== void 0 && !isRecord(input)) issues.push({ path: "", message: "A chart must be an object." });
916
+ const value = isRecord2(input) ? input : {};
917
+ if (input !== void 0 && !isRecord2(input)) issues.push({ path: "", message: "A chart must be an object." });
711
918
  const type = pick(value.type, CHART_TYPES, "bar", "type", issues);
712
919
  const rawSeries = Array.isArray(value.series) ? value.series : [];
713
920
  if (value.series !== void 0 && !Array.isArray(value.series)) issues.push({ path: "series", message: "series must be an array." });
@@ -715,16 +922,21 @@ function normalizeChartSpec(input) {
715
922
  if (Array.isArray(value.categories)) categories = value.categories.map((c, i) => text(c, "", `categories[${i}]`, issues));
716
923
  else {
717
924
  if (value.categories !== void 0) issues.push({ path: "categories", message: "categories must be an array of labels." });
718
- const longest = rawSeries.reduce((n, s) => Math.max(n, isRecord(s) && Array.isArray(s.values) ? s.values.length : 0), 0);
925
+ const longest = rawSeries.reduce((n, s) => Math.max(n, isRecord2(s) && Array.isArray(s.values) ? s.values.length : 0), 0);
719
926
  categories = Array.from({ length: longest }, (_, i) => `${i + 1}`);
720
927
  }
721
928
  const tree = normalizeChartTree(value.tree, issues);
722
- if (tree.length) categories = tree.map((n) => n.name);
929
+ const links = normalizeChartLinks(value.links, issues);
930
+ const sankeyNodes = chartSankeyNodes(links, Array.isArray(value.sankeyNodes) ? value.sankeyNodes.map(String) : [], issues);
931
+ if (value.sankeyNodes !== void 0 && value.sankeyNodes !== null && !Array.isArray(value.sankeyNodes)) issues.push({ path: "sankeyNodes", message: "sankeyNodes must be an array of node names." });
932
+ if (tree.length && links.length) issues.push({ path: "links", message: "A chart holds either grouped data (tree) or flows (links), not both; the flows were used." });
933
+ if (links.length) categories = [...sankeyNodes];
934
+ else if (tree.length) categories = tree.map((n) => n.name);
723
935
  const used = /* @__PURE__ */ new Set();
724
936
  const series = rawSeries.map((s, i) => normalizeSeries(s, categories.length, used, i, issues)).filter((s) => s !== null);
725
- if (tree.length) {
937
+ if (tree.length || links.length) {
726
938
  if (!series.length) series.push({ id: uniqueChartSeriesId(series), name: "Values", values: [] });
727
- series[0] = { ...series[0], values: tree.map(chartTreeTotal) };
939
+ series[0] = { ...series[0], values: links.length ? chartSankeyNodeValues(links, sankeyNodes) : tree.map(chartTreeTotal) };
728
940
  }
729
941
  let palette = [...DEFAULT_CHART_PALETTE];
730
942
  if (Array.isArray(value.palette)) {
@@ -774,6 +986,12 @@ function normalizeChartSpec(input) {
774
986
  if (Number.isInteger(m) && m >= 1) parliamentMajority = m;
775
987
  else issues.push({ path: "parliamentMajority", message: "parliamentMajority must be a whole number of seats, 1 or more, or null for half plus one." });
776
988
  }
989
+ let raceTopN = 10;
990
+ if (value.raceTopN !== void 0) {
991
+ const n = Number(value.raceTopN);
992
+ if (value.raceTopN !== null && Number.isInteger(n) && n >= 1 && n <= 30) raceTopN = n;
993
+ else issues.push({ path: "raceTopN", message: "raceTopN must be a whole number of bars from 1 to 30." });
994
+ }
777
995
  let radarTicks = null;
778
996
  if (value.radarTicks !== void 0 && value.radarTicks !== null) {
779
997
  const t = Number(value.radarTicks);
@@ -815,8 +1033,12 @@ function normalizeChartSpec(input) {
815
1033
  parliamentArc: fraction("parliamentArc", 180, 90, 360),
816
1034
  parliamentMajority,
817
1035
  parliamentLabel: text(value.parliamentLabel, "", "parliamentLabel", issues),
818
- tree,
819
- sunburstLabel: text(value.sunburstLabel, "", "sunburstLabel", issues)
1036
+ tree: links.length ? [] : tree,
1037
+ sunburstLabel: text(value.sunburstLabel, "", "sunburstLabel", issues),
1038
+ links,
1039
+ sankeyNodes: links.length ? sankeyNodes : [],
1040
+ sankeyLinkColor: pick(value.sankeyLinkColor, CHART_SANKEY_LINK_COLORS, "source", "sankeyLinkColor", issues),
1041
+ raceTopN
820
1042
  };
821
1043
  if (spec.stacking === "percent")
822
1044
  for (const name of ["yAxis", "yAxis2"]) {
@@ -840,7 +1062,9 @@ function cloneChartSpec(spec) {
840
1062
  highlight: [...spec.highlight],
841
1063
  referenceLines: spec.referenceLines.map((l) => ({ ...l })),
842
1064
  referenceBands: spec.referenceBands.map((b) => ({ ...b })),
843
- tree: cloneChartTree(spec.tree)
1065
+ tree: cloneChartTree(spec.tree),
1066
+ links: spec.links.map((l) => ({ ...l })),
1067
+ sankeyNodes: [...spec.sankeyNodes]
844
1068
  };
845
1069
  }
846
1070
  function cloneChartSeries(series) {
@@ -909,204 +1133,120 @@ function chartColorInputValue(color) {
909
1133
  return "#000000";
910
1134
  }
911
1135
 
912
- // src/chart/tree.ts
913
- var MAX_CHART_TREE_DEPTH = 6;
914
- var MAX_CHART_TREE_NODES = 5e3;
915
- var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
916
- function normalizeChartTree(value, issues = [], path = "tree") {
1136
+ // src/chart/links.ts
1137
+ var MAX_CHART_LINKS = 2e3;
1138
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1139
+ var nameOf = (value) => typeof value === "number" ? String(value) : typeof value === "string" ? value.trim() : "";
1140
+ function normalizeChartLinks(value, issues = [], path = "links") {
917
1141
  if (value === void 0 || value === null) return [];
918
1142
  if (!Array.isArray(value)) {
919
- issues.push({ path, message: `${path} must be an array of nodes such as {"name": "Europe", "children": [{"name": "France", "value": 40}]}.` });
1143
+ issues.push({ path, message: `${path} must be an array of flows such as {"source": "Website", "target": "Signup", "value": 120}.` });
920
1144
  return [];
921
1145
  }
922
- let count = 0;
923
- const read = (list, at, depth) => {
924
- const out = [];
925
- const names = /* @__PURE__ */ new Set();
926
- list.forEach((raw, i) => {
927
- const where = `${at}[${i}]`;
928
- if (!isRecord2(raw)) {
929
- issues.push({ path: where, message: `${where} must be an object with a name and a value or children.` });
930
- return;
931
- }
932
- const name = typeof raw.name === "number" ? String(raw.name) : typeof raw.name === "string" ? raw.name.trim() : "";
933
- if (!name) {
934
- issues.push({ path: `${where}.name`, message: `${where} needs a name.` });
935
- return;
936
- }
937
- if (names.has(name)) {
938
- issues.push({ path: `${where}.name`, message: `"${name}" appears twice under the same group in ${at}; sibling names must be unique, so the second was left out.` });
939
- return;
940
- }
941
- if (++count > MAX_CHART_TREE_NODES) {
942
- if (count === MAX_CHART_TREE_NODES + 1) issues.push({ path, message: `The tree has more than ${MAX_CHART_TREE_NODES} nodes; the rest were left out.` });
943
- return;
944
- }
945
- names.add(name);
946
- for (const k of Object.keys(raw)) if (!["name", "value", "children", "color"].includes(k)) issues.push({ path: `${where}.${k}`, message: `${where} has no "${k}"; a node takes name, value, children and color.` });
947
- const node = { name };
948
- if (typeof raw.color === "string" && raw.color.trim()) node.color = raw.color.trim();
949
- if (Array.isArray(raw.children) && raw.children.length) {
950
- if (depth >= MAX_CHART_TREE_DEPTH) {
951
- issues.push({ path: `${where}.children`, message: `${where} is more than ${MAX_CHART_TREE_DEPTH} levels deep; its children were left out.` });
952
- node.value = 0;
953
- } else {
954
- if (raw.value !== void 0 && raw.value !== null) issues.push({ path: `${where}.value`, message: `"${name}" is a group: its value is the sum of its children, so the value given was ignored.` });
955
- const children = read(raw.children, `${where}.children`, depth + 1);
956
- if (children.length) node.children = children;
957
- else node.value = 0;
958
- }
959
- } else {
960
- if (raw.children !== void 0 && raw.children !== null && !Array.isArray(raw.children)) issues.push({ path: `${where}.children`, message: `${where}.children must be an array of nodes.` });
961
- const v = parseChartValue(raw.value);
962
- if (raw.value === void 0 || raw.value === null) node.value = 0;
963
- else if (typeof v === "number" && v >= 0) node.value = v;
964
- else {
965
- issues.push({ path: `${where}.value`, message: `"${name}" needs a value of 0 or more; got ${JSON.stringify(raw.value)}.` });
966
- node.value = 0;
967
- }
968
- }
969
- out.push(node);
970
- });
971
- return out;
972
- };
973
- return read(value, path, 1);
974
- }
975
- function chartTreeTotal(node) {
976
- return node.children ? node.children.reduce((t, c) => t + chartTreeTotal(c), 0) : _nullishCoalesce(node.value, () => ( 0));
977
- }
978
- function chartTreeDepth(tree) {
979
- return tree.reduce((d, n) => Math.max(d, 1 + (n.children ? chartTreeDepth(n.children) : 0)), 0);
980
- }
981
- function findChartTreeNode(tree, path) {
982
- let list = tree;
983
- let node;
984
- for (const name of path) {
985
- node = _optionalChain([list, 'optionalAccess', _18 => _18.find, 'call', _19 => _19((n) => n.name === name)]);
986
- if (!node) return void 0;
987
- list = node.children;
988
- }
989
- return node;
990
- }
991
- function chartTreeToRows(tree) {
992
1146
  const out = [];
993
- const walk = (list, prefix) => {
994
- for (const n of list) {
995
- if (n.children) walk(n.children, [...prefix, n.name]);
996
- else out.push({ path: [...prefix, n.name], value: _nullishCoalesce(n.value, () => ( 0)) });
1147
+ const seen = /* @__PURE__ */ new Set();
1148
+ const next = /* @__PURE__ */ new Map();
1149
+ const reaches = (to, from) => {
1150
+ const stack = [to];
1151
+ const visited = /* @__PURE__ */ new Set();
1152
+ while (stack.length) {
1153
+ const n = stack.pop();
1154
+ if (n === from) return true;
1155
+ if (visited.has(n)) continue;
1156
+ visited.add(n);
1157
+ for (const m of _nullishCoalesce(next.get(n), () => ( []))) stack.push(m);
997
1158
  }
1159
+ return false;
998
1160
  };
999
- walk(tree, []);
1000
- return out;
1001
- }
1002
- function chartTreeFromRows(rows, issues = []) {
1003
- const tree = [];
1004
- rows.forEach((row, r) => {
1005
- const path = [];
1006
- for (const cell of row.path) {
1007
- const name = cell === null || cell === void 0 ? "" : String(cell).trim();
1008
- if (!name) break;
1009
- path.push(name);
1161
+ value.forEach((raw, i) => {
1162
+ const where = `${path}[${i}]`;
1163
+ if (out.length >= MAX_CHART_LINKS) {
1164
+ if (out.length === MAX_CHART_LINKS && i === MAX_CHART_LINKS) issues.push({ path, message: `There are more than ${MAX_CHART_LINKS} flows; the rest were left out.` });
1165
+ return;
1010
1166
  }
1011
- if (!path.length) return;
1012
- if (path.length > MAX_CHART_TREE_DEPTH) {
1013
- issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} is ${path.length} levels deep; the tree keeps at most ${MAX_CHART_TREE_DEPTH}, so the row was left out.` });
1167
+ if (!isRecord3(raw)) {
1168
+ issues.push({ path: where, message: `${where} must be an object with source, target and value.` });
1014
1169
  return;
1015
1170
  }
1016
- const v = parseChartValue(row.value);
1017
- if (row.value !== null && row.value !== void 0 && String(row.value).trim() !== "" && !(typeof v === "number" && v >= 0)) {
1018
- issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} (${path.join(" / ")}) needs a value of 0 or more; got ${JSON.stringify(row.value)}. It was counted as 0.` });
1171
+ for (const k of Object.keys(raw)) if (!["source", "target", "value", "color"].includes(k)) issues.push({ path: `${where}.${k}`, message: `${where} has no "${k}"; a flow takes source, target, value and color.` });
1172
+ const source = nameOf(raw.source);
1173
+ const target = nameOf(raw.target);
1174
+ if (!source || !target) {
1175
+ issues.push({ path: where, message: `${where} needs a source and a target (node names).` });
1176
+ return;
1019
1177
  }
1020
- const value = typeof v === "number" && v >= 0 ? v : 0;
1021
- let list = tree;
1022
- for (let k = 0; k < path.length; k++) {
1023
- const last = k === path.length - 1;
1024
- let node = list.find((n) => n.name === path[k]);
1025
- if (!node) {
1026
- node = last ? { name: path[k], value } : { name: path[k], children: [] };
1027
- list.push(node);
1028
- if (last) return;
1029
- } else if (last) {
1030
- issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} (${path.join(" / ")}) ${node.children ? "names a group that already has items" : "repeats an earlier row"}; it was left out.` });
1031
- return;
1032
- } else if (!node.children) {
1033
- issues.push({ path: `rows[${r}]`, message: `Row ${r + 1} puts items under "${path.slice(0, k + 1).join(" / ")}", which an earlier row made an item with its own value; it was left out.` });
1034
- return;
1035
- }
1036
- list = node.children;
1178
+ if (source === target) {
1179
+ issues.push({ path: where, message: `${where} goes from "${source}" to itself; a flow must join two different nodes.` });
1180
+ return;
1181
+ }
1182
+ const v = parseChartValue(raw.value);
1183
+ if (!(typeof v === "number" && v > 0)) {
1184
+ issues.push({ path: `${where}.value`, message: `The flow from "${source}" to "${target}" needs a value above 0; got ${JSON.stringify(raw.value)}.` });
1185
+ return;
1037
1186
  }
1187
+ const key2 = `${source}\0${target}`;
1188
+ if (seen.has(key2)) {
1189
+ issues.push({ path: where, message: `The flow from "${source}" to "${target}" appears twice; add the values into one flow. The second was left out.` });
1190
+ return;
1191
+ }
1192
+ if (reaches(target, source)) {
1193
+ issues.push({ path: where, message: `The flow from "${source}" to "${target}" would close a loop (${target} already leads back to ${source}); flows must only go forwards, so it was left out.` });
1194
+ return;
1195
+ }
1196
+ seen.add(key2);
1197
+ if (!next.has(source)) next.set(source, /* @__PURE__ */ new Set());
1198
+ next.get(source).add(target);
1199
+ const link = { source, target, value: v };
1200
+ if (typeof raw.color === "string" && raw.color.trim()) link.color = raw.color.trim();
1201
+ out.push(link);
1038
1202
  });
1039
- return tree;
1203
+ return out;
1040
1204
  }
1041
- function chartTreeFromTable(rows, options = {}, issues = []) {
1042
- const grid = rows.filter((r) => r.some((c) => c !== null && c !== void 0 && String(c).trim() !== ""));
1043
- if (!grid.length) return [];
1044
- const width = Math.max(...grid.map((r) => r.length));
1045
- if (width < 2) {
1046
- issues.push({ path: "rows", message: "A tree table needs at least two columns: names, then values." });
1047
- return [];
1048
- }
1049
- const header = _nullishCoalesce(options.header, () => ( "auto"));
1050
- const skip = header === true || header === "auto" && typeof parseChartValue(grid[0][width - 1]) !== "number";
1051
- const body = skip ? grid.slice(1) : grid;
1052
- let above = [];
1053
- return chartTreeFromRows(
1054
- body.map((r) => {
1055
- const cells = Array.from({ length: width - 1 }, (_, k) => r[k] === null || r[k] === void 0 ? "" : String(r[k]).trim());
1056
- const first = cells.findIndex((c) => c !== "");
1057
- if (first < 0) return { path: [], value: r[width - 1] };
1058
- const own = [];
1059
- for (const c of cells.slice(first)) {
1060
- if (!c) break;
1061
- own.push(c);
1205
+ function chartSankeyNodes(links, order = [], issues = []) {
1206
+ const used = /* @__PURE__ */ new Set();
1207
+ const named = [];
1208
+ for (const l of links)
1209
+ for (const n of [l.source, l.target])
1210
+ if (!used.has(n)) {
1211
+ used.add(n);
1212
+ named.push(n);
1062
1213
  }
1063
- const path = [...above.slice(0, first), ...own];
1064
- above = path;
1065
- return { path, value: r[width - 1] };
1066
- }),
1067
- issues
1068
- );
1069
- }
1070
- function chartTreeFromSeries(categories, values) {
1071
- const seen = /* @__PURE__ */ new Set();
1072
1214
  const out = [];
1073
- categories.forEach((c, i) => {
1074
- const name = c.trim() || `${i + 1}`;
1075
- if (seen.has(name)) return;
1076
- seen.add(name);
1077
- const v = values[i];
1078
- out.push({ name, value: v !== null && v !== void 0 && Number.isFinite(v) && v > 0 ? v : 0 });
1079
- });
1215
+ for (const n of order) {
1216
+ if (!used.has(n)) {
1217
+ if (links.length) issues.push({ path: "sankeyNodes", message: `sankeyNodes names "${n}", which no flow uses; it was left out.` });
1218
+ continue;
1219
+ }
1220
+ if (!out.includes(n)) out.push(n);
1221
+ }
1222
+ for (const n of named) if (!out.includes(n)) out.push(n);
1080
1223
  return out;
1081
1224
  }
1082
- function cloneChartTree(tree) {
1083
- return tree.map((n) => ({ ...n, ...n.children ? { children: cloneChartTree(n.children) } : {} }));
1084
- }
1085
- function chartTreeColumnLabels(depth) {
1086
- if (depth <= 1) return ["Item"];
1087
- return Array.from({ length: depth }, (_, k) => k === 0 ? "Group" : k === depth - 1 ? "Item" : `Level ${k + 1}`);
1088
- }
1089
- function setChartTreeRowName(rows, row, level, name) {
1090
- return rows.map((r, i) => {
1091
- if (i !== row) return { path: [...r.path], value: r.value };
1092
- const path = [...r.path];
1093
- while (path.length <= level) path.push("");
1094
- path[level] = name.trim();
1095
- return { path, value: r.value };
1096
- });
1225
+ function chartSankeyNodeValues(links, nodes) {
1226
+ const inflow = /* @__PURE__ */ new Map();
1227
+ const outflow = /* @__PURE__ */ new Map();
1228
+ for (const l of links) {
1229
+ outflow.set(l.source, (_nullishCoalesce(outflow.get(l.source), () => ( 0))) + l.value);
1230
+ inflow.set(l.target, (_nullishCoalesce(inflow.get(l.target), () => ( 0))) + l.value);
1231
+ }
1232
+ return nodes.map((n) => Math.max(_nullishCoalesce(inflow.get(n), () => ( 0)), _nullishCoalesce(outflow.get(n), () => ( 0))));
1097
1233
  }
1098
- function setChartTreeRowValue(rows, row, value) {
1099
- return rows.map((r, i) => ({ path: [...r.path], value: i === row ? value : r.value }));
1234
+ function chartLinksFromTable(rows, options = {}) {
1235
+ const grid = rows.filter((r) => r.some((c) => c !== null && c !== void 0 && String(c).trim() !== ""));
1236
+ if (!grid.length) return [];
1237
+ const header = _nullishCoalesce(options.header, () => ( "auto"));
1238
+ const skip = header === true || header === "auto" && typeof parseChartValue(grid[0][2]) !== "number";
1239
+ return (skip ? grid.slice(1) : grid).map((r) => ({ source: r[0], target: r[1], value: r[2] }));
1100
1240
  }
1101
- function addChartTreeRow(rows) {
1102
- const parent = rows.length ? rows[rows.length - 1].path.slice(0, -1) : [];
1103
- const taken = new Set(rows.filter((r) => r.path.length === parent.length + 1 && parent.every((p, k) => r.path[k] === p)).map((r) => r.path[parent.length]));
1104
- let n = rows.length + 1;
1105
- while (taken.has(`Item ${n}`)) n++;
1106
- return [...rows.map((r) => ({ path: [...r.path], value: r.value })), { path: [...parent, `Item ${n}`], value: 0 }];
1241
+ function setChartLinkField(links, index, field, text2) {
1242
+ return links.map((l, i) => i === index ? { ...l, [field]: field === "value" ? text2 : text2.trim() } : { ...l });
1107
1243
  }
1108
- function removeChartTreeRow(rows, row) {
1109
- return rows.filter((_, i) => i !== row).map((r) => ({ path: [...r.path], value: r.value }));
1244
+ function addChartLink(links) {
1245
+ const last = links[links.length - 1];
1246
+ const names = new Set(links.flatMap((l) => [l.source, l.target]));
1247
+ let n = names.size + 1;
1248
+ while (names.has(`Step ${n}`)) n++;
1249
+ return [...links.map((l) => ({ ...l })), { source: _nullishCoalesce(_optionalChain([last, 'optionalAccess', _20 => _20.target]), () => ( "Start")), target: `Step ${n}`, value: _nullishCoalesce(_optionalChain([last, 'optionalAccess', _21 => _21.value]), () => ( 1)) }];
1110
1250
  }
1111
1251
 
1112
1252
 
@@ -1194,4 +1334,12 @@ function removeChartTreeRow(rows, row) {
1194
1334
 
1195
1335
 
1196
1336
 
1197
- exports.parseChartColor = parseChartColor; exports.chartRgbToHex = chartRgbToHex; exports.shadeChartColor = shadeChartColor; exports.CHART_PALETTE_SHADES = CHART_PALETTE_SHADES; exports.cyclePaletteColor = cyclePaletteColor; exports.chartLuminance = chartLuminance; exports.chartContrastRatio = chartContrastRatio; exports.chartTextOn = chartTextOn; exports.chartRgbToOklab = chartRgbToOklab; exports.chartColorDistance = chartColorDistance; exports.simulateChartVision = simulateChartVision; exports.checkChartColors = checkChartColors; exports.CHART_ERROR_BAR_TYPES = CHART_ERROR_BAR_TYPES; exports.CHART_ERROR_BAR_DIRECTIONS = CHART_ERROR_BAR_DIRECTIONS; exports.CHART_ERROR_BAR_LABELS = CHART_ERROR_BAR_LABELS; exports.defaultChartErrorBarValue = defaultChartErrorBarValue; exports.chartErrorAmounts = chartErrorAmounts; exports.describeChartErrorBars = describeChartErrorBars; exports.chartErrorBarsOfType = chartErrorBarsOfType; exports.formatChartAmountList = formatChartAmountList; exports.parseChartAmountList = parseChartAmountList; exports.MAX_CHART_TREE_DEPTH = MAX_CHART_TREE_DEPTH; exports.MAX_CHART_TREE_NODES = MAX_CHART_TREE_NODES; exports.normalizeChartTree = normalizeChartTree; exports.chartTreeTotal = chartTreeTotal; exports.chartTreeDepth = chartTreeDepth; exports.findChartTreeNode = findChartTreeNode; exports.chartTreeToRows = chartTreeToRows; exports.chartTreeFromRows = chartTreeFromRows; exports.chartTreeFromTable = chartTreeFromTable; exports.chartTreeFromSeries = chartTreeFromSeries; exports.cloneChartTree = cloneChartTree; exports.chartTreeColumnLabels = chartTreeColumnLabels; exports.setChartTreeRowName = setChartTreeRowName; exports.setChartTreeRowValue = setChartTreeRowValue; exports.addChartTreeRow = addChartTreeRow; exports.removeChartTreeRow = removeChartTreeRow; exports.CHART_TYPES = CHART_TYPES; exports.CHART_MARKERS = CHART_MARKERS; exports.CHART_TRENDLINE_TYPES = CHART_TRENDLINE_TYPES; exports.CHART_LABEL_POSITIONS = CHART_LABEL_POSITIONS; exports.CHART_AXIS_KINDS = CHART_AXIS_KINDS; exports.CHART_AXIS_SCALES = CHART_AXIS_SCALES; exports.CHART_SERIES_TYPES = CHART_SERIES_TYPES; exports.CHART_STACKINGS = CHART_STACKINGS; exports.CHART_CURVES = CHART_CURVES; exports.CHART_RADAR_GRATICULES = CHART_RADAR_GRATICULES; exports.CHART_LIKERT_NEUTRALS = CHART_LIKERT_NEUTRALS; exports.CHART_LIKERT_SORTS = CHART_LIKERT_SORTS; exports.CHART_REFERENCE_AXES = CHART_REFERENCE_AXES; exports.CHART_ORIENTATIONS = CHART_ORIENTATIONS; exports.CHART_LEGEND_POSITIONS = CHART_LEGEND_POSITIONS; exports.CHART_NUMBER_STYLES = CHART_NUMBER_STYLES; exports.CHART_TYPE_LABELS = CHART_TYPE_LABELS; exports.DEFAULT_CHART_PALETTE = DEFAULT_CHART_PALETTE; exports.DEFAULT_CHART_THEME = DEFAULT_CHART_THEME; exports.CSS_VARIABLE_CHART_THEME = CSS_VARIABLE_CHART_THEME; exports.isCartesianChart = isCartesianChart; exports.isXyChart = isXyChart; exports.chartFamily = chartFamily; exports.chartColorsByCategory = chartColorsByCategory; exports.chartSpecColorsByCategory = chartSpecColorsByCategory; exports.defaultChartAxis = defaultChartAxis; exports.createChartSpec = createChartSpec; exports.chartSeriesColor = chartSeriesColor; exports.chartLikertColor = chartLikertColor; exports.chartSliceColor = chartSliceColor; exports.chartVariesColors = chartVariesColors; exports.chartPointColor = chartPointColor; exports.chartSeriesType = chartSeriesType; exports.uniqueChartSeriesId = uniqueChartSeriesId; exports.parseChartValue = parseChartValue; exports.normalizeChartAxis = normalizeChartAxis; exports.normalizeChartValues = normalizeChartValues; exports.normalizeChartReferenceLine = normalizeChartReferenceLine; exports.normalizeChartReferenceBand = normalizeChartReferenceBand; exports.normalizeChartErrorBars = normalizeChartErrorBars; exports.normalizeChartTrendline = normalizeChartTrendline; exports.normalizeChartSpec = normalizeChartSpec; exports.cloneChartSpec = cloneChartSpec; exports.cloneChartSeries = cloneChartSeries; exports.chartDataFromTable = chartDataFromTable; exports.chartDataToTable = chartDataToTable; exports.chartColorInputValue = chartColorInputValue;
1337
+
1338
+
1339
+
1340
+
1341
+
1342
+
1343
+
1344
+
1345
+ exports.parseChartColor = parseChartColor; exports.chartRgbToHex = chartRgbToHex; exports.shadeChartColor = shadeChartColor; exports.CHART_PALETTE_SHADES = CHART_PALETTE_SHADES; exports.cyclePaletteColor = cyclePaletteColor; exports.chartLuminance = chartLuminance; exports.chartContrastRatio = chartContrastRatio; exports.chartTextOn = chartTextOn; exports.chartRgbToOklab = chartRgbToOklab; exports.chartColorDistance = chartColorDistance; exports.simulateChartVision = simulateChartVision; exports.checkChartColors = checkChartColors; exports.CHART_ERROR_BAR_TYPES = CHART_ERROR_BAR_TYPES; exports.CHART_ERROR_BAR_DIRECTIONS = CHART_ERROR_BAR_DIRECTIONS; exports.CHART_ERROR_BAR_LABELS = CHART_ERROR_BAR_LABELS; exports.defaultChartErrorBarValue = defaultChartErrorBarValue; exports.chartErrorAmounts = chartErrorAmounts; exports.describeChartErrorBars = describeChartErrorBars; exports.chartErrorBarsOfType = chartErrorBarsOfType; exports.formatChartAmountList = formatChartAmountList; exports.parseChartAmountList = parseChartAmountList; exports.MAX_CHART_LINKS = MAX_CHART_LINKS; exports.normalizeChartLinks = normalizeChartLinks; exports.chartSankeyNodes = chartSankeyNodes; exports.chartSankeyNodeValues = chartSankeyNodeValues; exports.chartLinksFromTable = chartLinksFromTable; exports.setChartLinkField = setChartLinkField; exports.addChartLink = addChartLink; exports.MAX_CHART_TREE_DEPTH = MAX_CHART_TREE_DEPTH; exports.MAX_CHART_TREE_NODES = MAX_CHART_TREE_NODES; exports.normalizeChartTree = normalizeChartTree; exports.chartTreeTotal = chartTreeTotal; exports.chartTreeDepth = chartTreeDepth; exports.findChartTreeNode = findChartTreeNode; exports.chartTreeToRows = chartTreeToRows; exports.chartTreeFromRows = chartTreeFromRows; exports.chartTreeFromTable = chartTreeFromTable; exports.chartTreeFromSeries = chartTreeFromSeries; exports.cloneChartTree = cloneChartTree; exports.chartTreeColumnLabels = chartTreeColumnLabels; exports.setChartTreeRowName = setChartTreeRowName; exports.setChartTreeRowValue = setChartTreeRowValue; exports.addChartTreeRow = addChartTreeRow; exports.removeChartTreeRow = removeChartTreeRow; exports.CHART_TYPES = CHART_TYPES; exports.CHART_MARKERS = CHART_MARKERS; exports.CHART_TRENDLINE_TYPES = CHART_TRENDLINE_TYPES; exports.CHART_LABEL_POSITIONS = CHART_LABEL_POSITIONS; exports.CHART_AXIS_KINDS = CHART_AXIS_KINDS; exports.CHART_AXIS_SCALES = CHART_AXIS_SCALES; exports.CHART_SERIES_TYPES = CHART_SERIES_TYPES; exports.CHART_STACKINGS = CHART_STACKINGS; exports.CHART_CURVES = CHART_CURVES; exports.CHART_RADAR_GRATICULES = CHART_RADAR_GRATICULES; exports.CHART_LIKERT_NEUTRALS = CHART_LIKERT_NEUTRALS; exports.CHART_LIKERT_SORTS = CHART_LIKERT_SORTS; exports.CHART_SANKEY_LINK_COLORS = CHART_SANKEY_LINK_COLORS; exports.CHART_REFERENCE_AXES = CHART_REFERENCE_AXES; exports.CHART_ORIENTATIONS = CHART_ORIENTATIONS; exports.CHART_LEGEND_POSITIONS = CHART_LEGEND_POSITIONS; exports.CHART_NUMBER_STYLES = CHART_NUMBER_STYLES; exports.CHART_TYPE_LABELS = CHART_TYPE_LABELS; exports.DEFAULT_CHART_PALETTE = DEFAULT_CHART_PALETTE; exports.DEFAULT_CHART_THEME = DEFAULT_CHART_THEME; exports.CSS_VARIABLE_CHART_THEME = CSS_VARIABLE_CHART_THEME; exports.isCartesianChart = isCartesianChart; exports.isXyChart = isXyChart; exports.chartFamily = chartFamily; exports.chartColorsByCategory = chartColorsByCategory; exports.chartSpecColorsByCategory = chartSpecColorsByCategory; exports.defaultChartAxis = defaultChartAxis; exports.createChartSpec = createChartSpec; exports.chartSeriesColor = chartSeriesColor; exports.chartLikertColor = chartLikertColor; exports.chartSliceColor = chartSliceColor; exports.chartVariesColors = chartVariesColors; exports.chartPointColor = chartPointColor; exports.chartSeriesType = chartSeriesType; exports.uniqueChartSeriesId = uniqueChartSeriesId; exports.parseChartValue = parseChartValue; exports.normalizeChartAxis = normalizeChartAxis; exports.normalizeChartValues = normalizeChartValues; exports.normalizeChartReferenceLine = normalizeChartReferenceLine; exports.normalizeChartReferenceBand = normalizeChartReferenceBand; exports.normalizeChartErrorBars = normalizeChartErrorBars; exports.normalizeChartTrendline = normalizeChartTrendline; exports.normalizeChartSpec = normalizeChartSpec; exports.cloneChartSpec = cloneChartSpec; exports.cloneChartSeries = cloneChartSeries; exports.chartDataFromTable = chartDataFromTable; exports.chartDataToTable = chartDataToTable; exports.chartColorInputValue = chartColorInputValue;