@aparte/plugin-chartjs 0.0.0-stage → 0.29.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sources":["../src/chartjs.css?raw","../src/source.ts","../src/palette.ts","../src/theme.ts","../src/words.ts","../src/core.ts"],"sourcesContent":["export default \"/* A Chart.js chart in a reply: the bubble's width up to a reading measure, at a set\\n height (`setupChartjs({ height })`, else 18rem) — or at the config's own aspect ratio,\\n when it asks for one. The canvas is sized by the plugin from its frame, so the frame\\n alone carries the size and the canvas is its only child (what Chart.js measures). */\\n.aparte-chartjs {\\n display: block;\\n position: relative;\\n inline-size: 100%;\\n max-inline-size: var(--aparte-chartjs-max-width, 40rem);\\n margin-block: var(--aparte-space-2);\\n}\\n\\n.aparte-chartjs__frame {\\n position: relative;\\n inline-size: 100%;\\n block-size: var(--aparte-chartjs-height, 18rem);\\n}\\n\\n.aparte-chartjs[data-aparte-chartjs-ratio] .aparte-chartjs__frame {\\n block-size: auto;\\n}\\n\\n.aparte-chartjs__canvas {\\n display: block;\\n}\\n\\n/* While the chart is on its way: a quiet box the size the chart will be. */\\n.aparte-chartjs__frame > .aparte-skeleton {\\n display: block;\\n block-size: 100%;\\n}\\n\\n/* A tool whose chart cannot be shown: one quiet line, where the chart would have been. */\\n.aparte-chartjs-note {\\n color: var(--aparte-text-muted);\\n font-size: var(--aparte-font-size-sm);\\n}\\n\"","/**\n * Reading a Chart.js config out of what a model wrote or a tool returned: untrusted\n * JSON, kept as data and cut down to what a chart needs to be drawn harmlessly.\n */\nimport { escapeAttr, escapeHtml } from '@aparte/core';\n\n/** A Chart.js config as JSON can carry it: `{ type, data, options }`. */\nexport interface ChartjsConfig {\n type: string;\n data: { labels?: unknown[]; datasets: Array<Record<string, unknown>>; [key: string]: unknown };\n options?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/** The most data points a chart may carry, all datasets together — and the most labels, and datasets. */\nexport const MAX_POINTS = 5000;\n/** The most ticks a scale may ask for: Chart.js builds one object per tick it is told to make. */\nconst MAX_TICKS = 100;\n/** The most dataset columns the hidden table lists. */\nconst MAX_COLUMNS = 50;\n/** The most cells in the hidden table's body, row names included, whatever the labels and datasets. */\nconst MAX_CELLS = 10_000;\n/**\n * The positions Chart.js' tooltip knows. Any other name is looked up and CALLED on every\n * hover (`positioners[name].call(…)`), and throws.\n */\nconst TOOLTIP_POSITIONS = new Set(['average', 'nearest']);\n/** Deeper than any real config; a bound so a hostile one cannot exhaust the stack. */\nconst MAX_DEPTH = 32;\n/**\n * Keys never copied. The first three are what a merge could follow to a prototype;\n * the others are what Chart.js CALLS on a value it takes for an object of its own (a\n * `pointStyle` object is drawn as an image when `toString()` names one), so a JSON\n * object carrying them as strings would make it throw on every frame.\n */\nconst DROPPED_KEYS = new Set(['__proto__', 'prototype', 'constructor', 'toString', 'valueOf', 'toJSON']);\n\n/**\n * A plain copy of `value`: objects, arrays, strings, finite numbers, booleans and\n * `null` only, without the dropped keys. `undefined` when it is too deep to be a config.\n *\n * Inside an `animation` or `animations` object, `loop` goes too: a looping animation plays\n * as long as the chart is on the page, motion no reader asked for and a frame drawn every\n * 16 ms for nothing.\n */\nfunction plain(value: unknown, depth = 0, animating = false): unknown {\n if (depth > MAX_DEPTH) throw new RangeError('too deep');\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? value : null;\n if (Array.isArray(value)) return value.map((item) => plain(item, depth + 1, animating));\n if (typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value as Record<string, unknown>)) {\n if (DROPPED_KEYS.has(key) || (animating && key === 'loop')) continue;\n const copy = plain(item, depth + 1, animating || key === 'animation' || key === 'animations');\n if (copy !== undefined) out[key] = copy;\n }\n return out;\n }\n return undefined;\n}\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** `parent[key]` when it is an object. */\nconst child = (parent: unknown, key: string): Record<string, unknown> | undefined => {\n const value = isObject(parent) ? parent[key] : undefined;\n return isObject(value) ? value : undefined;\n};\n\n/** Delete `keys` from `target`: options Chart.js calls as functions, which JSON never carries. */\nfunction dropCalls(target: Record<string, unknown> | undefined, keys: string[]): void {\n if (target) for (const key of keys) delete target[key];\n}\n\n/** Whether `Intl` takes `tag`: Chart.js formats every tick with it, and a bad one throws. */\nfunction isLocale(tag: unknown): boolean {\n if (typeof tag !== 'string') return false;\n try { return Intl.NumberFormat.supportedLocalesOf(tag).length > 0; } catch { return false; }\n}\n\n/**\n * The config in `value`, copied and made harmless, or `null` when it is not one Chart.js\n * could draw: a `type`, and `data.datasets` as a list of objects whose `data` are lists.\n *\n * What is taken out, and why — Chart.js draws everything on the canvas, so no string in\n * a config becomes HTML, and JSON carries no function, so no callback can arrive:\n * - the config's own `plugins` list. Chart.js runs each entry's hooks; from JSON an entry\n * is an object with no function in it, so it can do nothing — and a list that can do\n * nothing is better not handed over at all. Options for the plugins the app REGISTERED\n * (`options.plugins.legend`, `.title`, `.tooltip`…) stay; Chart.js ignores the rest;\n * - `options.devicePixelRatio`: it multiplies the canvas' pixels, and a large one asks the\n * browser for a canvas bigger than any it will allocate;\n * - `options.locale` when `Intl` refuses it, which would throw on the first tick;\n * - a string where Chart.js CALLS a function, on every hover: the tooltip's `external`,\n * `filter`, `itemSort` and `callbacks`, the legend labels' `generateLabels`, `filter` and\n * `sort` — and a tooltip `position` that is not one of Chart.js' own;\n * - `loop` in any animation (see `plain`).\n * `options.aspectRatio` is held to 1:4–4:1 for the same reason as the pixel ratio: a tiny\n * one makes the canvas millions of pixels tall. A scale's `ticks.count` and\n * `ticks.maxTicksLimit` are held to 100: Chart.js makes as many ticks as `count` says, and\n * a hundred million of them is a tab out of memory. `maxPoints` bounds the labels and the\n * datasets as well as the points: each is a row or a column of work.\n */\nexport function readChartConfig(value: unknown, maxPoints = MAX_POINTS): ChartjsConfig | null {\n let copy: unknown;\n try { copy = plain(value); } catch { return null; }\n if (!isObject(copy) || typeof copy['type'] !== 'string' || !/^[A-Za-z][\\w-]*$/.test(copy['type'])) return null;\n const data = copy['data'];\n if (!isObject(data) || !Array.isArray(data['datasets'])) return null;\n const datasets = data['datasets'] as unknown[];\n if (!datasets.every((d) => isObject(d) && Array.isArray(d['data']))) return null;\n if (data['labels'] !== undefined && !Array.isArray(data['labels'])) return null;\n const points = datasets.reduce<number>((sum, d) => sum + ((d as Record<string, unknown[]>)['data']!).length, 0);\n const labels = Array.isArray(data['labels']) ? data['labels'].length : 0;\n if (points > maxPoints || labels > maxPoints || datasets.length > maxPoints) return null;\n\n delete copy['plugins'];\n if (copy['options'] !== undefined && !isObject(copy['options'])) delete copy['options'];\n const options = copy['options'] as Record<string, unknown> | undefined;\n if (options) {\n delete options['devicePixelRatio'];\n if (options['locale'] !== undefined && !isLocale(options['locale'])) delete options['locale'];\n const ratio = options['aspectRatio'];\n if (ratio !== undefined) {\n if (typeof ratio !== 'number' || ratio <= 0) delete options['aspectRatio'];\n else options['aspectRatio'] = Math.min(4, Math.max(0.25, ratio));\n }\n const scales = child(options, 'scales');\n for (const id of scales ? Object.keys(scales) : []) {\n const ticks = child(scales![id], 'ticks');\n if (!ticks) continue;\n for (const key of ['count', 'maxTicksLimit']) {\n const n = ticks[key];\n if (n === undefined) continue;\n if (typeof n !== 'number' || n < 1) delete ticks[key];\n else ticks[key] = Math.min(MAX_TICKS, Math.floor(n));\n }\n }\n const plugins = child(options, 'plugins');\n const tooltip = child(plugins, 'tooltip');\n dropCalls(tooltip, ['external', 'filter', 'itemSort', 'callbacks']);\n if (tooltip && tooltip['position'] !== undefined && !TOOLTIP_POSITIONS.has(tooltip['position'] as string)) delete tooltip['position'];\n dropCalls(child(child(plugins, 'legend'), 'labels'), ['generateLabels', 'filter', 'sort']);\n }\n return copy as unknown as ChartjsConfig;\n}\n\n/** Whether a config has a point to draw: a chart still arriving shows nothing until it does. */\nexport function hasPoints(config: ChartjsConfig): boolean {\n return config.data.datasets.some((d) => (d['data'] as unknown[]).length > 0);\n}\n\n/**\n * The longest prefix of a JSON text still being written that is a whole value once its\n * open brackets are closed, closed — or `null` when there is none yet.\n *\n * A Chart.js config is one object, so it parses only once its last brace has arrived:\n * without this, a chart streams as a blank box and appears at the end. The cut points are\n * where a value has just ended — before a comma, after a closing bracket — or a container\n * has just opened, so a half-written string, number or key is never part of the result\n * (`[1, 2` gives `[1]`: the `2` may be the start of `25`).\n */\nexport function closeJson(text: string): string | null {\n const stack: string[] = [];\n let inString = false;\n let escaped = false;\n let cut = -1;\n let closers = '';\n for (let i = 0; i < text.length; i++) {\n const c = text[i]!;\n if (inString) {\n if (escaped) escaped = false;\n else if (c === '\\\\') escaped = true;\n else if (c === '\"') inString = false;\n continue;\n }\n if (c === '\"') inString = true;\n else if (c === '{' || c === '[') {\n stack.push(c === '{' ? '}' : ']');\n cut = i + 1;\n closers = stack.slice().reverse().join('');\n } else if (c === '}' || c === ']') {\n if (stack.pop() !== c) return null;\n cut = i + 1;\n closers = stack.slice().reverse().join('');\n if (stack.length === 0) return text.slice(0, cut);\n } else if (c === ',') {\n cut = i;\n closers = stack.slice().reverse().join('');\n }\n }\n return cut < 0 ? null : text.slice(0, cut) + closers;\n}\n\n/** The config a fence holds: all of it once closed, its closed prefix while it arrives. */\nexport function parseFence(source: string, open: boolean, maxPoints = MAX_POINTS): ChartjsConfig | null {\n const text = open ? closeJson(source) : source;\n if (text === null) return null;\n try { return readChartConfig(JSON.parse(text), maxPoints); } catch { return null; }\n}\n\n/** The title a config gives itself (`options.plugins.title.text`), as one line. */\nexport function titleOf(config: ChartjsConfig): string | undefined {\n const title = ((config.options?.['plugins'] as Record<string, unknown> | undefined)?.['title'] as Record<string, unknown> | undefined)?.['text'];\n const text = Array.isArray(title) ? title.filter((t) => typeof t === 'string').join(' ') : typeof title === 'string' ? title : '';\n return text.trim() || undefined;\n}\n\n/** One value of a dataset as text: a number, or the `y` of a point (with its `r` for a bubble). */\nfunction cell(value: unknown, format: (n: number) => string): string {\n if (typeof value === 'number') return format(value);\n if (typeof value === 'string') return value;\n if (isObject(value)) {\n const y = value['y'];\n const shown = typeof y === 'number' ? format(y) : typeof y === 'string' ? y : '';\n return typeof value['r'] === 'number' ? `${shown} (${format(value['r'])})` : shown;\n }\n return '';\n}\n\n/** The name of a row: its label, else the `x` of its first point among `datasets`, else its rank. */\nfunction rowName(config: ChartjsConfig, datasets: Array<Record<string, unknown>>, index: number, format: (n: number) => string): string {\n const label = config.data.labels?.[index];\n if (typeof label === 'string' || typeof label === 'number') return String(label);\n if (Array.isArray(label)) return label.join(' ');\n for (const dataset of datasets) {\n const point = (dataset['data'] as unknown[])[index];\n if (isObject(point) && (typeof point['x'] === 'number' || typeof point['x'] === 'string')) {\n return typeof point['x'] === 'number' ? format(point['x']) : point['x'];\n }\n }\n return String(index + 1);\n}\n\n/**\n * The data as a table, for a screen reader: one row per label, one column per dataset with\n * data. Every value is escaped; a dataset with no label has an empty header that `relabel`\n * fills with \"Series n\" in the chat's language, as it fills the caption. Held to\n * `MAX_COLUMNS` columns and `MAX_CELLS` cells — a few bytes of empty datasets beside many\n * labels would otherwise be millions of cells — with a last line, filled by `relabel`, when\n * it lists only part of the data.\n */\nexport function dataTable(config: ChartjsConfig, locale?: string): string {\n let formatter: Intl.NumberFormat;\n try { formatter = new Intl.NumberFormat(locale); } catch { formatter = new Intl.NumberFormat(); }\n const format = (n: number): string => formatter.format(n);\n // Each dataset with data, and its rank among all of them, which an unlabelled one is called by.\n const all = config.data.datasets\n .map((d, i) => ({ d, i }))\n .filter(({ d }) => (d['data'] as unknown[]).length > 0);\n const datasets = all.slice(0, MAX_COLUMNS);\n const withData = all.map(({ d }) => d);\n const height = Math.max(config.data.labels?.length ?? 0, ...datasets.map(({ d }) => (d['data'] as unknown[]).length));\n const rows = Math.min(height, Math.floor(MAX_CELLS / (datasets.length + 1)));\n const head = datasets.map(({ d, i }) => {\n const label = typeof d['label'] === 'string' && d['label'].trim() ? d['label'] : '';\n return label\n ? `<th scope=\"col\">${escapeHtml(label)}</th>`\n : `<th scope=\"col\" data-aparte-chartjs-series=\"${escapeAttr(String(i + 1))}\"></th>`;\n }).join('');\n const body: string[] = [];\n for (let r = 0; r < rows; r++) {\n const cells = datasets.map(({ d }) => `<td>${escapeHtml(cell((d['data'] as unknown[])[r], format))}</td>`).join('');\n body.push(`<tr><th scope=\"row\">${escapeHtml(rowName(config, withData, r, format))}</th>${cells}</tr>`); // safe-text: cells are built above, every value through escapeHtml\n }\n const cut = datasets.length < all.length || rows < height\n ? `<tfoot><tr><td colspan=\"${datasets.length + 1}\" data-aparte-chartjs-cut></td></tr></tfoot>` // safe-attr: a count, computed here\n : '';\n return `<table><caption data-aparte-chartjs-name></caption><thead><tr><td></td>${head}</tr></thead>${cut}<tbody>${body.join('')}</tbody></table>`; // safe-text: head and rows are built above, every value through escapeHtml\n}\n","/**\n * The series colours: the chat's `--aparte-primary` first, then a fixed set of hues.\n *\n * The fixed set is the eight hues of a categorical palette validated for a chart surface\n * in BOTH modes (OKLCH lightness 0.43–0.77 on light and 0.48–0.67 on dark, chroma\n * ≥ 0.1, ≥ 3:1 against a white and a near-black surface), each taken at the one step that\n * passes both, so the same eight read on a light and a dark chat without a second table:\n *\n * blue #2a78d6 · orange #d95926 · aqua #199e70 · yellow #c98500 ·\n * magenta #d55181 · green #008300 · violet #9085e9 · red #e34948\n *\n * The primary is the app's, so the order after it cannot be fixed in advance: a brass\n * primary sits on top of the orange and the yellow, a blue one on the blue. So the order\n * is CHOSEN once per primary, by the same measures a palette validator applies (OKLab\n * ΔE×100): a hue is dropped when it is within 15 of the primary under normal vision (a\n * series that reads as the brand's), and each next series is the first remaining hue\n * at least 15 from the one before it under normal vision and at least 8 under simulated\n * protanopia and deuteranopia (Machado 2009, severity 1) — the pairs a stacked bar or a\n * legend puts side by side. Measured over thirteen primaries (aparté's brass in both\n * modes, Tailwind's blue/red/green/violet/teal/pink/orange, black, grey): every one\n * passes those checks, with five to eight hues.\n *\n * A series past the last hue is drawn in the muted text colour, never a recycled hue:\n * a ninth series painted like the first would claim to BE the first. A chart with that\n * many series wants grouping (\"Other\"), not more colours.\n */\n\n/** The fixed hues, in order of preference. */\nexport const SERIES_HUES = ['#2a78d6', '#d95926', '#199e70', '#c98500', '#d55181', '#008300', '#9085e9', '#e34948'] as const;\n\nconst NORMAL_MIN = 15;\nconst CVD_MIN = 8;\nconst MAX_SERIES = 8;\n\ntype Rgb = [number, number, number];\n\n/** `#rgb`, `#rrggbb`, `rgb()`/`rgba()` → 0–255 channels; `null` for anything else. */\nexport function parseColor(color: string): Rgb | null {\n const value = color.trim().toLowerCase();\n const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/.exec(value)?.[1];\n if (hex) {\n const full = hex.length === 3 ? hex.split('').map((c) => c + c).join('') : hex;\n return [0, 2, 4].map((i) => parseInt(full.slice(i, i + 2), 16)) as Rgb;\n }\n const rgb = /^rgba?\\(\\s*([\\d.]+)[\\s,]+([\\d.]+)[\\s,]+([\\d.]+)/.exec(value);\n if (rgb) return [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])];\n return null;\n}\n\n/** `color` at `alpha`, or `color` itself when it cannot be read (a keyword, `oklch()`…). */\nexport function withAlpha(color: string, alpha: number): string {\n const rgb = parseColor(color);\n return rgb ? `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${alpha})` : color;\n}\n\nconst toLinear = (c: number): number => {\n const s = c / 255;\n return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n};\n\n/** Machado, Oliveira & Fernandes (2009), severity 1, on linear sRGB. */\nconst CVD: Record<'protan' | 'deutan', number[][]> = {\n protan: [[0.152286, 1.052583, -0.204868], [0.114503, 0.786281, 0.099216], [-0.003882, -0.048116, 1.051998]],\n deutan: [[0.367322, 0.860646, -0.227968], [0.280085, 0.672501, 0.047413], [-0.011820, 0.042940, 0.968881]],\n};\n\nfunction oklab([r, g, b]: number[]): number[] {\n const l = Math.cbrt(0.4122214708 * r! + 0.5363325363 * g! + 0.0514459929 * b!);\n const m = Math.cbrt(0.2119034982 * r! + 0.6806995451 * g! + 0.1073969566 * b!);\n const s = Math.cbrt(0.0883024619 * r! + 0.2817188376 * g! + 0.6299787005 * b!);\n return [\n 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,\n 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,\n 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s,\n ];\n}\n\n/** OKLab distance ×100 between two colours, as seen normally or under a colour-vision deficiency. */\nfunction distance(a: Rgb, b: Rgb, kind?: 'protan' | 'deutan'): number {\n const see = (c: Rgb): number[] => {\n const lin = c.map(toLinear);\n if (!kind) return oklab(lin);\n return oklab(CVD[kind].map((row) => Math.min(1, Math.max(0, row[0]! * lin[0]! + row[1]! * lin[1]! + row[2]! * lin[2]!))));\n };\n const [x, y] = [see(a), see(b)];\n return 100 * Math.hypot(x[0]! - y[0]!, x[1]! - y[1]!, x[2]! - y[2]!);\n}\n\nconst cache = new Map<string, string[]>();\n\n/**\n * The series colours for a chat whose primary is `primary`: the primary, then the fixed\n * hues in the order chosen for it (see the module's comment). A primary that cannot be\n * read as a colour is kept first, followed by the fixed hues as they are.\n */\nexport function seriesPalette(primary: string): string[] {\n const hit = cache.get(primary);\n if (hit) return hit;\n const brand = parseColor(primary);\n let palette: string[];\n if (!brand) {\n palette = [primary, ...SERIES_HUES].slice(0, MAX_SERIES);\n } else {\n palette = [primary];\n let previous = brand;\n const left = SERIES_HUES.map((hex) => ({ hex, rgb: parseColor(hex)! }))\n .filter(({ rgb }) => distance(rgb, brand) >= NORMAL_MIN);\n while (palette.length < MAX_SERIES) {\n const i = left.findIndex(({ rgb }) => distance(rgb, previous) >= NORMAL_MIN\n && Math.min(distance(rgb, previous, 'protan'), distance(rgb, previous, 'deutan')) >= CVD_MIN);\n if (i < 0) break;\n const [next] = left.splice(i, 1);\n palette.push(next!.hex);\n previous = next!.rgb;\n }\n }\n if (cache.size > 32) cache.clear();\n cache.set(primary, palette);\n return palette;\n}\n","/**\n * The chat's look, as a Chart.js config: the colours and the font a chart takes from the\n * chat's tokens wherever its config does not set its own.\n */\nimport type { ChartjsConfig } from './source.js';\nimport { seriesPalette, withAlpha } from './palette.js';\n\n/** The chat's tokens a chart is drawn in, as colours a canvas reads. */\nexport interface ChartjsTokens {\n /** Text: titles, legends, point labels (`--aparte-text`). */\n text: string;\n /** Quieter text: axis ticks and titles, subtitles (`--aparte-text-muted`). */\n muted: string;\n /** Grid and axis lines, the tooltip's border (`--aparte-border`). */\n border: string;\n /** The tooltip's ground (`--aparte-surface-1`). */\n surface: string;\n /** The page behind the chart: the gap between slices, a radial tick's backdrop (`--aparte-bg`). */\n background: string;\n /** The first series (`--aparte-primary`); the others follow it, see `seriesPalette`. */\n primary: string;\n /** The chat's font (`--aparte-font-family`). */\n fontFamily: string;\n}\n\ntype Obj = Record<string, unknown>;\n\n/** `parent[key]` as an object, made one if it is not set; `null` if it is set to anything else. */\nfunction child(parent: Obj, key: string): Obj | null {\n const value = parent[key];\n if (value === undefined) return (parent[key] = {}) as Obj;\n return typeof value === 'object' && value !== null && !Array.isArray(value) ? value as Obj : null;\n}\n\n/** Set `key` only where the config left it unset: the config's own values win. */\nfunction fill(target: Obj | null, values: Obj): void {\n if (!target) return;\n for (const [key, value] of Object.entries(values)) if (target[key] === undefined) target[key] = value;\n}\n\n/** The scales a chart of this type draws, when its config names none. */\nfunction defaultScales(type: string): string[] {\n if (type === 'radar' || type === 'polarArea') return ['r'];\n if (type === 'pie' || type === 'doughnut') return [];\n return ['x', 'y'];\n}\n\nconst ARCS = new Set(['pie', 'doughnut', 'polarArea']);\n\n/** One dataset with no colour of its own, given its series colour by its type's convention. */\nfunction colourDataset(dataset: Obj, type: string, colour: string, palette: (i: number) => string, tokens: ChartjsTokens): void {\n if (dataset['backgroundColor'] !== undefined || dataset['borderColor'] !== undefined) return;\n if (ARCS.has(type)) {\n // A slice per point, each its own series; the page's colour between them is the gap.\n const count = (dataset['data'] as unknown[]).length;\n const alpha = type === 'polarArea' ? 0.7 : 1;\n dataset['backgroundColor'] = Array.from({ length: count }, (_, i) => withAlpha(palette(i), alpha));\n dataset['borderColor'] = tokens.background;\n fill(dataset, { borderWidth: 2 });\n return;\n }\n if (type === 'line' || type === 'radar') {\n // The area under a line only shows when `fill` is set; the line itself is solid.\n dataset['borderColor'] = colour;\n dataset['backgroundColor'] = withAlpha(colour, 0.2);\n fill(dataset, { pointBackgroundColor: colour, borderWidth: 2 });\n return;\n }\n dataset['borderColor'] = colour;\n dataset['backgroundColor'] = withAlpha(colour, type === 'bar' ? 0.8 : 0.6);\n if (type === 'bar') fill(dataset, { borderWidth: 1, borderRadius: 4 });\n}\n\n/**\n * `config` in the chat's look: a new config, `config` itself untouched. Every value the\n * config sets stays; what it leaves out is taken from `tokens`.\n *\n * - text in `text`, axis ticks and titles in `muted`, grid and axis lines in `border`;\n * - the tooltip on `surface`, with a `border` outline;\n * - the chat's font;\n * - each dataset with no colour of its own in its series colour: the primary, then the\n * fixed hues (`seriesPalette`) — solid lines and borders, translucent fills for bars,\n * areas and points; one colour per slice for a pie, a doughnut or a polar area, with\n * the page's colour between slices. A series past the last hue is drawn in `muted`.\n *\n * The background stays transparent: the chart sits on whatever the bubble is.\n *\n * @example\n * themeChartjsConfig({ type: 'bar', data: { labels: ['a'], datasets: [{ data: [1] }] } }, tokens);\n */\nexport function themeChartjsConfig(config: ChartjsConfig, tokens: ChartjsTokens): ChartjsConfig {\n // A JSON config copies as JSON; Chart.js also takes ownership of the arrays it is given.\n const out = JSON.parse(JSON.stringify(config)) as ChartjsConfig;\n const options = child(out as unknown as Obj, 'options')!;\n fill(options, { color: tokens.text, borderColor: tokens.border });\n fill(child(options, 'font'), { family: tokens.fontFamily });\n\n const plugins = child(options, 'plugins');\n if (plugins) {\n const legend = child(plugins, 'legend');\n if (legend) fill(child(legend, 'labels'), { color: tokens.text });\n fill(child(plugins, 'title'), { color: tokens.text });\n fill(child(plugins, 'subtitle'), { color: tokens.muted });\n fill(child(plugins, 'tooltip'), {\n backgroundColor: tokens.surface, titleColor: tokens.text, bodyColor: tokens.text,\n footerColor: tokens.muted, borderColor: tokens.border, borderWidth: 1,\n });\n }\n\n const scales = child(options, 'scales');\n if (scales) {\n for (const id of defaultScales(out.type)) if (scales[id] === undefined) scales[id] = {};\n for (const id of Object.keys(scales)) {\n const scale = child(scales, id);\n if (!scale) continue;\n fill(child(scale, 'grid'), { color: tokens.border });\n fill(child(scale, 'border'), { color: tokens.border });\n fill(child(scale, 'ticks'), { color: tokens.muted, backdropColor: tokens.background });\n fill(child(scale, 'title'), { color: tokens.muted });\n if (id === 'r' || scale['type'] === 'radialLinear') {\n fill(child(scale, 'angleLines'), { color: tokens.border });\n fill(child(scale, 'pointLabels'), { color: tokens.text });\n }\n }\n // A pie with no scales of its own gets none: every key here is drawn as an axis.\n if (Object.keys(scales).length === 0) delete options['scales'];\n }\n\n const palette = seriesPalette(tokens.primary);\n const colourAt = (i: number): string => palette[i] ?? tokens.muted;\n out.data.datasets.forEach((dataset, i) => {\n const type = typeof dataset['type'] === 'string' ? dataset['type'] : out.type;\n colourDataset(dataset, type, colourAt(i), colourAt, tokens);\n });\n return out;\n}\n\n/** The fallbacks when a token is not set, or cannot be read (no `getComputedStyle`). */\nconst TOKENS: Record<Exclude<keyof ChartjsTokens, 'fontFamily'>, [string, string]> = {\n text: ['--aparte-text', '#1a1a1a'],\n muted: ['--aparte-text-muted', '#666666'],\n border: ['--aparte-border', '#d0d0d0'],\n surface: ['--aparte-surface-1', '#ffffff'],\n background: ['--aparte-bg', '#ffffff'],\n primary: ['--aparte-primary', '#3b5bdb'],\n};\n\nlet scratch: CanvasRenderingContext2D | null | undefined;\n\n/**\n * The chat's tokens at `host`, as colours a canvas and Chart.js' colour helpers read. A\n * token may be `color-mix()` or `oklch()`, which Chart.js cannot lighten for a hover, so\n * each is resolved by the browser — a probe's computed colour — and normalised by a\n * canvas to hex or `rgba()`.\n */\nexport function readTokens(host: HTMLElement): ChartjsTokens {\n const fontFamily = (typeof getComputedStyle === 'function' ? getComputedStyle(host).fontFamily : '')\n || 'system-ui, sans-serif';\n const fallback = Object.fromEntries(Object.entries(TOKENS).map(([k, [, v]]) => [k, v])) as Omit<ChartjsTokens, 'fontFamily'>;\n if (typeof getComputedStyle !== 'function') return { ...fallback, fontFamily };\n const probe = document.createElement('span');\n probe.style.display = 'none';\n host.appendChild(probe);\n scratch ??= document.createElement('canvas').getContext?.('2d') ?? null;\n const read = ([token, fallbackColour]: [string, string]): string => {\n probe.style.color = `var(${token}, ${fallbackColour})`;\n const computed = getComputedStyle(probe).color || fallbackColour;\n if (!scratch) return computed;\n scratch.fillStyle = '#000';\n scratch.fillStyle = computed;\n return String(scratch.fillStyle);\n };\n try {\n return {\n text: read(TOKENS.text), muted: read(TOKENS.muted), border: read(TOKENS.border),\n surface: read(TOKENS.surface), background: read(TOKENS.background), primary: read(TOKENS.primary),\n fontFamily,\n };\n } finally {\n probe.remove();\n }\n}\n\n/** Forget the scratch canvas: for tests that swap `getContext`. */\nexport function resetScratch(): void { scratch = undefined; }\n","/**\n * The words a chart draws, in the chat's language.\n *\n * Core's locale is a closed list of what CORE draws, so this plugin reads its own keys off\n * `getLocale()` and defaults them here: the app's locale first, then this table for the\n * chat's language (the ten `@aparte/locales` ships), then English.\n *\n * aparteGlobalConfig.extendLocale({ chartjsChart: 'Graphique' });\n */\nimport type { AparteConfig } from '@aparte/core';\n\ntype Language = 'fr' | 'de' | 'es' | 'it' | 'ja' | 'ko' | 'pt' | 'ru' | 'zh';\ntype Word = { en: string } & Partial<Record<Language, string>>;\n\nexport const WORDS = {\n /** A chart's accessible name when its config gives it no title. */\n chartjsChart: {\n en: 'Chart', fr: 'Graphique', de: 'Diagramm', es: 'Gráfico', it: 'Grafico',\n ja: 'グラフ', ko: '차트', pt: 'Gráfico', ru: 'Диаграмма', zh: '图表',\n },\n /** The note over the source of a chart that could not be drawn. */\n chartjsError: {\n en: 'This chart could not be drawn.', fr: 'Ce graphique n’a pas pu être tracé.',\n de: 'Dieses Diagramm konnte nicht gezeichnet werden.', es: 'No se pudo dibujar este gráfico.',\n it: 'Impossibile disegnare questo grafico.', ja: 'このグラフは描画できませんでした。',\n ko: '이 차트를 그릴 수 없습니다.', pt: 'Não foi possível desenhar este gráfico.',\n ru: 'Не удалось построить эту диаграмму.', zh: '无法绘制此图表。',\n },\n /** A tool that should have returned a chart and did not run, or failed. */\n chartjsUnavailable: {\n en: 'Chart not available', fr: 'Graphique indisponible', de: 'Diagramm nicht verfügbar',\n es: 'Gráfico no disponible', it: 'Grafico non disponibile', ja: 'グラフは利用できません',\n ko: '차트를 사용할 수 없음', pt: 'Gráfico indisponível', ru: 'Диаграмма недоступна', zh: '图表不可用',\n },\n /** A column of the data table for a dataset with no label. */\n chartjsSeries: {\n en: 'Series {n}', fr: 'Série {n}', de: 'Reihe {n}', es: 'Serie {n}', it: 'Serie {n}',\n ja: '系列 {n}', ko: '계열 {n}', pt: 'Série {n}', ru: 'Ряд {n}', zh: '系列 {n}',\n },\n /** The data table's last line, when it lists only part of the data. */\n chartjsTableCut: {\n en: 'Only part of the data is listed.', fr: 'Seule une partie des données est listée.',\n de: 'Nur ein Teil der Daten ist aufgeführt.', es: 'Solo se muestra una parte de los datos.',\n it: 'È elencata solo una parte dei dati.', ja: 'データの一部のみを表示しています。',\n ko: '데이터의 일부만 표시됩니다.', pt: 'Apenas parte dos dados está listada.',\n ru: 'Показана только часть данных.', zh: '仅列出部分数据。',\n },\n} satisfies Record<string, Word>;\n\nexport type WordKey = keyof typeof WORDS;\n\n/** The language part of the locale's tag: `pt-BR` → `pt`, none → `en`. */\nfunction languageOf(config: AparteConfig): string {\n const tag = config.getLocale().tag;\n return typeof tag === 'string' && tag ? tag.split('-')[0]!.toLowerCase() : 'en';\n}\n\n/** The word for `key`, with `{name}` placeholders filled from `vars`. */\nexport function word(config: AparteConfig, key: WordKey, vars: Record<string, string> = {}): string {\n const fromApp = (config.getLocale() as Record<string, unknown>)[key];\n const table: Word = WORDS[key];\n const text = (typeof fromApp === 'string' && fromApp) || table[languageOf(config) as Language] || table.en;\n return text.replace(/\\{(\\w+)\\}/g, (all, name: string) => vars[name] ?? all);\n}\n","import {\n aparteGlobalConfig,\n escapeAttr,\n escapeHtml,\n type AparteConfig,\n type AparteCodeLanguageContext,\n type AparteToolCallSegment,\n type AparteToolMatcher,\n type AparteToolRenderer,\n} from '@aparte/core';\nimport styles from './chartjs.css?raw';\nimport { MAX_POINTS, dataTable, hasPoints, parseFence, readChartConfig, titleOf, type ChartjsConfig } from './source.js';\nimport { readTokens, themeChartjsConfig } from './theme.js';\nimport { word } from './words.js';\n\nexport { themeChartjsConfig, type ChartjsTokens } from './theme.js';\nexport { readChartConfig, type ChartjsConfig } from './source.js';\nexport { seriesPalette, SERIES_HUES } from './palette.js';\n\n/**\n * The part of a Chart.js chart this plugin calls. Structural, so this module imports\n * nothing from `chart.js` — a bundle that hands over its own `Chart` carries one copy.\n */\nexport interface ChartjsChart {\n options: unknown;\n data: unknown;\n update(mode?: 'none'): void;\n resize(width?: number, height?: number): void;\n destroy(): void;\n}\n\n/** Chart.js' `Chart` class, or anything constructed the same way. */\nexport interface ChartjsConstructor {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Chart.js' own config type, unnamed here so nothing is imported from it.\n new (canvas: HTMLCanvasElement, config: any): ChartjsChart;\n}\n\n/** A function that loads Chart.js on the first chart: the class, or a module carrying it. */\nexport type ChartjsLoader = () => Promise<ChartjsConstructor | { Chart?: ChartjsConstructor; default?: ChartjsConstructor }>;\n\nexport interface ChartjsOptions {\n /**\n * Redraw a chart while its fence arrives, from the part of its JSON that has arrived\n * (default `true`). `false` draws it once the fence has closed.\n */\n progressive?: boolean;\n /**\n * The height of a chart, as a CSS length (default `18rem`). A config that sets `aspectRatio`, or\n * `maintainAspectRatio: true`, is as tall as its width says instead.\n */\n height?: string;\n /**\n * Tools whose result is a Chart.js config, drawn as the chart in place of the tool's\n * row: its `structuredContent`, else its text read as JSON. An exact name, a RegExp,\n * or a list of them.\n */\n tool?: AparteToolMatcher | AparteToolMatcher[];\n /**\n * Turn a tool's own result into a Chart.js config — for a tool that returns data\n * rather than a chart. Given the result (as `tool` reads it) and the call's input.\n * The result is `any`, as in `@aparte/plugin-cards`: its shape is your tool's, and `map` is\n * where you read it.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n map?: (result: any, input: Record<string, unknown>) => unknown;\n /** The most data points a chart may carry, all datasets together — and the most labels, and datasets (default 5000). */\n maxPoints?: number;\n}\n\n/** The fence languages drawn as a chart. */\nexport const CHARTJS_LANGUAGES = ['chartjs', 'chart.js'] as const;\n\n/** The least time between two drawings of a chart still arriving. */\nconst PROGRESSIVE_MS = 300;\nconst CONFIG_ATTR = 'data-aparte-chartjs-config';\n/**\n * How many released frames stay watched for a return. Each holds its detached message alive\n * (a reset canvas, the table) until it comes back or is pushed out, so the count is bounded:\n * the oldest stops being watched first.\n */\nconst PARKED = 32;\nconst STYLE_ATTR = 'data-aparte-chartjs-styles';\n\nconst warned = new WeakSet<object>();\nfunction warnOnce(key: object, message: string, error: unknown): void {\n if (warned.has(key)) return;\n warned.add(key);\n console.warn(`[aparte-chartjs] ${message}`, error);\n}\n\nconst isChartClass = (value: unknown): value is ChartjsConstructor =>\n typeof value === 'function' && typeof (value as { prototype?: { update?: unknown } }).prototype?.update === 'function';\n\nconst reducedMotion = (): boolean =>\n typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n/**\n * Put the chart's stylesheet where `node` is drawn: the document's head, or the shadow\n * root a widget mounted the chat in, which a sheet in the head does not reach. A code\n * language has no `getStyles()` seam, and a tool renderer's goes to the head only.\n */\nfunction ensureStyles(node: Node): void {\n const root = node.getRootNode();\n const target = typeof ShadowRoot !== 'undefined' && root instanceof ShadowRoot ? root\n : root === document ? document.head : null;\n if (!target || target.querySelector(`style[${STYLE_ATTR}]`)) return;\n const sheet = document.createElement('style');\n sheet.setAttribute(STYLE_ATTR, '');\n sheet.textContent = styles;\n target.appendChild(sheet);\n}\n\n/**\n * Draw charts with a Chart.js you provide — its `Chart` class, or a function that loads\n * it on the first chart. The entry point for a bundle that controls what it ships:\n * `@aparte/plugin-chartjs` itself is this, with `() => import('chart.js/auto')`.\n *\n * Draws every ```` ```chartjs ```` (or ```` ```chart.js ````) fence whose body is a\n * Chart.js config as JSON, and — with `options.tool` — the result of the tools named\n * there. The chart takes the chat's colours and font wherever its config leaves them\n * out (`themeChartjsConfig`), and follows a light/dark switch in place.\n *\n * @returns a disposer that unregisters the renderers, stops watching the theme and\n * releases every chart drawn.\n * @example\n * import { Chart, BarController, BarElement, CategoryScale, LinearScale, Tooltip } from 'chart.js';\n * import { setupChartjsWith } from '@aparte/plugin-chartjs/core';\n * Chart.register(BarController, BarElement, CategoryScale, LinearScale, Tooltip);\n * setupChartjsWith(Chart);\n */\nexport function setupChartjsWith(\n chartjs: ChartjsConstructor | ChartjsLoader,\n options: ChartjsOptions = {},\n config: AparteConfig = aparteGlobalConfig,\n): () => void {\n const charts = createCharts(chartjs, config);\n const unregister = config.registerCodeLanguageRenderer(createFenceRenderer(charts, options));\n const matchers = options.tool === undefined ? [] : Array.isArray(options.tool) ? options.tool : [options.tool];\n const toolRenderer = createToolRenderer(charts, options, config);\n for (const match of matchers) config.registerToolRenderer(match, toolRenderer);\n return () => {\n unregister();\n for (const match of matchers) config.unregisterToolRenderer(match);\n charts.stop();\n };\n}\n\n/** One chart on the page: the Chart.js instance and the config it was drawn from. */\ninterface Mounted {\n chart: ChartjsChart;\n config: ChartjsConfig;\n /** Whether it follows its frame's size: `false` when its config says `responsive: false`. */\n responsive: boolean;\n}\n\ntype Charts = ReturnType<typeof createCharts>;\n\n/**\n * The charts of one setup: loading Chart.js, mounting a chart on each drawn canvas,\n * fitting it to its frame, re-theming it, and releasing it once it has left the page.\n *\n * Chart.js' own `responsive` mode is not used. It puts a MutationObserver on the whole\n * document per chart, called on every token a reply streams, and it never releases a\n * chart taken off the page: `Chart.instances` keeps it until `destroy()`. So the plugin\n * passes `responsive: false` and keeps ONE ResizeObserver for all its charts: a frame\n * that changes size is fitted (`chart.resize()`), and a frame reported off the page is\n * released after the task — a framework only MOVING a message puts it back before then.\n * A released frame stays watched (up to `PARKED`), so a message that comes back later — a\n * list that virtualises its rows, a conversation reopened from a cache — is drawn again on\n * its first size, as an ECharts chart is when its element reconnects.\n */\nfunction createCharts(chartjs: ChartjsConstructor | ChartjsLoader, config: AparteConfig) {\n const load: ChartjsLoader = isChartClass(chartjs) ? () => Promise.resolve(chartjs) : chartjs;\n let loading: Promise<ChartjsConstructor> | undefined;\n // Handed over as the class itself: ready at once, so a chart is drawn in the same pass.\n let Chart: ChartjsConstructor | undefined = isChartClass(chartjs) ? chartjs : undefined;\n const mounted = new Map<HTMLElement, Mounted>();\n /** Released frames still watched, oldest first, by their wrapper: what a return redraws. */\n const parked = new Map<HTMLElement, HTMLElement>();\n const shadows = new WeakSet<ShadowRoot>();\n let sweep: ReturnType<typeof setTimeout> | undefined;\n let stopped = false;\n\n const chartOf = (): Promise<ChartjsConstructor> => {\n // Not poisoned: a failed load is tried again on the next chart.\n loading ??= load().then((m) => {\n const found = isChartClass(m) ? m : (m.default ?? m.Chart);\n if (!isChartClass(found)) throw new TypeError('the loader returned no Chart class');\n Chart = found;\n return found;\n }).catch((error: unknown) => { loading = undefined; throw error; });\n return loading;\n };\n\n const frameOf = (wrapper: HTMLElement): HTMLElement | null => wrapper.querySelector<HTMLElement>('.aparte-chartjs__frame');\n\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver((entries) => {\n for (const entry of entries) {\n const wrapper = (entry.target as HTMLElement).parentElement;\n if (!wrapper) continue;\n if (!wrapper.isConnected) { release.later(); continue; }\n // Hidden, or skipped by `content-visibility`: nothing to fit until it shows.\n if (entry.contentRect.width === 0 && entry.contentRect.height === 0) continue;\n const chart = mounted.get(wrapper);\n // One chart that throws is that chart's note, not the end of the batch.\n try {\n if (!chart) mount(wrapper);\n else if (chart.responsive) chart.chart.resize();\n } catch (error) {\n release(wrapper);\n fail(wrapper, error);\n }\n }\n });\n\n /** Stop watching a frame: its chart is gone for good. */\n const unwatch = (wrapper: HTMLElement): void => {\n const frame = parked.get(wrapper) ?? frameOf(wrapper);\n parked.delete(wrapper);\n if (frame) observer?.unobserve(frame);\n };\n\n /** Destroy a chart. Its frame stays watched when `park`, so the chart is drawn again if it returns. */\n const release = Object.assign((wrapper: HTMLElement, park = false): void => {\n const chart = mounted.get(wrapper);\n if (chart) {\n mounted.delete(wrapper);\n try { chart.chart.destroy(); } catch { /* a chart that failed half-way has nothing more to release */ }\n }\n const frame = frameOf(wrapper);\n if (!park || !frame || !observer) { unwatch(wrapper); return; }\n parked.delete(wrapper);\n parked.set(wrapper, frame);\n for (const [oldest] of parked) {\n if (parked.size <= PARKED) break;\n unwatch(oldest);\n }\n }, {\n /** Release every chart off the page, after the task: a move puts its nodes back before then. */\n later(): void {\n if (sweep !== undefined) return;\n sweep = setTimeout(() => {\n sweep = undefined;\n for (const wrapper of [...mounted.keys()]) if (!wrapper.isConnected) release(wrapper, true);\n }, 0);\n },\n });\n\n /** The config a chart is drawn with: the chat's look, and the plugin's own sizing. */\n const drawn = (chart: ChartjsConfig, wrapper: HTMLElement, animate: boolean): { config: ChartjsConfig; responsive: boolean } => {\n const themed = themeChartjsConfig(chart, readTokens(wrapper));\n const o = themed.options ??= {};\n const responsive = o['responsive'] !== false;\n o['maintainAspectRatio'] = o['aspectRatio'] !== undefined || o['maintainAspectRatio'] === true;\n o['responsive'] = false;\n if (!animate) o['animation'] = false;\n return { config: themed, responsive };\n };\n\n const fail = (wrapper: HTMLElement, error: unknown): void => {\n warnOnce(config, 'a chart could not be drawn; its note is shown instead. Is its type registered on your Chart?', error);\n wrapper.setAttribute('data-aparte-chartjs-failed', '');\n const note = document.createElement('span');\n note.className = 'aparte-code-error';\n note.setAttribute('role', 'note');\n note.setAttribute('data-aparte-chartjs-error', '');\n note.textContent = word(config, 'chartjsError');\n unwatch(wrapper);\n frameOf(wrapper)?.replaceWith(note);\n };\n\n function mount(wrapper: HTMLElement): void {\n if (stopped || mounted.has(wrapper) || !wrapper.isConnected || wrapper.hasAttribute('data-aparte-chartjs-failed')) return;\n if (!Chart) {\n void chartOf().then(() => mount(wrapper), (error: unknown) => warnOnce(load, 'Chart.js could not be loaded.', error));\n return;\n }\n const canvas = wrapper.querySelector('canvas');\n const frame = frameOf(wrapper);\n if (!canvas || !frame) return;\n let source: ChartjsConfig | null;\n try { source = readChartConfig(JSON.parse(wrapper.getAttribute(CONFIG_ATTR) ?? ''), Infinity); } catch { source = null; }\n if (!source) return;\n ensureStyles(wrapper);\n watchRoot(wrapper);\n // A chart still arriving is a new chart every frame: an entrance animation would\n // replay on each. Nor does the chart it settles into (`still`) play one after the\n // reader watched it grow, unless its config asks for an animation itself.\n const animate = !reducedMotion() && !wrapper.hasAttribute('data-aparte-chartjs-open')\n && (!wrapper.hasAttribute('data-aparte-chartjs-still') || source.options?.['animation'] !== undefined);\n const { config: final, responsive } = drawn(source, wrapper, animate);\n let chart: ChartjsChart | undefined;\n try {\n chart = new Chart(canvas, final);\n if (responsive) chart.resize();\n } catch (error) {\n // Constructed and then failed: still in `Chart.instances` until destroyed.\n try { chart?.destroy(); } catch { /* nothing more to release */ }\n fail(wrapper, error);\n return;\n }\n mounted.set(wrapper, { chart, config: source, responsive });\n parked.delete(wrapper);\n release.later();\n }\n\n /** Re-read the chat's colours for every chart on the page, in place. One that throws shows its note; the rest go on. */\n const retheme = (): void => {\n for (const [wrapper, chart] of [...mounted]) {\n if (!wrapper.isConnected) { release.later(); continue; }\n try {\n const { config: final } = drawn(chart.config, wrapper, false);\n chart.chart.options = final.options;\n chart.chart.data = final.data;\n chart.chart.update('none');\n } catch (error) {\n release(wrapper);\n fail(wrapper, error);\n }\n }\n };\n const watch = watchTheme(retheme);\n const watchRoot = (node: Node): void => {\n const root = node.getRootNode();\n if (typeof ShadowRoot === 'undefined' || !(root instanceof ShadowRoot) || shadows.has(root)) return;\n shadows.add(root);\n watch.add(root);\n };\n\n return {\n chartOf,\n warm(): void { void chartOf().catch(() => undefined); },\n /** Mount a chart on every drawn canvas in `root` that has none. */\n activate(root: Element): void {\n const wrappers = root.matches(`.aparte-chartjs[${CONFIG_ATTR}]`) ? [root as HTMLElement]\n : Array.from(root.querySelectorAll<HTMLElement>(`.aparte-chartjs[${CONFIG_ATTR}]`));\n for (const wrapper of wrappers) {\n // Watched from the start: a canvas drawn off the page (a message being\n // rebuilt) is mounted when its frame first shows.\n const frame = frameOf(wrapper);\n if (frame && !wrapper.hasAttribute('data-aparte-chartjs-failed')) observer?.observe(frame);\n mount(wrapper);\n }\n },\n /** Release the charts in `root` now: it is gone for good. */\n releaseIn(root: Element): void {\n for (const wrapper of [...mounted.keys(), ...parked.keys()]) if (root === wrapper || root.contains(wrapper)) release(wrapper);\n },\n stop(): void {\n stopped = true;\n watch.stop();\n if (sweep !== undefined) clearTimeout(sweep);\n sweep = undefined;\n for (const wrapper of [...mounted.keys()]) release(wrapper);\n parked.clear();\n observer?.disconnect();\n },\n };\n}\n\n/** The markup of a chart: the sized frame its canvas is drawn in, and its data as a table. */\nfunction chartMarkup(chart: ChartjsConfig, locale: string | undefined, flags: { open?: boolean; still?: boolean } = {}): string {\n const o = chart.options ?? {};\n const ratio = o['aspectRatio'] !== undefined || o['maintainAspectRatio'] === true;\n const title = titleOf(chart);\n const named = title ? ` data-aparte-chartjs-title=\"${escapeAttr(title)}\"` : '';\n const marks = `${ratio ? ' data-aparte-chartjs-ratio' : ''}${flags.open ? ' data-aparte-chartjs-open' : ''}${flags.still ? ' data-aparte-chartjs-still' : ''}`;\n // A figure, named like the canvas, holding the canvas and the chart's data — the shape an\n // ECharts chart has. The canvas is `role=\"img\"` with the chart's name; its data is a\n // table BESIDE it, not inside it as fallback content: an image's children are\n // presentational, so a screen reader would never reach a table in there. `role` rather\n // than `<figure>`, whose default margins the reply's own styles would have to undo.\n return `<div class=\"aparte-chartjs\" role=\"figure\"${named}${marks} ${CONFIG_ATTR}=\"${escapeAttr(JSON.stringify(chart))}\">`\n + '<div class=\"aparte-chartjs__frame\"><canvas class=\"aparte-chartjs__canvas\" role=\"img\"></canvas></div>'\n + `<div class=\"aparte-sr-only\">${dataTable(chart, locale)}</div></div>`; // safe-text: dataTable escapes every value it writes\n}\n\n/** What a chart on its way shows: a quiet box the size the chart will be. */\nfunction pendingMarkup(): string {\n return '<div class=\"aparte-chartjs\"><div class=\"aparte-chartjs__frame\">'\n + '<span class=\"aparte-skeleton aparte-skeleton--rect\" aria-hidden=\"true\"></span></div></div>';\n}\n\n/**\n * The words of every chart in `root`, in the chat's language — its name, its unnamed\n * series, its notes — and the height the app asked for (`options.height`), set before\n * the chart's first frame is painted.\n */\nfunction relabelCharts(root: Element, config: AparteConfig, options: ChartjsOptions): void {\n const wrappers = root.matches('.aparte-chartjs') ? [root as HTMLElement] : Array.from(root.querySelectorAll<HTMLElement>('.aparte-chartjs'));\n for (const wrapper of wrappers) {\n if (options.height) wrapper.style.setProperty('--aparte-chartjs-height', options.height);\n const name = wrapper.getAttribute('data-aparte-chartjs-title') || word(config, 'chartjsChart');\n if (wrapper.getAttribute('role') === 'figure') wrapper.setAttribute('aria-label', name);\n wrapper.querySelector('canvas')?.setAttribute('aria-label', name);\n const caption = wrapper.querySelector('[data-aparte-chartjs-name]');\n if (caption) caption.textContent = name;\n for (const th of Array.from(wrapper.querySelectorAll('[data-aparte-chartjs-series]'))) {\n th.textContent = word(config, 'chartjsSeries', { n: th.getAttribute('data-aparte-chartjs-series') ?? '' });\n }\n for (const cut of Array.from(wrapper.querySelectorAll('[data-aparte-chartjs-cut]'))) cut.textContent = word(config, 'chartjsTableCut');\n }\n for (const note of Array.from(root.querySelectorAll('[data-aparte-chartjs-error]'))) note.textContent = word(config, 'chartjsError');\n for (const note of Array.from(root.querySelectorAll('[data-aparte-chartjs-unavailable]'))) note.textContent = word(config, 'chartjsUnavailable');\n for (const note of Array.from(root.querySelectorAll('[data-aparte-chartjs-waiting]'))) note.textContent = config.t('approvalWaiting');\n}\n\nconst localeOf = (config: AparteConfig): string | undefined => {\n const tag = config.getLocale().tag;\n return typeof tag === 'string' && tag ? tag : undefined;\n};\n\n/** The note and the source, for a fence that is not a chart. Its text is set by `relabel`. */\nfunction failed(ctx: AparteCodeLanguageContext): string {\n return `<span class=\"aparte-code-error\" role=\"note\" data-aparte-chartjs-error></span><pre class=\"aparte-code-error\">${escapeHtml(ctx.source)}</pre>`;\n}\n\nfunction createFenceRenderer(charts: Charts, options: ChartjsOptions) {\n const progressive = options.progressive !== false;\n const maxPoints = options.maxPoints ?? MAX_POINTS;\n /**\n * A chart still arriving, by the part of the reply it is in — a rebuild replaces its\n * placeholder, never its part: the latest source waiting to be drawn, whether a timer\n * is set, when the last drawing was made, the source of the chart this part started\n * showing (`null` before its first frame), and the config last drawn, so a token that\n * changes nothing drawable (a key half-written) draws nothing.\n */\n interface Arriving {\n waiting: { ctx: AparteCodeLanguageContext; answer: (html: string | null) => void } | null;\n timer: ReturnType<typeof setTimeout> | undefined;\n at: number;\n started: string | null;\n drawn: string | null;\n }\n const arriving = new WeakMap<Element, Arriving>();\n\n const stateOf = (host: HTMLElement): Arriving => {\n const part = host.closest('[data-segment-id]') ?? host.closest('pre')?.parentElement ?? host.parentElement ?? host;\n let state = arriving.get(part);\n if (!state) {\n state = { waiting: null, timer: undefined, at: 0, started: null, drawn: null };\n arriving.set(part, state);\n }\n return state;\n };\n\n const frame = async (ctx: AparteCodeLanguageContext, state: Arriving): Promise<string | null> => {\n const chart = parseFence(ctx.source, true, maxPoints);\n if (!chart || !hasPoints(chart)) return null;\n const key = JSON.stringify(chart);\n if (key === state.drawn) return null;\n await charts.chartOf();\n state.drawn = key;\n return chartMarkup(chart, localeOf(ctx.config), { open: true });\n };\n\n const schedule = (state: Arriving): void => {\n if (state.timer !== undefined || !state.waiting) return;\n const wait = Math.max(0, state.at + PROGRESSIVE_MS - performance.now());\n state.timer = setTimeout(() => {\n state.timer = undefined;\n const job = state.waiting;\n state.waiting = null;\n if (!job) return;\n state.at = performance.now();\n void frame(job.ctx, state).then(job.answer, () => job.answer(null));\n }, wait);\n };\n\n return {\n languages: CHARTJS_LANGUAGES,\n lazy: true,\n render(ctx: AparteCodeLanguageContext): string | null | Promise<string | null> {\n if (!ctx.display) return null;\n const state = stateOf(ctx.host);\n const waiting = state.waiting;\n if (!ctx.open) {\n // The end of the chart that was arriving: its waiting frame is not drawn, and\n // its last drawing is not followed by an entrance animation.\n const streamed = state.started !== null && ctx.source.startsWith(state.started);\n if (streamed) {\n waiting?.answer(null);\n state.waiting = null;\n state.started = null;\n state.drawn = null;\n }\n const chart = parseFence(ctx.source, false, maxPoints);\n if (!chart) return failed(ctx);\n return charts.chartOf().then(() => chartMarkup(chart, localeOf(ctx.config), { still: streamed }));\n }\n // A newer source supersedes the one still waiting.\n waiting?.answer(null);\n state.waiting = null;\n if (!progressive) return null;\n // A chart's first frame: the box it will be drawn in, at once, in place of its\n // JSON — and Chart.js on its way meanwhile.\n if (state.started === null || !ctx.source.startsWith(state.started)) {\n state.started = ctx.source;\n state.drawn = null;\n charts.warm();\n return pendingMarkup();\n }\n return new Promise<string | null>((answer) => {\n state.waiting = { ctx, answer };\n schedule(state);\n });\n },\n relabel(drawing: HTMLElement, ctx: AparteCodeLanguageContext): void {\n relabelCharts(drawing, ctx.config, options);\n // Called once the markup is in the page: the canvas is there to draw on. Nodes\n // core MOVED keep their chart; `activate` skips a canvas that has one.\n charts.activate(drawing);\n },\n };\n}\n\n/** A tool's result as data: its `structuredContent`, else its text read as JSON. */\nfunction resultOf(segment: AparteToolCallSegment): unknown {\n if (segment.structuredResult !== undefined) return segment.structuredResult;\n if (typeof segment.result !== 'string') return undefined;\n try { return JSON.parse(segment.result); } catch { return undefined; }\n}\n\nfunction createToolRenderer(charts: Charts, options: ChartjsOptions, config: AparteConfig): AparteToolRenderer {\n const maxPoints = options.maxPoints ?? MAX_POINTS;\n /** What each element was last drawn from, so an update that changes nothing keeps the chart. */\n const drawnFrom = new WeakMap<HTMLElement, string>();\n\n const note = (attr: string): string => `<span class=\"aparte-chartjs-note\" role=\"note\" ${attr}></span>`;\n\n /** Draw the call into `element`; `false` when it already shows this. */\n const draw = (element: HTMLElement, segment: AparteToolCallSegment): boolean => {\n const status = segment.status;\n const signature = status === 'resolved' ? `resolved|${segment.result ?? ''}|${JSON.stringify(segment.structuredResult ?? null)}` : status;\n if (drawnFrom.get(element) === signature) return false;\n drawnFrom.set(element, signature);\n charts.releaseIn(element);\n element.removeAttribute('aria-busy');\n let html: string;\n if (status === 'pending') {\n element.setAttribute('aria-busy', 'true');\n charts.warm();\n html = pendingMarkup();\n } else if (status === 'awaiting-approval') {\n html = note('data-aparte-chartjs-waiting');\n } else if (status === 'resolved') {\n let chart: ChartjsConfig | null = null;\n try {\n const result = resultOf(segment);\n chart = readChartConfig(options.map ? options.map(result, segment.toolCall?.input ?? {}) : result, maxPoints);\n } catch (error) {\n warnOnce(options, 'the `map` given for a chart tool threw.', error);\n }\n html = chart\n ? chartMarkup(chart, localeOf(config))\n : `<span class=\"aparte-code-error\" role=\"note\" data-aparte-chartjs-error></span>`;\n } else {\n html = note('data-aparte-chartjs-unavailable');\n }\n element.innerHTML = html;\n relabelCharts(element, config, options);\n return true;\n };\n\n return {\n render(segment) {\n const element = document.createElement('div');\n element.className = 'aparte-chartjs-tool';\n draw(element, segment);\n return element;\n },\n setup(element) {\n ensureStyles(element);\n charts.activate(element);\n },\n update(element, segment) {\n if (!draw(element, segment)) return;\n ensureStyles(element);\n charts.activate(element);\n },\n relabel(element) {\n relabelCharts(element, config, options);\n },\n cleanup(element) {\n charts.releaseIn(element);\n },\n };\n}\n\n/**\n * Call `changed` when the chat's theme or the system's colour scheme changes — on the\n * page, and in each shadow root handed to `add`.\n */\nfunction watchTheme(changed: () => void): { add: (root: ShadowRoot) => void; stop: () => void } {\n if (typeof MutationObserver === 'undefined' || typeof document === 'undefined') return { add: () => {}, stop: () => {} };\n const observer = new MutationObserver(changed);\n const init: MutationObserverInit = { attributes: true, subtree: true, attributeFilter: ['data-aparte-theme'] };\n observer.observe(document.documentElement, init);\n const media = typeof matchMedia === 'function' ? matchMedia('(prefers-color-scheme: dark)') : null;\n media?.addEventListener('change', changed);\n return {\n add: (root) => observer.observe(root, init),\n stop: () => { observer.disconnect(); media?.removeEventListener('change', changed); },\n };\n}\n"],"names":["child"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { type AparteConfig } from '@aparte/core';
2
+ import { type ChartjsOptions } from './core.js';
3
+ export { setupChartjsWith, themeChartjsConfig, readChartConfig, seriesPalette, SERIES_HUES, CHARTJS_LANGUAGES, } from './core.js';
4
+ export type { ChartjsChart, ChartjsConfig, ChartjsConstructor, ChartjsLoader, ChartjsOptions, ChartjsTokens, } from './core.js';
5
+ /**
6
+ * Draw charts with [Chart.js](https://www.chartjs.org): every ```` ```chartjs ```` (or
7
+ * ```` ```chart.js ````) fence in a reply whose body is a Chart.js config as JSON —
8
+ * whichever Markdown provider is installed, or none — and, with `options.tool`, the
9
+ * result of the tools named there. A chart is redrawn as its fence arrives, from the
10
+ * part of its JSON that has arrived; once the fence closes, one that is not a chart
11
+ * shows a note and its source.
12
+ *
13
+ * The chart takes the chat's colours and font wherever its config leaves them out, and
14
+ * follows a light/dark switch in place. Chart.js (`chart.js/auto`, every chart type
15
+ * registered) is loaded on the first chart, and a chart is drawn only once it comes near
16
+ * the screen, so reopening a long conversation stays fast.
17
+ *
18
+ * @returns a disposer that unregisters the renderers and releases every chart drawn.
19
+ * @example
20
+ * import { setupChartjs } from '@aparte/plugin-chartjs';
21
+ * setupChartjs({ tool: 'plot_sales' });
22
+ */
23
+ export declare function setupChartjs(options?: ChartjsOptions, config?: AparteConfig): () => void;
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAElE,OAAO,EACH,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,WAAW,EACX,iBAAiB,GACpB,MAAM,WAAW,CAAC;AACnB,YAAY,EACR,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,aAAa,GAChB,MAAM,WAAW,CAAC;AAEnB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,cAAmB,EAAE,MAAM,GAAE,YAAiC,GAAG,MAAM,IAAI,CAEhH"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ import { aparteGlobalConfig } from "@aparte/core";
2
+ import { setupChartjsWith } from "./core.js";
3
+ import { CHARTJS_LANGUAGES, SERIES_HUES, readChartConfig, seriesPalette, themeChartjsConfig } from "./core.js";
4
+ function setupChartjs(options = {}, config = aparteGlobalConfig) {
5
+ return setupChartjsWith(() => import("chart.js/auto"), options, config);
6
+ }
7
+ export {
8
+ CHARTJS_LANGUAGES,
9
+ SERIES_HUES,
10
+ readChartConfig,
11
+ seriesPalette,
12
+ setupChartjs,
13
+ setupChartjsWith,
14
+ themeChartjsConfig
15
+ };
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { aparteGlobalConfig, type AparteConfig } from '@aparte/core';\nimport { setupChartjsWith, type ChartjsOptions } from './core.js';\n\nexport {\n setupChartjsWith,\n themeChartjsConfig,\n readChartConfig,\n seriesPalette,\n SERIES_HUES,\n CHARTJS_LANGUAGES,\n} from './core.js';\nexport type {\n ChartjsChart,\n ChartjsConfig,\n ChartjsConstructor,\n ChartjsLoader,\n ChartjsOptions,\n ChartjsTokens,\n} from './core.js';\n\n/**\n * Draw charts with [Chart.js](https://www.chartjs.org): every ```` ```chartjs ```` (or\n * ```` ```chart.js ````) fence in a reply whose body is a Chart.js config as JSON —\n * whichever Markdown provider is installed, or none — and, with `options.tool`, the\n * result of the tools named there. A chart is redrawn as its fence arrives, from the\n * part of its JSON that has arrived; once the fence closes, one that is not a chart\n * shows a note and its source.\n *\n * The chart takes the chat's colours and font wherever its config leaves them out, and\n * follows a light/dark switch in place. Chart.js (`chart.js/auto`, every chart type\n * registered) is loaded on the first chart, and a chart is drawn only once it comes near\n * the screen, so reopening a long conversation stays fast.\n *\n * @returns a disposer that unregisters the renderers and releases every chart drawn.\n * @example\n * import { setupChartjs } from '@aparte/plugin-chartjs';\n * setupChartjs({ tool: 'plot_sales' });\n */\nexport function setupChartjs(options: ChartjsOptions = {}, config: AparteConfig = aparteGlobalConfig): () => void {\n return setupChartjsWith(() => import('chart.js/auto'), options, config);\n}\n"],"names":[],"mappings":";;;AAsCO,SAAS,aAAa,UAA0B,IAAI,SAAuB,oBAAgC;AAC9G,SAAO,iBAAiB,MAAM,OAAO,eAAe,GAAG,SAAS,MAAM;AAC1E;"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The series colours: the chat's `--aparte-primary` first, then a fixed set of hues.
3
+ *
4
+ * The fixed set is the eight hues of a categorical palette validated for a chart surface
5
+ * in BOTH modes (OKLCH lightness 0.43–0.77 on light and 0.48–0.67 on dark, chroma
6
+ * ≥ 0.1, ≥ 3:1 against a white and a near-black surface), each taken at the one step that
7
+ * passes both, so the same eight read on a light and a dark chat without a second table:
8
+ *
9
+ * blue #2a78d6 · orange #d95926 · aqua #199e70 · yellow #c98500 ·
10
+ * magenta #d55181 · green #008300 · violet #9085e9 · red #e34948
11
+ *
12
+ * The primary is the app's, so the order after it cannot be fixed in advance: a brass
13
+ * primary sits on top of the orange and the yellow, a blue one on the blue. So the order
14
+ * is CHOSEN once per primary, by the same measures a palette validator applies (OKLab
15
+ * ΔE×100): a hue is dropped when it is within 15 of the primary under normal vision (a
16
+ * series that reads as the brand's), and each next series is the first remaining hue
17
+ * at least 15 from the one before it under normal vision and at least 8 under simulated
18
+ * protanopia and deuteranopia (Machado 2009, severity 1) — the pairs a stacked bar or a
19
+ * legend puts side by side. Measured over thirteen primaries (aparté's brass in both
20
+ * modes, Tailwind's blue/red/green/violet/teal/pink/orange, black, grey): every one
21
+ * passes those checks, with five to eight hues.
22
+ *
23
+ * A series past the last hue is drawn in the muted text colour, never a recycled hue:
24
+ * a ninth series painted like the first would claim to BE the first. A chart with that
25
+ * many series wants grouping ("Other"), not more colours.
26
+ */
27
+ /** The fixed hues, in order of preference. */
28
+ export declare const SERIES_HUES: readonly ["#2a78d6", "#d95926", "#199e70", "#c98500", "#d55181", "#008300", "#9085e9", "#e34948"];
29
+ type Rgb = [number, number, number];
30
+ /** `#rgb`, `#rrggbb`, `rgb()`/`rgba()` → 0–255 channels; `null` for anything else. */
31
+ export declare function parseColor(color: string): Rgb | null;
32
+ /** `color` at `alpha`, or `color` itself when it cannot be read (a keyword, `oklch()`…). */
33
+ export declare function withAlpha(color: string, alpha: number): string;
34
+ /**
35
+ * The series colours for a chat whose primary is `primary`: the primary, then the fixed
36
+ * hues in the order chosen for it (see the module's comment). A primary that cannot be
37
+ * read as a colour is kept first, followed by the fixed hues as they are.
38
+ */
39
+ export declare function seriesPalette(primary: string): string[];
40
+ export {};
41
+ //# sourceMappingURL=palette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"palette.d.ts","sourceRoot":"","sources":["../src/palette.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,8CAA8C;AAC9C,eAAO,MAAM,WAAW,mGAAoG,CAAC;AAM7H,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEpC,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAUpD;AAED,4FAA4F;AAC5F,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9D;AAqCD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBvD"}
@@ -0,0 +1,64 @@
1
+ /** A Chart.js config as JSON can carry it: `{ type, data, options }`. */
2
+ export interface ChartjsConfig {
3
+ type: string;
4
+ data: {
5
+ labels?: unknown[];
6
+ datasets: Array<Record<string, unknown>>;
7
+ [key: string]: unknown;
8
+ };
9
+ options?: Record<string, unknown>;
10
+ [key: string]: unknown;
11
+ }
12
+ /** The most data points a chart may carry, all datasets together — and the most labels, and datasets. */
13
+ export declare const MAX_POINTS = 5000;
14
+ /**
15
+ * The config in `value`, copied and made harmless, or `null` when it is not one Chart.js
16
+ * could draw: a `type`, and `data.datasets` as a list of objects whose `data` are lists.
17
+ *
18
+ * What is taken out, and why — Chart.js draws everything on the canvas, so no string in
19
+ * a config becomes HTML, and JSON carries no function, so no callback can arrive:
20
+ * - the config's own `plugins` list. Chart.js runs each entry's hooks; from JSON an entry
21
+ * is an object with no function in it, so it can do nothing — and a list that can do
22
+ * nothing is better not handed over at all. Options for the plugins the app REGISTERED
23
+ * (`options.plugins.legend`, `.title`, `.tooltip`…) stay; Chart.js ignores the rest;
24
+ * - `options.devicePixelRatio`: it multiplies the canvas' pixels, and a large one asks the
25
+ * browser for a canvas bigger than any it will allocate;
26
+ * - `options.locale` when `Intl` refuses it, which would throw on the first tick;
27
+ * - a string where Chart.js CALLS a function, on every hover: the tooltip's `external`,
28
+ * `filter`, `itemSort` and `callbacks`, the legend labels' `generateLabels`, `filter` and
29
+ * `sort` — and a tooltip `position` that is not one of Chart.js' own;
30
+ * - `loop` in any animation (see `plain`).
31
+ * `options.aspectRatio` is held to 1:4–4:1 for the same reason as the pixel ratio: a tiny
32
+ * one makes the canvas millions of pixels tall. A scale's `ticks.count` and
33
+ * `ticks.maxTicksLimit` are held to 100: Chart.js makes as many ticks as `count` says, and
34
+ * a hundred million of them is a tab out of memory. `maxPoints` bounds the labels and the
35
+ * datasets as well as the points: each is a row or a column of work.
36
+ */
37
+ export declare function readChartConfig(value: unknown, maxPoints?: number): ChartjsConfig | null;
38
+ /** Whether a config has a point to draw: a chart still arriving shows nothing until it does. */
39
+ export declare function hasPoints(config: ChartjsConfig): boolean;
40
+ /**
41
+ * The longest prefix of a JSON text still being written that is a whole value once its
42
+ * open brackets are closed, closed — or `null` when there is none yet.
43
+ *
44
+ * A Chart.js config is one object, so it parses only once its last brace has arrived:
45
+ * without this, a chart streams as a blank box and appears at the end. The cut points are
46
+ * where a value has just ended — before a comma, after a closing bracket — or a container
47
+ * has just opened, so a half-written string, number or key is never part of the result
48
+ * (`[1, 2` gives `[1]`: the `2` may be the start of `25`).
49
+ */
50
+ export declare function closeJson(text: string): string | null;
51
+ /** The config a fence holds: all of it once closed, its closed prefix while it arrives. */
52
+ export declare function parseFence(source: string, open: boolean, maxPoints?: number): ChartjsConfig | null;
53
+ /** The title a config gives itself (`options.plugins.title.text`), as one line. */
54
+ export declare function titleOf(config: ChartjsConfig): string | undefined;
55
+ /**
56
+ * The data as a table, for a screen reader: one row per label, one column per dataset with
57
+ * data. Every value is escaped; a dataset with no label has an empty header that `relabel`
58
+ * fills with "Series n" in the chat's language, as it fills the caption. Held to
59
+ * `MAX_COLUMNS` columns and `MAX_CELLS` cells — a few bytes of empty datasets beside many
60
+ * labels would otherwise be millions of cells — with a last line, filled by `relabel`, when
61
+ * it lists only part of the data.
62
+ */
63
+ export declare function dataTable(config: ChartjsConfig, locale?: string): string;
64
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"AAMA,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;QAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,yGAAyG;AACzG,eAAO,MAAM,UAAU,OAAO,CAAC;AAmE/B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,SAAa,GAAG,aAAa,GAAG,IAAI,CA0C5F;AAED,gGAAgG;AAChG,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAExD;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA8BrD;AAED,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,SAAa,GAAG,aAAa,GAAG,IAAI,CAItG;AAED,mFAAmF;AACnF,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAIjE;AA4BD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CA2BxE"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The chat's look, as a Chart.js config: the colours and the font a chart takes from the
3
+ * chat's tokens wherever its config does not set its own.
4
+ */
5
+ import type { ChartjsConfig } from './source.js';
6
+ /** The chat's tokens a chart is drawn in, as colours a canvas reads. */
7
+ export interface ChartjsTokens {
8
+ /** Text: titles, legends, point labels (`--aparte-text`). */
9
+ text: string;
10
+ /** Quieter text: axis ticks and titles, subtitles (`--aparte-text-muted`). */
11
+ muted: string;
12
+ /** Grid and axis lines, the tooltip's border (`--aparte-border`). */
13
+ border: string;
14
+ /** The tooltip's ground (`--aparte-surface-1`). */
15
+ surface: string;
16
+ /** The page behind the chart: the gap between slices, a radial tick's backdrop (`--aparte-bg`). */
17
+ background: string;
18
+ /** The first series (`--aparte-primary`); the others follow it, see `seriesPalette`. */
19
+ primary: string;
20
+ /** The chat's font (`--aparte-font-family`). */
21
+ fontFamily: string;
22
+ }
23
+ /**
24
+ * `config` in the chat's look: a new config, `config` itself untouched. Every value the
25
+ * config sets stays; what it leaves out is taken from `tokens`.
26
+ *
27
+ * - text in `text`, axis ticks and titles in `muted`, grid and axis lines in `border`;
28
+ * - the tooltip on `surface`, with a `border` outline;
29
+ * - the chat's font;
30
+ * - each dataset with no colour of its own in its series colour: the primary, then the
31
+ * fixed hues (`seriesPalette`) — solid lines and borders, translucent fills for bars,
32
+ * areas and points; one colour per slice for a pie, a doughnut or a polar area, with
33
+ * the page's colour between slices. A series past the last hue is drawn in `muted`.
34
+ *
35
+ * The background stays transparent: the chart sits on whatever the bubble is.
36
+ *
37
+ * @example
38
+ * themeChartjsConfig({ type: 'bar', data: { labels: ['a'], datasets: [{ data: [1] }] } }, tokens);
39
+ */
40
+ export declare function themeChartjsConfig(config: ChartjsConfig, tokens: ChartjsTokens): ChartjsConfig;
41
+ /**
42
+ * The chat's tokens at `host`, as colours a canvas and Chart.js' colour helpers read. A
43
+ * token may be `color-mix()` or `oklch()`, which Chart.js cannot lighten for a hover, so
44
+ * each is resolved by the browser — a probe's computed colour — and normalised by a
45
+ * canvas to hex or `rgba()`.
46
+ */
47
+ export declare function readTokens(host: HTMLElement): ChartjsTokens;
48
+ /** Forget the scratch canvas: for tests that swap `getContext`. */
49
+ export declare function resetScratch(): void;
50
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC1B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,mGAAmG;IACnG,UAAU,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;CACtB;AAkDD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,GAAG,aAAa,CA6C9F;AAcD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,aAAa,CA0B3D;AAED,mEAAmE;AACnE,wBAAgB,YAAY,IAAI,IAAI,CAAyB"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * The words a chart draws, in the chat's language.
3
+ *
4
+ * Core's locale is a closed list of what CORE draws, so this plugin reads its own keys off
5
+ * `getLocale()` and defaults them here: the app's locale first, then this table for the
6
+ * chat's language (the ten `@aparte/locales` ships), then English.
7
+ *
8
+ * aparteGlobalConfig.extendLocale({ chartjsChart: 'Graphique' });
9
+ */
10
+ import type { AparteConfig } from '@aparte/core';
11
+ export declare const WORDS: {
12
+ /** A chart's accessible name when its config gives it no title. */
13
+ chartjsChart: {
14
+ en: string;
15
+ fr: string;
16
+ de: string;
17
+ es: string;
18
+ it: string;
19
+ ja: string;
20
+ ko: string;
21
+ pt: string;
22
+ ru: string;
23
+ zh: string;
24
+ };
25
+ /** The note over the source of a chart that could not be drawn. */
26
+ chartjsError: {
27
+ en: string;
28
+ fr: string;
29
+ de: string;
30
+ es: string;
31
+ it: string;
32
+ ja: string;
33
+ ko: string;
34
+ pt: string;
35
+ ru: string;
36
+ zh: string;
37
+ };
38
+ /** A tool that should have returned a chart and did not run, or failed. */
39
+ chartjsUnavailable: {
40
+ en: string;
41
+ fr: string;
42
+ de: string;
43
+ es: string;
44
+ it: string;
45
+ ja: string;
46
+ ko: string;
47
+ pt: string;
48
+ ru: string;
49
+ zh: string;
50
+ };
51
+ /** A column of the data table for a dataset with no label. */
52
+ chartjsSeries: {
53
+ en: string;
54
+ fr: string;
55
+ de: string;
56
+ es: string;
57
+ it: string;
58
+ ja: string;
59
+ ko: string;
60
+ pt: string;
61
+ ru: string;
62
+ zh: string;
63
+ };
64
+ /** The data table's last line, when it lists only part of the data. */
65
+ chartjsTableCut: {
66
+ en: string;
67
+ fr: string;
68
+ de: string;
69
+ es: string;
70
+ it: string;
71
+ ja: string;
72
+ ko: string;
73
+ pt: string;
74
+ ru: string;
75
+ zh: string;
76
+ };
77
+ };
78
+ export type WordKey = keyof typeof WORDS;
79
+ /** The word for `key`, with `{name}` placeholders filled from `vars`. */
80
+ export declare function word(config: AparteConfig, key: WordKey, vars?: Record<string, string>): string;
81
+ //# sourceMappingURL=words.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"words.d.ts","sourceRoot":"","sources":["../src/words.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAKjD,eAAO,MAAM,KAAK;IACd,mEAAmE;;;;;;;;;;;;;IAKnE,mEAAmE;;;;;;;;;;;;;IAQnE,2EAA2E;;;;;;;;;;;;;IAM3E,8DAA8D;;;;;;;;;;;;;IAK9D,uEAAuE;;;;;;;;;;;;;CAQ3C,CAAC;AAEjC,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,KAAK,CAAC;AAQzC,yEAAyE;AACzE,wBAAgB,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAAG,MAAM,CAKlG"}
package/package.json CHANGED
@@ -1,6 +1,68 @@
1
1
  {
2
2
  "name": "@aparte/plugin-chartjs",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.29.0",
4
+ "homepage": "https://apartejs.dev/guides/charts/",
5
+ "description": "Charts for aparté via Chart.js — a Chart.js config a model writes, or a tool returns, drawn as it streams.",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "@aparte-workspace/source": "./src/index.ts",
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./core": {
19
+ "@aparte-workspace/source": "./src/core.ts",
20
+ "types": "./dist/core.d.ts",
21
+ "import": "./dist/core.js",
22
+ "default": "./dist/core.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "peerDependencies": {
34
+ "@aparte/core": ">=0.29.0 <1.0.0",
35
+ "chart.js": ">=4.4.0 <5.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "chart.js": "^4.6.1",
39
+ "typescript": "~5.7.0",
40
+ "vite": "^6.0.0",
41
+ "@aparte/core": "0.29.0"
42
+ },
43
+ "keywords": [
44
+ "aparte",
45
+ "plugin",
46
+ "chartjs",
47
+ "chart.js",
48
+ "charts",
49
+ "data-visualization"
50
+ ],
51
+ "license": "MIT",
52
+ "repository": {
53
+ "type": "git",
54
+ "url": "git+https://github.com/apartejs/aparte.git",
55
+ "directory": "packages/plugins/chartjs"
56
+ },
57
+ "bugs": {
58
+ "url": "https://github.com/apartejs/aparte/issues"
59
+ },
60
+ "scripts": {
61
+ "dev": "vite",
62
+ "build": "vite build && tsc -b --emitDeclarationOnly --force && node ../../../scripts/stamp-dist.mjs",
63
+ "preview": "vite preview",
64
+ "test": "vitest",
65
+ "test:run": "vitest run",
66
+ "test:coverage": "vitest run --coverage"
67
+ }
6
68
  }