@pixerate/editor 0.50.0 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/chart/index.cjs +509 -56
  2. package/dist/chart/index.cjs.map +1 -1
  3. package/dist/chart/index.d.cts +194 -7
  4. package/dist/chart/index.d.ts +194 -7
  5. package/dist/chart/index.js +461 -8
  6. package/dist/chart/index.js.map +1 -1
  7. package/dist/{chunk-2Q46FKZK.cjs → chunk-2DEBI6KU.cjs} +23 -23
  8. package/dist/{chunk-2Q46FKZK.cjs.map → chunk-2DEBI6KU.cjs.map} +1 -1
  9. package/dist/{chunk-3JNRQVTS.cjs → chunk-3ZBAXET6.cjs} +323 -3
  10. package/dist/chunk-3ZBAXET6.cjs.map +1 -0
  11. package/dist/{chunk-T6XU4B54.js → chunk-5N6AQCVB.js} +6 -6
  12. package/dist/{chunk-MR2764X2.js → chunk-AGGUNWT6.js} +2 -322
  13. package/dist/chunk-AGGUNWT6.js.map +1 -0
  14. package/dist/{chunk-LNKZPIQD.cjs → chunk-M5Z77GFH.cjs} +21 -21
  15. package/dist/{chunk-LNKZPIQD.cjs.map → chunk-M5Z77GFH.cjs.map} +1 -1
  16. package/dist/{chunk-3CC7OPUQ.cjs → chunk-O6FO6VI3.cjs} +3 -323
  17. package/dist/chunk-O6FO6VI3.cjs.map +1 -0
  18. package/dist/{chunk-GJBIVWB2.js → chunk-UAW5FEMD.js} +322 -2
  19. package/dist/chunk-UAW5FEMD.js.map +1 -0
  20. package/dist/{chunk-DQPBQXKG.js → chunk-YMKPMXZ4.js} +6 -6
  21. package/dist/{controller-DtbEo3om.d.ts → controller-BdKzaxs2.d.ts} +1 -1
  22. package/dist/{controller-ChjCCMLl.d.cts → controller-DS3drIvy.d.cts} +1 -1
  23. package/dist/export/index.cjs +4 -4
  24. package/dist/export/index.js +3 -3
  25. package/dist/extensions/index.cjs +4 -4
  26. package/dist/extensions/index.d.cts +2 -2
  27. package/dist/extensions/index.d.ts +2 -2
  28. package/dist/extensions/index.js +3 -3
  29. package/dist/{index-DoHqT1mq.d.cts → index-DOy9HzfC.d.cts} +1 -1
  30. package/dist/{index-YLZ0fKqE.d.ts → index-Dt9lzydb.d.ts} +1 -1
  31. package/dist/index.cjs +12 -11
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +6 -113
  34. package/dist/index.d.ts +6 -113
  35. package/dist/index.js +10 -9
  36. package/dist/index.js.map +1 -1
  37. package/dist/zip-4nG0YAH7.d.cts +110 -0
  38. package/dist/zip-4nG0YAH7.d.ts +110 -0
  39. package/package.json +1 -1
  40. package/dist/chunk-3CC7OPUQ.cjs.map +0 -1
  41. package/dist/chunk-3JNRQVTS.cjs.map +0 -1
  42. package/dist/chunk-GJBIVWB2.js.map +0 -1
  43. package/dist/chunk-MR2764X2.js.map +0 -1
  44. /package/dist/{chunk-T6XU4B54.js.map → chunk-5N6AQCVB.js.map} +0 -0
  45. /package/dist/{chunk-DQPBQXKG.js.map → chunk-YMKPMXZ4.js.map} +0 -0
