@pixerate/editor 0.48.0 → 0.49.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 (54) hide show
  1. package/dist/agent/index.cjs +24 -22
  2. package/dist/agent/index.cjs.map +1 -1
  3. package/dist/agent/index.js +6 -4
  4. package/dist/agent/index.js.map +1 -1
  5. package/dist/chart/index.cjs +81 -79
  6. package/dist/chart/index.cjs.map +1 -1
  7. package/dist/chart/index.js +52 -50
  8. package/dist/chart/index.js.map +1 -1
  9. package/dist/{chunk-TXXNBDQX.cjs → chunk-2Q46FKZK.cjs} +113 -34
  10. package/dist/chunk-2Q46FKZK.cjs.map +1 -0
  11. package/dist/{chunk-PIMCHMZE.cjs → chunk-3JNRQVTS.cjs} +16 -16
  12. package/dist/{chunk-PIMCHMZE.cjs.map → chunk-3JNRQVTS.cjs.map} +1 -1
  13. package/dist/{chunk-HXUL3LS7.js → chunk-5TWIN7ZJ.js} +2 -2
  14. package/dist/{chunk-VKPEPWKZ.js → chunk-DQPBQXKG.js} +4 -4
  15. package/dist/{chunk-MUCYWI6W.cjs → chunk-FEMIVHN3.cjs} +17 -15
  16. package/dist/chunk-FEMIVHN3.cjs.map +1 -0
  17. package/dist/{chunk-XUUD43OV.js → chunk-GJBIVWB2.js} +2 -2
  18. package/dist/{chunk-PRSJJZBS.cjs → chunk-GTZXF4BI.cjs} +191 -841
  19. package/dist/chunk-GTZXF4BI.cjs.map +1 -0
  20. package/dist/chunk-JOX6MJW4.cjs +674 -0
  21. package/dist/chunk-JOX6MJW4.cjs.map +1 -0
  22. package/dist/{chunk-WXEHCVJQ.cjs → chunk-LNKZPIQD.cjs} +52 -52
  23. package/dist/{chunk-WXEHCVJQ.cjs.map → chunk-LNKZPIQD.cjs.map} +1 -1
  24. package/dist/chunk-OV7UAKIY.js +674 -0
  25. package/dist/chunk-OV7UAKIY.js.map +1 -0
  26. package/dist/{chunk-PKBWLEL7.js → chunk-QFTVLEVN.js} +6 -4
  27. package/dist/{chunk-PKBWLEL7.js.map → chunk-QFTVLEVN.js.map} +1 -1
  28. package/dist/{chunk-KRGXUQ2Y.js → chunk-T6XU4B54.js} +82 -3
  29. package/dist/chunk-T6XU4B54.js.map +1 -0
  30. package/dist/{chunk-U6IZKC7B.cjs → chunk-TGBCBQFV.cjs} +14 -14
  31. package/dist/{chunk-U6IZKC7B.cjs.map → chunk-TGBCBQFV.cjs.map} +1 -1
  32. package/dist/{chunk-LTUM52X6.js → chunk-WBOAXDKD.js} +109 -759
  33. package/dist/chunk-WBOAXDKD.js.map +1 -0
  34. package/dist/export/index.cjs +4 -2
  35. package/dist/export/index.cjs.map +1 -1
  36. package/dist/export/index.d.cts +5 -1
  37. package/dist/export/index.d.ts +5 -1
  38. package/dist/export/index.js +3 -1
  39. package/dist/extensions/index.cjs +6 -5
  40. package/dist/extensions/index.cjs.map +1 -1
  41. package/dist/extensions/index.js +5 -4
  42. package/dist/index.cjs +12 -11
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.js +6 -5
  45. package/dist/index.js.map +1 -1
  46. package/package.json +1 -1
  47. package/dist/chunk-KRGXUQ2Y.js.map +0 -1
  48. package/dist/chunk-LTUM52X6.js.map +0 -1
  49. package/dist/chunk-MUCYWI6W.cjs.map +0 -1
  50. package/dist/chunk-PRSJJZBS.cjs.map +0 -1
  51. package/dist/chunk-TXXNBDQX.cjs.map +0 -1
  52. /package/dist/{chunk-HXUL3LS7.js.map → chunk-5TWIN7ZJ.js.map} +0 -0
  53. /package/dist/{chunk-VKPEPWKZ.js.map → chunk-DQPBQXKG.js.map} +0 -0
  54. /package/dist/{chunk-XUUD43OV.js.map → chunk-GJBIVWB2.js.map} +0 -0
