@pixerate/editor 0.47.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.
- package/dist/agent/index.cjs +248 -29
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +18 -6
- package/dist/agent/index.d.ts +18 -6
- package/dist/agent/index.js +228 -9
- package/dist/agent/index.js.map +1 -1
- package/dist/{builtins-xs7rK6ys.d.cts → builtins-1kqwLUnb.d.cts} +1 -1
- package/dist/{builtins-D2nMtw2W.d.ts → builtins-BKYlHH1s.d.ts} +1 -1
- package/dist/chart/index.cjs +216 -3541
- package/dist/chart/index.cjs.map +1 -1
- package/dist/chart/index.d.cts +4 -494
- package/dist/chart/index.d.ts +4 -494
- package/dist/chart/index.js +155 -3480
- package/dist/chart/index.js.map +1 -1
- package/dist/{chunk-5VXI5GED.js → chunk-2CJ3LPYQ.js} +3 -3
- package/dist/{chunk-TXXNBDQX.cjs → chunk-2Q46FKZK.cjs} +113 -34
- package/dist/chunk-2Q46FKZK.cjs.map +1 -0
- package/dist/chunk-3JNRQVTS.cjs +521 -0
- package/dist/chunk-3JNRQVTS.cjs.map +1 -0
- package/dist/chunk-5TWIN7ZJ.js +113 -0
- package/dist/chunk-5TWIN7ZJ.js.map +1 -0
- package/dist/{chunk-PQFAHT5O.js → chunk-DQPBQXKG.js} +27 -94
- package/dist/chunk-DQPBQXKG.js.map +1 -0
- package/dist/{chunk-ZFBBVGCI.js → chunk-EKYHKQPG.js} +243 -243
- package/dist/{chunk-ZFBBVGCI.js.map → chunk-EKYHKQPG.js.map} +1 -1
- package/dist/{chunk-J3R2OWOE.cjs → chunk-FEMIVHN3.cjs} +610 -15
- package/dist/chunk-FEMIVHN3.cjs.map +1 -0
- package/dist/{chunk-C3NWJE3K.js → chunk-GJBIVWB2.js} +11 -672
- package/dist/chunk-GJBIVWB2.js.map +1 -0
- package/dist/chunk-GTZXF4BI.cjs +3424 -0
- package/dist/chunk-GTZXF4BI.cjs.map +1 -0
- package/dist/chunk-JOX6MJW4.cjs +674 -0
- package/dist/chunk-JOX6MJW4.cjs.map +1 -0
- package/dist/{chunk-REAGGLXS.js → chunk-JQ3BB3E6.js} +2 -2
- package/dist/{chunk-H4PICYNL.cjs → chunk-LNKZPIQD.cjs} +113 -180
- package/dist/chunk-LNKZPIQD.cjs.map +1 -0
- package/dist/chunk-OV7UAKIY.js +674 -0
- package/dist/chunk-OV7UAKIY.js.map +1 -0
- package/dist/{chunk-TK2XZK6Z.js → chunk-QFTVLEVN.js} +596 -1
- package/dist/chunk-QFTVLEVN.js.map +1 -0
- package/dist/{chunk-KRGXUQ2Y.js → chunk-T6XU4B54.js} +82 -3
- package/dist/chunk-T6XU4B54.js.map +1 -0
- package/dist/chunk-TGBCBQFV.cjs +113 -0
- package/dist/chunk-TGBCBQFV.cjs.map +1 -0
- package/dist/{chunk-SQC26VQV.cjs → chunk-UN33YOXW.cjs} +191 -191
- package/dist/{chunk-SQC26VQV.cjs.map → chunk-UN33YOXW.cjs.map} +1 -1
- package/dist/chunk-WBOAXDKD.js +3424 -0
- package/dist/chunk-WBOAXDKD.js.map +1 -0
- package/dist/{chunk-LYGEEDNR.cjs → chunk-XP5IUZUO.cjs} +242 -242
- package/dist/chunk-XP5IUZUO.cjs.map +1 -0
- package/dist/{chunk-BBRK6MOI.cjs → chunk-YU4WQE5M.cjs} +100 -100
- package/dist/{chunk-BBRK6MOI.cjs.map → chunk-YU4WQE5M.cjs.map} +1 -1
- package/dist/commands/index.d.cts +1 -1
- package/dist/commands/index.d.ts +1 -1
- package/dist/controller-ChjCCMLl.d.cts +822 -0
- package/dist/controller-DtbEo3om.d.ts +822 -0
- package/dist/export/index.cjs +4 -2
- package/dist/export/index.cjs.map +1 -1
- package/dist/export/index.d.cts +5 -1
- package/dist/export/index.d.ts +5 -1
- package/dist/export/index.js +3 -1
- package/dist/extensions/index.cjs +33 -4
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +4 -2
- package/dist/extensions/index.d.ts +4 -2
- package/dist/extensions/index.js +32 -3
- package/dist/{index-QrqMmZ75.d.cts → index-DoHqT1mq.d.cts} +203 -7
- package/dist/{index-CSUCQYAH.d.ts → index-YLZ0fKqE.d.ts} +203 -7
- package/dist/index.cjs +39 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -7
- package/dist/index.d.ts +9 -7
- package/dist/index.js +33 -7
- package/dist/index.js.map +1 -1
- package/dist/spreadsheet/index.cjs +4 -4
- package/dist/spreadsheet/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-C3NWJE3K.js.map +0 -1
- package/dist/chunk-H4PICYNL.cjs.map +0 -1
- package/dist/chunk-J3R2OWOE.cjs.map +0 -1
- package/dist/chunk-KRGXUQ2Y.js.map +0 -1
- package/dist/chunk-LYGEEDNR.cjs.map +0 -1
- package/dist/chunk-NEOO6QJD.cjs +0 -1182
- package/dist/chunk-NEOO6QJD.cjs.map +0 -1
- package/dist/chunk-PQFAHT5O.js.map +0 -1
- package/dist/chunk-TK2XZK6Z.js.map +0 -1
- package/dist/chunk-TXXNBDQX.cjs.map +0 -1
- package/dist/types-BVqWm3d1.d.cts +0 -320
- package/dist/types-BVqWm3d1.d.ts +0 -320
- /package/dist/{chunk-5VXI5GED.js.map → chunk-2CJ3LPYQ.js.map} +0 -0
- /package/dist/{chunk-REAGGLXS.js.map → chunk-JQ3BB3E6.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-NEOO6QJD.cjs","../src/chart/color.ts","../src/chart/spec.ts","../src/chart/ooxml.ts"],"names":["text","hex","path"],"mappings":"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B,QAAA;AACG,QAAA;AACyC,QAAA;AACR,QAAA;AACxE,QAAA;AACb,MAAA;AACuG,IAAA;AAC3D,IAAA;AACS,MAAA;AACmC,MAAA;AACG,MAAA;AACR,MAAA;AAC7E,IAAA;AAC0E,MAAA;AACmB,MAAA;AACpG,IAAA;AAC0E,IAAA;AACS,IAAA;AACpD,IAAA;AAChC,EAAA;AAC2F,EAAA;AACxD,EAAA;AAC2E,IAAA;AAChC,IAAA;AAC7D,IAAA;AACH,MAAA;AACQ,QAAA;AACuD,QAAA;AACgC,QAAA;AAC3B,QAAA;AACO,QAAA;AACvF,MAAA;AACK,MAAA;AACsF,QAAA;AACtF,MAAA;AAC4F,QAAA;AAC5F,MAAA;AACkG,QAAA;AAClG,MAAA;AACyF,QAAA;AACzF,MAAA;AAC0F,QAAA;AAC1F,MAAA;AAC0D,QAAA;AAC/D,MAAA;AAC0D,QAAA;AAC3D,IAAA;AACA,EAAA;AACyH,EAAA;AACvC,EAAA;AACrD,EAAA;AACU,IAAA;AACgD,IAAA;AACxF,EAAA;AACwC,EAAA;AACN,EAAA;AACA,EAAA;AAEU,EAAA;AAEC,EAAA;AAClC,EAAA;AACG,EAAA;AAC4F,IAAA;AACH,IAAA;AACO,IAAA;AAC9G,EAAA;AACuF,EAAA;AACb,EAAA;AAC4B,EAAA;AACX,EAAA;AACN,EAAA;AAG5C,EAAA;AAGE,EAAA;AAC5C;AH8hBkH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-NEOO6QJD.cjs","sourcesContent":[null,"// Portions ported from gridcraft-main/crates/chart/src/resolve.rs (palette tint cycle) and\n// crates/chart/src/render.rs (`on`: label colour from the fill), and designcraft-main/crates/color/src/cms/mod.rs\n// (`simulate_cvd`, Machado–Oliveira–Fernandes 2009 matrices) (MIT OR Apache-2.0, © ArtCraft Team and contributors).\n// See THIRD_PARTY_NOTICES.md.\n\n/** An sRGB colour with channels from 0 to 1. */\nexport type ChartRgb = [number, number, number];\n\nconst NAMED: Record<string, string> = {\n\tblack: '#000000',\n\twhite: '#ffffff',\n\tred: '#ff0000',\n\tgreen: '#008000',\n\tblue: '#0000ff',\n\tyellow: '#ffff00',\n\torange: '#ffa500',\n\tpurple: '#800080',\n\tgray: '#808080',\n\tgrey: '#808080',\n\tteal: '#008080',\n\tnavy: '#000080',\n\tmaroon: '#800000',\n\tolive: '#808000',\n\tsilver: '#c0c0c0',\n\tlime: '#00ff00',\n\taqua: '#00ffff',\n\tcyan: '#00ffff',\n\tfuchsia: '#ff00ff',\n\tmagenta: '#ff00ff',\n\tpink: '#ffc0cb',\n\tbrown: '#a52a2a'\n};\n\n/**\n * Reads `#rgb`, `#rrggbb`, `#rrggbbaa`, `rgb()` / `rgba()` and a few colour names. Returns\n * `null` for anything else (CSS variables, `hsl()`, …), so callers can leave such colours alone.\n */\nexport function parseChartColor(color: string): ChartRgb | null {\n\tconst text = color.trim().toLowerCase();\n\tconst hex = NAMED[text] ?? text;\n\tlet m = /^#([0-9a-f]{3})$/.exec(hex);\n\tif (m) return [...m[1]].map((c) => parseInt(c + c, 16) / 255) as ChartRgb;\n\tm = /^#([0-9a-f]{6})([0-9a-f]{2})?$/.exec(hex);\n\tif (m) return [0, 2, 4].map((i) => parseInt(m![1].slice(i, i + 2), 16) / 255) as ChartRgb;\n\tm = /^rgba?\\(\\s*([\\d.]+)%?[\\s,]+([\\d.]+)%?[\\s,]+([\\d.]+)%?(?:[\\s,/]+[\\d.]+%?)?\\s*\\)$/.exec(text);\n\tif (m) {\n\t\tconst pct = text.includes('%');\n\t\treturn [m[1], m[2], m[3]].map((v) => Math.max(0, Math.min(1, Number(v) / (pct ? 100 : 255)))) as ChartRgb;\n\t}\n\treturn null;\n}\n\nexport function chartRgbToHex(rgb: ChartRgb): string {\n\treturn `#${rgb.map((c) => Math.round(Math.max(0, Math.min(1, c)) * 255).toString(16).padStart(2, '0')).join('')}`;\n}\n\n/**\n * A lighter (`amount` > 0, towards white) or darker (`amount` < 0, towards black) shade of\n * `color`. Colours it cannot read (CSS variables) come back unchanged.\n */\nexport function shadeChartColor(color: string, amount: number): string {\n\tconst rgb = parseChartColor(color);\n\tif (!rgb || !amount) return color;\n\tconst t = Math.max(-1, Math.min(1, amount));\n\treturn chartRgbToHex(rgb.map((c) => (t > 0 ? c + (1 - c) * t : c * (1 + t))) as ChartRgb);\n}\n\n/** Shades used once a palette is used up: the same hues, lighter then darker (GridCraft's cycle). */\nexport const CHART_PALETTE_SHADES: readonly number[] = [0, 0.4, -0.25, 0.6, -0.5, 0.2];\n\n/** The palette colour for the `index`-th item: the palette in order, then lighter and darker rounds of it. */\nexport function cyclePaletteColor(palette: readonly string[], index: number): string {\n\tconst base = palette[index % palette.length];\n\tconst round = Math.floor(index / palette.length) % CHART_PALETTE_SHADES.length;\n\treturn shadeChartColor(base, CHART_PALETTE_SHADES[round]);\n}\n\nconst toLinear = (c: number) => (c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));\nconst fromLinear = (c: number) => (c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055);\n\n/** WCAG relative luminance (0 black – 1 white). */\nexport function chartLuminance(rgb: ChartRgb): number {\n\tconst [r, g, b] = rgb.map(toLinear);\n\treturn 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/** WCAG contrast ratio (1–21) between two colours, or `null` when either cannot be read. */\nexport function chartContrastRatio(a: string, b: string): number | null {\n\tconst ra = parseChartColor(a);\n\tconst rb = parseChartColor(b);\n\tif (!ra || !rb) return null;\n\tconst la = chartLuminance(ra);\n\tconst lb = chartLuminance(rb);\n\treturn (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\n/**\n * Dark or white text, whichever reads better on `fill`; `fallback` when the fill cannot be\n * read (a CSS variable).\n */\nexport function chartTextOn(fill: string, fallback: string, dark = '#0f172a', light = '#ffffff'): string {\n\tconst rgb = parseChartColor(fill);\n\tif (!rgb) return fallback;\n\tconst darkRatio = chartContrastRatio(fill, dark)!;\n\tconst lightRatio = chartContrastRatio(fill, light)!;\n\treturn darkRatio >= lightRatio ? dark : light;\n}\n\n/** OKLab (Ottosson) from sRGB. */\nexport function chartRgbToOklab(rgb: ChartRgb): [number, number, number] {\n\tconst [r, g, b] = rgb.map(toLinear);\n\tconst l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);\n\tconst m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);\n\tconst s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);\n\treturn [\n\t\t0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n\t\t1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n\t\t0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s\n\t];\n}\n\n/** Perceptual distance between two colours (Euclidean in OKLab; ≈0.02 is just noticeable). */\nexport function chartColorDistance(a: ChartRgb, b: ChartRgb): number {\n\tconst p = chartRgbToOklab(a);\n\tconst q = chartRgbToOklab(b);\n\treturn Math.hypot(p[0] - q[0], p[1] - q[1], p[2] - q[2]);\n}\n\nexport type ChartVision = 'normal' | 'protanopia' | 'deuteranopia' | 'tritanopia';\n\n// Machado, Oliveira & Fernandes (2009), severity 1.0, applied in linear RGB.\nconst CVD: Record<Exclude<ChartVision, 'normal'>, number[][]> = {\n\tprotanopia: [\n\t\t[0.152286, 1.052583, -0.204868],\n\t\t[0.114503, 0.786281, 0.099216],\n\t\t[-0.003882, -0.048116, 1.051998]\n\t],\n\tdeuteranopia: [\n\t\t[0.367322, 0.860646, -0.227968],\n\t\t[0.280085, 0.672501, 0.047413],\n\t\t[-0.01182, 0.04294, 0.968881]\n\t],\n\ttritanopia: [\n\t\t[1.255528, -0.076749, -0.178779],\n\t\t[-0.078411, 0.930809, 0.147602],\n\t\t[0.004733, 0.691367, 0.3039]\n\t]\n};\n\n/** How a colour looks with a colour-vision deficiency (Machado et al. 2009, full severity). */\nexport function simulateChartVision(rgb: ChartRgb, vision: ChartVision): ChartRgb {\n\tif (vision === 'normal') return rgb;\n\tconst lin = rgb.map(toLinear);\n\tconst m = CVD[vision];\n\treturn m.map((row) => fromLinear(Math.max(0, Math.min(1, row[0] * lin[0] + row[1] * lin[1] + row[2] * lin[2])))) as ChartRgb;\n}\n\nexport interface ChartColorPair {\n\ta: number;\n\tb: number;\n\tcolors: [string, string];\n\tdistance: number;\n}\n\nexport interface ChartColorCheck {\n\t/** No problems found. */\n\tok: boolean;\n\t/** Per vision: the closest pair of colours and their OKLab distance. */\n\tclosest: Partial<Record<ChartVision, ChartColorPair>>;\n\t/** Contrast ratio of each colour against the surface (null when unreadable). */\n\tcontrast: (number | null)[];\n\t/** Problems, as sentences. */\n\tissues: string[];\n\t/** Colours that could not be read (CSS variables, `hsl()`); they are skipped. */\n\tskipped: string[];\n}\n\nexport interface ChartColorCheckOptions {\n\t/** The page colour behind the marks (default white). */\n\tsurface?: string;\n\t/** Smallest OKLab distance two series may have (default 0.08). */\n\tminDistance?: number;\n\t/** Smallest contrast ratio against the surface (default 1.5; WCAG asks 3 for graphics). */\n\tminContrast?: number;\n\t/** Visions to check (default all four). */\n\tvisions?: readonly ChartVision[];\n}\n\n/**\n * Checks that series colours can be told apart, with normal vision and the three\n * colour-vision deficiencies, and stand out from the surface. Returns the closest pair per\n * vision and problems as sentences.\n */\nexport function checkChartColors(colors: readonly string[], options: ChartColorCheckOptions = {}): ChartColorCheck {\n\tconst surface = options.surface ?? '#ffffff';\n\tconst minDistance = options.minDistance ?? 0.08;\n\tconst minContrast = options.minContrast ?? 1.5;\n\tconst visions = options.visions ?? (['normal', 'protanopia', 'deuteranopia', 'tritanopia'] as const);\n\tconst parsed = colors.map(parseChartColor);\n\tconst skipped = colors.filter((_, i) => !parsed[i]);\n\tconst issues: string[] = [];\n\tconst closest: ChartColorCheck['closest'] = {};\n\tfor (const vision of visions) {\n\t\tconst seen = parsed.map((c) => (c ? simulateChartVision(c, vision) : null));\n\t\tlet best: ChartColorPair | undefined;\n\t\tfor (let a = 0; a < seen.length; a++) {\n\t\t\tfor (let b = a + 1; b < seen.length; b++) {\n\t\t\t\tif (!seen[a] || !seen[b]) continue;\n\t\t\t\tconst distance = chartColorDistance(seen[a]!, seen[b]!);\n\t\t\t\tif (!best || distance < best.distance) best = { a, b, colors: [colors[a], colors[b]], distance: Math.round(distance * 1000) / 1000 };\n\t\t\t}\n\t\t}\n\t\tif (best) {\n\t\t\tclosest[vision] = best;\n\t\t\tif (best.distance < minDistance) {\n\t\t\t\tconst who = vision === 'normal' ? '' : ` with ${vision}`;\n\t\t\t\tissues.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}).`);\n\t\t\t}\n\t\t}\n\t}\n\tconst contrast = colors.map((c) => {\n\t\tconst ratio = chartContrastRatio(c, surface);\n\t\treturn ratio === null ? null : Math.round(ratio * 100) / 100;\n\t});\n\tcontrast.forEach((ratio, i) => {\n\t\tif (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).`);\n\t});\n\treturn { ok: issues.length === 0, closest, contrast, issues, skipped };\n}\n","import { cyclePaletteColor } from './color.js';\nimport type {\n\tChartAxis,\n\tChartCurve,\n\tChartIssue,\n\tChartLabelPosition,\n\tChartLegend,\n\tChartLegendPosition,\n\tChartMarker,\n\tChartNumberFormat,\n\tChartOrientation,\n\tChartSeries,\n\tChartSeriesType,\n\tChartSpec,\n\tChartStacking,\n\tChartTheme,\n\tChartTrendline,\n\tChartTrendlineType,\n\tChartType,\n\tChartValue\n} from './types.js';\n\nexport const CHART_TYPES: readonly ChartType[] = ['bar', 'line', 'area', 'scatter', 'bubble', 'pie', 'donut', 'radar', 'waterfall', 'histogram', 'box', 'funnel', 'treemap'];\nexport const CHART_MARKERS: readonly ChartMarker[] = ['circle', 'square', 'diamond', 'triangle', 'cross', 'none'];\nexport const CHART_TRENDLINE_TYPES: readonly ChartTrendlineType[] = ['linear', 'exponential', 'logarithmic', 'power', 'polynomial', 'moving-average'];\nexport const CHART_LABEL_POSITIONS: readonly ChartLabelPosition[] = ['auto', 'inside', 'outside', 'center'];\nexport const CHART_AXIS_KINDS: readonly NonNullable<ChartAxis['kind']>[] = ['category', 'date'];\nexport const CHART_SERIES_TYPES: readonly ChartSeriesType[] = ['bar', 'line', 'area', 'scatter'];\nexport const CHART_STACKINGS: readonly ChartStacking[] = ['none', 'stacked', 'percent'];\nexport const CHART_CURVES: readonly ChartCurve[] = ['linear', 'smooth', 'step'];\nexport const CHART_ORIENTATIONS: readonly ChartOrientation[] = ['vertical', 'horizontal'];\nexport const CHART_LEGEND_POSITIONS: readonly ChartLegendPosition[] = ['top', 'bottom', 'left', 'right'];\nexport const CHART_NUMBER_STYLES: readonly NonNullable<ChartNumberFormat['style']>[] = ['number', 'percent', 'currency', 'compact'];\n\n/** Labels for pickers, shared by the React and Svelte components. */\nexport const CHART_TYPE_LABELS: Readonly<Record<ChartType, string>> = {\n\tbar: 'Bar',\n\tline: 'Line',\n\tarea: 'Area',\n\tscatter: 'Scatter',\n\tbubble: 'Bubble',\n\tpie: 'Pie',\n\tdonut: 'Donut',\n\tradar: 'Radar',\n\twaterfall: 'Waterfall',\n\thistogram: 'Histogram',\n\tbox: 'Box plot',\n\tfunnel: 'Funnel',\n\ttreemap: 'Treemap'\n};\n\n/** How a chart type is laid out. */\nexport type ChartFamily = 'cartesian' | 'xy' | 'pie' | 'radar' | 'waterfall' | 'histogram' | 'box' | 'funnel' | 'treemap';\n\n/** A colour-blind-friendly categorical palette (Okabe–Ito order, tuned for light and dark backgrounds). */\nexport const DEFAULT_CHART_PALETTE: readonly string[] = ['#4269d0', '#efb118', '#ff725c', '#6cc5b0', '#3ca951', '#ff8ab7', '#a463f2', '#97bbf5', '#9c6b4e', '#9498a0'];\n\nexport const DEFAULT_CHART_THEME: ChartTheme = {\n\tbackground: '#ffffff',\n\tsurface: '#ffffff',\n\ttext: '#0f172a',\n\tmutedText: '#64748b',\n\tgrid: '#e2e8f0',\n\taxis: '#94a3b8',\n\tselection: '#2563eb',\n\tpositive: '#3ca951',\n\tnegative: '#e5484d',\n\tfontFamily: 'system-ui, -apple-system, \"Segoe UI\", sans-serif',\n\tfontSize: 11,\n\ttitleSize: 15\n};\n\n/**\n * A theme that reads the page's `--background`, `--foreground`, `--muted-foreground`,\n * `--border` and `--primary` colour variables (full colours, as the other components'\n * inline styles use them), with light fallbacks. The React and Svelte canvases use it.\n */\nexport const CSS_VARIABLE_CHART_THEME: ChartTheme = {\n\t...DEFAULT_CHART_THEME,\n\tbackground: 'transparent',\n\tsurface: 'var(--background, #ffffff)',\n\ttext: 'var(--foreground, #0f172a)',\n\tmutedText: 'var(--muted-foreground, #64748b)',\n\tgrid: 'var(--border, #e2e8f0)',\n\taxis: 'var(--muted-foreground, #94a3b8)',\n\tselection: 'var(--primary, #2563eb)',\n\tfontFamily: 'inherit'\n};\n\n/** Bar, line, area and scatter: categories (or numeric x) against values, series may mix. */\nexport function isCartesianChart(type: ChartType): boolean {\n\treturn type === 'bar' || type === 'line' || type === 'area' || type === 'scatter';\n}\n\n/** Whether the chart plots series against a numeric x axis (bubble charts, and scatter charts with x values). */\nexport function isXyChart(spec: Pick<ChartSpec, 'type' | 'series'>): boolean {\n\tif (spec.type === 'bubble') return true;\n\treturn spec.type === 'scatter' && spec.series.some((s) => !s.hidden && Array.isArray(s.x) && s.x.some((v) => v !== null));\n}\n\n/** How a chart is laid out (see `ChartType`). */\nexport function chartFamily(spec: Pick<ChartSpec, 'type' | 'series'>): ChartFamily {\n\tif (isXyChart(spec)) return 'xy';\n\tswitch (spec.type) {\n\t\tcase 'pie':\n\t\tcase 'donut':\n\t\t\treturn 'pie';\n\t\tcase 'radar':\n\t\tcase 'waterfall':\n\t\tcase 'histogram':\n\t\tcase 'box':\n\t\tcase 'funnel':\n\t\tcase 'treemap':\n\t\t\treturn spec.type;\n\t\tcase 'bubble':\n\t\t\treturn 'xy';\n\t\tdefault:\n\t\t\treturn 'cartesian';\n\t}\n}\n\n/** Chart types whose legend lists categories (one colour per category) rather than series. */\nexport function chartColorsByCategory(type: ChartType): boolean {\n\treturn type === 'pie' || type === 'donut' || type === 'funnel' || type === 'treemap';\n}\n\nexport function defaultChartAxis(value: boolean): ChartAxis {\n\treturn { show: true, title: '', min: null, max: null, step: null, gridlines: value, format: {} };\n}\n\n/** A complete spec with every default filled in. */\nexport function createChartSpec(partial: Partial<ChartSpec> = {}): ChartSpec {\n\treturn normalizeChartSpec(partial).spec;\n}\n\n/** The colour a series is drawn with. */\nexport function chartSeriesColor(spec: Pick<ChartSpec, 'palette' | 'series'>, seriesIndex: number): string {\n\tconst series = spec.series[seriesIndex];\n\tif (series?.color) return series.color;\n\treturn cyclePaletteColor(spec.palette.length ? spec.palette : DEFAULT_CHART_PALETTE, seriesIndex);\n}\n\n/** The colour a pie slice is drawn with (by category). */\nexport function chartSliceColor(spec: Pick<ChartSpec, 'palette'>, index: number): string {\n\treturn cyclePaletteColor(spec.palette.length ? spec.palette : DEFAULT_CHART_PALETTE, index);\n}\n\n/** The colour of one bar or point: its override, else the series colour. */\nexport function chartPointColor(spec: Pick<ChartSpec, 'palette' | 'series'>, seriesIndex: number, index: number): string {\n\treturn spec.series[seriesIndex]?.pointColors?.[index] || chartSeriesColor(spec, seriesIndex);\n}\n\n/** How a series is drawn in a cartesian chart. */\nexport function chartSeriesType(spec: Pick<ChartSpec, 'type'>, series: ChartSeries): ChartSeriesType {\n\tif (series.type) return series.type;\n\tif (spec.type === 'bubble') return 'scatter';\n\treturn isCartesianChart(spec.type) ? (spec.type as ChartSeriesType) : 'bar';\n}\n\n/** An id not used by any series (`series-1`, `series-2`, …). */\nexport function uniqueChartSeriesId(series: readonly Pick<ChartSeries, 'id'>[], base = 'series'): string {\n\tconst used = new Set(series.map((s) => s.id));\n\tfor (let i = series.length + 1; ; i++) {\n\t\tconst id = `${base}-${i}`;\n\t\tif (!used.has(id)) return id;\n\t}\n}\n\n/**\n * Reads a number the way people type it in a data grid: `1,234.5`, `12%` (→ 0.12),\n * `$40`, `(3)` (→ -3). Blank → `null`. Returns `undefined` when it is not a number.\n */\nexport function parseChartValue(input: unknown): ChartValue | undefined {\n\tif (input === null || input === undefined) return null;\n\tif (typeof input === 'number') return Number.isFinite(input) ? input : undefined;\n\tif (typeof input !== 'string') return undefined;\n\tlet text = input.trim();\n\tif (!text || text === '-' || text === '—') return null;\n\tlet negative = false;\n\tif (/^\\(.*\\)$/.test(text)) {\n\t\tnegative = true;\n\t\ttext = text.slice(1, -1).trim();\n\t}\n\tconst percent = text.endsWith('%');\n\tif (percent) text = text.slice(0, -1).trim();\n\ttext = text.replace(/^[$€£¥]/, '').replace(/,/g, '').replace(/\\s+/g, '');\n\tif (!/^[-+]?(\\d+\\.?\\d*|\\.\\d+)(e[-+]?\\d+)?$/i.test(text)) return undefined;\n\tlet value = Number(text);\n\tif (percent) value /= 100;\n\tif (negative) value = -value;\n\treturn Number.isFinite(value) ? value : undefined;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value);\n\nfunction pick<T extends string>(value: unknown, options: readonly T[], fallback: T, path: string, issues: ChartIssue[]): T {\n\tif (value === undefined) return fallback;\n\tif (typeof value === 'string' && (options as readonly string[]).includes(value)) return value as T;\n\tissues.push({ path, message: `${path} must be one of ${options.map((o) => `\"${o}\"`).join(', ')}; got ${JSON.stringify(value)}.` });\n\treturn fallback;\n}\n\nfunction text(value: unknown, fallback: string, path: string, issues: ChartIssue[]): string {\n\tif (value === undefined || value === null) return fallback;\n\tif (typeof value === 'string') return value;\n\tif (typeof value === 'number' || typeof value === 'boolean') return String(value);\n\tissues.push({ path, message: `${path} must be text.` });\n\treturn fallback;\n}\n\nfunction flag(value: unknown, fallback: boolean, path: string, issues: ChartIssue[]): boolean {\n\tif (value === undefined) return fallback;\n\tif (typeof value === 'boolean') return value;\n\tissues.push({ path, message: `${path} must be true or false.` });\n\treturn fallback;\n}\n\nfunction bound(value: unknown, path: string, issues: ChartIssue[]): number | null {\n\tif (value === undefined || value === null || value === '') return null;\n\tconst n = typeof value === 'string' ? Number(value) : value;\n\tif (typeof n === 'number' && Number.isFinite(n)) return n;\n\tissues.push({ path, message: `${path} must be a number or null.` });\n\treturn null;\n}\n\nfunction normalizeFormat(value: unknown, path: string, issues: ChartIssue[]): ChartNumberFormat {\n\tif (value === undefined || value === null) return {};\n\tif (!isRecord(value)) {\n\t\tissues.push({ path, message: `${path} must be an object.` });\n\t\treturn {};\n\t}\n\tconst out: ChartNumberFormat = {};\n\tif (value.style !== undefined) out.style = pick(value.style, CHART_NUMBER_STYLES, 'number', `${path}.style`, issues);\n\tif (value.decimals !== undefined) {\n\t\tconst d = Number(value.decimals);\n\t\tif (Number.isInteger(d) && d >= 0 && d <= 10) out.decimals = d;\n\t\telse issues.push({ path: `${path}.decimals`, message: `${path}.decimals must be a whole number from 0 to 10.` });\n\t}\n\tfor (const key of ['currency', 'prefix', 'suffix', 'code'] as const) {\n\t\tif (value[key] !== undefined) out[key] = text(value[key], '', `${path}.${key}`, issues);\n\t}\n\treturn out;\n}\n\nexport function normalizeChartAxis(value: unknown, fallback: ChartAxis, path: string, issues: ChartIssue[] = []): ChartAxis {\n\tif (value === undefined) return { ...fallback, format: { ...fallback.format } };\n\tif (!isRecord(value)) {\n\t\tissues.push({ path, message: `${path} must be an object.` });\n\t\treturn { ...fallback, format: { ...fallback.format } };\n\t}\n\tlet min = 'min' in value ? bound(value.min, `${path}.min`, issues) : fallback.min;\n\tlet max = 'max' in value ? bound(value.max, `${path}.max`, issues) : fallback.max;\n\tif (min !== null && max !== null && min >= max) {\n\t\tissues.push({ path: `${path}.max`, message: `${path}.max (${max}) must be greater than ${path}.min (${min}).` });\n\t\tmin = fallback.min;\n\t\tmax = fallback.max;\n\t}\n\tlet step = 'step' in value ? bound(value.step, `${path}.step`, issues) : fallback.step;\n\tif (step !== null && !(step > 0)) {\n\t\tissues.push({ path: `${path}.step`, message: `${path}.step must be greater than 0 (or null for automatic).` });\n\t\tstep = fallback.step;\n\t}\n\tconst axis: ChartAxis = {\n\t\tshow: flag(value.show, fallback.show, `${path}.show`, issues),\n\t\ttitle: text(value.title, fallback.title, `${path}.title`, issues),\n\t\tmin,\n\t\tmax,\n\t\tstep,\n\t\tgridlines: flag(value.gridlines, fallback.gridlines, `${path}.gridlines`, issues),\n\t\tformat: 'format' in value ? normalizeFormat(value.format, `${path}.format`, issues) : { ...fallback.format }\n\t};\n\tconst kind = 'kind' in value ? (value.kind === null ? undefined : pick(value.kind, CHART_AXIS_KINDS, 'category', `${path}.kind`, issues)) : fallback.kind;\n\tif (kind && kind !== 'category') axis.kind = kind;\n\treturn axis;\n}\n\nfunction normalizeLegend(value: unknown, issues: ChartIssue[]): ChartLegend {\n\tconst fallback: ChartLegend = { show: true, position: 'bottom' };\n\tif (value === undefined) return fallback;\n\tif (!isRecord(value)) {\n\t\tissues.push({ path: 'legend', message: 'legend must be an object.' });\n\t\treturn fallback;\n\t}\n\treturn {\n\t\tshow: flag(value.show, fallback.show, 'legend.show', issues),\n\t\tposition: pick(value.position, CHART_LEGEND_POSITIONS, fallback.position, 'legend.position', issues)\n\t};\n}\n\n/** Values as numbers or nulls; bad entries become null and are reported. */\nexport function normalizeChartValues(value: unknown, length: number | undefined, path: string, issues: ChartIssue[] = []): ChartValue[] {\n\tif (value === undefined) return length ? Array(length).fill(null) : [];\n\tif (!Array.isArray(value)) {\n\t\tissues.push({ path, message: `${path} must be an array of numbers.` });\n\t\treturn length ? Array(length).fill(null) : [];\n\t}\n\tconst out = value.map((v, i) => {\n\t\tconst parsed = parseChartValue(v);\n\t\tif (parsed === undefined) {\n\t\t\tissues.push({ path: `${path}[${i}]`, message: `${path}[${i}] is not a number: ${JSON.stringify(v)}.` });\n\t\t\treturn null;\n\t\t}\n\t\treturn parsed;\n\t});\n\tif (length === undefined) return out;\n\tif (out.length > length) return out.slice(0, length);\n\twhile (out.length < length) out.push(null);\n\treturn out;\n}\n\nfunction normalizeSeries(value: unknown, length: number, used: Set<string>, index: number, issues: ChartIssue[]): ChartSeries | null {\n\tconst path = `series[${index}]`;\n\tif (!isRecord(value)) {\n\t\tissues.push({ path, message: `${path} must be an object with a name and values.` });\n\t\treturn null;\n\t}\n\tlet id = typeof value.id === 'string' && value.id.trim() ? value.id.trim() : '';\n\tif (!id || used.has(id)) {\n\t\tif (id) issues.push({ path: `${path}.id`, message: `Series id \"${id}\" is used twice; the second was renamed.` });\n\t\tfor (let n = index + 1; ; n++) {\n\t\t\tid = `series-${n}`;\n\t\t\tif (!used.has(id)) break;\n\t\t}\n\t}\n\tused.add(id);\n\tconst series: ChartSeries = {\n\t\tid,\n\t\tname: text(value.name, `Series ${index + 1}`, `${path}.name`, issues),\n\t\tvalues: normalizeChartValues(value.values, length, `${path}.values`, issues)\n\t};\n\tif (typeof value.color === 'string' && value.color.trim()) series.color = value.color.trim();\n\tif (value.type !== undefined && value.type !== null) series.type = pick(value.type, CHART_SERIES_TYPES, 'bar', `${path}.type`, issues);\n\tif (value.hidden === true) series.hidden = true;\n\tif (value.axis !== undefined && value.axis !== null && pick(value.axis, ['primary', 'secondary'] as const, 'primary', `${path}.axis`, issues) === 'secondary') series.axis = 'secondary';\n\tfor (const key of ['x', 'sizes'] as const) {\n\t\tif (value[key] === undefined || value[key] === null) continue;\n\t\tconst list = normalizeChartValues(value[key], length, `${path}.${key}`, issues);\n\t\tif (list.some((v) => v !== null)) series[key] = list;\n\t}\n\tif (key(value, 'sizes') && series.sizes?.some((v) => v !== null && v < 0)) {\n\t\tissues.push({ path: `${path}.sizes`, message: `${path}.sizes must not be negative.` });\n\t\tseries.sizes = series.sizes.map((v) => (v !== null && v < 0 ? null : v));\n\t}\n\tif (value.pointColors !== undefined && value.pointColors !== null) {\n\t\tif (!Array.isArray(value.pointColors)) issues.push({ path: `${path}.pointColors`, message: `${path}.pointColors must be an array of colours or nulls.` });\n\t\telse {\n\t\t\tconst colors = Array.from({ length }, (_, i) => {\n\t\t\t\tconst c = (value.pointColors as unknown[])[i];\n\t\t\t\treturn typeof c === 'string' && c.trim() ? c.trim() : null;\n\t\t\t});\n\t\t\tif (colors.some((c) => c !== null)) series.pointColors = colors;\n\t\t}\n\t}\n\tif (value.marker !== undefined && value.marker !== null) series.marker = pick(value.marker, CHART_MARKERS, 'circle', `${path}.marker`, issues);\n\tif (value.curve !== undefined && value.curve !== null) series.curve = pick(value.curve, CHART_CURVES, 'linear', `${path}.curve`, issues);\n\tif (value.dataLabels !== undefined && value.dataLabels !== null) series.dataLabels = flag(value.dataLabels, false, `${path}.dataLabels`, issues);\n\tif (value.format !== undefined && value.format !== null) {\n\t\tconst format = normalizeFormat(value.format, `${path}.format`, issues);\n\t\tif (Object.keys(format).length) series.format = format;\n\t}\n\tif (value.trendlines !== undefined && value.trendlines !== null) {\n\t\tif (!Array.isArray(value.trendlines)) issues.push({ path: `${path}.trendlines`, message: `${path}.trendlines must be an array.` });\n\t\telse {\n\t\t\tconst lines = value.trendlines.map((t, i) => normalizeChartTrendline(t, `${path}.trendlines[${i}]`, issues)).filter((t): t is ChartTrendline => t !== null);\n\t\t\tif (lines.length) series.trendlines = lines;\n\t\t}\n\t}\n\treturn series;\n}\n\nconst key = (value: Record<string, unknown>, name: string) => value[name] !== undefined && value[name] !== null;\n\n/** Checks one trendline; `null` (reported) when it is unusable. */\nexport function normalizeChartTrendline(value: unknown, path: string, issues: ChartIssue[] = []): ChartTrendline | null {\n\tif (!isRecord(value)) {\n\t\tissues.push({ path, message: `${path} must be an object with a type.` });\n\t\treturn null;\n\t}\n\tconst type = pick(value.type, CHART_TRENDLINE_TYPES, 'linear', `${path}.type`, issues);\n\tconst line: ChartTrendline = { type };\n\tconst whole = (name: 'order' | 'period', min: number, max: number) => {\n\t\tif (!key(value, name)) return;\n\t\tconst n = Number(value[name]);\n\t\tif (Number.isInteger(n) && n >= min && n <= max) line[name] = n;\n\t\telse issues.push({ path: `${path}.${name}`, message: `${path}.${name} must be a whole number from ${min} to ${max}.` });\n\t};\n\tif (type === 'polynomial') whole('order', 2, 6);\n\tif (type === 'moving-average') whole('period', 2, 1000);\n\tfor (const name of ['forward', 'backward'] as const) {\n\t\tif (!key(value, name)) continue;\n\t\tconst n = Number(value[name]);\n\t\tif (Number.isFinite(n) && n >= 0) {\n\t\t\tif (n > 0) line[name] = n;\n\t\t} else issues.push({ path: `${path}.${name}`, message: `${path}.${name} must be a number of x units, 0 or more.` });\n\t}\n\tif (key(value, 'intercept')) {\n\t\tconst n = Number(value.intercept);\n\t\tif (!Number.isFinite(n)) issues.push({ path: `${path}.intercept`, message: `${path}.intercept must be a number or null.` });\n\t\telse if (type === 'linear' || type === 'polynomial' || type === 'exponential') {\n\t\t\tif (type === 'exponential' && n <= 0) issues.push({ path: `${path}.intercept`, message: `${path}.intercept must be greater than 0 for an exponential trendline.` });\n\t\t\telse line.intercept = n;\n\t\t} else issues.push({ path: `${path}.intercept`, message: `A ${type} trendline cannot have a fixed intercept; use linear, polynomial or exponential.` });\n\t}\n\tfor (const name of ['showEquation', 'showR2'] as const) if (key(value, name) && flag(value[name], false, `${path}.${name}`, issues)) line[name] = true;\n\tfor (const name of ['name', 'color'] as const) {\n\t\tif (!key(value, name)) continue;\n\t\tconst t = text(value[name], '', `${path}.${name}`, issues).trim();\n\t\tif (t) line[name] = t;\n\t}\n\treturn line;\n}\n\n/**\n * Turns any input (a partial spec, saved JSON, an agent's arguments) into a complete,\n * valid `ChartSpec`. Every value is checked; problems are reported as sentences in\n * `issues` and replaced by defaults, so the result is always drawable. Series values are\n * padded or trimmed to the number of categories.\n */\nexport function normalizeChartSpec(input: unknown): { spec: ChartSpec; issues: ChartIssue[] } {\n\tconst issues: ChartIssue[] = [];\n\tconst value = isRecord(input) ? input : {};\n\tif (input !== undefined && !isRecord(input)) issues.push({ path: '', message: 'A chart must be an object.' });\n\tconst type = pick(value.type, CHART_TYPES, 'bar', 'type', issues);\n\tconst rawSeries = Array.isArray(value.series) ? value.series : [];\n\tif (value.series !== undefined && !Array.isArray(value.series)) issues.push({ path: 'series', message: 'series must be an array.' });\n\n\tlet categories: string[];\n\tif (Array.isArray(value.categories)) categories = value.categories.map((c, i) => text(c, '', `categories[${i}]`, issues));\n\telse {\n\t\tif (value.categories !== undefined) issues.push({ path: 'categories', message: 'categories must be an array of labels.' });\n\t\tconst longest = rawSeries.reduce<number>((n, s) => Math.max(n, isRecord(s) && Array.isArray(s.values) ? s.values.length : 0), 0);\n\t\tcategories = Array.from({ length: longest }, (_, i) => `${i + 1}`);\n\t}\n\n\tconst used = new Set<string>();\n\tconst series = rawSeries.map((s, i) => normalizeSeries(s, categories.length, used, i, issues)).filter((s): s is ChartSeries => s !== null);\n\n\tlet palette = [...DEFAULT_CHART_PALETTE];\n\tif (Array.isArray(value.palette)) {\n\t\tconst colors = value.palette.filter((c): c is string => typeof c === 'string' && c.trim() !== '');\n\t\tif (colors.length) palette = colors;\n\t\telse issues.push({ path: 'palette', message: 'palette must list at least one colour.' });\n\t} else if (value.palette !== undefined) issues.push({ path: 'palette', message: 'palette must be an array of colours.' });\n\n\tconst fraction = (name: string, fallback: number, min: number, max: number) => {\n\t\tif (value[name] === undefined) return fallback;\n\t\tconst r = Number(value[name]);\n\t\tif (value[name] !== null && Number.isFinite(r) && r >= min && r <= max) return r;\n\t\tissues.push({ path: name, message: `${name} must be a number from ${min} to ${max}.` });\n\t\treturn fallback;\n\t};\n\tconst innerRadius = fraction('innerRadius', 0.55, 0, 0.9);\n\tconst gapWidth = fraction('gapWidth', 0.28, 0, 0.9);\n\tconst overlap = fraction('overlap', -0.12, -1, 1);\n\tlet bins: number | null = null;\n\tif (value.bins !== undefined && value.bins !== null) {\n\t\tconst b = Number(value.bins);\n\t\tif (Number.isInteger(b) && b >= 1 && b <= 500) bins = b;\n\t\telse issues.push({ path: 'bins', message: 'bins must be a whole number from 1 to 500, or null for automatic.' });\n\t}\n\n\tconst spec: ChartSpec = {\n\t\tversion: 1,\n\t\ttype,\n\t\ttitle: text(value.title, '', 'title', issues),\n\t\tsubtitle: text(value.subtitle, '', 'subtitle', issues),\n\t\tcategories,\n\t\tseries,\n\t\txAxis: normalizeChartAxis(value.xAxis, defaultChartAxis(false), 'xAxis', issues),\n\t\tyAxis: normalizeChartAxis(value.yAxis, defaultChartAxis(true), 'yAxis', issues),\n\t\tyAxis2: normalizeChartAxis(value.yAxis2, defaultChartAxis(false), 'yAxis2', issues),\n\t\tlegend: normalizeLegend(value.legend, issues),\n\t\tstacking: pick(value.stacking, CHART_STACKINGS, 'none', 'stacking', issues),\n\t\torientation: pick(value.orientation, CHART_ORIENTATIONS, 'vertical', 'orientation', issues),\n\t\tcurve: pick(value.curve, CHART_CURVES, 'linear', 'curve', issues),\n\t\tpoints: flag(value.points, true, 'points', issues),\n\t\tdataLabels: flag(value.dataLabels, false, 'dataLabels', issues),\n\t\tlabelPosition: pick(value.labelPosition, CHART_LABEL_POSITIONS, 'auto', 'labelPosition', issues),\n\t\tpalette,\n\t\tinnerRadius,\n\t\tgapWidth,\n\t\toverlap,\n\t\tbins,\n\t\tshowTotal: flag(value.showTotal, true, 'showTotal', issues)\n\t};\n\treturn { spec, issues };\n}\n\n/** A deep copy (specs are plain JSON). */\nexport function cloneChartSpec(spec: ChartSpec): ChartSpec {\n\treturn {\n\t\t...spec,\n\t\tcategories: [...spec.categories],\n\t\tseries: spec.series.map(cloneChartSeries),\n\t\txAxis: { ...spec.xAxis, format: { ...spec.xAxis.format } },\n\t\tyAxis: { ...spec.yAxis, format: { ...spec.yAxis.format } },\n\t\tyAxis2: { ...spec.yAxis2, format: { ...spec.yAxis2.format } },\n\t\tlegend: { ...spec.legend },\n\t\tpalette: [...spec.palette]\n\t};\n}\n\nexport function cloneChartSeries(series: ChartSeries): ChartSeries {\n\tconst copy: ChartSeries = { ...series, values: [...series.values] };\n\tif (series.x) copy.x = [...series.x];\n\tif (series.sizes) copy.sizes = [...series.sizes];\n\tif (series.pointColors) copy.pointColors = [...series.pointColors];\n\tif (series.format) copy.format = { ...series.format };\n\tif (series.trendlines) copy.trendlines = series.trendlines.map((t) => ({ ...t }));\n\treturn copy;\n}\n\nexport interface ChartTableOptions {\n\t/**\n\t * `columns` (default): the first column holds categories and each other column is a\n\t * series. `rows`: the first row holds categories and each other row is a series.\n\t */\n\tseriesIn?: 'columns' | 'rows';\n\t/**\n\t * The first row (or column for `rows`) holds series names (default `true`). `auto`: only\n\t * when one of its value cells is not a number.\n\t */\n\theader?: boolean | 'auto';\n\t/**\n\t * Read the first column as numeric x values (scatter and bubble charts): every series gets\n\t * them as `x`, and the categories are their labels.\n\t */\n\txFromFirstColumn?: boolean;\n}\n\nexport interface ChartTableData {\n\tcategories: string[];\n\tseries: ChartSeries[];\n\tissues: ChartIssue[];\n}\n\n/**\n * Reads a grid of cells (from CSV, a spreadsheet range or the clipboard) as categories and\n * series. Cells that are not numbers become gaps and are reported.\n */\nexport function chartDataFromTable(rows: ReadonlyArray<ReadonlyArray<unknown>>, options: ChartTableOptions = {}): ChartTableData {\n\tlet grid = rows.map((r) => [...r]).filter((r) => r.some((c) => c !== null && c !== undefined && String(c).trim() !== ''));\n\tif (options.seriesIn === 'rows') {\n\t\tconst width = Math.max(0, ...grid.map((r) => r.length));\n\t\tgrid = Array.from({ length: width }, (_, c) => grid.map((r) => r[c]));\n\t}\n\tconst issues: ChartIssue[] = [];\n\tif (!grid.length) return { categories: [], series: [], issues };\n\tconst width = Math.max(...grid.map((r) => r.length));\n\tconst header = options.header === 'auto' ? grid[0].slice(1).some((c) => c !== null && c !== undefined && String(c).trim() !== '' && parseChartValue(c) === undefined) : (options.header ?? true);\n\tconst names = header ? grid[0] : [];\n\tconst body = header ? grid.slice(1) : grid;\n\tconst categories = body.map((r, i) => {\n\t\tconst c = r[0];\n\t\treturn c === null || c === undefined || String(c).trim() === '' ? `${i + 1}` : String(c).trim();\n\t});\n\tconst series: ChartSeries[] = [];\n\tfor (let col = 1; col < width; col++) {\n\t\tconst name = names[col] !== undefined && String(names[col]).trim() ? String(names[col]).trim() : `Series ${col}`;\n\t\tconst values = body.map((r, i) => {\n\t\t\tconst parsed = parseChartValue(r[col] ?? null);\n\t\t\tif (parsed === undefined) {\n\t\t\t\tissues.push({ path: `${name}[${i}]`, message: `\"${String(r[col])}\" in ${name} (${categories[i]}) is not a number; it was left blank.` });\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn parsed;\n\t\t});\n\t\tseries.push({ id: `series-${col}`, name, values });\n\t}\n\tif (options.xFromFirstColumn) {\n\t\tconst x = body.map((r, i) => {\n\t\t\tconst parsed = parseChartValue(r[0] ?? null);\n\t\t\tif (parsed === undefined) issues.push({ path: `x[${i}]`, message: `\"${String(r[0])}\" in the first column is not a number; that point has no x.` });\n\t\t\treturn parsed ?? null;\n\t\t});\n\t\tfor (const s of series) s.x = [...x];\n\t}\n\treturn { categories, series, issues };\n}\n\n/** The chart's data as a grid: a header row of series names, then one row per category. */\nexport function chartDataToTable(spec: Pick<ChartSpec, 'categories' | 'series'>, options: { categoryHeader?: string } = {}): (string | number | null)[][] {\n\treturn [\n\t\t[options.categoryHeader ?? '', ...spec.series.map((s) => s.name)],\n\t\t...spec.categories.map((category, i) => [category, ...spec.series.map((s) => s.values[i] ?? null)])\n\t];\n}\n\n/** A colour input value (`#rrggbb`) for a CSS colour: `#abc` expands, non-hex colours show as black. */\nexport function chartColorInputValue(color: string): string {\n\tif (/^#[0-9a-f]{6}$/i.test(color)) return color.toLowerCase();\n\tif (/^#[0-9a-f]{3}$/i.test(color)) return `#${[...color.slice(1)].map((c) => c + c).join('')}`.toLowerCase();\n\treturn '#000000';\n}\n","// The DrawingML chart reader follows deckcraft-main/crates/pptx/src/read/chart.rs (group → type mapping,\n// secondary-axis detection by axis id and position, cache / literal data, number-format precedence) and the\n// chartex reader gridcraft-main/crates/xlsx/src/chartex.rs; the \"editable only when it regenerates\" idea is\n// from wordcraft-main/crates/docx/src/chart_spec.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors).\n// See THIRD_PARTY_NOTICES.md.\nimport { parseChartColor, chartRgbToHex } from './color.js';\nimport { chartFamily, chartSeriesType, defaultChartAxis, isCartesianChart, normalizeChartSpec } from './spec.js';\nimport type { ChartAxis, ChartIssue, ChartLegendPosition, ChartMarker, ChartNumberFormat, ChartSeries, ChartSpec, ChartTrendline, ChartTrendlineType, ChartType, ChartValue } from './types.js';\n\nexport const CHART_OOXML_NS = 'http://schemas.openxmlformats.org/drawingml/2006/chart';\nexport const CHART_OOXML_CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml';\nexport const CHART_OOXML_RELATIONSHIP = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart';\n\ntype ParserCtor = { new (): DOMParser };\n\nexport interface ChartOoxmlReadOptions {\n\t/** `DOMParser` constructor (default: the global one; pass jsdom's or xmldom's in Node). */\n\tDOMParser?: ParserCtor;\n}\n\nexport interface ChartOoxmlReadResult {\n\tspec: ChartSpec;\n\t/** Problems and simplifications, as sentences. */\n\tissues: ChartIssue[];\n\t/** Features the file uses that the spec cannot hold (3-D, stock, surface, gradients, …). */\n\tunsupported: string[];\n\t/**\n\t * Nothing was dropped: writing `spec` back gives an equivalent chart. When false, keep the\n\t * original part (or show the chart read-only) if the file must stay as it was.\n\t */\n\tlossless: boolean;\n}\n\nconst MAX_POINTS = 100_000;\n\nfunction parser(options: ChartOoxmlReadOptions): ParserCtor {\n\tconst P = options.DOMParser ?? (globalThis as { DOMParser?: ParserCtor }).DOMParser;\n\tif (!P) throw new Error('Reading chart XML needs a DOMParser. In Node, pass one (e.g. from jsdom or @xmldom/xmldom) as { DOMParser }.');\n\treturn P;\n}\n\nconst kids = (el: Element | null | undefined, name?: string): Element[] => (el ? Array.from(el.childNodes).filter((n): n is Element => n.nodeType === 1 && (!name || (n as Element).localName === name)) : []);\nconst kid = (el: Element | null | undefined, name: string) => kids(el, name)[0];\nconst val = (el: Element | null | undefined, name: string) => kid(el, name)?.getAttribute('val') ?? undefined;\nconst path = (el: Element | null | undefined, ...names: string[]) => names.reduce<Element | undefined>((e, n) => kid(e, n), el ?? undefined);\nconst find = (el: Element | null | undefined, name: string): Element | undefined => {\n\tif (!el) return undefined;\n\tfor (const c of kids(el)) {\n\t\tif (c.localName === name) return c;\n\t\tconst deep = find(c, name);\n\t\tif (deep) return deep;\n\t}\n\treturn undefined;\n};\nconst bool = (v: string | undefined, fallback = false) => (v === undefined ? fallback : v === '1' || v === 'true');\nconst numberOf = (v: string | undefined) => {\n\tif (v === undefined) return null;\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : null;\n};\n\nfunction strings(e: Element | undefined): string[] {\n\tif (!e) return [];\n\tconst cache = find(e, 'strCache') ?? find(e, 'numCache') ?? find(e, 'strLit') ?? find(e, 'numLit');\n\tif (cache) {\n\t\tconst n = Math.min(MAX_POINTS, Number(val(cache, 'ptCount') ?? 0) || 0);\n\t\tconst out: string[] = Array(n).fill('');\n\t\tfor (const pt of kids(cache, 'pt').slice(0, MAX_POINTS)) {\n\t\t\tconst i = Number(pt.getAttribute('idx') ?? 0);\n\t\t\tif (!Number.isInteger(i) || i < 0 || i >= MAX_POINTS) continue;\n\t\t\twhile (out.length <= i) out.push('');\n\t\t\tout[i] = kid(pt, 'v')?.textContent ?? '';\n\t\t}\n\t\treturn out;\n\t}\n\tconst multi = find(e, 'multiLvlStrCache');\n\tif (multi) {\n\t\tconst lvl = kid(multi, 'lvl');\n\t\treturn kids(lvl, 'pt').map((p) => kid(p, 'v')?.textContent ?? '');\n\t}\n\tconst v = kid(e, 'v') ?? find(e, 'v');\n\treturn v ? [v.textContent ?? ''] : [];\n}\n\nconst numbers = (e: Element | undefined): ChartValue[] => strings(e).map((s) => (s.trim() === '' ? null : numberOf(s.trim())));\n\nfunction formatCodeOf(e: Element | undefined): string | undefined {\n\treturn find(e, 'numCache') ? (kid(find(e, 'numCache'), 'formatCode')?.textContent ?? undefined) : undefined;\n}\n\nfunction richText(t: Element | undefined): string | undefined {\n\tif (!t) return undefined;\n\tconst tx = kid(t, 'tx');\n\tconst rich = kid(tx, 'rich');\n\tif (rich) return kids(rich, 'p').map((p) => Array.from(p.getElementsByTagNameNS('*', 't')).map((x) => x.textContent ?? '').join('')).join('\\n');\n\tconst v = find(tx, 'v');\n\treturn v?.textContent ?? undefined;\n}\n\nfunction solidColor(spPr: Element | undefined): string | undefined {\n\tconst fill = kid(spPr, 'solidFill') ?? kid(kid(spPr, 'ln'), 'solidFill');\n\tconst srgb = kid(fill, 'srgbClr');\n\tconst v = srgb?.getAttribute('val');\n\treturn v && /^[0-9a-f]{6}$/i.test(v) ? `#${v.toLowerCase()}` : undefined;\n}\n\nconst MARKERS: Record<string, ChartMarker> = { circle: 'circle', dot: 'circle', square: 'square', diamond: 'diamond', triangle: 'triangle', x: 'cross', plus: 'cross', star: 'cross', dash: 'square', none: 'none' };\nconst TREND_IN: Record<string, ChartTrendlineType> = { linear: 'linear', exp: 'exponential', log: 'logarithmic', poly: 'polynomial', power: 'power', movingAvg: 'moving-average' };\nconst TREND_OUT: Record<ChartTrendlineType, string> = { linear: 'linear', exponential: 'exp', logarithmic: 'log', polynomial: 'poly', power: 'power', 'moving-average': 'movingAvg' };\nconst LEGEND_IN: Record<string, ChartLegendPosition> = { b: 'bottom', t: 'top', l: 'left', r: 'right', tr: 'right' };\n\n/**\n * Reads a DrawingML chart part (`c:chartSpace`, e.g. `word/charts/chart1.xml`) or an Office\n * 2016 chartex part (`cx:chartSpace`: waterfall, funnel, treemap, histogram, box & whisker)\n * as a chart spec. Data comes from the cached values. Never throws for a malformed chart: it\n * returns an empty chart and says why.\n */\nexport function chartFromOoxml(xml: string, options: ChartOoxmlReadOptions = {}): ChartOoxmlReadResult {\n\tconst issues: ChartIssue[] = [];\n\tconst unsupported: string[] = [];\n\tlet doc: Document;\n\ttry {\n\t\tdoc = new (parser(options))().parseFromString(xml, 'application/xml');\n\t} catch (error) {\n\t\treturn { spec: normalizeChartSpec({}).spec, issues: [{ path: '', message: `The chart XML could not be read: ${(error as Error).message}` }], unsupported, lossless: false };\n\t}\n\tconst root = doc.documentElement;\n\tif (!root || root.localName !== 'chartSpace' || doc.getElementsByTagName('parsererror').length) {\n\t\treturn { spec: normalizeChartSpec({}).spec, issues: [{ path: '', message: 'This is not a chart part: expected a c:chartSpace (or cx:chartSpace) root element.' }], unsupported, lossless: false };\n\t}\n\tif (root.namespaceURI?.includes('chartex') || kid(root, 'chartData')) return readChartex(root, issues, unsupported);\n\n\tconst chart = kid(root, 'chart');\n\tconst plot = kid(chart, 'plotArea');\n\tconst input: Record<string, unknown> = { type: 'bar' };\n\tconst titleEl = kid(chart, 'title');\n\tif (titleEl && !bool(val(chart, 'autoTitleDeleted'))) input.title = richText(titleEl) ?? '';\n\tconst legend = kid(chart, 'legend');\n\tinput.legend = { show: !!legend, position: LEGEND_IN[val(legend, 'legendPos') ?? 'r'] ?? 'right' };\n\tif (kid(root, 'roundedCorners') && bool(val(root, 'roundedCorners'))) unsupported.push('rounded corners');\n\n\tconst valAxes = kids(plot, 'valAx');\n\tconst vertical = (a: Element) => ['l', 'r'].includes(val(a, 'axPos') ?? '');\n\tconst secondaryPlaced = (a: Element) => val(a, 'crosses') === 'max' || ['r', 't'].includes(val(a, 'axPos') ?? '');\n\tconst groupValAxis = (g: Element) => {\n\t\tconst ids = kids(g, 'axId').map((a) => a.getAttribute('val'));\n\t\tconst axes = ids.map((id) => valAxes.find((a) => val(a, 'axId') === id)).filter((a): a is Element => !!a);\n\t\treturn axes.find(vertical) ?? axes[0];\n\t};\n\tconst groups = kids(plot).filter((g) => g.localName.endsWith('Chart'));\n\tconst used = groups.map(groupValAxis).filter((a): a is Element => !!a);\n\tconst primaryAxis = used.find((a) => !secondaryPlaced(a)) ?? used[0];\n\n\tconst series: ChartSeries[] = [];\n\tlet categories: string[] = [];\n\tconst types: ChartType[] = [];\n\tlet stacking: ChartSpec['stacking'] = 'none';\n\tlet orientation: ChartSpec['orientation'] = 'vertical';\n\tlet dataLabels = false;\n\tlet points = false;\n\tfor (const g of groups) {\n\t\tconst name = g.localName;\n\t\tconst grouping = val(g, 'grouping') ?? 'clustered';\n\t\tlet type: ChartType;\n\t\tif (/3D/.test(name)) unsupported.push(`3-D ${name.replace(/3DChart$/, '')} chart (drawn flat)`);\n\t\tswitch (name) {\n\t\t\tcase 'barChart':\n\t\t\tcase 'bar3DChart':\n\t\t\t\ttype = 'bar';\n\t\t\t\tif (val(g, 'barDir') === 'bar') orientation = 'horizontal';\n\t\t\t\tbreak;\n\t\t\tcase 'lineChart':\n\t\t\tcase 'line3DChart':\n\t\t\t\ttype = 'line';\n\t\t\t\t// Points are on when a series shows a marker (the group's marker flag is usually set).\n\t\t\t\tif (kids(g, 'ser').some((s) => (val(kid(s, 'marker'), 'symbol') ?? (bool(val(g, 'marker'), true) ? 'auto' : 'none')) !== 'none')) points = true;\n\t\t\t\tbreak;\n\t\t\tcase 'areaChart':\n\t\t\tcase 'area3DChart':\n\t\t\t\ttype = 'area';\n\t\t\t\tbreak;\n\t\t\tcase 'pieChart':\n\t\t\tcase 'pie3DChart':\n\t\t\tcase 'ofPieChart':\n\t\t\t\ttype = 'pie';\n\t\t\t\tif (name === 'ofPieChart') unsupported.push('pie-of-pie (drawn as one pie)');\n\t\t\t\tbreak;\n\t\t\tcase 'doughnutChart':\n\t\t\t\ttype = 'donut';\n\t\t\t\tinput.innerRadius = Math.min(0.9, Math.max(0, (numberOf(val(g, 'holeSize')) ?? 50) / 100));\n\t\t\t\tbreak;\n\t\t\tcase 'scatterChart':\n\t\t\t\ttype = 'scatter';\n\t\t\t\tif (/line/i.test(val(g, 'scatterStyle') ?? '')) points = true;\n\t\t\t\tbreak;\n\t\t\tcase 'bubbleChart':\n\t\t\t\ttype = 'bubble';\n\t\t\t\tbreak;\n\t\t\tcase 'radarChart':\n\t\t\t\ttype = 'radar';\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tunsupported.push(`${name.replace(/Chart$/, '')} chart`);\n\t\t\t\tcontinue;\n\t\t}\n\t\tif (grouping === 'stacked') stacking = 'stacked';\n\t\telse if (grouping === 'percentStacked') stacking = 'percent';\n\t\tconst gap = numberOf(val(g, 'gapWidth'));\n\t\tconst overlap = numberOf(val(g, 'overlap'));\n\t\tif (type === 'bar') {\n\t\t\t// gapWidth is the gap between groups as a % of one bar; ours is a fraction of the category.\n\t\t\tconst n = Math.max(1, kids(g, 'ser').length);\n\t\t\tconst g100 = (gap ?? 150) / 100;\n\t\t\tconst o = Math.max(-1, Math.min(1, (overlap ?? (grouping === 'clustered' ? 0 : 100)) / 100));\n\t\t\tconst barsWidth = grouping === 'clustered' ? n - (n - 1) * o : 1;\n\t\t\tinput.gapWidth = Math.round(Math.max(0, Math.min(0.9, g100 / (barsWidth + g100))) * 100) / 100;\n\t\t\tif (grouping === 'clustered') input.overlap = o;\n\t\t}\n\t\tif (bool(val(kid(g, 'dLbls'), 'showVal'))) dataLabels = true;\n\t\ttypes.push(type);\n\t\tconst axis = groupValAxis(g);\n\t\tconst secondary = !!axis && !!primaryAxis && axis !== primaryAxis;\n\t\tfor (const s of kids(g, 'ser').slice(0, 1000)) {\n\t\t\tconst out: ChartSeries = { id: `series-${series.length + 1}`, name: strings(kid(s, 'tx'))[0] ?? `Series ${series.length + 1}`, values: [] };\n\t\t\tconst color = solidColor(kid(s, 'spPr'));\n\t\t\tif (color) out.color = color;\n\t\t\tout.type = type === 'bubble' ? undefined : (type as ChartSeries['type']);\n\t\t\tif (secondary) out.axis = 'secondary';\n\t\t\tconst marker = val(kid(s, 'marker'), 'symbol');\n\t\t\t// Circles (and none on a line chart without points) are the defaults; other shapes are kept.\n\t\t\tif (marker && MARKERS[marker] && MARKERS[marker] !== 'circle' && (type === 'scatter' || MARKERS[marker] !== 'none') && (type === 'line' || type === 'scatter')) out.marker = MARKERS[marker];\n\t\t\tif (bool(val(s, 'smooth'))) out.curve = 'smooth';\n\t\t\tconst sl = kid(s, 'dLbls');\n\t\t\tif (sl && val(sl, 'showVal') !== undefined) out.dataLabels = bool(val(sl, 'showVal'));\n\t\t\tconst labelCode = [kid(sl, 'numFmt'), kid(kid(g, 'dLbls'), 'numFmt')].find((n) => n && n.getAttribute('sourceLinked') !== '1')?.getAttribute('formatCode');\n\t\t\tconst valueEl = kid(s, 'val') ?? kid(s, 'yVal');\n\t\t\tconst code = labelCode ?? formatCodeOf(valueEl);\n\t\t\tif (code && code !== 'General') out.format = { code };\n\t\t\tconst catEl = kid(s, 'cat') ?? kid(s, 'xVal');\n\t\t\tconst cats = strings(catEl);\n\t\t\tif (type === 'scatter' || type === 'bubble') {\n\t\t\t\tconst xs = cats.map((c) => numberOf(c.trim()));\n\t\t\t\tif (xs.some((x) => x !== null)) out.x = xs;\n\t\t\t}\n\t\t\tif (!categories.length && cats.length) categories = cats;\n\t\t\tout.values = numbers(valueEl);\n\t\t\tif (type === 'bubble') out.sizes = numbers(kid(s, 'bubbleSize'));\n\t\t\tconst dPts = kids(s, 'dPt');\n\t\t\tif (dPts.length) {\n\t\t\t\tconst pc: (string | null)[] = [];\n\t\t\t\tfor (const dp of dPts) {\n\t\t\t\t\tconst i = Number(val(dp, 'idx'));\n\t\t\t\t\tconst c = solidColor(kid(dp, 'spPr'));\n\t\t\t\t\tif (Number.isInteger(i) && i >= 0 && i < MAX_POINTS && c) {\n\t\t\t\t\t\twhile (pc.length <= i) pc.push(null);\n\t\t\t\t\t\tpc[i] = c;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (pc.some(Boolean)) out.pointColors = pc;\n\t\t\t}\n\t\t\tconst trends: ChartTrendline[] = [];\n\t\t\tfor (const t of kids(s, 'trendline')) {\n\t\t\t\tconst kind = TREND_IN[val(t, 'trendlineType') ?? ''];\n\t\t\t\tif (!kind) continue;\n\t\t\t\tconst line: ChartTrendline = { type: kind };\n\t\t\t\tconst order = numberOf(val(t, 'order'));\n\t\t\t\tconst period = numberOf(val(t, 'period'));\n\t\t\t\tif (kind === 'polynomial' && order) line.order = order;\n\t\t\t\tif (kind === 'moving-average' && period) line.period = period;\n\t\t\t\tfor (const k of ['forward', 'backward', 'intercept'] as const) {\n\t\t\t\t\tconst n = numberOf(val(t, k));\n\t\t\t\t\tif (n !== null && (k === 'intercept' || n > 0)) line[k] = n;\n\t\t\t\t}\n\t\t\t\tif (bool(val(t, 'dispEq'))) line.showEquation = true;\n\t\t\t\tif (bool(val(t, 'dispRSqr'))) line.showR2 = true;\n\t\t\t\tconst tName = kid(t, 'name')?.textContent;\n\t\t\t\tif (tName) line.name = tName;\n\t\t\t\tconst tc = solidColor(kid(t, 'spPr'));\n\t\t\t\tif (tc) line.color = tc;\n\t\t\t\ttrends.push(line);\n\t\t\t}\n\t\t\tif (trends.length) out.trendlines = trends;\n\t\t\tif (kids(s, 'errBars').length) unsupported.push('error bars');\n\t\t\tseries.push(out);\n\t\t}\n\t}\n\tif (!types.length) {\n\t\tissues.push({ path: 'plotArea', message: unsupported.length ? `The chart type is not supported: ${unsupported.join(', ')}.` : 'The chart has no plot groups.' });\n\t\treturn { spec: normalizeChartSpec(input).spec, issues, unsupported, lossless: false };\n\t}\n\tconst first = types[0];\n\tconst combo = types.some((t) => t !== first);\n\tinput.type = combo ? (types.includes('bar') ? 'bar' : first) : first;\n\tif (!combo) for (const s of series) delete s.type;\n\telse for (const s of series) if (s.type === input.type) delete s.type;\n\tif (!combo) for (const s of series) if (s.axis) issues.push({ path: 'series', message: `\"${s.name}\" is on a second value axis.` });\n\tif (types.some((t) => !isCartesianChart(t)) && combo) unsupported.push('a pie or radar mixed with other chart types');\n\tinput.series = series;\n\tinput.categories = categories.length ? categories : Array.from({ length: Math.max(0, ...series.map((s) => s.values.length)) }, (_, i) => `${i + 1}`);\n\tinput.stacking = stacking;\n\tinput.orientation = orientation;\n\tinput.dataLabels = dataLabels;\n\tinput.points = points || first !== 'line';\n\tif (bool(val(groups[0], 'varyColors')) && (first === 'bar' || first === 'line') && series.length === 1) unsupported.push('one colour per point (vary colours)');\n\n\t// Axes\n\tconst readAxis = (a: Element | undefined, fallback: ChartAxis): ChartAxis => {\n\t\tconst axis: ChartAxis = { ...fallback, format: {} };\n\t\tif (!a) return axis;\n\t\taxis.show = !bool(val(a, 'delete'));\n\t\taxis.title = richText(kid(a, 'title')) ?? '';\n\t\taxis.gridlines = !!kid(a, 'majorGridlines');\n\t\tconst scaling = kid(a, 'scaling');\n\t\taxis.min = numberOf(val(scaling, 'min'));\n\t\taxis.max = numberOf(val(scaling, 'max'));\n\t\tif (val(scaling, 'orientation') === 'maxMin') unsupported.push('a reversed axis');\n\t\tif (kid(scaling, 'logBase')) unsupported.push('a logarithmic axis');\n\t\taxis.step = numberOf(val(a, 'majorUnit'));\n\t\tconst fmt = kid(a, 'numFmt');\n\t\tconst code = fmt?.getAttribute('formatCode');\n\t\tif (code && code !== 'General' && fmt?.getAttribute('sourceLinked') !== '1') axis.format = { code };\n\t\treturn axis;\n\t};\n\tconst xy = first === 'scatter' || first === 'bubble';\n\tconst catAxis = kid(plot, 'catAx') ?? kid(plot, 'dateAx') ?? (xy ? valAxes.find((a) => !vertical(a)) : undefined);\n\tinput.xAxis = readAxis(catAxis, defaultChartAxis(false));\n\tif (kid(plot, 'dateAx')) (input.xAxis as ChartAxis).kind = 'date';\n\tinput.yAxis = readAxis(primaryAxis ?? valAxes.find(vertical) ?? valAxes[0], defaultChartAxis(true));\n\tconst secondaryAxis = valAxes.find((a) => a !== primaryAxis && (xy ? vertical(a) && a !== primaryAxis : true) && a !== catAxis);\n\tif (series.some((s) => s.axis === 'secondary') && secondaryAxis) input.yAxis2 = readAxis(secondaryAxis, defaultChartAxis(false));\n\tconst { spec, issues: specIssues } = normalizeChartSpec(input);\n\tissues.push(...specIssues);\n\tconst uniq = [...new Set(unsupported)];\n\treturn { spec, issues, unsupported: uniq, lossless: uniq.length === 0 && specIssues.length === 0 };\n}\n\nfunction readChartex(root: Element, issues: ChartIssue[], unsupported: string[]): ChartOoxmlReadResult {\n\tconst data = new Map<string, { categories: string[]; values: ChartValue[]; code?: string }>();\n\tfor (const d of kids(kid(root, 'chartData'), 'data')) {\n\t\tconst id = d.getAttribute('id') ?? String(data.size);\n\t\tconst strDim = kids(d, 'strDim')[0];\n\t\tconst numDim = kids(d, 'numDim')[0];\n\t\tconst lvl = (dim: Element | undefined) => kids(dim, 'lvl')[0];\n\t\tconst cats = kids(lvl(strDim), 'pt').map((p) => p.textContent ?? '');\n\t\tconst values: ChartValue[] = [];\n\t\tconst nl = lvl(numDim);\n\t\tfor (const p of kids(nl, 'pt')) {\n\t\t\tconst i = Number(p.getAttribute('idx') ?? values.length);\n\t\t\twhile (values.length < i) values.push(null);\n\t\t\tvalues[i] = numberOf((p.textContent ?? '').trim());\n\t\t}\n\t\tdata.set(id, { categories: cats, values, code: nl?.getAttribute('formatCode') ?? undefined });\n\t}\n\tconst chart = kid(root, 'chart');\n\tconst region = path(chart, 'plotArea', 'plotAreaRegion');\n\tconst seriesEls = kids(region, 'series');\n\tconst layouts: Record<string, ChartType> = { waterfall: 'waterfall', funnel: 'funnel', treemap: 'treemap', sunburst: 'treemap', boxWhisker: 'box', clusteredColumn: 'histogram', paretoLine: 'histogram' };\n\tconst layout = seriesEls[0]?.getAttribute('layoutId') ?? '';\n\tconst type = layouts[layout];\n\tif (!type) {\n\t\tissues.push({ path: 'plotArea', message: `The chart layout \"${layout || 'unknown'}\" is not supported.` });\n\t\treturn { spec: normalizeChartSpec({}).spec, issues, unsupported: [layout || 'unknown chartex layout'], lossless: false };\n\t}\n\tif (layout === 'sunburst') unsupported.push('sunburst (drawn as a treemap of the innermost level)');\n\tif (layout === 'paretoLine') unsupported.push('pareto line');\n\tconst series: ChartSeries[] = [];\n\tlet categories: string[] = [];\n\tfor (const s of seriesEls) {\n\t\tif (s.getAttribute('layoutId') === 'paretoLine') continue;\n\t\tconst ref = kid(s, 'dataId')?.getAttribute('val') ?? '0';\n\t\tconst d = data.get(ref);\n\t\tif (!d) continue;\n\t\tif (!categories.length) categories = d.categories;\n\t\tconst out: ChartSeries = { id: `series-${series.length + 1}`, name: strings(kid(s, 'tx'))[0] ?? `Series ${series.length + 1}`, values: d.values };\n\t\tif (d.code && d.code !== 'General') out.format = { code: d.code };\n\t\tseries.push(out);\n\t}\n\tconst title = richText(kid(chart, 'title'));\n\tconst longest = Math.max(0, ...series.map((s) => s.values.length));\n\tconst input: Record<string, unknown> = {\n\t\ttype,\n\t\ttitle: title ?? '',\n\t\tseries,\n\t\tcategories: categories.length >= longest ? categories : Array.from({ length: longest }, (_, i) => categories[i] ?? `${i + 1}`)\n\t};\n\tif (type === 'waterfall') input.showTotal = false;\n\tconst { spec, issues: specIssues } = normalizeChartSpec(input);\n\tissues.push(...specIssues);\n\treturn { spec, issues, unsupported, lossless: unsupported.length === 0 && specIssues.length === 0 };\n}\n\n// ---------------------------------------------------------------------------\n// Writing\n// ---------------------------------------------------------------------------\n\nconst esc = (s: string) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\nconst hex = (color: string | undefined) => {\n\tif (!color) return null;\n\tconst rgb = parseChartColor(color);\n\treturn rgb ? chartRgbToHex(rgb).slice(1).toUpperCase() : null;\n};\nconst fillXml = (color: string | undefined, line = false) => {\n\tconst h = hex(color);\n\tif (!h) return '';\n\treturn line ? `<c:spPr><a:ln w=\"28575\" cap=\"rnd\"><a:solidFill><a:srgbClr val=\"${h}\"/></a:solidFill><a:round/></a:ln></c:spPr>` : `<c:spPr><a:solidFill><a:srgbClr val=\"${h}\"/></a:solidFill></c:spPr>`;\n};\nconst rich = (text: string) => `<c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>${esc(text)}</a:t></a:r></a:p></c:rich></c:tx><c:overlay val=\"0\"/>`;\nconst strLit = (items: readonly string[]) => `<c:strLit><c:ptCount val=\"${items.length}\"/>${items.map((s, i) => `<c:pt idx=\"${i}\"><c:v>${esc(s)}</c:v></c:pt>`).join('')}</c:strLit>`;\nconst numLit = (items: readonly ChartValue[], code = 'General') =>\n\t`<c:numLit><c:formatCode>${esc(code)}</c:formatCode><c:ptCount val=\"${items.length}\"/>${items.map((v, i) => (v === null || !Number.isFinite(v) ? '' : `<c:pt idx=\"${i}\"><c:v>${v}</c:v></c:pt>`)).join('')}</c:numLit>`;\n\nconst CURRENCY_SYMBOL: Record<string, string> = { USD: '$', EUR: '€', GBP: '£', JPY: '¥', CNY: '¥', INR: '₹' };\n\n/** A spreadsheet format code equivalent to a chart number format (`General` when there is none). */\nexport function chartFormatToCode(format: ChartNumberFormat): string {\n\tif (format.code) return format.code;\n\tconst d = format.decimals;\n\tconst frac = (n: number | undefined, fallback: number) => {\n\t\tconst k = n ?? fallback;\n\t\treturn k > 0 ? `.${'0'.repeat(k)}` : '';\n\t};\n\tconst wrap = (body: string) => `${format.prefix ? `\"${format.prefix.replace(/\"/g, '')}\"` : ''}${body}${format.suffix ? `\"${format.suffix.replace(/\"/g, '')}\"` : ''}`;\n\tswitch (format.style) {\n\t\tcase 'percent':\n\t\t\treturn wrap(`#,##0${frac(d, 0)}%`);\n\t\tcase 'currency': {\n\t\t\tconst code = (format.currency || 'USD').toUpperCase();\n\t\t\tconst sym = CURRENCY_SYMBOL[code] ?? `${code} `;\n\t\t\treturn wrap(`\"${sym}\"#,##0${frac(d, 0)}`);\n\t\t}\n\t\tcase 'compact':\n\t\t\treturn wrap('#,##0.0,,\"M\"');\n\t\tdefault:\n\t\t\treturn d !== undefined || format.prefix || format.suffix ? wrap(`#,##0${frac(d, 0)}`) : 'General';\n\t}\n}\n\nconst MARKER_OUT: Record<ChartMarker, string> = { circle: 'circle', square: 'square', diamond: 'diamond', triangle: 'triangle', cross: 'x', none: 'none' };\n\nexport interface ChartOoxmlWriteResult {\n\t/** The `c:chartSpace` part. */\n\txml: string;\n\t/** What could not be written as DrawingML, as sentences (empty when nothing was lost). */\n\tissues: string[];\n}\n\n/**\n * Writes a chart as a DrawingML chart part (`c:chartSpace`) with the data as literals (no\n * embedded workbook), for `word/charts/chartN.xml` or `ppt/charts/chartN.xml`. Waterfall,\n * histogram, box, funnel and treemap charts need Office 2016 chartex parts; they are written\n * as the closest classic chart and reported in `issues`.\n */\nexport function chartToOoxml(spec: ChartSpec): ChartOoxmlWriteResult {\n\tconst issues: string[] = [];\n\tconst family = chartFamily(spec);\n\tlet type = spec.type;\n\tif (family === 'waterfall' || family === 'histogram' || family === 'box' || family === 'funnel') {\n\t\tissues.push(`${spec.type} charts are written as bar charts: DrawingML has no ${spec.type} type (Office 2016 stores it as chartex).`);\n\t\ttype = 'bar';\n\t} else if (family === 'treemap') {\n\t\tissues.push('Treemaps are written as pie charts: DrawingML has no treemap type.');\n\t\ttype = 'pie';\n\t}\n\tconst visible = spec.series.filter((s) => !s.hidden);\n\tif (visible.length < spec.series.length) issues.push(`${spec.series.length - visible.length} hidden series ${spec.series.length - visible.length === 1 ? 'is' : 'are'} left out.`);\n\tconst grouping = spec.stacking === 'stacked' ? 'stacked' : spec.stacking === 'percent' ? 'percentStacked' : 'standard';\n\tconst ids = { cat: 500000001, val: 500000002, cat2: 500000003, val2: 500000004 };\n\tconst xy = family === 'xy' || type === 'scatter' || type === 'bubble';\n\tconst pieLike = type === 'pie' || type === 'donut';\n\tconst typed = (s: ChartSeries) => (pieLike || type === 'radar' ? type : type === 'bubble' ? 'bubble' : xy ? 'scatter' : chartSeriesType({ type }, s));\n\t// Groups: (kind, axis) in first-appearance order.\n\tconst groups: { kind: string; secondary: boolean; series: { s: ChartSeries; index: number }[] }[] = [];\n\tvisible.forEach((s) => {\n\t\tconst kind = typed(s);\n\t\tconst secondary = !pieLike && s.axis === 'secondary';\n\t\tlet g = groups.find((x) => x.kind === kind && x.secondary === secondary);\n\t\tif (!g) groups.push((g = { kind, secondary, series: [] }));\n\t\tg.series.push({ s, index: spec.series.indexOf(s) });\n\t});\n\tif (pieLike && visible.length > 1) issues.push('Only the first series of a pie is drawn; the others are kept in the part.');\n\tconst catLit = strLit(spec.categories);\n\tlet order = 0;\n\tconst serXml = (kind: string, s: ChartSeries, index: number) => {\n\t\tconst parts = [`<c:idx val=\"${index}\"/>`, `<c:order val=\"${order++}\"/>`, `<c:tx><c:v>${esc(s.name)}</c:v></c:tx>`];\n\t\tconst lineLike = kind === 'line' || kind === 'scatter' || kind === 'radar';\n\t\tparts.push(fillXml(s.color, lineLike));\n\t\tif (kind === 'bar' || kind === 'bubble') parts.push('<c:invertIfNegative val=\"0\"/>');\n\t\tif (lineLike) {\n\t\t\tconst marker = s.marker ?? (spec.points || kind === 'scatter' ? 'circle' : 'none');\n\t\t\tparts.push(`<c:marker><c:symbol val=\"${MARKER_OUT[marker]}\"/>${marker !== 'none' ? '<c:size val=\"5\"/>' : ''}</c:marker>`);\n\t\t}\n\t\t(s.pointColors ?? []).forEach((c, i) => {\n\t\t\tconst h = hex(c ?? undefined);\n\t\t\tif (h) parts.push(`<c:dPt><c:idx val=\"${i}\"/>${kind === 'bar' ? '<c:invertIfNegative val=\"0\"/>' : ''}<c:bubble3D val=\"0\"/><c:spPr><a:solidFill><a:srgbClr val=\"${h}\"/></a:solidFill></c:spPr></c:dPt>`);\n\t\t});\n\t\tconst labels = s.dataLabels ?? spec.dataLabels;\n\t\tconst code = s.format ? chartFormatToCode(s.format) : undefined;\n\t\tif (s.dataLabels !== undefined || code)\n\t\t\tparts.push(`<c:dLbls>${code ? `<c:numFmt formatCode=\"${esc(code)}\" sourceLinked=\"0\"/>` : ''}<c:showLegendKey val=\"0\"/><c:showVal val=\"${labels ? 1 : 0}\"/><c:showCatName val=\"0\"/><c:showSerName val=\"0\"/><c:showPercent val=\"0\"/><c:showBubbleSize val=\"0\"/></c:dLbls>`);\n\t\tif (!pieLike && kind !== 'radar')\n\t\t\tfor (const t of s.trendlines ?? []) {\n\t\t\t\tlet x = `<c:trendline>${t.name ? `<c:name>${esc(t.name)}</c:name>` : ''}${fillXml(t.color, true)}<c:trendlineType val=\"${TREND_OUT[t.type]}\"/>`;\n\t\t\t\tif (t.type === 'polynomial') x += `<c:order val=\"${t.order ?? 2}\"/>`;\n\t\t\t\tif (t.type === 'moving-average') x += `<c:period val=\"${t.period ?? 2}\"/>`;\n\t\t\t\tif (t.forward) x += `<c:forward val=\"${t.forward}\"/>`;\n\t\t\t\tif (t.backward) x += `<c:backward val=\"${t.backward}\"/>`;\n\t\t\t\tif (t.intercept !== undefined && t.intercept !== null) x += `<c:intercept val=\"${t.intercept}\"/>`;\n\t\t\t\tx += `<c:dispRSqr val=\"${t.showR2 ? 1 : 0}\"/><c:dispEq val=\"${t.showEquation ? 1 : 0}\"/></c:trendline>`;\n\t\t\t\tparts.push(x);\n\t\t\t}\n\t\tconst valCode = chartFormatToCode(s.format ?? (s.axis === 'secondary' ? spec.yAxis2.format : spec.yAxis.format));\n\t\tif (kind === 'scatter' || kind === 'bubble') {\n\t\t\tconst xs = s.x ?? spec.categories.map((_, i) => i + 1);\n\t\t\tparts.push(`<c:xVal>${numLit(xs)}</c:xVal><c:yVal>${numLit(s.values, valCode)}</c:yVal>`);\n\t\t\tif (kind === 'bubble') parts.push(`<c:bubbleSize>${numLit(s.sizes ?? s.values.map(() => 1))}</c:bubbleSize><c:bubble3D val=\"0\"/>`);\n\t\t\telse parts.push(`<c:smooth val=\"${(s.curve ?? spec.curve) === 'smooth' ? 1 : 0}\"/>`);\n\t\t} else {\n\t\t\tparts.push(`<c:cat>${catLit}</c:cat><c:val>${numLit(s.values, valCode)}</c:val>`);\n\t\t\tif (kind === 'line') parts.push(`<c:smooth val=\"${(s.curve ?? spec.curve) === 'smooth' ? 1 : 0}\"/>`);\n\t\t}\n\t\tif ((s.curve ?? spec.curve) === 'step' && lineLike) issues.push(`\"${s.name}\" uses step lines, which DrawingML cannot express; they are written straight.`);\n\t\tif ((s.curve ?? spec.curve) === 'smooth' && kind === 'area') issues.push(`\"${s.name}\" is a smoothed area, which DrawingML cannot express; it is written straight.`);\n\t\treturn `<c:ser>${parts.join('')}</c:ser>`;\n\t};\n\tconst dLbls = `<c:dLbls><c:showLegendKey val=\"0\"/><c:showVal val=\"${spec.dataLabels ? 1 : 0}\"/><c:showCatName val=\"0\"/><c:showSerName val=\"0\"/><c:showPercent val=\"0\"/><c:showBubbleSize val=\"0\"/></c:dLbls>`;\n\tconst groupXml = groups.map((g) => {\n\t\tconst axes = g.secondary ? `<c:axId val=\"${ids.cat2}\"/><c:axId val=\"${ids.val2}\"/>` : `<c:axId val=\"${ids.cat}\"/><c:axId val=\"${ids.val}\"/>`;\n\t\tconst sers = g.series.map(({ s, index }) => serXml(g.kind, s, index)).join('');\n\t\tswitch (g.kind) {\n\t\t\tcase 'bar': {\n\t\t\t\tconst n = g.series.length;\n\t\t\t\tconst barsWidth = spec.stacking === 'none' ? n - (n - 1) * spec.overlap : 1;\n\t\t\t\tconst gapPct = Math.round(Math.min(500, (spec.gapWidth / Math.max(0.01, 1 - spec.gapWidth)) * barsWidth * 100));\n\t\t\t\tconst overlapPct = spec.stacking === 'none' ? Math.round(spec.overlap * 100) : 100;\n\t\t\t\treturn `<c:barChart><c:barDir val=\"${spec.orientation === 'horizontal' ? 'bar' : 'col'}\"/><c:grouping val=\"${spec.stacking === 'none' ? 'clustered' : grouping}\"/><c:varyColors val=\"0\"/>${sers}${dLbls}<c:gapWidth val=\"${gapPct}\"/><c:overlap val=\"${overlapPct}\"/>${axes}</c:barChart>`;\n\t\t\t}\n\t\t\tcase 'line':\n\t\t\t\treturn `<c:lineChart><c:grouping val=\"${spec.stacking === 'none' ? 'standard' : 'standard'}\"/><c:varyColors val=\"0\"/>${sers}${dLbls}<c:marker val=\"1\"/>${axes}</c:lineChart>`;\n\t\t\tcase 'area':\n\t\t\t\treturn `<c:areaChart><c:grouping val=\"${grouping}\"/><c:varyColors val=\"0\"/>${sers}${dLbls}${axes}</c:areaChart>`;\n\t\t\tcase 'scatter':\n\t\t\t\treturn `<c:scatterChart><c:scatterStyle val=\"lineMarker\"/><c:varyColors val=\"0\"/>${sers}${dLbls}${axes}</c:scatterChart>`;\n\t\t\tcase 'bubble':\n\t\t\t\treturn `<c:bubbleChart><c:varyColors val=\"0\"/>${sers}${dLbls}<c:bubbleScale val=\"100\"/>${axes}</c:bubbleChart>`;\n\t\t\tcase 'radar':\n\t\t\t\treturn `<c:radarChart><c:radarStyle val=\"marker\"/><c:varyColors val=\"0\"/>${sers}${dLbls}${axes}</c:radarChart>`;\n\t\t\tcase 'donut':\n\t\t\t\treturn `<c:doughnutChart><c:varyColors val=\"1\"/>${sers}${dLbls}<c:firstSliceAng val=\"0\"/><c:holeSize val=\"${Math.round(Math.max(10, Math.min(90, spec.innerRadius * 100)))}\"/></c:doughnutChart>`;\n\t\t\tdefault:\n\t\t\t\treturn `<c:pieChart><c:varyColors val=\"1\"/>${sers}${dLbls}<c:firstSliceAng val=\"0\"/></c:pieChart>`;\n\t\t}\n\t});\n\tconst scaling = (a: ChartAxis) => `<c:scaling><c:orientation val=\"minMax\"/>${a.max !== null ? `<c:max val=\"${a.max}\"/>` : ''}${a.min !== null ? `<c:min val=\"${a.min}\"/>` : ''}</c:scaling>`;\n\tconst title = (a: ChartAxis) => (a.title ? `<c:title>${rich(a.title)}</c:title>` : '');\n\tconst fmt = (a: ChartAxis) => {\n\t\tconst code = chartFormatToCode(a.format);\n\t\treturn code !== 'General' ? `<c:numFmt formatCode=\"${esc(code)}\" sourceLinked=\"0\"/>` : `<c:numFmt formatCode=\"${spec.stacking === 'percent' ? '0%' : 'General'}\" sourceLinked=\"1\"/>`;\n\t};\n\tconst horizontal = spec.orientation === 'horizontal';\n\tconst catPos = horizontal ? 'l' : 'b';\n\tconst valPos = horizontal ? 'b' : 'l';\n\tconst valAx = (id: number, cross: number, a: ChartAxis, pos: string, crosses: string, catKind = false) =>\n\t\t`<c:valAx><c:axId val=\"${id}\"/>${scaling(a)}<c:delete val=\"${a.show ? 0 : 1}\"/><c:axPos val=\"${pos}\"/>${a.gridlines ? '<c:majorGridlines/>' : ''}${title(a)}${fmt(a)}<c:majorTickMark val=\"out\"/><c:minorTickMark val=\"none\"/><c:tickLblPos val=\"nextTo\"/><c:crossAx val=\"${cross}\"/><c:crosses val=\"${crosses}\"/><c:crossBetween val=\"${catKind ? 'midCat' : 'between'}\"/>${a.step ? `<c:majorUnit val=\"${a.step}\"/>` : ''}</c:valAx>`;\n\tconst catAx = (id: number, cross: number, a: ChartAxis, pos: string, hidden = false) =>\n\t\t`<c:${a.kind === 'date' ? 'dateAx' : 'catAx'}><c:axId val=\"${id}\"/><c:scaling><c:orientation val=\"minMax\"/></c:scaling><c:delete val=\"${a.show && !hidden ? 0 : 1}\"/><c:axPos val=\"${pos}\"/>${a.gridlines ? '<c:majorGridlines/>' : ''}${title(a)}<c:numFmt formatCode=\"General\" sourceLinked=\"1\"/><c:majorTickMark val=\"out\"/><c:minorTickMark val=\"none\"/><c:tickLblPos val=\"nextTo\"/><c:crossAx val=\"${cross}\"/><c:crosses val=\"autoZero\"/><c:auto val=\"1\"/>${a.kind === 'date' ? '<c:lblOffset val=\"100\"/><c:baseTimeUnit val=\"days\"/>' : '<c:lblAlgn val=\"ctr\"/><c:lblOffset val=\"100\"/><c:noMultiLvlLbl val=\"0\"/>'}</c:${a.kind === 'date' ? 'dateAx' : 'catAx'}>`;\n\tlet axes = '';\n\tif (!pieLike) {\n\t\tif (xy) axes = valAx(ids.cat, ids.val, spec.xAxis, 'b', 'autoZero', true) + valAx(ids.val, ids.cat, spec.yAxis, 'l', 'autoZero', true);\n\t\telse axes = catAx(ids.cat, ids.val, spec.xAxis, catPos) + valAx(ids.val, ids.cat, spec.yAxis, valPos, 'autoZero');\n\t\tif (groups.some((g) => g.secondary)) axes += catAx(ids.cat2, ids.val2, spec.xAxis, catPos, true) + valAx(ids.val2, ids.cat2, spec.yAxis2, horizontal ? 't' : 'r', 'max');\n\t}\n\tconst legendPos = { bottom: 'b', top: 't', left: 'l', right: 'r' }[spec.legend.position];\n\tconst legend = spec.legend.show ? `<c:legend><c:legendPos val=\"${legendPos}\"/><c:overlay val=\"0\"/></c:legend>` : '';\n\tconst titleXml = spec.title ? `<c:title>${rich(spec.title)}</c:title><c:autoTitleDeleted val=\"0\"/>` : '<c:autoTitleDeleted val=\"1\"/>';\n\tif (spec.subtitle) issues.push('The subtitle is left out: DrawingML charts have one title.');\n\tif (family === 'radar' && spec.series.some((s) => s.trendlines?.length)) issues.push('Trendlines on a radar chart are left out.');\n\tconst xml =\n\t\t`<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>` +\n\t\t`<c:chartSpace xmlns:c=\"${CHART_OOXML_NS}\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">` +\n\t\t`<c:roundedCorners val=\"0\"/><c:chart>${titleXml}<c:plotArea><c:layout/>${groupXml.join('')}${axes}</c:plotArea>${legend}<c:plotVisOnly val=\"1\"/><c:dispBlanksAs val=\"gap\"/></c:chart>` +\n\t\t`<c:txPr><a:bodyPr/><a:lstStyle/><a:p><a:pPr><a:defRPr sz=\"${Math.round(10 * 100)}\"/></a:pPr><a:endParaRPr lang=\"en-US\"/></a:p></c:txPr></c:chartSpace>`;\n\treturn { xml, issues: [...new Set(issues)] };\n}\n"]}
|