@@ -14,8 +14,9 @@ import {
14
14
  CHART_OOXML_RELATIONSHIP,
15
15
  chartFormatToCode,
16
16
  chartFromOoxml,
17
- chartToOoxml
18
- } from "../chunk-GJBIVWB2.js";
17
+ chartToOoxml,
18
+ readZip
19
+ } from "../chunk-UAW5FEMD.js";
19
20
  import {
20
21
  CHART_COLOR_SCHEMES,
21
22
  CHART_DATE_STEPS,
@@ -128,6 +129,127 @@ import {
128
129
  } from "../chunk-664LQYFJ.js";
129
130
  import "../chunk-QVZZGT4X.js";
130
131
 
132
+ // src/chart/raster.ts
133
+ var CHART_RASTER_MAX_SIZE = 1024;
134
+ function chartRasterSize(width, height, scale = 2, maxSize = CHART_RASTER_MAX_SIZE) {
135
+ if (!(width > 0) || !(height > 0)) throw new Error(`A chart image needs a positive width and height; got ${width} \xD7 ${height}.`);
136
+ const factor = Math.min(scale > 0 ? scale : 1, maxSize / Math.max(width, height));
137
+ return { width: Math.max(1, Math.round(width * factor)), height: Math.max(1, Math.round(height * factor)), scale: factor };
138
+ }
139
+ function closingParen(text, open2) {
140
+ let depth = 0;
141
+ for (let i = open2; i < text.length; i++) {
142
+ if (text[i] === "(") depth++;
143
+ else if (text[i] === ")" && --depth === 0) return i + 1;
144
+ }
145
+ return -1;
146
+ }
147
+ function substituteVars(value, lookup, depth = 0) {
148
+ let out = "";
149
+ let i = 0;
150
+ for (let at = value.indexOf("var(", i); at !== -1; at = value.indexOf("var(", i)) {
151
+ const end = closingParen(value, at + 3);
152
+ if (end === -1) break;
153
+ const inner = value.slice(at + 4, end - 1);
154
+ let comma = -1;
155
+ for (let j = 0, d = 0; j < inner.length; j++) {
156
+ if (inner[j] === "(") d++;
157
+ else if (inner[j] === ")") d--;
158
+ else if (inner[j] === "," && d === 0) {
159
+ comma = j;
160
+ break;
161
+ }
162
+ }
163
+ const name = (comma === -1 ? inner : inner.slice(0, comma)).trim();
164
+ const fallback = comma === -1 ? "" : inner.slice(comma + 1).trim();
165
+ const found = lookup(name).trim();
166
+ const resolved = found || fallback;
167
+ out += value.slice(i, at) + (depth < 8 && resolved.includes("var(") ? substituteVars(resolved, lookup, depth + 1) : resolved);
168
+ i = end;
169
+ }
170
+ return out + value.slice(i);
171
+ }
172
+ var PAINT_ATTRIBUTE = /(\s)([\w:-]+)="([^"]*)"/g;
173
+ function resolveChartSvgPaints(svg, options = {}) {
174
+ const doc = globalThis.document;
175
+ const scope = options.scope ?? doc?.documentElement ?? null;
176
+ const view = scope?.ownerDocument?.defaultView ?? globalThis;
177
+ let computed;
178
+ const style = () => {
179
+ if (computed === void 0) computed = scope && typeof view?.getComputedStyle === "function" ? view.getComputedStyle(scope) : null;
180
+ return computed;
181
+ };
182
+ const lookup = options.lookup ?? ((name) => style()?.getPropertyValue(name) ?? "");
183
+ return svg.replace(PAINT_ATTRIBUTE, (match, space, name, raw) => {
184
+ let value = raw;
185
+ if (value.includes("var(")) value = substituteVars(value.replace(/&quot;/g, '"'), lookup).replace(/"/g, "&quot;");
186
+ if (/currentcolor/i.test(value)) value = value.replace(/currentcolor/gi, style()?.color || "#000000");
187
+ if (name === "font-family" && /^(inherit|unset|initial)$/i.test(value.trim())) value = (style()?.fontFamily || "sans-serif").replace(/"/g, "'");
188
+ if (value === raw) return match;
189
+ return value.trim() ? `${space}${name}="${value}"` : "";
190
+ });
191
+ }
192
+ var NO_DOM = 'Rasterizing a chart needs a browser DOM (document, Image and a 2D canvas). Outside a browser, render SVG with chartToSvg (or render_chart with format "svg") and convert it with an SVG renderer, or pass your own rasterize option to createChartToolProvider.';
193
+ async function svgToPngDataUrl(svg, options) {
194
+ const doc = options.document ?? globalThis.document;
195
+ const ImageCtor = doc?.defaultView?.Image ?? globalThis.Image;
196
+ if (!doc || typeof doc.createElement !== "function" || !ImageCtor) throw new Error(NO_DOM);
197
+ const size = chartRasterSize(options.width, options.height, options.scale, options.maxSize);
198
+ const canvas = doc.createElement("canvas");
199
+ canvas.width = size.width;
200
+ canvas.height = size.height;
201
+ let context = null;
202
+ try {
203
+ context = canvas.getContext("2d");
204
+ } catch {
205
+ context = null;
206
+ }
207
+ if (!context) throw new Error(NO_DOM);
208
+ const drawable = resolveChartSvgPaints(svg, options);
209
+ const image = new ImageCtor();
210
+ await new Promise((resolve2, reject) => {
211
+ image.onload = () => resolve2();
212
+ image.onerror = () => reject(new Error("The chart SVG could not be loaded as an image; check that it is a complete <svg> document with the SVG namespace."));
213
+ image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(drawable)}`;
214
+ });
215
+ const background = options.background ?? "#ffffff";
216
+ if (background !== "transparent") {
217
+ context.fillStyle = background;
218
+ context.fillRect(0, 0, size.width, size.height);
219
+ }
220
+ context.drawImage(image, 0, 0, size.width, size.height);
221
+ const url = canvas.toDataURL("image/png");
222
+ if (!url.startsWith("data:image/png")) throw new Error("This canvas cannot export PNG (toDataURL returned no image).");
223
+ return { url, width: size.width, height: size.height };
224
+ }
225
+ async function rasterizeChartSvg(svg, options) {
226
+ const { url, width, height } = await svgToPngDataUrl(svg, options);
227
+ return { data: url.slice(url.indexOf(",") + 1), mimeType: "image/png", width, height };
228
+ }
229
+ function createChartRasterizer(options = {}) {
230
+ const doc = options.document ?? globalThis.document;
231
+ if (!doc || typeof doc.createElement !== "function") return void 0;
232
+ return (svg, size) => rasterizeChartSvg(svg, { ...options, ...size });
233
+ }
234
+ function chartFileName(spec, extension) {
235
+ const base = (spec.title ?? "").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-+|-+$/g, "").slice(0, 60);
236
+ return `${base || "chart"}.${extension}`;
237
+ }
238
+ async function downloadChartPng(spec, options = {}) {
239
+ const { width = 800, height = 450, filename = chartFileName(spec, "png"), theme, ...raster } = options;
240
+ const svg = chartToSvg(spec, { width, height, theme });
241
+ const { url, width: w, height: h } = await svgToPngDataUrl(svg, { ...raster, width, height });
242
+ const doc = raster.document ?? globalThis.document;
243
+ const link = doc.createElement("a");
244
+ link.href = url;
245
+ link.download = filename;
246
+ link.style.display = "none";
247
+ doc.body?.appendChild(link);
248
+ link.click();
249
+ link.remove();
250
+ return { data: url.slice(url.indexOf(",") + 1), mimeType: "image/png", width: w, height: h, filename };
251
+ }
252
+
131
253
  // src/chart/recommend.ts
132
254
  var blank = (c) => c === null || c === void 0 || String(c).trim() === "";
133
255
  var isText = (c) => blank(c) || parseChartValue(c) === void 0;
@@ -763,6 +885,7 @@ function chartHitTest(scene, point, measure = (t, s, w = 400) => t.length * s *
763
885
  }
764
886
 
765
887
  // src/chart/tools.ts
888
+ var RENDER_CHART_INSTRUCTIONS = 'Draws the chart without changing it so you can check how it looks: SVG text by default, or a PNG image with format "png" when the host can rasterize. Returns the layout warnings (hidden or thinned labels, dropped markers) alongside. An image costs tokens, so render after a batch of edits rather than after each one, and use it to check what text cannot tell you: overlapping or cut-off labels, a crowded legend, unreadable colours, whether the chart reads at the given size. For values, ids and warnings alone, get_chart is cheaper.';
766
889
  var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
767
890
  var WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
768
891
  var WRITE_IDEMPOTENT = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
@@ -817,21 +940,36 @@ function createChartToolProvider(controller, options = {}) {
817
940
  const renderChart = {
818
941
  name: "render_chart",
819
942
  title: "Render the chart",
820
- description: 'Draws the chart without changing it, to check it visually: SVG text by default, or a PNG image (format "png") when the host can rasterize. Look for overlapping or hidden labels; get_chart lists layout warnings without a render.',
943
+ description: RENDER_CHART_INSTRUCTIONS,
821
944
  inputSchema: {
822
945
  type: "object",
823
- properties: { width: WIDTH, height: HEIGHT, format: { type: "string", enum: ["svg", "png"], description: "svg (default) or png (only when the host supports it)." } },
946
+ properties: {
947
+ width: WIDTH,
948
+ height: HEIGHT,
949
+ format: { type: "string", enum: ["svg", "png"], description: "svg (default): the SVG document as text. png: an image to look at (only when the host can rasterize; costs image tokens)." }
950
+ },
824
951
  additionalProperties: false
825
952
  },
826
953
  annotations: { title: "Render the chart", ...READ },
827
954
  handler: async ({ width = size.width, height = size.height, format = "svg" }) => {
828
- const svg = chartToSvg(controller.toJSON(), { width, height });
955
+ const json = controller.toJSON();
956
+ const scene = layoutChart(json, { width, height, selection: null });
957
+ const svg = chartSceneToSvg(scene, { ariaLabel: json.title || void 0, description: describeChart(json) });
958
+ const warnings = scene.warnings;
829
959
  if (format === "png") {
830
- if (!options.rasterize) fail2('This host cannot rasterize charts; call render_chart with format "svg".');
960
+ if (!options.rasterize) fail2('This host cannot rasterize charts; call render_chart with format "svg", or get_chart for the layout warnings.');
831
961
  const image = await options.rasterize(svg, { width, height });
832
- return { content: [{ type: "image", data: image.data, mimeType: image.mimeType }], structuredContent: { width, height, mimeType: image.mimeType } };
962
+ const info = { width, height, mimeType: image.mimeType, warnings };
963
+ const note = warnings.length ? `Layout warnings: ${warnings.map((w) => w.message).join(" ")}` : "No layout warnings.";
964
+ return {
965
+ content: [
966
+ { type: "image", data: image.data, mimeType: image.mimeType },
967
+ { type: "text", text: `Chart at ${width} \xD7 ${height}. ${note}` }
968
+ ],
969
+ structuredContent: info
970
+ };
833
971
  }
834
- return { svg, width, height };
972
+ return { svg, width, height, warnings };
835
973
  }
836
974
  };
837
975
  const checkColors = {
@@ -1666,6 +1804,306 @@ var ChartDirector = class {
1666
1804
  }
1667
1805
  }
1668
1806
  };
1807
+
1808
+ // src/chart/office-files.ts
1809
+ var OfficeChartsError = class extends Error {
1810
+ constructor(message, options) {
1811
+ super(message, options);
1812
+ this.name = "OfficeChartsError";
1813
+ }
1814
+ };
1815
+ var CHARTEX_NS = "http://schemas.microsoft.com/office/drawing/2014/chartex";
1816
+ var STRICT_CHART_NS = "http://purl.oclc.org/ooxml/drawingml/chart";
1817
+ var dirname = (path) => path.slice(0, path.lastIndexOf("/") + 1);
1818
+ function resolvePartPath(baseDir, target) {
1819
+ const out = [];
1820
+ for (const segment of (target.startsWith("/") ? target.slice(1) : baseDir + target).split("/")) {
1821
+ if (segment === "" || segment === ".") continue;
1822
+ if (segment === "..") out.pop();
1823
+ else out.push(segment);
1824
+ }
1825
+ return out.join("/");
1826
+ }
1827
+ var relsPathFor = (partPath) => `${dirname(partPath)}_rels/${partPath.slice(dirname(partPath).length)}.rels`;
1828
+ function elements(root, localName) {
1829
+ return Array.from(root.getElementsByTagNameNS("*", localName));
1830
+ }
1831
+ function relId(el, localName = "id") {
1832
+ for (const a of Array.from(el.attributes)) {
1833
+ if (a.localName === localName && (a.namespaceURI?.includes("relationships") || a.prefix === "r")) return a.value;
1834
+ }
1835
+ return void 0;
1836
+ }
1837
+ var firstChild = (el, localName) => el ? Array.from(el.children ?? el.childNodes).find((n) => n.nodeType === 1 && n.localName === localName) : void 0;
1838
+ async function readXml(r, path) {
1839
+ const entry = r.zip.get(path) ?? r.zip.entries.find((e) => e.name.toLowerCase() === path.toLowerCase());
1840
+ if (!entry) return null;
1841
+ const doc = new r.Parser().parseFromString(await r.zip.readText(entry), "application/xml");
1842
+ return doc.getElementsByTagName("parsererror").length ? null : doc;
1843
+ }
1844
+ async function readRels(r, partPath) {
1845
+ const rels = /* @__PURE__ */ new Map();
1846
+ const doc = await readXml(r, relsPathFor(partPath));
1847
+ if (!doc) return rels;
1848
+ for (const rel of elements(doc, "Relationship")) {
1849
+ const id = rel.getAttribute("Id");
1850
+ if (!id) continue;
1851
+ const target = rel.getAttribute("Target") ?? "";
1852
+ const external = rel.getAttribute("TargetMode")?.toLowerCase() === "external";
1853
+ rels.set(id, { type: rel.getAttribute("Type") ?? "", target: external ? target : resolvePartPath(dirname(partPath), target), external });
1854
+ }
1855
+ return rels;
1856
+ }
1857
+ var isChartRef = (el) => el.localName === "chart" && (el.namespaceURI === CHART_OOXML_NS || el.namespaceURI === STRICT_CHART_NS || el.namespaceURI === CHARTEX_NS);
1858
+ function frameName(ref) {
1859
+ for (let el = ref.parentElement; el; el = el.parentElement) {
1860
+ if (el.localName === "graphicFrame") {
1861
+ const nv = elements(el, "cNvPr")[0];
1862
+ return nv?.getAttribute("name") || void 0;
1863
+ }
1864
+ }
1865
+ return void 0;
1866
+ }
1867
+ async function chartsInPart(r, root, rels, place) {
1868
+ for (const ref of elements(root, "chart")) {
1869
+ if (r.charts.length >= r.max) return;
1870
+ if (!isChartRef(ref)) continue;
1871
+ const rel = rels.get(relId(ref) ?? "");
1872
+ if (!rel || rel.external || r.seen.has(rel.target)) continue;
1873
+ const entry = r.zip.get(rel.target);
1874
+ if (!entry) continue;
1875
+ r.seen.add(rel.target);
1876
+ const read = chartFromOoxml(await r.zip.readText(entry), { DOMParser: r.Parser });
1877
+ const name = frameName(ref);
1878
+ r.charts.push({
1879
+ id: `chart-${r.charts.length + 1}`,
1880
+ path: rel.target,
1881
+ partKind: ref.namespaceURI === CHARTEX_NS ? "chartex" : "chart",
1882
+ ...place(ref),
1883
+ ...name ? { name } : {},
1884
+ title: read.spec.title,
1885
+ spec: read.spec,
1886
+ issues: read.issues.map((i) => i.message),
1887
+ unsupported: read.unsupported,
1888
+ lossless: read.lossless
1889
+ });
1890
+ }
1891
+ }
1892
+ function columnName(index) {
1893
+ let name = "";
1894
+ for (let n = index + 1; n > 0; n = Math.floor((n - 1) / 26)) name = String.fromCharCode(65 + (n - 1) % 26) + name;
1895
+ return name;
1896
+ }
1897
+ function anchorCell(marker) {
1898
+ const col = Number(firstChild(marker, "col")?.textContent);
1899
+ const row = Number(firstChild(marker, "row")?.textContent);
1900
+ return Number.isInteger(col) && Number.isInteger(row) && col >= 0 && row >= 0 ? `${columnName(col)}${row + 1}` : void 0;
1901
+ }
1902
+ function drawingPlace(ref) {
1903
+ for (let el = ref.parentElement; el; el = el.parentElement) {
1904
+ if (el.localName === "twoCellAnchor" || el.localName === "oneCellAnchor") {
1905
+ const anchor = anchorCell(firstChild(el, "from"));
1906
+ const to = el.localName === "twoCellAnchor" ? anchorCell(firstChild(el, "to")) : void 0;
1907
+ return { ...anchor ? { anchor } : {}, ...anchor && to ? { range: `${anchor}:${to}` } : {} };
1908
+ }
1909
+ if (el.localName === "absoluteAnchor") return {};
1910
+ }
1911
+ return {};
1912
+ }
1913
+ async function readMainPart(r) {
1914
+ const rootRels = await readRels(r, "");
1915
+ const main = [...rootRels.values()].find((rel) => /\/officeDocument$/.test(rel.type) && !rel.external);
1916
+ const doc = main ? await readXml(r, main.target) : null;
1917
+ if (!main || !doc) throw new OfficeChartsError("This is not an Office file: _rels/.rels names no main part (expected an .xlsx, .pptx or .docx file).");
1918
+ return { path: main.target, doc };
1919
+ }
1920
+ async function readXlsx(r, main) {
1921
+ const rels = await readRels(r, main.path);
1922
+ for (const sheet of elements(main.doc, "sheet")) {
1923
+ const rel = rels.get(relId(sheet) ?? "");
1924
+ if (!rel || rel.external) continue;
1925
+ const doc = await readXml(r, rel.target);
1926
+ if (!doc) continue;
1927
+ const sheetName = sheet.getAttribute("name") ?? rel.target;
1928
+ const sheetRels = await readRels(r, rel.target);
1929
+ for (const drawingRef of elements(doc, "drawing")) {
1930
+ const drawingRel = sheetRels.get(relId(drawingRef) ?? "");
1931
+ if (!drawingRel || drawingRel.external) continue;
1932
+ const drawing = await readXml(r, drawingRel.target);
1933
+ if (!drawing) continue;
1934
+ const drawingRels = await readRels(r, drawingRel.target);
1935
+ await chartsInPart(r, drawing, drawingRels, (ref) => ({ sheet: sheetName, ...drawingPlace(ref) }));
1936
+ }
1937
+ }
1938
+ }
1939
+ async function readPptx(r, main) {
1940
+ const rels = await readRels(r, main.path);
1941
+ const slides = elements(main.doc, "sldId");
1942
+ for (let i = 0; i < slides.length; i++) {
1943
+ const rel = rels.get(relId(slides[i]) ?? "");
1944
+ if (!rel || rel.external) continue;
1945
+ const doc = await readXml(r, rel.target);
1946
+ if (!doc) continue;
1947
+ await chartsInPart(r, doc, await readRels(r, rel.target), () => ({ slide: i + 1 }));
1948
+ }
1949
+ }
1950
+ async function readDocx(r, main) {
1951
+ await chartsInPart(r, main.doc, await readRels(r, main.path), () => ({}));
1952
+ }
1953
+ async function open(bytes, options) {
1954
+ const Parser = options.DOMParser ?? globalThis.DOMParser;
1955
+ if (!Parser) throw new OfficeChartsError("Reading Office files needs a DOMParser. In Node, pass one (e.g. from jsdom or @xmldom/xmldom) as { DOMParser }.");
1956
+ let zip;
1957
+ try {
1958
+ zip = await readZip(bytes, options);
1959
+ } catch (error) {
1960
+ throw new OfficeChartsError(`This is not an Office file: ${error.message}`, { cause: error });
1961
+ }
1962
+ const r = { zip, Parser, max: options.maxCharts ?? 500, charts: [], seen: /* @__PURE__ */ new Set() };
1963
+ const main = await readMainPart(r);
1964
+ const root = main.doc.documentElement.localName;
1965
+ const format = root === "workbook" ? "xlsx" : root === "presentation" ? "pptx" : root === "document" ? "docx" : void 0;
1966
+ if (!format) throw new OfficeChartsError(`The main part ${main.path} is a <${root}>; expected an Excel workbook, PowerPoint presentation or Word document.`);
1967
+ return { r, main, format };
1968
+ }
1969
+ var READERS = { xlsx: readXlsx, pptx: readPptx, docx: readDocx };
1970
+ async function extractOfficeCharts(bytes, options = {}) {
1971
+ const { r, main, format } = await open(bytes, options);
1972
+ await READERS[format](r, main);
1973
+ return { format, charts: r.charts };
1974
+ }
1975
+ async function extractAs(format, bytes, options) {
1976
+ const result = await extractOfficeCharts(bytes, options);
1977
+ if (result.format !== format) throw new OfficeChartsError(`This is a .${result.format} file, not a .${format} file.`);
1978
+ return result.charts;
1979
+ }
1980
+ var extractXlsxCharts = (bytes, options = {}) => extractAs("xlsx", bytes, options);
1981
+ var extractPptxCharts = (bytes, options = {}) => extractAs("pptx", bytes, options);
1982
+ var extractDocxCharts = (bytes, options = {}) => extractAs("docx", bytes, options);
1983
+ function describeOfficeChartPlace(chart) {
1984
+ if (chart.sheet !== void 0) return chart.anchor ? `${chart.sheet}!${chart.anchor}` : chart.sheet;
1985
+ if (chart.slide !== void 0) return `Slide ${chart.slide}`;
1986
+ return "Document";
1987
+ }
1988
+
1989
+ // src/chart/file-tools.ts
1990
+ var READ2 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
1991
+ var DESTRUCTIVE2 = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false };
1992
+ var fail3 = (message) => {
1993
+ throw new Error(message);
1994
+ };
1995
+ function fromBase64(data) {
1996
+ const clean = data.replace(/^data:[^,]*,/, "").replace(/\s+/g, "");
1997
+ let binary;
1998
+ try {
1999
+ binary = atob(clean);
2000
+ } catch {
2001
+ return fail3('"data" is not valid base64. Pass the whole .xlsx, .pptx or .docx file encoded as base64 (a data: URL prefix is fine).');
2002
+ }
2003
+ const out = new Uint8Array(binary.length);
2004
+ for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
2005
+ return out;
2006
+ }
2007
+ function chartSummary(chart) {
2008
+ return {
2009
+ id: chart.id,
2010
+ place: describeOfficeChartPlace(chart),
2011
+ ...chart.sheet !== void 0 ? { sheet: chart.sheet } : {},
2012
+ ...chart.slide !== void 0 ? { slide: chart.slide } : {},
2013
+ ...chart.anchor ? { anchor: chart.anchor } : {},
2014
+ ...chart.range ? { range: chart.range } : {},
2015
+ ...chart.name ? { name: chart.name } : {},
2016
+ path: chart.path,
2017
+ partKind: chart.partKind,
2018
+ title: chart.title,
2019
+ type: chart.spec.type,
2020
+ series: chart.spec.series.map((s) => s.name),
2021
+ categories: chart.spec.categories.length,
2022
+ issues: chart.issues,
2023
+ unsupported: chart.unsupported,
2024
+ lossless: chart.lossless
2025
+ };
2026
+ }
2027
+ function createOfficeChartToolProvider(options = {}) {
2028
+ const cache = /* @__PURE__ */ new WeakMap();
2029
+ const readOnlyReason = () => {
2030
+ const r = typeof options.readOnly === "function" ? options.readOnly() : options.readOnly;
2031
+ if (!r) return null;
2032
+ return typeof r === "string" ? r : "This chart is read-only.";
2033
+ };
2034
+ const source = options.files ? { file: { type: "string", description: "Name of an open file, as list_open_files returns it." } } : { data: { type: "string", description: "The whole .xlsx, .pptx or .docx file as base64 (a data: URL prefix is allowed)." } };
2035
+ const sourceKey = options.files ? "file" : "data";
2036
+ const read = (args) => {
2037
+ if (!options.files) return extractOfficeCharts(fromBase64(String(args.data)), { DOMParser: options.DOMParser });
2038
+ const files = options.files();
2039
+ const file = files.find((f) => f.name === args.file);
2040
+ if (!file) return fail3(`There is no open file "${String(args.file)}"; expected one of ${files.map((f) => JSON.stringify(f.name)).join(", ") || "(none; ask the user to open an .xlsx, .pptx or .docx file)"}.`);
2041
+ let result = cache.get(file);
2042
+ if (!result) {
2043
+ result = extractOfficeCharts(file.data, { DOMParser: options.DOMParser });
2044
+ cache.set(file, result);
2045
+ result.catch(() => cache.delete(file));
2046
+ }
2047
+ return result;
2048
+ };
2049
+ const chartById = async (args) => {
2050
+ const { charts } = await read(args);
2051
+ const chart = charts.find((c) => c.id === args.chartId);
2052
+ if (!chart) fail3(`There is no chart "${String(args.chartId)}" in this file; expected one of ${charts.map((c) => `"${c.id}" (${describeOfficeChartPlace(c)})`).join(", ") || "(none: the file has no charts)"}.`);
2053
+ return chart;
2054
+ };
2055
+ const CHART_ID = { type: "string", description: 'Chart id from list_file_charts, e.g. "chart-1".' };
2056
+ const listOpenFiles = {
2057
+ name: "list_open_files",
2058
+ title: "List open files",
2059
+ description: "Lists the Office files the user opened that charts can be read from. Returns their names and sizes in bytes.",
2060
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
2061
+ annotations: { title: "List open files", ...READ2 },
2062
+ handler: () => ({ files: (options.files?.() ?? []).map((f) => ({ name: f.name, bytes: f.data.byteLength })) })
2063
+ };
2064
+ const listCharts = {
2065
+ name: "list_file_charts",
2066
+ title: "List the charts in an Office file",
2067
+ description: "Reads an Excel workbook, PowerPoint deck or Word document and lists its charts in file order: id, where it sits (sheet name and anchor cell, or slide number), title, type, series names, number of categories, read problems, features the chart editor cannot hold, and whether it reads losslessly. Pass an id to get_file_chart or load_file_chart.",
2068
+ inputSchema: { type: "object", properties: { ...source }, required: [sourceKey], additionalProperties: false },
2069
+ annotations: { title: "List the charts in an Office file", ...READ2 },
2070
+ handler: async (args) => {
2071
+ const result = await read(args);
2072
+ return { format: result.format, charts: result.charts.map(chartSummary) };
2073
+ }
2074
+ };
2075
+ const getChart = {
2076
+ name: "get_file_chart",
2077
+ title: "Get a chart from an Office file",
2078
+ description: "Returns one chart of an Office file with its full chart spec (data, axes, series styles), where it sits and its read problems.",
2079
+ inputSchema: { type: "object", properties: { ...source, chartId: CHART_ID }, required: [sourceKey, "chartId"], additionalProperties: false },
2080
+ annotations: { title: "Get a chart from an Office file", ...READ2 },
2081
+ handler: async (args) => {
2082
+ const chart = await chartById(args);
2083
+ return { ...chartSummary(chart), spec: chart.spec };
2084
+ }
2085
+ };
2086
+ const loadChart = {
2087
+ name: "load_file_chart",
2088
+ title: "Load a chart from an Office file",
2089
+ description: "Replaces the chart in the editor with a chart from an Office file, as one undo step. Returns the new spec, read problems and features the editor could not keep.",
2090
+ inputSchema: { type: "object", properties: { ...source, chartId: CHART_ID }, required: [sourceKey, "chartId"], additionalProperties: false },
2091
+ annotations: { title: "Load a chart from an Office file", ...DESTRUCTIVE2 },
2092
+ handler: async (args) => {
2093
+ const reason = readOnlyReason();
2094
+ if (reason) fail3(reason);
2095
+ const chart = await chartById(args);
2096
+ const result = options.controller.update({ ...chart.spec }, { label: "Load chart from file" });
2097
+ if (!result.ok) fail3(result.message ?? "The chart could not be loaded.");
2098
+ return { id: chart.id, place: describeOfficeChartPlace(chart), spec: options.controller.toJSON(), issues: chart.issues, unsupported: chart.unsupported, lossless: chart.lossless };
2099
+ }
2100
+ };
2101
+ return {
2102
+ id: options.id ?? "office-charts",
2103
+ ...options.namespace ? { namespace: options.namespace } : {},
2104
+ tools: () => [...options.files ? [listOpenFiles] : [], listCharts, getChart, ...options.controller && !readOnlyReason() ? [loadChart] : []]
2105
+ };
2106
+ }
1669
2107
  export {
1670
2108
  CHART_AXIS_KINDS,
1671
2109
  CHART_COLOR_SCHEMES,
@@ -1683,6 +2121,7 @@ export {
1683
2121
  CHART_ORIENTATIONS,
1684
2122
  CHART_PALETTE_SHADES,
1685
2123
  CHART_PRESENCE_COLORS,
2124
+ CHART_RASTER_MAX_SIZE,
1686
2125
  CHART_SERIES_TYPES,
1687
2126
  CHART_STACKINGS,
1688
2127
  CHART_TRENDLINE_TYPES,
@@ -1698,6 +2137,8 @@ export {
1698
2137
  MAX_CHART_DATA_LABELS,
1699
2138
  MAX_CHART_LINE_POINTS,
1700
2139
  MAX_CHART_MARKERS,
2140
+ OfficeChartsError,
2141
+ RENDER_CHART_INSTRUCTIONS,
1701
2142
  analyzeChartTable,
1702
2143
  blendChartScenes,
1703
2144
  canDragChartPoint,
@@ -1712,6 +2153,7 @@ export {
1712
2153
  chartDateLabels,
1713
2154
  chartDateToSerial,
1714
2155
  chartFamily,
2156
+ chartFileName,
1715
2157
  chartFormatToCode,
1716
2158
  chartFromMermaid,
1717
2159
  chartFromOoxml,
@@ -1725,6 +2167,7 @@ export {
1725
2167
  chartPointColor,
1726
2168
  chartPresetPatch,
1727
2169
  chartQuantile,
2170
+ chartRasterSize,
1728
2171
  chartRgbToHex,
1729
2172
  chartRgbToOklab,
1730
2173
  chartSceneToDomSpec,
@@ -1751,16 +2194,24 @@ export {
1751
2194
  contrastingTextColor,
1752
2195
  createChartCommands,
1753
2196
  createChartController,
2197
+ createChartRasterizer,
1754
2198
  createChartSpec,
1755
2199
  createChartToolProvider,
1756
2200
  createInstantChartClock,
2201
+ createOfficeChartToolProvider,
1757
2202
  cyclePaletteColor,
1758
2203
  decimateChartPoints,
1759
2204
  defaultChartAxis,
1760
2205
  describeChart,
1761
2206
  describeChartSelection,
2207
+ describeOfficeChartPlace,
2208
+ downloadChartPng,
1762
2209
  easeChartTween,
1763
2210
  estimateChartTextWidth,
2211
+ extractDocxCharts,
2212
+ extractOfficeCharts,
2213
+ extractPptxCharts,
2214
+ extractXlsxCharts,
1764
2215
  findMermaidCharts,
1765
2216
  fitChartTrendline,
1766
2217
  formatChartDate,
@@ -1784,9 +2235,11 @@ export {
1784
2235
  parseChartValue,
1785
2236
  parsePartialChartJson,
1786
2237
  planChartCommand,
2238
+ rasterizeChartSvg,
1787
2239
  realChartClock,
1788
2240
  recommendChartTypes,
1789
2241
  recommendForShape,
2242
+ resolveChartSvgPaints,
1790
2243
  sameChartSelection,
1791
2244
  shadeChartColor,
1792
2245
  simulateChartVision,