@@ -0,0 +1,674 @@
1
+ // src/chart/color.ts
2
+ var NAMED = {
3
+ black: "#000000",
4
+ white: "#ffffff",
5
+ red: "#ff0000",
6
+ green: "#008000",
7
+ blue: "#0000ff",
8
+ yellow: "#ffff00",
9
+ orange: "#ffa500",
10
+ purple: "#800080",
11
+ gray: "#808080",
12
+ grey: "#808080",
13
+ teal: "#008080",
14
+ navy: "#000080",
15
+ maroon: "#800000",
16
+ olive: "#808000",
17
+ silver: "#c0c0c0",
18
+ lime: "#00ff00",
19
+ aqua: "#00ffff",
20
+ cyan: "#00ffff",
21
+ fuchsia: "#ff00ff",
22
+ magenta: "#ff00ff",
23
+ pink: "#ffc0cb",
24
+ brown: "#a52a2a"
25
+ };
26
+ function parseChartColor(color) {
27
+ const text2 = color.trim().toLowerCase();
28
+ const hex = NAMED[text2] ?? text2;
29
+ let m = /^#([0-9a-f]{3})$/.exec(hex);
30
+ if (m) return [...m[1]].map((c) => parseInt(c + c, 16) / 255);
31
+ m = /^#([0-9a-f]{6})([0-9a-f]{2})?$/.exec(hex);
32
+ if (m) return [0, 2, 4].map((i) => parseInt(m[1].slice(i, i + 2), 16) / 255);
33
+ m = /^rgba?\(\s*([\d.]+)%?[\s,]+([\d.]+)%?[\s,]+([\d.]+)%?(?:[\s,/]+[\d.]+%?)?\s*\)$/.exec(text2);
34
+ if (m) {
35
+ const pct = text2.includes("%");
36
+ return [m[1], m[2], m[3]].map((v) => Math.max(0, Math.min(1, Number(v) / (pct ? 100 : 255))));
37
+ }
38
+ return null;
39
+ }
40
+ function chartRgbToHex(rgb) {
41
+ return `#${rgb.map((c) => Math.round(Math.max(0, Math.min(1, c)) * 255).toString(16).padStart(2, "0")).join("")}`;
42
+ }
43
+ function shadeChartColor(color, amount) {
44
+ const rgb = parseChartColor(color);
45
+ if (!rgb || !amount) return color;
46
+ const t = Math.max(-1, Math.min(1, amount));
47
+ return chartRgbToHex(rgb.map((c) => t > 0 ? c + (1 - c) * t : c * (1 + t)));
48
+ }
49
+ var CHART_PALETTE_SHADES = [0, 0.4, -0.25, 0.6, -0.5, 0.2];
50
+ function cyclePaletteColor(palette, index) {
51
+ const base = palette[index % palette.length];
52
+ const round = Math.floor(index / palette.length) % CHART_PALETTE_SHADES.length;
53
+ return shadeChartColor(base, CHART_PALETTE_SHADES[round]);
54
+ }
55
+ var toLinear = (c) => c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
56
+ var fromLinear = (c) => c <= 31308e-7 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
57
+ function chartLuminance(rgb) {
58
+ const [r, g, b] = rgb.map(toLinear);
59
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
60
+ }
61
+ function chartContrastRatio(a, b) {
62
+ const ra = parseChartColor(a);
63
+ const rb = parseChartColor(b);
64
+ if (!ra || !rb) return null;
65
+ const la = chartLuminance(ra);
66
+ const lb = chartLuminance(rb);
67
+ return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
68
+ }
69
+ function chartTextOn(fill, fallback, dark = "#0f172a", light = "#ffffff") {
70
+ const rgb = parseChartColor(fill);
71
+ if (!rgb) return fallback;
72
+ const darkRatio = chartContrastRatio(fill, dark);
73
+ const lightRatio = chartContrastRatio(fill, light);
74
+ return darkRatio >= lightRatio ? dark : light;
75
+ }
76
+ function chartRgbToOklab(rgb) {
77
+ const [r, g, b] = rgb.map(toLinear);
78
+ const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
79
+ const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
80
+ const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
81
+ return [
82
+ 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
83
+ 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
84
+ 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
85
+ ];
86
+ }
87
+ function chartColorDistance(a, b) {
88
+ const p = chartRgbToOklab(a);
89
+ const q = chartRgbToOklab(b);
90
+ return Math.hypot(p[0] - q[0], p[1] - q[1], p[2] - q[2]);
91
+ }
92
+ var CVD = {
93
+ protanopia: [
94
+ [0.152286, 1.052583, -0.204868],
95
+ [0.114503, 0.786281, 0.099216],
96
+ [-3882e-6, -0.048116, 1.051998]
97
+ ],
98
+ deuteranopia: [
99
+ [0.367322, 0.860646, -0.227968],
100
+ [0.280085, 0.672501, 0.047413],
101
+ [-0.01182, 0.04294, 0.968881]
102
+ ],
103
+ tritanopia: [
104
+ [1.255528, -0.076749, -0.178779],
105
+ [-0.078411, 0.930809, 0.147602],
106
+ [4733e-6, 0.691367, 0.3039]
107
+ ]
108
+ };
109
+ function simulateChartVision(rgb, vision) {
110
+ if (vision === "normal") return rgb;
111
+ const lin = rgb.map(toLinear);
112
+ const m = CVD[vision];
113
+ return m.map((row) => fromLinear(Math.max(0, Math.min(1, row[0] * lin[0] + row[1] * lin[1] + row[2] * lin[2]))));
114
+ }
115
+ function checkChartColors(colors, options = {}) {
116
+ const surface = options.surface ?? "#ffffff";
117
+ const minDistance = options.minDistance ?? 0.08;
118
+ const minContrast = options.minContrast ?? 1.5;
119
+ const visions = options.visions ?? ["normal", "protanopia", "deuteranopia", "tritanopia"];
120
+ const parsed = colors.map(parseChartColor);
121
+ const skipped = colors.filter((_, i) => !parsed[i]);
122
+ const issues = [];
123
+ const closest = {};
124
+ for (const vision of visions) {
125
+ const seen = parsed.map((c) => c ? simulateChartVision(c, vision) : null);
126
+ let best;
127
+ for (let a = 0; a < seen.length; a++) {
128
+ for (let b = a + 1; b < seen.length; b++) {
129
+ if (!seen[a] || !seen[b]) continue;
130
+ const distance = chartColorDistance(seen[a], seen[b]);
131
+ if (!best || distance < best.distance) best = { a, b, colors: [colors[a], colors[b]], distance: Math.round(distance * 1e3) / 1e3 };
132
+ }
133
+ }
134
+ if (best) {
135
+ closest[vision] = best;
136
+ if (best.distance < minDistance) {
137
+ const who = vision === "normal" ? "" : ` with ${vision}`;
138
+ issues.push(`Colours ${best.a + 1} (${best.colors[0]}) and ${best.b + 1} (${best.colors[1]}) are hard to tell apart${who} (distance ${best.distance}, want at least ${minDistance}).`);
139
+ }
140
+ }
141
+ }
142
+ const contrast = colors.map((c) => {
143
+ const ratio = chartContrastRatio(c, surface);
144
+ return ratio === null ? null : Math.round(ratio * 100) / 100;
145
+ });
146
+ contrast.forEach((ratio, i) => {
147
+ if (ratio !== null && ratio < minContrast) issues.push(`Colour ${i + 1} (${colors[i]}) barely stands out from the background ${surface} (contrast ${ratio}:1, want at least ${minContrast}:1).`);
148
+ });
149
+ return { ok: issues.length === 0, closest, contrast, issues, skipped };
150
+ }
151
+
152
+ // src/chart/spec.ts
153
+ var CHART_TYPES = ["bar", "line", "area", "scatter", "bubble", "pie", "donut", "radar", "waterfall", "histogram", "box", "funnel", "treemap"];
154
+ var CHART_MARKERS = ["circle", "square", "diamond", "triangle", "cross", "none"];
155
+ var CHART_TRENDLINE_TYPES = ["linear", "exponential", "logarithmic", "power", "polynomial", "moving-average"];
156
+ var CHART_LABEL_POSITIONS = ["auto", "inside", "outside", "center"];
157
+ var CHART_AXIS_KINDS = ["category", "date"];
158
+ var CHART_SERIES_TYPES = ["bar", "line", "area", "scatter"];
159
+ var CHART_STACKINGS = ["none", "stacked", "percent"];
160
+ var CHART_CURVES = ["linear", "smooth", "step"];
161
+ var CHART_ORIENTATIONS = ["vertical", "horizontal"];
162
+ var CHART_LEGEND_POSITIONS = ["top", "bottom", "left", "right"];
163
+ var CHART_NUMBER_STYLES = ["number", "percent", "currency", "compact"];
164
+ var CHART_TYPE_LABELS = {
165
+ bar: "Bar",
166
+ line: "Line",
167
+ area: "Area",
168
+ scatter: "Scatter",
169
+ bubble: "Bubble",
170
+ pie: "Pie",
171
+ donut: "Donut",
172
+ radar: "Radar",
173
+ waterfall: "Waterfall",
174
+ histogram: "Histogram",
175
+ box: "Box plot",
176
+ funnel: "Funnel",
177
+ treemap: "Treemap"
178
+ };
179
+ var DEFAULT_CHART_PALETTE = ["#4269d0", "#efb118", "#ff725c", "#6cc5b0", "#3ca951", "#ff8ab7", "#a463f2", "#97bbf5", "#9c6b4e", "#9498a0"];
180
+ var DEFAULT_CHART_THEME = {
181
+ background: "#ffffff",
182
+ surface: "#ffffff",
183
+ text: "#0f172a",
184
+ mutedText: "#64748b",
185
+ grid: "#e2e8f0",
186
+ axis: "#94a3b8",
187
+ selection: "#2563eb",
188
+ positive: "#3ca951",
189
+ negative: "#e5484d",
190
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
191
+ fontSize: 11,
192
+ titleSize: 15
193
+ };
194
+ var CSS_VARIABLE_CHART_THEME = {
195
+ ...DEFAULT_CHART_THEME,
196
+ background: "transparent",
197
+ surface: "var(--background, #ffffff)",
198
+ text: "var(--foreground, #0f172a)",
199
+ mutedText: "var(--muted-foreground, #64748b)",
200
+ grid: "var(--border, #e2e8f0)",
201
+ axis: "var(--muted-foreground, #94a3b8)",
202
+ selection: "var(--primary, #2563eb)",
203
+ fontFamily: "inherit"
204
+ };
205
+ function isCartesianChart(type) {
206
+ return type === "bar" || type === "line" || type === "area" || type === "scatter";
207
+ }
208
+ function isXyChart(spec) {
209
+ if (spec.type === "bubble") return true;
210
+ return spec.type === "scatter" && spec.series.some((s) => !s.hidden && Array.isArray(s.x) && s.x.some((v) => v !== null));
211
+ }
212
+ function chartFamily(spec) {
213
+ if (isXyChart(spec)) return "xy";
214
+ switch (spec.type) {
215
+ case "pie":
216
+ case "donut":
217
+ return "pie";
218
+ case "radar":
219
+ case "waterfall":
220
+ case "histogram":
221
+ case "box":
222
+ case "funnel":
223
+ case "treemap":
224
+ return spec.type;
225
+ case "bubble":
226
+ return "xy";
227
+ default:
228
+ return "cartesian";
229
+ }
230
+ }
231
+ function chartColorsByCategory(type) {
232
+ return type === "pie" || type === "donut" || type === "funnel" || type === "treemap";
233
+ }
234
+ function defaultChartAxis(value) {
235
+ return { show: true, title: "", min: null, max: null, step: null, gridlines: value, format: {} };
236
+ }
237
+ function createChartSpec(partial = {}) {
238
+ return normalizeChartSpec(partial).spec;
239
+ }
240
+ function chartSeriesColor(spec, seriesIndex) {
241
+ const series = spec.series[seriesIndex];
242
+ if (series?.color) return series.color;
243
+ return cyclePaletteColor(spec.palette.length ? spec.palette : DEFAULT_CHART_PALETTE, seriesIndex);
244
+ }
245
+ function chartSliceColor(spec, index) {
246
+ return cyclePaletteColor(spec.palette.length ? spec.palette : DEFAULT_CHART_PALETTE, index);
247
+ }
248
+ function chartPointColor(spec, seriesIndex, index) {
249
+ return spec.series[seriesIndex]?.pointColors?.[index] || chartSeriesColor(spec, seriesIndex);
250
+ }
251
+ function chartSeriesType(spec, series) {
252
+ if (series.type) return series.type;
253
+ if (spec.type === "bubble") return "scatter";
254
+ return isCartesianChart(spec.type) ? spec.type : "bar";
255
+ }
256
+ function uniqueChartSeriesId(series, base = "series") {
257
+ const used = new Set(series.map((s) => s.id));
258
+ for (let i = series.length + 1; ; i++) {
259
+ const id = `${base}-${i}`;
260
+ if (!used.has(id)) return id;
261
+ }
262
+ }
263
+ function parseChartValue(input) {
264
+ if (input === null || input === void 0) return null;
265
+ if (typeof input === "number") return Number.isFinite(input) ? input : void 0;
266
+ if (typeof input !== "string") return void 0;
267
+ let text2 = input.trim();
268
+ if (!text2 || text2 === "-" || text2 === "\u2014") return null;
269
+ let negative = false;
270
+ if (/^\(.*\)$/.test(text2)) {
271
+ negative = true;
272
+ text2 = text2.slice(1, -1).trim();
273
+ }
274
+ const percent = text2.endsWith("%");
275
+ if (percent) text2 = text2.slice(0, -1).trim();
276
+ text2 = text2.replace(/^[$€£¥]/, "").replace(/,/g, "").replace(/\s+/g, "");
277
+ if (!/^[-+]?(\d+\.?\d*|\.\d+)(e[-+]?\d+)?$/i.test(text2)) return void 0;
278
+ let value = Number(text2);
279
+ if (percent) value /= 100;
280
+ if (negative) value = -value;
281
+ return Number.isFinite(value) ? value : void 0;
282
+ }
283
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
284
+ function pick(value, options, fallback, path, issues) {
285
+ if (value === void 0) return fallback;
286
+ if (typeof value === "string" && options.includes(value)) return value;
287
+ issues.push({ path, message: `${path} must be one of ${options.map((o) => `"${o}"`).join(", ")}; got ${JSON.stringify(value)}.` });
288
+ return fallback;
289
+ }
290
+ function text(value, fallback, path, issues) {
291
+ if (value === void 0 || value === null) return fallback;
292
+ if (typeof value === "string") return value;
293
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
294
+ issues.push({ path, message: `${path} must be text.` });
295
+ return fallback;
296
+ }
297
+ function flag(value, fallback, path, issues) {
298
+ if (value === void 0) return fallback;
299
+ if (typeof value === "boolean") return value;
300
+ issues.push({ path, message: `${path} must be true or false.` });
301
+ return fallback;
302
+ }
303
+ function bound(value, path, issues) {
304
+ if (value === void 0 || value === null || value === "") return null;
305
+ const n = typeof value === "string" ? Number(value) : value;
306
+ if (typeof n === "number" && Number.isFinite(n)) return n;
307
+ issues.push({ path, message: `${path} must be a number or null.` });
308
+ return null;
309
+ }
310
+ function normalizeFormat(value, path, issues) {
311
+ if (value === void 0 || value === null) return {};
312
+ if (!isRecord(value)) {
313
+ issues.push({ path, message: `${path} must be an object.` });
314
+ return {};
315
+ }
316
+ const out = {};
317
+ if (value.style !== void 0) out.style = pick(value.style, CHART_NUMBER_STYLES, "number", `${path}.style`, issues);
318
+ if (value.decimals !== void 0) {
319
+ const d = Number(value.decimals);
320
+ if (Number.isInteger(d) && d >= 0 && d <= 10) out.decimals = d;
321
+ else issues.push({ path: `${path}.decimals`, message: `${path}.decimals must be a whole number from 0 to 10.` });
322
+ }
323
+ for (const key2 of ["currency", "prefix", "suffix", "code"]) {
324
+ if (value[key2] !== void 0) out[key2] = text(value[key2], "", `${path}.${key2}`, issues);
325
+ }
326
+ return out;
327
+ }
328
+ function normalizeChartAxis(value, fallback, path, issues = []) {
329
+ if (value === void 0) return { ...fallback, format: { ...fallback.format } };
330
+ if (!isRecord(value)) {
331
+ issues.push({ path, message: `${path} must be an object.` });
332
+ return { ...fallback, format: { ...fallback.format } };
333
+ }
334
+ let min = "min" in value ? bound(value.min, `${path}.min`, issues) : fallback.min;
335
+ let max = "max" in value ? bound(value.max, `${path}.max`, issues) : fallback.max;
336
+ if (min !== null && max !== null && min >= max) {
337
+ issues.push({ path: `${path}.max`, message: `${path}.max (${max}) must be greater than ${path}.min (${min}).` });
338
+ min = fallback.min;
339
+ max = fallback.max;
340
+ }
341
+ let step = "step" in value ? bound(value.step, `${path}.step`, issues) : fallback.step;
342
+ if (step !== null && !(step > 0)) {
343
+ issues.push({ path: `${path}.step`, message: `${path}.step must be greater than 0 (or null for automatic).` });
344
+ step = fallback.step;
345
+ }
346
+ const axis = {
347
+ show: flag(value.show, fallback.show, `${path}.show`, issues),
348
+ title: text(value.title, fallback.title, `${path}.title`, issues),
349
+ min,
350
+ max,
351
+ step,
352
+ gridlines: flag(value.gridlines, fallback.gridlines, `${path}.gridlines`, issues),
353
+ format: "format" in value ? normalizeFormat(value.format, `${path}.format`, issues) : { ...fallback.format }
354
+ };
355
+ const kind = "kind" in value ? value.kind === null ? void 0 : pick(value.kind, CHART_AXIS_KINDS, "category", `${path}.kind`, issues) : fallback.kind;
356
+ if (kind && kind !== "category") axis.kind = kind;
357
+ return axis;
358
+ }
359
+ function normalizeLegend(value, issues) {
360
+ const fallback = { show: true, position: "bottom" };
361
+ if (value === void 0) return fallback;
362
+ if (!isRecord(value)) {
363
+ issues.push({ path: "legend", message: "legend must be an object." });
364
+ return fallback;
365
+ }
366
+ return {
367
+ show: flag(value.show, fallback.show, "legend.show", issues),
368
+ position: pick(value.position, CHART_LEGEND_POSITIONS, fallback.position, "legend.position", issues)
369
+ };
370
+ }
371
+ function normalizeChartValues(value, length, path, issues = []) {
372
+ if (value === void 0) return length ? Array(length).fill(null) : [];
373
+ if (!Array.isArray(value)) {
374
+ issues.push({ path, message: `${path} must be an array of numbers.` });
375
+ return length ? Array(length).fill(null) : [];
376
+ }
377
+ const out = value.map((v, i) => {
378
+ const parsed = parseChartValue(v);
379
+ if (parsed === void 0) {
380
+ issues.push({ path: `${path}[${i}]`, message: `${path}[${i}] is not a number: ${JSON.stringify(v)}.` });
381
+ return null;
382
+ }
383
+ return parsed;
384
+ });
385
+ if (length === void 0) return out;
386
+ if (out.length > length) return out.slice(0, length);
387
+ while (out.length < length) out.push(null);
388
+ return out;
389
+ }
390
+ function normalizeSeries(value, length, used, index, issues) {
391
+ const path = `series[${index}]`;
392
+ if (!isRecord(value)) {
393
+ issues.push({ path, message: `${path} must be an object with a name and values.` });
394
+ return null;
395
+ }
396
+ let id = typeof value.id === "string" && value.id.trim() ? value.id.trim() : "";
397
+ if (!id || used.has(id)) {
398
+ if (id) issues.push({ path: `${path}.id`, message: `Series id "${id}" is used twice; the second was renamed.` });
399
+ for (let n = index + 1; ; n++) {
400
+ id = `series-${n}`;
401
+ if (!used.has(id)) break;
402
+ }
403
+ }
404
+ used.add(id);
405
+ const series = {
406
+ id,
407
+ name: text(value.name, `Series ${index + 1}`, `${path}.name`, issues),
408
+ values: normalizeChartValues(value.values, length, `${path}.values`, issues)
409
+ };
410
+ if (typeof value.color === "string" && value.color.trim()) series.color = value.color.trim();
411
+ if (value.type !== void 0 && value.type !== null) series.type = pick(value.type, CHART_SERIES_TYPES, "bar", `${path}.type`, issues);
412
+ if (value.hidden === true) series.hidden = true;
413
+ if (value.axis !== void 0 && value.axis !== null && pick(value.axis, ["primary", "secondary"], "primary", `${path}.axis`, issues) === "secondary") series.axis = "secondary";
414
+ for (const key2 of ["x", "sizes"]) {
415
+ if (value[key2] === void 0 || value[key2] === null) continue;
416
+ const list = normalizeChartValues(value[key2], length, `${path}.${key2}`, issues);
417
+ if (list.some((v) => v !== null)) series[key2] = list;
418
+ }
419
+ if (key(value, "sizes") && series.sizes?.some((v) => v !== null && v < 0)) {
420
+ issues.push({ path: `${path}.sizes`, message: `${path}.sizes must not be negative.` });
421
+ series.sizes = series.sizes.map((v) => v !== null && v < 0 ? null : v);
422
+ }
423
+ if (value.pointColors !== void 0 && value.pointColors !== null) {
424
+ if (!Array.isArray(value.pointColors)) issues.push({ path: `${path}.pointColors`, message: `${path}.pointColors must be an array of colours or nulls.` });
425
+ else {
426
+ const colors = Array.from({ length }, (_, i) => {
427
+ const c = value.pointColors[i];
428
+ return typeof c === "string" && c.trim() ? c.trim() : null;
429
+ });
430
+ if (colors.some((c) => c !== null)) series.pointColors = colors;
431
+ }
432
+ }
433
+ if (value.marker !== void 0 && value.marker !== null) series.marker = pick(value.marker, CHART_MARKERS, "circle", `${path}.marker`, issues);
434
+ if (value.curve !== void 0 && value.curve !== null) series.curve = pick(value.curve, CHART_CURVES, "linear", `${path}.curve`, issues);
435
+ if (value.dataLabels !== void 0 && value.dataLabels !== null) series.dataLabels = flag(value.dataLabels, false, `${path}.dataLabels`, issues);
436
+ if (value.format !== void 0 && value.format !== null) {
437
+ const format = normalizeFormat(value.format, `${path}.format`, issues);
438
+ if (Object.keys(format).length) series.format = format;
439
+ }
440
+ if (value.trendlines !== void 0 && value.trendlines !== null) {
441
+ if (!Array.isArray(value.trendlines)) issues.push({ path: `${path}.trendlines`, message: `${path}.trendlines must be an array.` });
442
+ else {
443
+ const lines = value.trendlines.map((t, i) => normalizeChartTrendline(t, `${path}.trendlines[${i}]`, issues)).filter((t) => t !== null);
444
+ if (lines.length) series.trendlines = lines;
445
+ }
446
+ }
447
+ return series;
448
+ }
449
+ var key = (value, name) => value[name] !== void 0 && value[name] !== null;
450
+ function normalizeChartTrendline(value, path, issues = []) {
451
+ if (!isRecord(value)) {
452
+ issues.push({ path, message: `${path} must be an object with a type.` });
453
+ return null;
454
+ }
455
+ const type = pick(value.type, CHART_TRENDLINE_TYPES, "linear", `${path}.type`, issues);
456
+ const line = { type };
457
+ const whole = (name, min, max) => {
458
+ if (!key(value, name)) return;
459
+ const n = Number(value[name]);
460
+ if (Number.isInteger(n) && n >= min && n <= max) line[name] = n;
461
+ else issues.push({ path: `${path}.${name}`, message: `${path}.${name} must be a whole number from ${min} to ${max}.` });
462
+ };
463
+ if (type === "polynomial") whole("order", 2, 6);
464
+ if (type === "moving-average") whole("period", 2, 1e3);
465
+ for (const name of ["forward", "backward"]) {
466
+ if (!key(value, name)) continue;
467
+ const n = Number(value[name]);
468
+ if (Number.isFinite(n) && n >= 0) {
469
+ if (n > 0) line[name] = n;
470
+ } else issues.push({ path: `${path}.${name}`, message: `${path}.${name} must be a number of x units, 0 or more.` });
471
+ }
472
+ if (key(value, "intercept")) {
473
+ const n = Number(value.intercept);
474
+ if (!Number.isFinite(n)) issues.push({ path: `${path}.intercept`, message: `${path}.intercept must be a number or null.` });
475
+ else if (type === "linear" || type === "polynomial" || type === "exponential") {
476
+ if (type === "exponential" && n <= 0) issues.push({ path: `${path}.intercept`, message: `${path}.intercept must be greater than 0 for an exponential trendline.` });
477
+ else line.intercept = n;
478
+ } else issues.push({ path: `${path}.intercept`, message: `A ${type} trendline cannot have a fixed intercept; use linear, polynomial or exponential.` });
479
+ }
480
+ for (const name of ["showEquation", "showR2"]) if (key(value, name) && flag(value[name], false, `${path}.${name}`, issues)) line[name] = true;
481
+ for (const name of ["name", "color"]) {
482
+ if (!key(value, name)) continue;
483
+ const t = text(value[name], "", `${path}.${name}`, issues).trim();
484
+ if (t) line[name] = t;
485
+ }
486
+ return line;
487
+ }
488
+ function normalizeChartSpec(input) {
489
+ const issues = [];
490
+ const value = isRecord(input) ? input : {};
491
+ if (input !== void 0 && !isRecord(input)) issues.push({ path: "", message: "A chart must be an object." });
492
+ const type = pick(value.type, CHART_TYPES, "bar", "type", issues);
493
+ const rawSeries = Array.isArray(value.series) ? value.series : [];
494
+ if (value.series !== void 0 && !Array.isArray(value.series)) issues.push({ path: "series", message: "series must be an array." });
495
+ let categories;
496
+ if (Array.isArray(value.categories)) categories = value.categories.map((c, i) => text(c, "", `categories[${i}]`, issues));
497
+ else {
498
+ if (value.categories !== void 0) issues.push({ path: "categories", message: "categories must be an array of labels." });
499
+ const longest = rawSeries.reduce((n, s) => Math.max(n, isRecord(s) && Array.isArray(s.values) ? s.values.length : 0), 0);
500
+ categories = Array.from({ length: longest }, (_, i) => `${i + 1}`);
501
+ }
502
+ const used = /* @__PURE__ */ new Set();
503
+ const series = rawSeries.map((s, i) => normalizeSeries(s, categories.length, used, i, issues)).filter((s) => s !== null);
504
+ let palette = [...DEFAULT_CHART_PALETTE];
505
+ if (Array.isArray(value.palette)) {
506
+ const colors = value.palette.filter((c) => typeof c === "string" && c.trim() !== "");
507
+ if (colors.length) palette = colors;
508
+ else issues.push({ path: "palette", message: "palette must list at least one colour." });
509
+ } else if (value.palette !== void 0) issues.push({ path: "palette", message: "palette must be an array of colours." });
510
+ const fraction = (name, fallback, min, max) => {
511
+ if (value[name] === void 0) return fallback;
512
+ const r = Number(value[name]);
513
+ if (value[name] !== null && Number.isFinite(r) && r >= min && r <= max) return r;
514
+ issues.push({ path: name, message: `${name} must be a number from ${min} to ${max}.` });
515
+ return fallback;
516
+ };
517
+ const innerRadius = fraction("innerRadius", 0.55, 0, 0.9);
518
+ const gapWidth = fraction("gapWidth", 0.28, 0, 0.9);
519
+ const overlap = fraction("overlap", -0.12, -1, 1);
520
+ let bins = null;
521
+ if (value.bins !== void 0 && value.bins !== null) {
522
+ const b = Number(value.bins);
523
+ if (Number.isInteger(b) && b >= 1 && b <= 500) bins = b;
524
+ else issues.push({ path: "bins", message: "bins must be a whole number from 1 to 500, or null for automatic." });
525
+ }
526
+ const spec = {
527
+ version: 1,
528
+ type,
529
+ title: text(value.title, "", "title", issues),
530
+ subtitle: text(value.subtitle, "", "subtitle", issues),
531
+ categories,
532
+ series,
533
+ xAxis: normalizeChartAxis(value.xAxis, defaultChartAxis(false), "xAxis", issues),
534
+ yAxis: normalizeChartAxis(value.yAxis, defaultChartAxis(true), "yAxis", issues),
535
+ yAxis2: normalizeChartAxis(value.yAxis2, defaultChartAxis(false), "yAxis2", issues),
536
+ legend: normalizeLegend(value.legend, issues),
537
+ stacking: pick(value.stacking, CHART_STACKINGS, "none", "stacking", issues),
538
+ orientation: pick(value.orientation, CHART_ORIENTATIONS, "vertical", "orientation", issues),
539
+ curve: pick(value.curve, CHART_CURVES, "linear", "curve", issues),
540
+ points: flag(value.points, true, "points", issues),
541
+ dataLabels: flag(value.dataLabels, false, "dataLabels", issues),
542
+ labelPosition: pick(value.labelPosition, CHART_LABEL_POSITIONS, "auto", "labelPosition", issues),
543
+ palette,
544
+ innerRadius,
545
+ gapWidth,
546
+ overlap,
547
+ bins,
548
+ showTotal: flag(value.showTotal, true, "showTotal", issues)
549
+ };
550
+ return { spec, issues };
551
+ }
552
+ function cloneChartSpec(spec) {
553
+ return {
554
+ ...spec,
555
+ categories: [...spec.categories],
556
+ series: spec.series.map(cloneChartSeries),
557
+ xAxis: { ...spec.xAxis, format: { ...spec.xAxis.format } },
558
+ yAxis: { ...spec.yAxis, format: { ...spec.yAxis.format } },
559
+ yAxis2: { ...spec.yAxis2, format: { ...spec.yAxis2.format } },
560
+ legend: { ...spec.legend },
561
+ palette: [...spec.palette]
562
+ };
563
+ }
564
+ function cloneChartSeries(series) {
565
+ const copy = { ...series, values: [...series.values] };
566
+ if (series.x) copy.x = [...series.x];
567
+ if (series.sizes) copy.sizes = [...series.sizes];
568
+ if (series.pointColors) copy.pointColors = [...series.pointColors];
569
+ if (series.format) copy.format = { ...series.format };
570
+ if (series.trendlines) copy.trendlines = series.trendlines.map((t) => ({ ...t }));
571
+ return copy;
572
+ }
573
+ function chartDataFromTable(rows, options = {}) {
574
+ let grid = rows.map((r) => [...r]).filter((r) => r.some((c) => c !== null && c !== void 0 && String(c).trim() !== ""));
575
+ if (options.seriesIn === "rows") {
576
+ const width2 = Math.max(0, ...grid.map((r) => r.length));
577
+ grid = Array.from({ length: width2 }, (_, c) => grid.map((r) => r[c]));
578
+ }
579
+ const issues = [];
580
+ if (!grid.length) return { categories: [], series: [], issues };
581
+ const width = Math.max(...grid.map((r) => r.length));
582
+ const header = options.header === "auto" ? grid[0].slice(1).some((c) => c !== null && c !== void 0 && String(c).trim() !== "" && parseChartValue(c) === void 0) : options.header ?? true;
583
+ const names = header ? grid[0] : [];
584
+ const body = header ? grid.slice(1) : grid;
585
+ const categories = body.map((r, i) => {
586
+ const c = r[0];
587
+ return c === null || c === void 0 || String(c).trim() === "" ? `${i + 1}` : String(c).trim();
588
+ });
589
+ const series = [];
590
+ for (let col = 1; col < width; col++) {
591
+ const name = names[col] !== void 0 && String(names[col]).trim() ? String(names[col]).trim() : `Series ${col}`;
592
+ const values = body.map((r, i) => {
593
+ const parsed = parseChartValue(r[col] ?? null);
594
+ if (parsed === void 0) {
595
+ issues.push({ path: `${name}[${i}]`, message: `"${String(r[col])}" in ${name} (${categories[i]}) is not a number; it was left blank.` });
596
+ return null;
597
+ }
598
+ return parsed;
599
+ });
600
+ series.push({ id: `series-${col}`, name, values });
601
+ }
602
+ if (options.xFromFirstColumn) {
603
+ const x = body.map((r, i) => {
604
+ const parsed = parseChartValue(r[0] ?? null);
605
+ if (parsed === void 0) issues.push({ path: `x[${i}]`, message: `"${String(r[0])}" in the first column is not a number; that point has no x.` });
606
+ return parsed ?? null;
607
+ });
608
+ for (const s of series) s.x = [...x];
609
+ }
610
+ return { categories, series, issues };
611
+ }
612
+ function chartDataToTable(spec, options = {}) {
613
+ return [
614
+ [options.categoryHeader ?? "", ...spec.series.map((s) => s.name)],
615
+ ...spec.categories.map((category, i) => [category, ...spec.series.map((s) => s.values[i] ?? null)])
616
+ ];
617
+ }
618
+ function chartColorInputValue(color) {
619
+ if (/^#[0-9a-f]{6}$/i.test(color)) return color.toLowerCase();
620
+ if (/^#[0-9a-f]{3}$/i.test(color)) return `#${[...color.slice(1)].map((c) => c + c).join("")}`.toLowerCase();
621
+ return "#000000";
622
+ }
623
+
624
+ export {
625
+ parseChartColor,
626
+ chartRgbToHex,
627
+ shadeChartColor,
628
+ CHART_PALETTE_SHADES,
629
+ cyclePaletteColor,
630
+ chartLuminance,
631
+ chartContrastRatio,
632
+ chartTextOn,
633
+ chartRgbToOklab,
634
+ chartColorDistance,
635
+ simulateChartVision,
636
+ checkChartColors,
637
+ CHART_TYPES,
638
+ CHART_MARKERS,
639
+ CHART_TRENDLINE_TYPES,
640
+ CHART_LABEL_POSITIONS,
641
+ CHART_AXIS_KINDS,
642
+ CHART_SERIES_TYPES,
643
+ CHART_STACKINGS,
644
+ CHART_CURVES,
645
+ CHART_ORIENTATIONS,
646
+ CHART_LEGEND_POSITIONS,
647
+ CHART_NUMBER_STYLES,
648
+ CHART_TYPE_LABELS,
649
+ DEFAULT_CHART_PALETTE,
650
+ DEFAULT_CHART_THEME,
651
+ CSS_VARIABLE_CHART_THEME,
652
+ isCartesianChart,
653
+ isXyChart,
654
+ chartFamily,
655
+ chartColorsByCategory,
656
+ defaultChartAxis,
657
+ createChartSpec,
658
+ chartSeriesColor,
659
+ chartSliceColor,
660
+ chartPointColor,
661
+ chartSeriesType,
662
+ uniqueChartSeriesId,
663
+ parseChartValue,
664
+ normalizeChartAxis,
665
+ normalizeChartValues,
666
+ normalizeChartTrendline,
667
+ normalizeChartSpec,
668
+ cloneChartSpec,
669
+ cloneChartSeries,
670
+ chartDataFromTable,
671
+ chartDataToTable,
672
+ chartColorInputValue
673
+ };
674
+ //# sourceMappingURL=chunk-OV7UAKIY.js.map