@openpresentation/opf 0.12.1 → 0.13.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 (50) hide show
  1. package/README.md +1 -1
  2. package/dist/audit.d.ts +9 -130
  3. package/dist/audit.js +5 -5
  4. package/dist/{catalogs-BnI7JKcn.d.ts → catalogs-ywCamd8C.d.ts} +1 -1
  5. package/dist/catalogs.d.ts +2 -2
  6. package/dist/chart-data-Cdr6rRVV.d.ts +177 -0
  7. package/dist/{chunk-6LZK2IZB.js → chunk-7N4VMAT4.js} +11 -7
  8. package/dist/{chunk-4LAM3N6S.js → chunk-7QBCWNWL.js} +424 -18
  9. package/dist/{chunk-SOSOBKQ2.js → chunk-ATGDDJPX.js} +4 -5
  10. package/dist/{chunk-V2537ENL.js → chunk-II5EYKZU.js} +9 -7
  11. package/dist/{chunk-MWPMJE6P.js → chunk-INU7ZNSB.js} +50 -2
  12. package/dist/{chunk-RUC7L5DJ.js → chunk-IS2CPNVJ.js} +1 -1
  13. package/dist/{chunk-JKVKYVWL.js → chunk-IYCJUKWD.js} +90 -471
  14. package/dist/{chunk-HOBSL7AJ.js → chunk-MPM7NRG7.js} +2396 -1408
  15. package/dist/{chunk-AUM5MXBR.js → chunk-RAYB46AB.js} +8 -6
  16. package/dist/{composition-DWbCiMwF.d.ts → composition-D7we69Tr.d.ts} +7 -0
  17. package/dist/composition.d.ts +2 -1
  18. package/dist/composition.js +1 -1
  19. package/dist/convert.d.ts +4 -1
  20. package/dist/convert.js +33 -16
  21. package/dist/data.d.ts +2 -1
  22. package/dist/data.js +3 -1
  23. package/dist/diff.d.ts +1 -1
  24. package/dist/diff.js +8 -7
  25. package/dist/docs.js +32 -26
  26. package/dist/examples.d.ts +1 -1
  27. package/dist/examples.js +102 -57
  28. package/dist/format.js +1 -1
  29. package/dist/index.d.ts +13 -10
  30. package/dist/index.js +8 -8
  31. package/dist/lint-B7azdiuE.d.ts +197 -0
  32. package/dist/lint.d.ts +7 -70
  33. package/dist/lint.js +4 -4
  34. package/dist/markdown.d.ts +7 -5
  35. package/dist/markdown.js +5 -5
  36. package/dist/pagination.d.ts +2 -1
  37. package/dist/pagination.js +4 -4
  38. package/dist/patch.js +4 -4
  39. package/dist/{presentation-DcF-MZcz.d.ts → presentation-mG-IYErQ.d.ts} +187 -14
  40. package/dist/repo-readme.js +1 -1
  41. package/dist/{schemas-BSjlG8-R.d.ts → schemas-BbPonsTz.d.ts} +18 -0
  42. package/dist/schemas.d.ts +1 -1
  43. package/dist/schemas.js +1 -1
  44. package/dist/spec/catalogs/manifest.json +3 -3
  45. package/dist/spec/schemas/opf.schema.json +238 -20
  46. package/dist/types.d.ts +4 -4
  47. package/dist/{validator-DfG9yxba.d.ts → validator-CIylxoIm.d.ts} +3 -3
  48. package/dist/validator.d.ts +4 -4
  49. package/dist/validator.js +3 -3
  50. package/package.json +3 -3
@@ -16,12 +16,13 @@ function tableGrid(value, path = "table") {
16
16
  continue;
17
17
  }
18
18
  const object = record(input), styled = Object.hasOwn(object, "value");
19
+ const column = !styled && hasHeaders && r === 0 && typeof object.name === "string";
19
20
  const rowSpan = styled ? object.rowSpan ?? 1 : 1, colSpan = styled ? object.colSpan ?? 1 : 1;
20
21
  if (!Number.isSafeInteger(rowSpan) || !Number.isSafeInteger(colSpan) || rowSpan < 1 || colSpan < 1 || r + rowSpan > rowCount || c + colSpan > columnCount || hasHeaders && r === 0 && rowSpan > 1) {
21
22
  issues.push({ path: cellPath, message: "Cell spans must be positive integers within the table grid and cannot cross from headers into body rows." });
22
23
  continue;
23
24
  }
24
- const cell = { input, value: styled ? object.value : input, style: styled ? record(object.style) : {}, row: r, column: c, rowSpan, colSpan, header: hasHeaders && r === 0, path: cellPath, valuePath: styled ? cellPath + ".value" : cellPath };
25
+ const cell = { input, value: styled ? object.value : column ? object.name : input, style: styled ? record(object.style) : {}, row: r, column: c, rowSpan, colSpan, header: hasHeaders && r === 0, path: cellPath, valuePath: styled ? cellPath + ".value" : column ? cellPath + ".name" : cellPath };
25
26
  rows2[r].push(cell);
26
27
  for (let y = r; y < r + rowSpan; y++) for (let x = c; x < c + colSpan; x++) {
27
28
  if (owners[y][x]) issues.push({ path: cellPath, message: "Cell spans cannot overlap another spanning cell." });
@@ -37,1131 +38,6 @@ function tableRowBoundaries(value) {
37
38
  return [...allowed];
38
39
  }
39
40
 
40
- // src/image-aspect.ts
41
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
42
- function sourceOf(value) {
43
- if (typeof value === "string") return value;
44
- return isRecord(value) && typeof value.src === "string" ? value.src : void 0;
45
- }
46
- var PREFIX_BYTES = 64 * 1024;
47
- var JPEG_PREFIX_BYTES = 1024 * 1024;
48
- function dataBytes(uri, limit) {
49
- const match = /^data:([^;,]*)((?:;[^;,]*)*),/i.exec(uri);
50
- if (!match) return void 0;
51
- const type = (match[1] ?? "").toLowerCase(), start = match[0].length;
52
- try {
53
- if (/;base64/i.test(match[2] ?? "")) {
54
- const chars = Math.ceil(limit / 3) * 4, text2 = uri.slice(start, start + chars + chars / 8).replace(/\s+/g, "").slice(0, chars);
55
- return { type, bytes: Uint8Array.from(atob(text2.slice(0, text2.length - text2.length % 4)), (char) => char.charCodeAt(0)) };
56
- }
57
- const prefix = uri.slice(start, start + limit).replace(/%[0-9a-f]?$/i, "");
58
- return { type, bytes: new TextEncoder().encode(decodeURIComponent(prefix)) };
59
- } catch {
60
- return void 0;
61
- }
62
- }
63
- var at = (b, i) => b[i] ?? 0;
64
- var u16be = (b, i) => at(b, i) << 8 | at(b, i + 1);
65
- var u32be = (b, i) => (at(b, i) << 24 | at(b, i + 1) << 16 | at(b, i + 2) << 8 | at(b, i + 3)) >>> 0;
66
- var u16le = (b, i) => at(b, i) | at(b, i + 1) << 8;
67
- var u24le = (b, i) => at(b, i) | at(b, i + 1) << 8 | at(b, i + 2) << 16;
68
- var ascii = (b, i, n) => String.fromCharCode(...b.subarray(i, i + n));
69
- function rasterDimensions(b) {
70
- if (b.length >= 24 && u32be(b, 0) === 2303741511 && ascii(b, 12, 4) === "IHDR") return { width: u32be(b, 16), height: u32be(b, 20) };
71
- if (b.length >= 10 && (ascii(b, 0, 6) === "GIF87a" || ascii(b, 0, 6) === "GIF89a")) return { width: u16le(b, 6), height: u16le(b, 8) };
72
- if (b.length >= 4 && b[0] === 255 && b[1] === 216) {
73
- let i = 2;
74
- while (i + 9 < b.length) {
75
- if (at(b, i) !== 255) {
76
- i++;
77
- continue;
78
- }
79
- const marker = at(b, i + 1);
80
- if (marker === 255) {
81
- i++;
82
- continue;
83
- }
84
- if (marker === 216 || marker === 1 || marker >= 208 && marker <= 215) {
85
- i += 2;
86
- continue;
87
- }
88
- if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return { height: u16be(b, i + 5), width: u16be(b, i + 7) };
89
- i += 2 + u16be(b, i + 2);
90
- }
91
- return void 0;
92
- }
93
- if (b.length >= 30 && ascii(b, 0, 4) === "RIFF" && ascii(b, 8, 4) === "WEBP") {
94
- const chunk = ascii(b, 12, 4);
95
- if (chunk === "VP8X") return { width: u24le(b, 24) + 1, height: u24le(b, 27) + 1 };
96
- if (chunk === "VP8L" && at(b, 20) === 47) {
97
- const bits = u16le(b, 21) | u16le(b, 23) << 16;
98
- return { width: (bits & 16383) + 1, height: (bits >>> 14 & 16383) + 1 };
99
- }
100
- if (chunk === "VP8 ") return { width: u16le(b, 26) & 16383, height: u16le(b, 28) & 16383 };
101
- }
102
- return void 0;
103
- }
104
- function svgDimensions(text2) {
105
- const root = /<svg\b([^>]*)>/i.exec(text2)?.[1];
106
- if (root === void 0) return void 0;
107
- const attribute = (name) => new RegExp(`\\s${name}\\s*=\\s*["']([^"']*)["']`, "i").exec(root)?.[1];
108
- const length = (value) => {
109
- const match = value && /^\s*([0-9]*\.?[0-9]+)\s*(px|pt|in|cm|mm|em)?\s*$/i.exec(value);
110
- return match ? Number(match[1]) : void 0;
111
- };
112
- const width = length(attribute("width")), height = length(attribute("height"));
113
- if (width && height) return { width, height };
114
- const box = attribute("viewBox")?.trim().split(/[\s,]+/).map(Number);
115
- if (box && box.length === 4 && box.every(Number.isFinite)) {
116
- const [, , width2 = 0, height2 = 0] = box;
117
- if (width2 > 0 && height2 > 0) return { width: width2, height: height2 };
118
- }
119
- return void 0;
120
- }
121
- var cache = /* @__PURE__ */ new Map();
122
- var CACHE_ENTRIES = 64;
123
- function readDimensions(source) {
124
- const first = dataBytes(source, PREFIX_BYTES);
125
- if (!first) return void 0;
126
- let dimensions;
127
- if (first.type.startsWith("image/svg")) dimensions = svgDimensions(new TextDecoder().decode(first.bytes));
128
- else {
129
- dimensions = rasterDimensions(first.bytes);
130
- if (!dimensions && first.bytes.length >= 2 && first.bytes[0] === 255 && first.bytes[1] === 216) {
131
- const more = dataBytes(source, JPEG_PREFIX_BYTES);
132
- if (more) dimensions = rasterDimensions(more.bytes);
133
- }
134
- }
135
- return dimensions && dimensions.width > 0 && dimensions.height > 0 ? dimensions : void 0;
136
- }
137
- function intrinsicImageSize(value, assets) {
138
- let source = sourceOf(value);
139
- for (let hop = 0; source?.startsWith("asset:") && hop < 8; hop++) source = sourceOf(isRecord(assets) ? assets[source.slice(6)] : void 0);
140
- if (!source?.startsWith("data:")) return void 0;
141
- if (!cache.has(source)) {
142
- if (cache.size >= CACHE_ENTRIES) cache.delete(cache.keys().next().value);
143
- cache.set(source, readDimensions(source));
144
- }
145
- const size = cache.get(source);
146
- return size ? { ...size, svg: /^data:image\/svg/i.test(source) } : void 0;
147
- }
148
- function intrinsicImageAspect(value, assets) {
149
- const size = intrinsicImageSize(value, assets);
150
- return size ? size.width / size.height : void 0;
151
- }
152
-
153
- // src/reading-order.ts
154
- function visualReadingOrder(items, direction = "ltr") {
155
- const index = new Map(items.map((item, i) => [item, i]));
156
- const along = direction === "rtl" ? (item) => -(item.box.x + item.box.width) : (item) => item.box.x;
157
- return readingRows(items).flatMap((row) => [...row].sort((a, b) => along(a) - along(b) || a.box.y - b.box.y || index.get(a) - index.get(b)));
158
- }
159
- function readingRows(items) {
160
- const index = new Map(items.map((item, i) => [item, i]));
161
- const sorted = [...items].sort((a, b) => a.box.y - b.box.y || a.box.x - b.box.x || index.get(a) - index.get(b));
162
- const rows2 = [];
163
- for (const item of sorted) {
164
- const centre = item.box.y + item.box.height / 2;
165
- const row = rows2.find((candidate) => centre >= candidate.top && centre <= candidate.bottom);
166
- if (row) {
167
- row.items.push(item);
168
- row.top = Math.min(row.top, item.box.y);
169
- row.bottom = Math.max(row.bottom, item.box.y + item.box.height);
170
- } else rows2.push({ top: item.box.y, bottom: item.box.y + item.box.height, items: [item] });
171
- }
172
- rows2.sort((a, b) => a.top - b.top);
173
- return rows2.map((row) => row.items.sort((a, b) => index.get(a) - index.get(b)));
174
- }
175
-
176
- // src/direction.ts
177
- var strongRtl = /[\u200F\u061C]|(?=\p{L})[\p{Script=Arabic}\p{Script=Hebrew}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Nko}\p{Script=Adlam}\p{Script=Hanifi_Rohingya}\p{Script=Mandaic}\p{Script=Samaritan}\p{Script=Mende_Kikakui}\p{Script=Imperial_Aramaic}\p{Script=Phoenician}\p{Script=Kharoshthi}\p{Script=Old_South_Arabian}\p{Script=Old_North_Arabian}\p{Script=Avestan}\p{Script=Inscriptional_Parthian}\p{Script=Inscriptional_Pahlavi}\p{Script=Psalter_Pahlavi}\p{Script=Old_Turkic}\p{Script=Old_Hungarian}\p{Script=Nabataean}\p{Script=Palmyrene}\p{Script=Hatran}\p{Script=Manichaean}\p{Script=Sogdian}\p{Script=Old_Sogdian}\p{Script=Elymaic}\p{Script=Chorasmian}\p{Script=Yezidi}\p{Script=Cypriot}\p{Script=Lydian}\p{Script=Meroitic_Cursive}\p{Script=Meroitic_Hieroglyphs}\u{10F70}-\u{10FAF}\u{10D40}-\u{10D8F}]/u;
178
- var strongLtr = /[\u200E\p{L}\p{Nl}]/u;
179
- function paragraphDirection(text2, deckDirection) {
180
- if (deckDirection !== "rtl") return "ltr";
181
- let isolates = 0;
182
- for (const char of String(text2 ?? "")) {
183
- if (char === "\u2066" || char === "\u2067" || char === "\u2068") isolates += 1;
184
- else if (char === "\u2069") isolates = Math.max(0, isolates - 1);
185
- else if (isolates === 0) {
186
- if (strongRtl.test(char)) return "rtl";
187
- if (strongLtr.test(char)) return "ltr";
188
- }
189
- }
190
- return "rtl";
191
- }
192
- function physicalAlignment(alignment, direction) {
193
- const value = alignment ?? "left";
194
- if (direction !== "rtl" || value === "center") return value;
195
- return value === "left" ? "right" : "left";
196
- }
197
- function paragraphDirectionAt(text2, deckDirection) {
198
- if (deckDirection !== "rtl") return () => "ltr";
199
- const source = String(text2 ?? ""), starts = [], directions = [];
200
- let start = 0;
201
- const close = (end) => {
202
- starts.push(start);
203
- directions.push(paragraphDirection(source.slice(start, end), "rtl"));
204
- };
205
- for (const match of source.matchAll(/\r\n|\r|\n/g)) {
206
- close(match.index);
207
- start = match.index + match[0].length;
208
- }
209
- close(source.length);
210
- return (offset) => {
211
- for (let index = starts.length - 1; index >= 0; index--) if (offset >= starts[index]) return directions[index];
212
- return directions[0];
213
- };
214
- }
215
-
216
- // src/content-walk.ts
217
- function isRecord2(value) {
218
- return typeof value === "object" && value !== null && !Array.isArray(value);
219
- }
220
- function pathFor(parentPath, key) {
221
- return parentPath === "/" ? `/${key}` : `${parentPath}/${key.replaceAll("~", "~0").replaceAll("/", "~1")}`;
222
- }
223
- var bareIdPattern = /^[a-z0-9][a-z0-9-]*$/;
224
- var promotedRegionKeys = [
225
- "left",
226
- "center",
227
- "right",
228
- "left+center",
229
- "center+right",
230
- "left+center+right",
231
- "top",
232
- "middle",
233
- "bottom",
234
- "top+middle",
235
- "middle+bottom",
236
- "top+middle+bottom",
237
- "top:left",
238
- "top:center",
239
- "top:right",
240
- "top:left+center",
241
- "top:center+right",
242
- "top:left+center+right",
243
- "middle:left",
244
- "middle:center",
245
- "middle:right",
246
- "middle:left+center",
247
- "middle:center+right",
248
- "middle:left+center+right",
249
- "bottom:left",
250
- "bottom:center",
251
- "bottom:right",
252
- "bottom:left+center",
253
- "bottom:center+right",
254
- "bottom:left+center+right",
255
- "top+middle:left",
256
- "top+middle:center",
257
- "top+middle:right",
258
- "top+middle:left+center",
259
- "top+middle:center+right",
260
- "top+middle:left+center+right",
261
- "middle+bottom:left",
262
- "middle+bottom:center",
263
- "middle+bottom:right",
264
- "middle+bottom:left+center",
265
- "middle+bottom:center+right",
266
- "middle+bottom:left+center+right",
267
- "top+middle+bottom:left",
268
- "top+middle+bottom:center",
269
- "top+middle+bottom:right",
270
- "top+middle+bottom:left+center",
271
- "top+middle+bottom:center+right",
272
- "top+middle+bottom:left+center+right"
273
- ];
274
- function visitContentPayloads(slide, slidePath, visit) {
275
- const walk = (payload, path, depth, ancestors) => {
276
- visit(payload, path);
277
- if (!Array.isArray(payload.blocks) || depth >= MAX_COMPOSITION_DEPTH || ancestors.includes(payload)) return;
278
- const nested = [...ancestors, payload];
279
- const blocksPath = pathFor(path, "blocks");
280
- payload.blocks.forEach((block, index) => {
281
- if (isRecord2(block)) walk(block, `${blocksPath}/${index}`, depth + 1, nested);
282
- });
283
- };
284
- if (Array.isArray(slide.blocks)) {
285
- const blocksPath = pathFor(slidePath, "blocks");
286
- slide.blocks.forEach((block, index) => {
287
- if (isRecord2(block)) walk(block, `${blocksPath}/${index}`, 0, []);
288
- });
289
- }
290
- for (const key of promotedRegionKeys) {
291
- const region = slide[key];
292
- if (isRecord2(region)) walk(region, pathFor(slidePath, key), 0, []);
293
- }
294
- }
295
-
296
- // src/script-fonts.ts
297
- var eastAsianScripts = /* @__PURE__ */ new Set(["Hani", "Hans", "Hant", "Hanb", "Jpan", "Kore", "Hang", "Jamo", "Hira", "Kana", "Hrkt", "Bopo", "Yiii"]);
298
- var complexScripts = /* @__PURE__ */ new Set([
299
- "Arab",
300
- "Hebr",
301
- "Syrc",
302
- "Thaa",
303
- "Nkoo",
304
- "Adlm",
305
- "Rohg",
306
- "Mand",
307
- "Samr",
308
- "Deva",
309
- "Beng",
310
- "Guru",
311
- "Gujr",
312
- "Orya",
313
- "Taml",
314
- "Telu",
315
- "Knda",
316
- "Mlym",
317
- "Sinh",
318
- "Thai",
319
- "Laoo",
320
- "Tibt",
321
- "Mymr",
322
- "Khmr",
323
- "Mong",
324
- "Bali",
325
- "Java",
326
- "Lana",
327
- "Tale",
328
- "Talu",
329
- "Cakm",
330
- "Olck"
331
- ]);
332
- var rtlScripts = /* @__PURE__ */ new Set(["Arab", "Hebr", "Syrc", "Thaa", "Nkoo", "Adlm", "Rohg", "Mand", "Samr"]);
333
- var latinSlotScripts = /* @__PURE__ */ new Set(["Latn", "Cyrl", "Grek", "Zyyy", "Zzzz"]);
334
- var deprecatedLanguages = { iw: "he", in: "id", ji: "yi" };
335
- var matchingLanguages = { ...deprecatedLanguages, no: "nb", zsm: "ms", ku: "kmr" };
336
- var likelyScripts = {
337
- zh: { script: "Hans", regions: { TW: "Hant", HK: "Hant", MO: "Hant" } },
338
- sr: { script: "Cyrl", regions: { ME: "Latn" } },
339
- pa: { script: "Guru", regions: { PK: "Arab" } },
340
- az: { script: "Latn", regions: { IR: "Arab" } },
341
- uz: { script: "Latn", regions: { AF: "Arab" } },
342
- bs: { script: "Latn" },
343
- mn: { script: "Cyrl", regions: { CN: "Mong" } },
344
- ms: { script: "Latn" }
345
- };
346
- var bareLanguageId = /^[a-z][a-z0-9-]*$/;
347
- function scriptFontRole(script) {
348
- const code = normalizeScript(script);
349
- if (code && eastAsianScripts.has(code)) return "eastAsian";
350
- if (code && complexScripts.has(code)) return "complexScript";
351
- return "latin";
352
- }
353
- function normalizeScript(value) {
354
- if (typeof value !== "string" || !/^[A-Za-z]{4}$/.test(value)) return void 0;
355
- return value.slice(0, 1).toUpperCase() + value.slice(1).toLowerCase();
356
- }
357
- function text(value) {
358
- return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
359
- }
360
- var tagSyntax = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/;
361
- function splitTag(tag) {
362
- if (!tagSyntax.test(tag)) return void 0;
363
- const [first = "", ...rest] = tag.split("-");
364
- const language = first.toLowerCase();
365
- if (language === "und") return void 0;
366
- let script;
367
- let region;
368
- for (const subtag of rest) {
369
- if (subtag.length === 1) break;
370
- if (!script && !region && /^[A-Za-z]{4}$/.test(subtag)) script = normalizeScript(subtag);
371
- else if (!region && /^(?:[A-Za-z]{2}|\d{3})$/.test(subtag)) region = subtag.toUpperCase();
372
- }
373
- return { language, script, region };
374
- }
375
- function canonicalTag(tag) {
376
- let extension = false;
377
- return tag.split("-").map((subtag, index) => {
378
- const lower = subtag.toLowerCase();
379
- if (index === 0) return deprecatedLanguages[lower] ?? lower;
380
- if (extension) return lower;
381
- if (subtag.length === 1) {
382
- extension = true;
383
- return lower;
384
- }
385
- if (/^[A-Za-z]{4}$/.test(subtag)) return normalizeScript(subtag);
386
- if (/^[A-Za-z]{2}$/.test(subtag)) return subtag.toUpperCase();
387
- return lower;
388
- }).join("-");
389
- }
390
- function vendoredScript(language, region) {
391
- const entry = likelyScripts[language];
392
- return entry ? (region && entry.regions?.[region]) ?? entry.script : void 0;
393
- }
394
- function inferScript(tag) {
395
- const parts = splitTag(tag);
396
- if (!parts) return void 0;
397
- const known = parts.script ?? vendoredScript(matchingLanguages[parts.language] ?? parts.language, parts.region);
398
- if (known) return known;
399
- try {
400
- return normalizeScript(new Intl.Locale(tag).maximize().script);
401
- } catch {
402
- return void 0;
403
- }
404
- }
405
- function inlineRecords(document, kind2) {
406
- const kinds = isRecord2(document.catalogs) ? document.catalogs : {};
407
- const entry = kinds[kind2];
408
- return isRecord2(entry) && Array.isArray(entry.records) ? entry.records.filter(isRecord2) : [];
409
- }
410
- function allRecords(document, kind2) {
411
- return [...inlineRecords(document, kind2), ...catalogs[kind2]];
412
- }
413
- function resolveReference(lookup, kind2, reference) {
414
- if (typeof reference === "string") return lookup(kind2, reference);
415
- if (!isRecord2(reference)) return void 0;
416
- const base = typeof reference.id === "string" ? lookup(kind2, reference.id) : void 0;
417
- return { ...base, ...reference };
418
- }
419
- function matchLanguageTag(document, tag) {
420
- const wanted = splitTag(tag);
421
- if (!wanted) return void 0;
422
- const records = allRecords(document, "languages").filter((record5) => typeof record5.bcp47 === "string");
423
- const key = canonicalTag(tag).toLowerCase();
424
- const exact = records.find((record5) => canonicalTag(record5.bcp47).toLowerCase() === key);
425
- if (exact) return exact;
426
- const language = matchingLanguages[wanted.language] ?? wanted.language;
427
- const script = wanted.script ?? vendoredScript(language, wanted.region);
428
- const candidates = records.flatMap((record5) => {
429
- const parts = splitTag(record5.bcp47);
430
- if (!parts || (matchingLanguages[parts.language] ?? parts.language) !== language) return [];
431
- const recordScript = normalizeScript(record5.script) ?? parts.script ?? vendoredScript(language, parts.region);
432
- if (script && recordScript && script !== recordScript) return [];
433
- return [{ record: record5, region: parts.region }];
434
- });
435
- return candidates.find((candidate) => wanted.region && candidate.region === wanted.region)?.record ?? candidates.find((candidate) => !candidate.region)?.record ?? candidates[0]?.record;
436
- }
437
- function resolveLanguage(document, lookup, reference) {
438
- if (typeof reference === "string") {
439
- const value = reference.trim();
440
- const byId = bareLanguageId.test(value) ? lookup("languages", value) : void 0;
441
- if (byId) return { record: byId, languageId: value };
442
- if (/^[a-z][a-z0-9+.-]*:/i.test(value) || !splitTag(value)) return void 0;
443
- const matched = matchLanguageTag(document, value);
444
- if (matched) return { record: matched, authoredTag: value, languageId: text(matched.id) };
445
- return inferScript(value) ? { record: {}, authoredTag: value } : void 0;
446
- }
447
- if (!isRecord2(reference)) return void 0;
448
- const authoredTag = text(reference.bcp47);
449
- if (authoredTag !== void 0 && !splitTag(authoredTag)) return void 0;
450
- const base = typeof reference.id === "string" ? lookup("languages", reference.id) : authoredTag ? matchLanguageTag(document, authoredTag) : void 0;
451
- const record5 = { ...base, ...reference };
452
- const tag = text(record5.bcp47);
453
- if (!tag || !splitTag(tag)) return void 0;
454
- return { record: record5, authoredTag, authoredOoxmlLang: text(reference.ooxmlLang), languageId: text(base?.id) };
455
- }
456
- function pairFamilies(value) {
457
- if (!isRecord2(value)) return void 0;
458
- const major = text(value.major);
459
- const minor = text(value.minor);
460
- const heading = major ?? minor;
461
- const body = minor ?? major;
462
- return heading && body ? { heading, body } : void 0;
463
- }
464
- function schemeServesLanguage(scheme, language) {
465
- const entries = Array.isArray(scheme.languages) ? scheme.languages.filter((entry) => typeof entry === "string") : [];
466
- if (entries.length === 0) return true;
467
- const name = text(language.name)?.toLowerCase();
468
- if (!name) return false;
469
- const base = name.replace(/\s*\([^)]*\)\s*$/, "");
470
- return entries.some((entry) => {
471
- const value = entry.trim().toLowerCase();
472
- return value === name || value === base;
473
- });
474
- }
475
- function resolveScriptFonts(input, options = {}) {
476
- const document = isRecord2(input) ? input : {};
477
- const lookup = (kind2, id) => inlineRecords(document, kind2).find((record6) => record6.id === id) ?? catalogs[kind2].find((record6) => record6.id === id);
478
- let design = isRecord2(document.design) ? document.design : {};
479
- if (options.slideIndex !== void 0) {
480
- const slides = Array.isArray(document.slides) ? document.slides : [];
481
- if (!Number.isInteger(options.slideIndex) || options.slideIndex < 0 || options.slideIndex >= slides.length) {
482
- throw new RangeError(`slideIndex must be an integer between 0 and ${slides.length - 1}.`);
483
- }
484
- const slide = slides[options.slideIndex];
485
- if (isRecord2(slide) && isRecord2(slide.design)) design = { ...design, ...slide.design };
486
- }
487
- const theme = resolveReference(lookup, "themes", design.theme ?? "minimal") ?? {};
488
- const scheme = resolveFontSchemeReference(design.fontScheme ?? theme.fontScheme ?? DEFAULT_FONT_SCHEME, (id) => lookup("fontSchemes", id)).scheme;
489
- const latin = resolveFontFamilies(scheme);
490
- const fromOption = options.language !== void 0 ? resolveLanguage(document, lookup, options.language) : void 0;
491
- const fromDocument = fromOption ? void 0 : resolveLanguage(document, lookup, document.language);
492
- const requestedDefault = text(options.defaultLanguage);
493
- const fallbackTag = requestedDefault && splitTag(requestedDefault) ? requestedDefault : "en-US";
494
- const language = fromOption ?? fromDocument ?? resolveLanguage(document, lookup, fallbackTag) ?? { record: {}, authoredTag: fallbackTag };
495
- const languageSource = fromOption ? "option" : fromDocument ? "document" : "default";
496
- const record5 = language.record;
497
- const bcp47 = canonicalTag(language.authoredTag ?? text(record5.bcp47) ?? fallbackTag);
498
- const curated = text(record5.ooxmlLang);
499
- const lang = language.authoredOoxmlLang ? canonicalTag(language.authoredOoxmlLang) : language.authoredTag && splitTag(language.authoredTag)?.region ? canonicalTag(language.authoredTag) : curated ? canonicalTag(curated) : bcp47;
500
- const parts = splitTag(bcp47);
501
- const script = normalizeScript(record5.script) ?? (language.languageId ? parts?.script ?? (parts ? vendoredScript(matchingLanguages[parts.language] ?? parts.language, parts.region) : void 0) ?? normalizeScript(matchLanguageTag({}, bcp47)?.script) : inferScript(bcp47)) ?? "Zzzz";
502
- const scriptRole = scriptFontRole(script);
503
- const declaredDirection = record5.direction;
504
- const direction = declaredDirection === "rtl" || declaredDirection === "ltr" ? declaredDirection : rtlScripts.has(script) ? "rtl" : "ltr";
505
- const app = options.app ?? "PowerPoint";
506
- const preferred = app === "Google Slides" ? record5.googleFontScheme : record5.fontScheme;
507
- const alternate = app === "Google Slides" ? record5.fontScheme : record5.googleFontScheme;
508
- const languageScheme = resolveReference(lookup, "fontSchemes", preferred) ?? resolveReference(lookup, "fontSchemes", alternate);
509
- const languageFamilies = pairFamilies(languageScheme);
510
- const schemeAdmitsLanguage = schemeServesLanguage(scheme, record5);
511
- const slot = (role, family) => {
512
- const explicit = pairFamilies(scheme[role]);
513
- if (explicit) return { ...explicit, source: "fontScheme" };
514
- if (scheme.languageFamily === family && schemeAdmitsLanguage) return { ...latin, source: "schemeFamily" };
515
- if (scriptRole === role && languageFamilies) return { ...languageFamilies, source: "language" };
516
- return { heading: latin.heading, body: latin.body, source: "latin" };
517
- };
518
- const eastAsian = slot("eastAsian", "ea");
519
- const complexScript = slot("complexScript", "cs");
520
- const heading = { latin: latin.heading, eastAsian: eastAsian.heading, complexScript: complexScript.heading };
521
- const body = { latin: latin.body, eastAsian: eastAsian.body, complexScript: complexScript.body };
522
- let supplement;
523
- if (!latinSlotScripts.has(script)) {
524
- const families = scriptRole === "latin" ? languageFamilies : (scriptRole === "eastAsian" ? eastAsian : complexScript).source === "latin" ? void 0 : { heading: heading[scriptRole], body: body[scriptRole] };
525
- if (families) supplement = { script: script === "Kore" ? "Hang" : script, heading: families.heading, body: families.body };
526
- }
527
- return {
528
- ...body,
529
- lang,
530
- bcp47,
531
- ...language.languageId ? { languageId: language.languageId } : {},
532
- languageSource,
533
- script,
534
- scriptRole,
535
- direction,
536
- rtl: direction === "rtl",
537
- heading,
538
- body,
539
- ...supplement ? { supplement } : {},
540
- sources: { eastAsian: eastAsian.source, complexScript: complexScript.source }
541
- };
542
- }
543
- function resolveSlideDirection(presentation, slideIndex) {
544
- if (!isRecord2(presentation) || presentation.language === void 0) return "ltr";
545
- try {
546
- return resolveScriptFonts(presentation, Number.isInteger(slideIndex) ? { slideIndex } : {}).direction;
547
- } catch {
548
- try {
549
- return resolveScriptFonts(presentation).direction;
550
- } catch {
551
- return "ltr";
552
- }
553
- }
554
- }
555
-
556
- // src/numbering.ts
557
- var NUMBERING_STYLES = ["arabic", "roman-upper", "roman-lower", "alpha-upper", "alpha-lower"];
558
- var NUMBERING_SUFFIXES = ["period", "paren", "paren-both"];
559
- var MAX_NUMBERING_VALUE = 32767;
560
- var MAX_ROMAN_VALUE = 3999;
561
- var MAX_NUMBERING_LEVELS = 9;
562
- var styleSet = new Set(NUMBERING_STYLES);
563
- var suffixSet = new Set(NUMBERING_SUFFIXES);
564
- var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
565
- function resolveEntry(entry, label) {
566
- if (typeof entry === "string") {
567
- if (!styleSet.has(entry)) throw new RangeError(`${label} must be one of ${NUMBERING_STYLES.join(", ")}.`);
568
- return { style: entry, start: 1, suffix: "period" };
569
- }
570
- if (!isObject(entry)) throw new TypeError(`${label} must be a style name or a numbering object.`);
571
- const { style = "arabic", start = 1, suffix = "period" } = entry;
572
- if (typeof style !== "string" || !styleSet.has(style)) throw new RangeError(`${label}.style must be one of ${NUMBERING_STYLES.join(", ")}.`);
573
- if (typeof suffix !== "string" || !suffixSet.has(suffix)) throw new RangeError(`${label}.suffix must be one of ${NUMBERING_SUFFIXES.join(", ")}.`);
574
- if (typeof start !== "number" || !Number.isInteger(start) || start < 1 || start > MAX_NUMBERING_VALUE) throw new RangeError(`${label}.start must be an integer from 1 to ${MAX_NUMBERING_VALUE}.`);
575
- return { style, start, suffix };
576
- }
577
- function resolveNumbering(input) {
578
- if (Array.isArray(input)) {
579
- if (!input.length || input.length > MAX_NUMBERING_LEVELS) throw new RangeError(`numbering must list from 1 to ${MAX_NUMBERING_LEVELS} levels.`);
580
- return input.map((entry, index) => resolveEntry(entry, `numbering[${index}]`));
581
- }
582
- return [resolveEntry(input, "numbering")];
583
- }
584
- function numberingAtLevel(levels, level) {
585
- return levels[Math.min(Math.max(0, level), levels.length - 1)];
586
- }
587
- var ROMAN = [[1e3, "m"], [900, "cm"], [500, "d"], [400, "cd"], [100, "c"], [90, "xc"], [50, "l"], [40, "xl"], [10, "x"], [9, "ix"], [5, "v"], [4, "iv"], [1, "i"]];
588
- function bareNumber(value, style) {
589
- if (style === "arabic") return String(value);
590
- if (style === "roman-upper" || style === "roman-lower") {
591
- if (value > MAX_ROMAN_VALUE) return null;
592
- let rest = value, out = "";
593
- for (const [amount, glyphs] of ROMAN) while (rest >= amount) {
594
- out += glyphs;
595
- rest -= amount;
596
- }
597
- return style === "roman-upper" ? out.toUpperCase() : out;
598
- }
599
- const letter = String.fromCharCode(97 + (value - 1) % 26).repeat(Math.floor((value - 1) / 26) + 1);
600
- return style === "alpha-upper" ? letter.toUpperCase() : letter;
601
- }
602
- function numberingStyleDraws(style, value) {
603
- return bareNumber(value, style) !== null;
604
- }
605
- function formatListNumber(value, style = "arabic", suffix = "period") {
606
- if (!Number.isInteger(value) || value < 1) throw new RangeError("A list number must be an integer of at least 1.");
607
- if (!styleSet.has(style)) throw new RangeError(`Unknown numbering style '${String(style)}'.`);
608
- if (!suffixSet.has(suffix)) throw new RangeError(`Unknown numbering suffix '${String(suffix)}'.`);
609
- const bare = bareNumber(value, style) ?? String(value);
610
- return suffix === "period" ? `${bare}.` : suffix === "paren" ? `${bare})` : `(${bare})`;
611
- }
612
- function listNumbers(items, numbering) {
613
- const levels = resolveNumbering(numbering);
614
- const counters = [];
615
- return items.map((item, index) => {
616
- const object = isObject(item) ? item : void 0;
617
- const level = object?.level === void 0 ? 0 : object.level;
618
- if (typeof level !== "number" || !Number.isInteger(level) || level < 0) throw new RangeError("List levels must be nonnegative integers.");
619
- const own = object?.start;
620
- if (own !== void 0 && (typeof own !== "number" || !Number.isInteger(own) || own < 1 || own > MAX_NUMBERING_VALUE)) throw new RangeError(`A list item start must be an integer from 1 to ${MAX_NUMBERING_VALUE}.`);
621
- const { style: authored, start, suffix } = numberingAtLevel(levels, level);
622
- const previous = counters[level];
623
- const value = own ?? (previous === void 0 ? start : previous + 1);
624
- counters[level] = value;
625
- counters.length = level + 1;
626
- const adapted = !numberingStyleDraws(authored, value);
627
- const style = adapted ? "arabic" : authored;
628
- return { index, level, value, style, suffix, text: formatListNumber(value, style, suffix), ...adapted ? { adapted: "roman-range" } : {} };
629
- });
630
- }
631
- function sliceNumberedItems(items, numbering, from, to = items.length) {
632
- const whole = listNumbers(items, numbering);
633
- const result = items.slice(from, to);
634
- for (let index = 0; index < result.length; index++) {
635
- const wanted = whole[from + index].value;
636
- if (listNumbers(result.slice(0, index + 1), numbering)[index].value === wanted) continue;
637
- const item = result[index];
638
- const object = isObject(item) ? item : { text: item };
639
- result[index] = { ...object, start: wanted };
640
- }
641
- return result;
642
- }
643
- function numberingFindings(payload) {
644
- const errors = [], warnings = [];
645
- const lists = ["items", "bullets"].filter((key) => Array.isArray(payload[key]));
646
- if (Object.hasOwn(payload, "numbering")) {
647
- if (!Object.hasOwn(payload, "items") && !Object.hasOwn(payload, "bullets")) {
648
- errors.push({ key: "numbering", message: "numbering applies to 'items' or 'bullets'; this payload has neither", params: {} });
649
- } else {
650
- for (const key of lists) {
651
- let numbers;
652
- try {
653
- numbers = listNumbers(payload[key], payload.numbering);
654
- } catch {
655
- continue;
656
- }
657
- const largest = numbers.reduce((max, number) => Math.max(max, number.value), 0);
658
- if (largest > MAX_NUMBERING_VALUE) errors.push({ key, message: `numbering counts up to ${largest}, past the native limit of ${MAX_NUMBERING_VALUE}`, params: { largest, limit: MAX_NUMBERING_VALUE } });
659
- }
660
- }
661
- } else {
662
- for (const key of lists) {
663
- if (payload[key].some((item) => isObject(item) && item.start !== void 0)) {
664
- warnings.push({ key, message: `'start' on ${key} entries has no effect without a 'numbering' field on the payload`, params: {} });
665
- }
666
- }
667
- }
668
- return { errors, warnings };
669
- }
670
-
671
- // src/furniture-fields.ts
672
- var DEFAULT_FURNITURE_DATE_FORMAT = "M/d/yyyy";
673
- var DEFAULT_SLIDE_NUMBER_FORMAT = "{current}";
674
- var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
675
- var weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
676
- var tokenWidths = { y: [1, 2, 4], M: [1, 2, 3, 4], d: [1, 2], E: [1, 2, 3, 4] };
677
- function parseFurnitureDateFormat(pattern) {
678
- if (!pattern) return "dateFormat must not be empty.";
679
- const tokens = [];
680
- const literal = (text2) => {
681
- const last = tokens[tokens.length - 1];
682
- if (last?.kind === "literal") last.text += text2;
683
- else tokens.push({ kind: "literal", text: text2 });
684
- };
685
- for (let index = 0; index < pattern.length; ) {
686
- const character = pattern.charAt(index);
687
- if (character === "'") {
688
- if (pattern[index + 1] === "'") {
689
- literal("'");
690
- index += 2;
691
- continue;
692
- }
693
- let end = index + 1, text2 = "";
694
- for (; ; ) {
695
- if (end >= pattern.length) return "dateFormat has an unterminated quoted literal.";
696
- if (pattern[end] === "'") {
697
- if (pattern[end + 1] === "'") {
698
- text2 += "'";
699
- end += 2;
700
- continue;
701
- }
702
- break;
703
- }
704
- text2 += pattern[end];
705
- end++;
706
- }
707
- literal(text2);
708
- index = end + 1;
709
- continue;
710
- }
711
- if (/[A-Za-z]/.test(character)) {
712
- let end = index;
713
- while (pattern[end] === character) end++;
714
- const width = end - index;
715
- if (!tokenWidths[character]?.includes(width)) return `dateFormat token '${pattern.slice(index, end)}' is not supported; use yyyy, yy, MMMM, MMM, MM, M, dd, d, EEEE or EEE, and quote literal letters.`;
716
- tokens.push({ kind: "field", letter: character, width });
717
- index = end;
718
- continue;
719
- }
720
- literal(character);
721
- index++;
722
- }
723
- return tokens;
724
- }
725
- function parseIsoDate(value) {
726
- const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
727
- if (!match) return null;
728
- const year = Number(match[1]), month = Number(match[2]), day = Number(match[3]);
729
- const date = new Date(Date.UTC(2e3, month - 1, day));
730
- date.setUTCFullYear(year);
731
- if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) return null;
732
- return { year, month, day };
733
- }
734
- function formatFurnitureDate(iso, pattern) {
735
- const date = parseIsoDate(iso);
736
- if (!date) return { error: `'${iso}' is not an ISO YYYY-MM-DD calendar date.` };
737
- const tokens = parseFurnitureDateFormat(pattern);
738
- if (typeof tokens === "string") return { error: tokens };
739
- const utc = new Date(Date.UTC(2e3, date.month - 1, date.day));
740
- utc.setUTCFullYear(date.year);
741
- const pad = (value, width) => String(value).padStart(width, "0");
742
- let text2 = "";
743
- for (const token of tokens) {
744
- if (token.kind === "literal") {
745
- text2 += token.text;
746
- continue;
747
- }
748
- const { letter, width } = token;
749
- if (letter === "y") text2 += width === 2 ? pad(date.year % 100, 2) : width === 4 ? pad(date.year, 4) : String(date.year);
750
- else if (letter === "M") {
751
- const name = months[date.month - 1] ?? "";
752
- text2 += width >= 3 ? width === 4 ? name : name.slice(0, 3) : pad(date.month, width);
753
- } else if (letter === "d") text2 += pad(date.day, width);
754
- else {
755
- const name = weekdays[utc.getUTCDay()] ?? "";
756
- text2 += width === 4 ? name : name.slice(0, 3);
757
- }
758
- }
759
- return { text: text2 };
760
- }
761
- function formatSlideNumber(format, current, total) {
762
- if (!format.includes("{current}")) return { error: "slideNumberFormat must contain {current}." };
763
- if (format.includes("{total}") && total === void 0) return { error: "slideNumberFormat uses {total}, which needs the rendered slide count." };
764
- let text2 = "";
765
- const fields2 = [];
766
- for (const piece of format.split(/(\{current\}|\{total\})/)) {
767
- if (piece === "{current}") {
768
- const value = String(current);
769
- fields2.push({ type: "slideNumber", start: text2.length, end: text2.length + value.length });
770
- text2 += value;
771
- } else if (piece === "{total}") text2 += String(total);
772
- else text2 += piece;
773
- }
774
- return { text: text2, fields: fields2 };
775
- }
776
-
777
- // src/color.ts
778
- var schemeColorSlots = /* @__PURE__ */ new Set([
779
- "accent1",
780
- "accent2",
781
- "accent3",
782
- "accent4",
783
- "accent5",
784
- "accent6",
785
- "dark1",
786
- "dark2",
787
- "light1",
788
- "light2",
789
- "hyperlink",
790
- "followedHyperlink"
791
- ]);
792
- var schemeColorRoles = /* @__PURE__ */ new Set([
793
- "primary",
794
- "secondary",
795
- "accent",
796
- "background",
797
- "surface",
798
- "text",
799
- "textSecondary"
800
- ]);
801
- var defaultRoleSlots = {
802
- primary: "accent1",
803
- secondary: "accent2",
804
- accent: "accent3",
805
- background: "light1",
806
- surface: "light2",
807
- text: "dark1",
808
- textSecondary: "dark2"
809
- };
810
- function normalizeHexColor(value) {
811
- if (typeof value !== "string") return void 0;
812
- let hex = value.trim();
813
- if (/^#[0-9a-f]{3}$/i.test(hex)) hex = `#${[...hex.slice(1)].map((char) => char + char).join("")}`;
814
- if (/^#[0-9a-f]{6}([0-9a-f]{2})?$/i.test(hex)) return hex.length === 9 ? `#${hex.slice(1, 7).toUpperCase()}` : hex.toUpperCase();
815
- return void 0;
816
- }
817
- function schemeSlot(colorScheme, slot, fallback) {
818
- return normalizeHexColor(colorScheme[slot]) ?? fallback;
819
- }
820
- function resolveVariable(variables, id, fallback) {
821
- const entry = variables[id];
822
- if (typeof entry === "string") return normalizeHexColor(entry) ?? fallback;
823
- if (entry && typeof entry === "object" && entry.type === "color") {
824
- return normalizeHexColor(entry.value) ?? fallback;
825
- }
826
- return fallback;
827
- }
828
- function resolveColorRef(reference, options) {
829
- const { colorScheme, roles, variables = {}, fallback } = options;
830
- const literal = normalizeHexColor(reference);
831
- if (literal) return literal;
832
- if (reference.startsWith("var:")) {
833
- const id = reference.slice("var:".length);
834
- if (/^[a-z][a-z0-9-]*$/.test(id)) return resolveVariable(variables, id, fallback);
835
- return fallback;
836
- }
837
- if (schemeColorRoles.has(reference)) {
838
- const roleKey = reference;
839
- const fromRoles = roles?.[roleKey];
840
- if (fromRoles) return normalizeHexColor(fromRoles) ?? fallback;
841
- const fromScheme = normalizeHexColor(colorScheme[reference]);
842
- if (fromScheme) return fromScheme;
843
- const slot = defaultRoleSlots[reference];
844
- return slot ? schemeSlot(colorScheme, slot, fallback) : fallback;
845
- }
846
- if (schemeColorSlots.has(reference)) return schemeSlot(colorScheme, reference, fallback);
847
- return fallback;
848
- }
849
- function luminance(value) {
850
- if (typeof value !== "string") return void 0;
851
- let hex = value.trim();
852
- if (/^#[0-9a-f]{3}$/i.test(hex)) hex = `#${[...hex.slice(1)].map((c) => c + c).join("")}`;
853
- if (/^#[0-9a-f]{6}ff$/i.test(hex)) hex = hex.slice(0, 7);
854
- if (!/^#[0-9a-f]{6}$/i.test(hex)) return void 0;
855
- return [0.2126, 0.7152, 0.0722].reduce((sum, weight, index) => {
856
- const offset = index * 2 + 1;
857
- const n = Number.parseInt(hex.slice(offset, offset + 2), 16) / 255;
858
- return sum + weight * (n <= 0.04045 ? n / 12.92 : ((n + 0.055) / 1.055) ** 2.4);
859
- }, 0);
860
- }
861
- function colorContrast(foreground, background) {
862
- const a = luminance(foreground), b = luminance(background);
863
- return a === void 0 || b === void 0 ? void 0 : (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
864
- }
865
- function textColorForFill(fill, preferred) {
866
- const background = luminance(fill), contrast = colorContrast(preferred, fill);
867
- if (background === void 0 || contrast === void 0 || contrast >= 4.5) return preferred;
868
- return (background + 0.05) / 0.05 >= 1.05 / (background + 0.05) ? "#000000" : "#FFFFFF";
869
- }
870
- function chartColorForFill(fill, preferred) {
871
- const contrast = colorContrast(preferred, fill);
872
- if (contrast === void 0 || contrast >= 3) return preferred;
873
- let hex = preferred.trim();
874
- if (hex.length === 4) hex = `#${[...hex.slice(1)].map((c) => c + c).join("")}`;
875
- const channels = [1, 3, 5].map((offset) => Number.parseInt(hex.slice(offset, offset + 2), 16));
876
- for (let step = 1; step <= 255; step++) {
877
- const candidates = [0, 255].map((target) => {
878
- const color = `#${channels.map((channel) => Math.round(channel + (target - channel) * step / 255).toString(16).padStart(2, "0")).join("")}`.toUpperCase();
879
- return { color, contrast: colorContrast(color, fill) ?? 0 };
880
- }).sort((a, b) => b.contrast - a.contrast);
881
- const best = candidates[0];
882
- if (best && best.contrast >= 3) return best.color;
883
- }
884
- return textColorForFill(fill, preferred);
885
- }
886
- var CHART_SERIES_MIN_LIGHTNESS_STEP = 11;
887
- var CHART_SERIES_MIN_DIFFERENCE = 12;
888
- var D65 = [0.95047, 1, 1.08883];
889
- function opaqueChannels(value) {
890
- if (luminance(value) === void 0) return void 0;
891
- let hex = value.trim();
892
- if (hex.length === 4) hex = `#${[...hex.slice(1)].map((c) => c + c).join("")}`;
893
- return [1, 3, 5].map((offset) => Number.parseInt(hex.slice(offset, offset + 2), 16));
894
- }
895
- var toLinear = (channel) => {
896
- const n = channel / 255;
897
- return n <= 0.04045 ? n / 12.92 : ((n + 0.055) / 1.055) ** 2.4;
898
- };
899
- var fromLinear = (value) => 255 * (value <= 31308e-7 ? value * 12.92 : 1.055 * value ** (1 / 2.4) - 0.055);
900
- function labOf(channels) {
901
- const [r, g, b] = channels.map(toLinear);
902
- const f = (t) => t > 216 / 24389 ? Math.cbrt(t) : (24389 / 27 * t + 16) / 116;
903
- const fx = f((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / D65[0]), fy = f(0.2126729 * r + 0.7151522 * g + 0.072175 * b), fz = f((0.0193339 * r + 0.119192 * g + 0.9503041 * b) / D65[2]);
904
- return [116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)];
905
- }
906
- function labToLinear([l, a, b]) {
907
- const fy = (l + 16) / 116, fx = fy + a / 500, fz = fy - b / 200;
908
- const inverse = (t) => t ** 3 > 216 / 24389 ? t ** 3 : (116 * t - 16) / (24389 / 27);
909
- const x = inverse(fx) * D65[0], y = l > 8 ? fy ** 3 : l / (24389 / 27), z = inverse(fz) * D65[2];
910
- const rgb = [
911
- 3.2404542 * x - 1.5371385 * y - 0.4985314 * z,
912
- -0.969266 * x + 1.8760108 * y + 0.041556 * z,
913
- 0.0556434 * x - 0.2040259 * y + 1.0572252 * z
914
- ];
915
- return rgb.every((channel) => channel >= -5e-4 && channel <= 1.0005) ? rgb : void 0;
916
- }
917
- var hexOfChannels = (channels) => `#${channels.map((channel) => Math.max(0, Math.min(255, Math.round(channel))).toString(16).padStart(2, "0")).join("").toUpperCase()}`;
918
- function labWithLightness(l, a, b) {
919
- const chroma = Math.hypot(a, b), hue = Math.atan2(b, a);
920
- const at2 = (scale) => labToLinear([l, Math.cos(hue) * chroma * scale, Math.sin(hue) * chroma * scale]);
921
- let linear = at2(1);
922
- if (!linear) {
923
- let low = 0, high = 1;
924
- for (let step = 0; step < 24; step++) {
925
- const mid = (low + high) / 2;
926
- if (at2(mid)) low = mid;
927
- else high = mid;
928
- }
929
- linear = at2(low) ?? [0, 0, 0];
930
- }
931
- return hexOfChannels(linear.map((channel) => fromLinear(Math.max(0, Math.min(1, channel)))));
932
- }
933
- function labDifference(a, b) {
934
- return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
935
- }
936
- var CHART_SERIES_SHORTFALL_WEIGHT = 8;
937
- function chartPaletteForFill(fill, palette) {
938
- const opaqueFill = colorContrast(fill, fill) !== void 0;
939
- const preferredLab = palette.map((color) => {
940
- const channels = opaqueChannels(color);
941
- return channels ? labOf(channels) : void 0;
942
- });
943
- const adjusted = palette.map((color) => chartColorForFill(fill, color));
944
- const kept = palette.map((color, index) => opaqueFill && preferredLab[index] !== void 0 && adjusted[index] === color);
945
- const placed = palette.map((_, index) => kept[index] || !opaqueFill || !preferredLab[index] ? adjusted[index] : void 0);
946
- const placedLab = placed.map((color) => {
947
- const channels = color === void 0 ? void 0 : opaqueChannels(color);
948
- return channels ? labOf(channels) : void 0;
949
- });
950
- palette.forEach((_, index) => {
951
- const wanted = preferredLab[index], base = adjusted[index];
952
- if (placed[index] !== void 0 || !wanted) return;
953
- const baseLab = labOf(opaqueChannels(base));
954
- const needs = palette.flatMap((_2, other) => {
955
- const before = preferredLab[other], lab = placedLab[other];
956
- return other !== index && before && lab && placed[other] !== void 0 ? [{ lab, adjacent: Math.abs(other - index) === 1, wide: Math.min(labDifference(before, wanted), CHART_SERIES_MIN_DIFFERENCE), step: Math.min(Math.abs(before[0] - wanted[0]), CHART_SERIES_MIN_LIGHTNESS_STEP) }] : [];
957
- });
958
- const shortfall = (candidate) => needs.reduce((worst, need) => {
959
- const lightness = Math.abs(need.lab[0] - candidate[0]) - need.step, difference = labDifference(need.lab, candidate) - need.wide;
960
- return need.adjacent ? { near: Math.min(worst.near, difference, lightness), far: worst.far } : { near: Math.min(worst.near, difference), far: Math.min(worst.far, lightness) };
961
- }, { near: Infinity, far: Infinity });
962
- const deficit = (value) => Math.max(0, -value - 1e-9);
963
- const cost = (candidate) => candidate.moved + CHART_SERIES_SHORTFALL_WEIGHT * deficit(candidate.near) + deficit(candidate.far);
964
- const start = shortfall(baseLab);
965
- if (deficit(start.near) === 0 && deficit(start.far) === 0) {
966
- placed[index] = base;
967
- placedLab[index] = baseLab;
968
- return;
969
- }
970
- let best = { color: base, lab: baseLab, cost: cost({ moved: 0, ...start }), moved: 0 };
971
- for (let step = 0; step <= 400; step++) {
972
- const color = labWithLightness(step / 4, wanted[1], wanted[2]), contrast = colorContrast(color, fill);
973
- if (contrast === void 0 || contrast < 3) continue;
974
- const lab = labOf(opaqueChannels(color)), moved = Math.abs(lab[0] - baseLab[0]), total = cost({ moved, ...shortfall(lab) });
975
- if (total < best.cost - 1e-9 || Math.abs(total - best.cost) <= 1e-9 && moved < best.moved) best = { color, lab, cost: total, moved };
976
- }
977
- placed[index] = best.color;
978
- placedLab[index] = best.lab;
979
- });
980
- return placed;
981
- }
982
-
983
- // src/annotations.ts
984
- var CITATION_MARKER_SCALE = 2 / 3;
985
- var CITATION_MARKER_RAISE = 0.3;
986
- var CAPTION_FONT_RATIO = 0.6;
987
- var CAPTION_MAX_RATIO = 0.35;
988
- var FOOTNOTE_MAX_RATIO = 0.35;
989
- var CAPTIONABLE_FIELDS = Object.freeze(["image", "chart", "table", "video"]);
990
- var record2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
991
- var isRecord3 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
992
- var round = (value) => Math.round(value * 1e6) / 1e6 || value;
993
- var regionKey = /^(top|middle|bottom|left|center|right)([+:]|$)/;
994
- var TEXT_FIELDS = ["text", "items", "bullets"];
995
- function annotationText(value) {
996
- if (typeof value === "string") return value;
997
- if (Array.isArray(value)) return value.map((run) => typeof run === "string" ? run : record2(run).text ?? "").join("");
998
- if (isRecord3(value) && value.text !== void 0) return annotationText(value.text);
999
- return value === void 0 || value === null ? "" : String(value);
1000
- }
1001
- function captionSettings(value) {
1002
- if (value === void 0 || value === null) return void 0;
1003
- if (typeof value === "string" || Array.isArray(value)) return { text: value, position: "below", align: "left" };
1004
- if (!isRecord3(value) || value.text === void 0) return void 0;
1005
- return { text: value.text, position: value.position === "above" ? "above" : "below", align: value.align === "center" || value.align === "right" ? value.align : "left" };
1006
- }
1007
- function walkCitationRuns(slide, basePath, visit) {
1008
- const runs = (value2, path) => {
1009
- if (!Array.isArray(value2)) return;
1010
- value2.forEach((run, index) => {
1011
- if (!isRecord3(run) || typeof run.text !== "string") return;
1012
- const cite = run.cite === void 0 ? [] : Array.isArray(run.cite) ? run.cite.filter((id) => typeof id === "string") : typeof run.cite === "string" ? [run.cite] : [];
1013
- const footnote = typeof run.footnote === "string" || Array.isArray(run.footnote) ? run.footnote : void 0;
1014
- if (!cite.length && footnote === void 0) return;
1015
- visit({ path: `${path}.${index}`, run, cite, footnote });
1016
- });
1017
- };
1018
- const list = (items, path) => {
1019
- if (!Array.isArray(items)) return;
1020
- items.forEach((item, index) => {
1021
- if (Array.isArray(item)) runs(item, `${path}.${index}`);
1022
- else if (isRecord3(item)) {
1023
- runs(item.text, `${path}.${index}.text`);
1024
- runs(item.description, `${path}.${index}.description`);
1025
- }
1026
- });
1027
- };
1028
- const host = (node, path, depth) => {
1029
- const payload = record2(node);
1030
- if (Array.isArray(payload.blocks)) {
1031
- if (depth > 32) return;
1032
- payload.blocks.forEach((block, index) => {
1033
- host(block, `${path}.blocks.${index}`, depth + 1);
1034
- });
1035
- return;
1036
- }
1037
- for (const field of TEXT_FIELDS) {
1038
- if (payload[field] === void 0) continue;
1039
- if (field === "text") runs(payload.text, `${path}.text`);
1040
- else list(payload[field], `${path}.${field}`);
1041
- }
1042
- };
1043
- const value = record2(slide);
1044
- const regions = Object.keys(value).filter((key) => regionKey.test(key)).sort();
1045
- if (regions.length) {
1046
- for (const key of regions) host(value[key], `${basePath}.${key}`, 0);
1047
- return;
1048
- }
1049
- if (Array.isArray(value.blocks)) {
1050
- value.blocks.forEach((block, index) => {
1051
- host(block, `${basePath}.blocks.${index}`, 0);
1052
- });
1053
- return;
1054
- }
1055
- host(value, basePath, 0);
1056
- }
1057
- var citationMarkerText = (numbers) => numbers.join(",");
1058
- function referenceList(presentation) {
1059
- const list = record2(presentation).references;
1060
- return Array.isArray(list) ? list.filter((item) => isRecord3(item) && typeof item.id === "string") : [];
1061
- }
1062
- function numberSlide(slide, slideIndex, references, numbering) {
1063
- const markers = /* @__PURE__ */ new Map(), marked = [], used = /* @__PURE__ */ new Map();
1064
- walkCitationRuns(slide, `slides.${slideIndex}`, (entry) => {
1065
- const numbers = [];
1066
- for (const id of entry.cite) {
1067
- let note = numbering.byId.get(id);
1068
- if (!note) {
1069
- const index = references.findIndex((item) => item.id === id), reference = references[index];
1070
- note = { number: numbering.notes.length + 1, kind: "reference", id, text: reference?.text ?? id, sourcePath: index >= 0 ? `references.${index}` : entry.path, resolved: !!reference, ...typeof reference?.url === "string" ? { url: reference.url } : {} };
1071
- numbering.notes.push(note);
1072
- numbering.byId.set(id, note);
1073
- }
1074
- if (!numbers.includes(note.number)) numbers.push(note.number);
1075
- used.set(note.number, note);
1076
- }
1077
- if (entry.footnote !== void 0) {
1078
- const note = { number: numbering.notes.length + 1, kind: "footnote", text: entry.footnote, sourcePath: entry.path, resolved: true };
1079
- numbering.notes.push(note);
1080
- numbers.push(note.number);
1081
- used.set(note.number, note);
1082
- }
1083
- const text2 = citationMarkerText(numbers);
1084
- markers.set(entry.path, text2);
1085
- marked.push({ path: entry.path, numbers, text: text2 });
1086
- });
1087
- if (!marked.length) return void 0;
1088
- return { markers, marked, notes: [...used.values()].sort((a, b) => a.number - b.number) };
1089
- }
1090
- function collectCitations(presentation) {
1091
- const references = referenceList(presentation), numbering = { notes: [], byId: /* @__PURE__ */ new Map() }, slides = /* @__PURE__ */ new Map();
1092
- const list = record2(presentation).slides;
1093
- if (Array.isArray(list)) list.forEach((slide, index) => {
1094
- const result = numberSlide(slide, index, references, numbering);
1095
- if (result) slides.set(index, result);
1096
- });
1097
- const cited = new Set(numbering.notes.filter((note) => note.kind === "reference").map((note) => note.id));
1098
- return { notes: numbering.notes, references: numbering.notes.filter((note) => note.kind === "reference"), slides, unused: references.map((item) => item.id).filter((id) => !cited.has(id)) };
1099
- }
1100
- function slideCitations(slide, slideIndex, presentation) {
1101
- const references = referenceList(presentation), numbering = { notes: [], byId: /* @__PURE__ */ new Map() };
1102
- const list = record2(presentation).slides;
1103
- if (Array.isArray(list)) for (let index = 0; index < Math.min(slideIndex, list.length); index += 1) numberSlide(list[index], index, references, numbering);
1104
- return numberSlide(slide, slideIndex, references, numbering);
1105
- }
1106
- var fitHeight = (fit) => Math.max(fit.placement?.height ?? 0, "richLines" in fit ? fit.height : fit.lines.length * fit.lineHeight);
1107
- function layoutCaption(value, box, path, options) {
1108
- const settings = captionSettings(value);
1109
- if (!settings) return void 0;
1110
- const fontSize = Math.max(options.minFontSize, CAPTION_FONT_RATIO * 25 * options.scale), gap = fontSize * 0.4;
1111
- const maxHeight = Math.max(fontSize * 1.22, box.height * CAPTION_MAX_RATIO);
1112
- const diagnostics = [];
1113
- const probe = options.fit(settings.text, { x: box.x, y: box.y, width: box.width, height: maxHeight }, fontSize, fontSize, path, settings.align);
1114
- const height = Math.min(maxHeight, Math.max(fontSize * 1.22, fitHeight(probe)));
1115
- const captionBox = { x: box.x, y: settings.position === "above" ? box.y : box.y + box.height - height, width: box.width, height };
1116
- const fit = options.fit(settings.text, captionBox, fontSize, fontSize, path, settings.align);
1117
- const overflow = fit.overflow || fitHeight(fit) > height + 0.01;
1118
- if (overflow) diagnostics.push({ code: "text-overflow", path, message: "The caption does not fit its band at the minimum font size; shorten it or give the block more space." });
1119
- const mediaHeight = Math.max(options.scale, box.height - height - gap);
1120
- const mediaBox = { x: box.x, y: settings.position === "above" ? box.y + height + gap : box.y, width: box.width, height: mediaHeight };
1121
- for (const target of [captionBox, mediaBox]) for (const key of ["x", "y", "width", "height"]) target[key] = round(target[key]);
1122
- return { path, value, text: settings.text, position: settings.position, alignment: settings.align, box: captionBox, mediaBox, fit, textStyle: options.textStyle(path), fontSize, overflow, diagnostics };
1123
- }
1124
- function layoutFootnotes(citations, options) {
1125
- const fontSize = Math.max(options.minFontSize, 13 * options.scale), thickness = Math.max(1, round(options.scale));
1126
- const gap = fontSize * 0.5, spacing = fontSize * 0.25, diagnostics = [];
1127
- const width = Math.max(options.scale, options.width);
1128
- const measured = citations.notes.map((note) => {
1129
- const value = typeof note.text === "string" ? `${note.number} ${note.text}` : [`${note.number} `, ...note.text];
1130
- const probe = options.fit(value, { x: options.x, y: 0, width, height: Number.MAX_SAFE_INTEGER }, fontSize, fontSize, note.sourcePath, "left");
1131
- return { note, value, height: Math.max(fontSize * 1.22, fitHeight(probe)) };
1132
- });
1133
- const total = thickness + gap + measured.reduce((sum, item) => sum + item.height, 0) + spacing * Math.max(0, measured.length - 1);
1134
- const height = round(Math.min(total, Math.max(fontSize * 1.22 + thickness + gap, options.maxHeight)));
1135
- const top = round(options.bottom - height), box = { x: round(options.x), y: top, width: round(width), height };
1136
- let cursor = top + thickness + gap, overflow = false;
1137
- const entries = measured.map((item, index) => {
1138
- const entryBox = { x: box.x, y: round(cursor), width: box.width, height: round(item.height) };
1139
- const fit = options.fit(item.value, entryBox, fontSize, fontSize, item.note.sourcePath, "left");
1140
- const exceeds = fit.overflow || entryBox.y + entryBox.height > options.bottom + 0.01;
1141
- if (exceeds) {
1142
- overflow = true;
1143
- diagnostics.push({ code: "text-overflow", path: item.note.sourcePath, message: `Footnote ${item.note.number} does not fit the slide's footnote area; shorten the note or split the slide.` });
1144
- }
1145
- cursor += item.height + (index < measured.length - 1 ? spacing : 0);
1146
- return { number: item.note.number, kind: item.note.kind, ...item.note.id !== void 0 ? { id: item.note.id } : {}, sourcePath: item.note.sourcePath, value: item.value, text: item.note.text, box: entryBox, fit, textStyle: options.textStyle(item.note.sourcePath), ...item.note.url !== void 0 ? { url: item.note.url } : {}, overflow: exceeds };
1147
- });
1148
- for (const note of citations.notes) if (!note.resolved) diagnostics.push({ code: "unresolved-content", path: note.sourcePath, message: `Reference '${note.id}' is not in the presentation's references list; its id is listed instead.` });
1149
- return { algorithm: "footnote-area-v1", path: options.path, box, rule: { x: box.x, y: top, width: box.width, thickness }, entries, markers: citations.marked, fontSize, overflow, diagnostics };
1150
- }
1151
- function referencesSlide(presentation, options = {}) {
1152
- const title = options.title ?? "References";
1153
- const { references } = collectCitations(presentation);
1154
- if (!references.length) return { title };
1155
- const items = references.map((note) => {
1156
- const label = `${note.number}. `;
1157
- if (typeof note.text === "string" && note.url === void 0) return `${label}${note.text}`;
1158
- const runs = [label, ...typeof note.text === "string" ? [note.text] : note.text];
1159
- if (note.url !== void 0) runs.push(" ", { text: note.url, link: note.url });
1160
- return runs;
1161
- });
1162
- return { title, items };
1163
- }
1164
-
1165
41
  // src/generated/catalog-ids.ts
1166
42
  var catalogIds = {
1167
43
  audiences: [
@@ -1659,211 +535,2315 @@ var catalogIds = {
1659
535
  "khmer-ui"
1660
536
  ]
1661
537
  };
1662
- var deprecatedCatalogIds = {
1663
- "audiences/executives": "executive",
1664
- "audiences/investors": "investor",
1665
- "audiences/customers": "customer",
1666
- "audiences/sales-team": "sales",
1667
- "audiences/marketing-team": "marketing",
1668
- "audiences/regulators": "regulatory",
1669
- "chartTypes/100pct-bullet-bar": "100pct-stacked-bar-3x",
1670
- "chartTypes/100pct-bullet-bar-2x": "100pct-stacked-bar-3x",
1671
- "chartTypes/100pct-bullet-bar-3x": "100pct-stacked-bar-3x",
1672
- "chartTypes/100pct-bullet-column": "100pct-stacked-column-3x",
1673
- "chartTypes/100pct-bullet-column-2x": "100pct-stacked-column-3x",
1674
- "chartTypes/100pct-bullet-column-3x": "100pct-stacked-column-3x",
1675
- "chartTypes/100pct-progress-bar": "100pct-stacked-bar-3x",
1676
- "chartTypes/100pct-stacked-area-2x": "100pct-stacked-area-3x",
1677
- "chartTypes/100pct-stacked-bar-2x": "100pct-stacked-bar-3x",
1678
- "chartTypes/100pct-stacked-column-2x": "100pct-stacked-column-3x",
1679
- "chartTypes/australia": "world",
1680
- "chartTypes/box-and-whisker-2x": "box-and-whisker",
1681
- "chartTypes/box-and-whisker-3x": "box-and-whisker",
1682
- "chartTypes/bullet-bar": "bar",
1683
- "chartTypes/bullet-bar-2x": "bar",
1684
- "chartTypes/bullet-bar-3x": "bar",
1685
- "chartTypes/bullet-column": "column",
1686
- "chartTypes/bullet-column-2x": "column",
1687
- "chartTypes/bullet-column-3x": "column",
1688
- "chartTypes/canada": "world",
1689
- "chartTypes/clustered-bar-2x": "bar",
1690
- "chartTypes/clustered-column": "column",
1691
- "chartTypes/dot-plot": "scatter",
1692
- "chartTypes/dot-plot-2x": "scatter",
1693
- "chartTypes/dot-plot-3x": "scatter",
1694
- "chartTypes/dot-plot-4x": "scatter",
1695
- "chartTypes/dot-plot-5x": "scatter",
1696
- "chartTypes/dot-plot-6x": "scatter",
1697
- "chartTypes/dumbbell": "scatter",
1698
- "chartTypes/line-2x": "line",
1699
- "chartTypes/line-3x": "line",
1700
- "chartTypes/line-with-high-low": "line",
1701
- "chartTypes/line-with-high-low-and-markers": "line-with-markers",
1702
- "chartTypes/line-with-markers-2x": "line-with-markers",
1703
- "chartTypes/line-with-markers-3x": "line-with-markers",
1704
- "chartTypes/sparkline": "line",
1705
- "chartTypes/sparkline-2x": "line",
1706
- "chartTypes/sparkline-3x": "line",
1707
- "chartTypes/sparkline-4x": "line",
1708
- "chartTypes/sparkline-5x": "line",
1709
- "chartTypes/sparkline-6x": "line",
1710
- "chartTypes/stacked-area-2x": "stacked-area-3x",
1711
- "chartTypes/stacked-bar-2x": "stacked-bar-3x",
1712
- "chartTypes/stacked-column-2x": "stacked-column-3x",
1713
- "chartTypes/stacked-line-2x": "stacked-line-3x",
1714
- "chartTypes/stacked-line-with-markers-2x": "stacked-line-with-markers-3x",
1715
- "chartTypes/treemap-2x": "treemap",
1716
- "chartTypes/treemap-3x": "treemap",
1717
- "chartTypes/united-kingdom": "world",
1718
- "chartTypes/united-states": "world"
538
+ var deprecatedCatalogIds = {
539
+ "audiences/executives": "executive",
540
+ "audiences/investors": "investor",
541
+ "audiences/customers": "customer",
542
+ "audiences/sales-team": "sales",
543
+ "audiences/marketing-team": "marketing",
544
+ "audiences/regulators": "regulatory",
545
+ "chartTypes/100pct-bullet-bar": "100pct-stacked-bar-3x",
546
+ "chartTypes/100pct-bullet-bar-2x": "100pct-stacked-bar-3x",
547
+ "chartTypes/100pct-bullet-bar-3x": "100pct-stacked-bar-3x",
548
+ "chartTypes/100pct-bullet-column": "100pct-stacked-column-3x",
549
+ "chartTypes/100pct-bullet-column-2x": "100pct-stacked-column-3x",
550
+ "chartTypes/100pct-bullet-column-3x": "100pct-stacked-column-3x",
551
+ "chartTypes/100pct-progress-bar": "100pct-stacked-bar-3x",
552
+ "chartTypes/100pct-stacked-area-2x": "100pct-stacked-area-3x",
553
+ "chartTypes/100pct-stacked-bar-2x": "100pct-stacked-bar-3x",
554
+ "chartTypes/100pct-stacked-column-2x": "100pct-stacked-column-3x",
555
+ "chartTypes/australia": "world",
556
+ "chartTypes/box-and-whisker-2x": "box-and-whisker",
557
+ "chartTypes/box-and-whisker-3x": "box-and-whisker",
558
+ "chartTypes/bullet-bar": "bar",
559
+ "chartTypes/bullet-bar-2x": "bar",
560
+ "chartTypes/bullet-bar-3x": "bar",
561
+ "chartTypes/bullet-column": "column",
562
+ "chartTypes/bullet-column-2x": "column",
563
+ "chartTypes/bullet-column-3x": "column",
564
+ "chartTypes/canada": "world",
565
+ "chartTypes/clustered-bar-2x": "bar",
566
+ "chartTypes/clustered-column": "column",
567
+ "chartTypes/dot-plot": "scatter",
568
+ "chartTypes/dot-plot-2x": "scatter",
569
+ "chartTypes/dot-plot-3x": "scatter",
570
+ "chartTypes/dot-plot-4x": "scatter",
571
+ "chartTypes/dot-plot-5x": "scatter",
572
+ "chartTypes/dot-plot-6x": "scatter",
573
+ "chartTypes/dumbbell": "scatter",
574
+ "chartTypes/line-2x": "line",
575
+ "chartTypes/line-3x": "line",
576
+ "chartTypes/line-with-high-low": "line",
577
+ "chartTypes/line-with-high-low-and-markers": "line-with-markers",
578
+ "chartTypes/line-with-markers-2x": "line-with-markers",
579
+ "chartTypes/line-with-markers-3x": "line-with-markers",
580
+ "chartTypes/sparkline": "line",
581
+ "chartTypes/sparkline-2x": "line",
582
+ "chartTypes/sparkline-3x": "line",
583
+ "chartTypes/sparkline-4x": "line",
584
+ "chartTypes/sparkline-5x": "line",
585
+ "chartTypes/sparkline-6x": "line",
586
+ "chartTypes/stacked-area-2x": "stacked-area-3x",
587
+ "chartTypes/stacked-bar-2x": "stacked-bar-3x",
588
+ "chartTypes/stacked-column-2x": "stacked-column-3x",
589
+ "chartTypes/stacked-line-2x": "stacked-line-3x",
590
+ "chartTypes/stacked-line-with-markers-2x": "stacked-line-with-markers-3x",
591
+ "chartTypes/treemap-2x": "treemap",
592
+ "chartTypes/treemap-3x": "treemap",
593
+ "chartTypes/united-kingdom": "world",
594
+ "chartTypes/united-states": "world"
595
+ };
596
+
597
+ // src/chart-options.ts
598
+ var END_POSITIONS = ["center", "inside-end", "inside-base", "outside-end"];
599
+ var STACKED_POSITIONS = ["center", "inside-end", "inside-base"];
600
+ var POINT_POSITIONS = ["above", "below", "left", "right", "center"];
601
+ var PIE_POSITIONS = ["center", "inside-end", "outside-end"];
602
+ var NONE = [];
603
+ var VALUE_CATEGORY = ["category", "value"];
604
+ var ALL_CONTENT = ["category", "value", "percent"];
605
+ var categoryKinds = /* @__PURE__ */ new Set(["bar", "line", "area", "scatter", "histogram", "pareto", "box", "waterfall", "funnel"]);
606
+ var valueKinds = /* @__PURE__ */ new Set(["bar", "line", "area", "scatter", "histogram", "pareto", "box", "waterfall"]);
607
+ var legendKinds = /* @__PURE__ */ new Set(["bar", "line", "area", "pie", "doughnut", "scatter", "radar", "box"]);
608
+ function labelSupport(target) {
609
+ const none = { supported: true, content: VALUE_CATEGORY, positions: NONE, defaultPosition: null, defaultOn: false };
610
+ switch (target.kind) {
611
+ case "bar":
612
+ return target.stacked ? { ...none, positions: STACKED_POSITIONS, defaultPosition: "center" } : { ...none, positions: END_POSITIONS, defaultPosition: "outside-end" };
613
+ case "line":
614
+ case "scatter":
615
+ return { ...none, positions: POINT_POSITIONS, defaultPosition: "above" };
616
+ case "pie":
617
+ return { ...none, content: ALL_CONTENT, positions: PIE_POSITIONS, defaultPosition: "outside-end" };
618
+ case "doughnut":
619
+ return { ...none, content: ALL_CONTENT };
620
+ // The funnel labels its bars with values and the treemap its tiles with category names by default.
621
+ case "funnel":
622
+ case "treemap":
623
+ return { ...none, defaultOn: true };
624
+ case "area":
625
+ case "radar":
626
+ return none;
627
+ case "histogram":
628
+ case "pareto":
629
+ case "waterfall":
630
+ return { ...none, positions: END_POSITIONS, defaultPosition: "outside-end" };
631
+ default:
632
+ return { ...none, supported: false, content: [] };
633
+ }
634
+ }
635
+ function chartOptionSupport(target) {
636
+ return {
637
+ axisTitles: { category: categoryKinds.has(target.kind), value: valueKinds.has(target.kind) },
638
+ legend: legendKinds.has(target.kind),
639
+ dataLabels: labelSupport(target)
640
+ };
641
+ }
642
+ var KEPT = {
643
+ column: { kind: "bar" },
644
+ "stacked-column-3x": { kind: "bar", stacked: true },
645
+ "100pct-stacked-column-3x": { kind: "bar", stacked: true },
646
+ bar: { kind: "bar" },
647
+ "stacked-bar-3x": { kind: "bar", stacked: true },
648
+ "100pct-stacked-bar-3x": { kind: "bar", stacked: true },
649
+ line: { kind: "line" },
650
+ "line-with-markers": { kind: "line" },
651
+ "stacked-line-3x": { kind: "line", stacked: true },
652
+ "stacked-line-with-markers-3x": { kind: "line", stacked: true },
653
+ area: { kind: "area" },
654
+ "stacked-area-3x": { kind: "area", stacked: true },
655
+ "100pct-stacked-area-3x": { kind: "area", stacked: true },
656
+ pie: { kind: "pie" },
657
+ doughnut: { kind: "doughnut" },
658
+ scatter: { kind: "scatter" },
659
+ radar: { kind: "radar" },
660
+ "radar-with-markers": { kind: "radar" },
661
+ "filled-radar": { kind: "radar" },
662
+ treemap: { kind: "treemap" },
663
+ histogram: { kind: "histogram" },
664
+ pareto: { kind: "pareto" },
665
+ "box-and-whisker": { kind: "box" },
666
+ waterfall: { kind: "waterfall" },
667
+ funnel: { kind: "funnel" },
668
+ world: { kind: "map" }
669
+ };
670
+ function chartOptionTarget(typeId) {
671
+ if (typeof typeId !== "string") return void 0;
672
+ const raw = typeId.trim().toLowerCase();
673
+ const id = raw === "donut" ? "doughnut" : raw;
674
+ const kept = deprecatedCatalogIds[`chartTypes/${id}`] ?? id;
675
+ const target = Object.hasOwn(KEPT, kept) ? KEPT[kept] : void 0;
676
+ return target ? { ...target } : void 0;
677
+ }
678
+ var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
679
+ var LEGEND_POSITIONS = /* @__PURE__ */ new Set(["none", "top", "bottom", "left", "right"]);
680
+ var CONTENT_ORDER = ["category", "value", "percent"];
681
+ var POSITIONS = /* @__PURE__ */ new Set(["auto", "center", "inside-end", "inside-base", "outside-end", "above", "below", "left", "right"]);
682
+ var DEFAULT_CHART_LABEL_SEPARATOR = ", ";
683
+ function resolveChartOptions(chart, target) {
684
+ const result = { active: false, axisTitles: {}, diagnostics: [] };
685
+ if (!record2(chart)) return result;
686
+ const support = target ? chartOptionSupport(target) : void 0;
687
+ const adapt = (option, reason, message) => result.diagnostics.push({ code: "chart-option-adapted", option, reason, message });
688
+ const kind2 = target?.kind ?? "unknown";
689
+ if (record2(chart.axisTitles)) {
690
+ for (const axis of ["category", "value"]) {
691
+ const raw = chart.axisTitles[axis];
692
+ if (typeof raw !== "string" || !raw.trim()) continue;
693
+ if (support && !support.axisTitles[axis]) {
694
+ adapt(`axisTitles.${axis}`, "unsupported-type", `A '${kind2}' chart has no ${axis} axis to title; the ${axis} axis title is not drawn or exported.`);
695
+ continue;
696
+ }
697
+ result.axisTitles[axis] = raw.trim();
698
+ }
699
+ }
700
+ if (typeof chart.legend === "string" && LEGEND_POSITIONS.has(chart.legend)) {
701
+ if (support && !support.legend) adapt("legend", "unsupported-type", `A '${kind2}' chart has no legend option; the legend setting is ignored.`);
702
+ else result.legend = chart.legend;
703
+ }
704
+ const labels = chart.dataLabels;
705
+ if (labels === false && support?.dataLabels.defaultOn) result.dataLabelsOff = true;
706
+ if (labels === true || record2(labels)) {
707
+ const options = record2(labels) ? labels : {};
708
+ if (support && !support.dataLabels.supported) {
709
+ adapt("dataLabels", "unsupported-type", `A '${kind2}' chart has no data label option; data labels are not drawn or exported.`);
710
+ } else {
711
+ const allowed = support?.dataLabels.content ?? ALL_CONTENT;
712
+ const requested = Array.isArray(options.content) ? options.content.filter((part) => typeof part === "string" && CONTENT_ORDER.includes(part)) : ["value"];
713
+ const content = CONTENT_ORDER.filter((part) => requested.includes(part) && allowed.includes(part));
714
+ for (const part of CONTENT_ORDER) {
715
+ if (requested.includes(part) && !allowed.includes(part)) adapt("dataLabels.content", "unsupported-content", `A '${kind2}' chart cannot label '${part}'${part === "percent" ? " (percent exists only on pie and doughnut charts)" : ""}; it is dropped from the data labels.`);
716
+ }
717
+ if (!content.length) content.push("value");
718
+ const positions = support?.dataLabels.positions ?? NONE;
719
+ let position = support?.dataLabels.defaultPosition ?? null;
720
+ if (typeof options.position === "string" && POSITIONS.has(options.position) && options.position !== "auto") {
721
+ if (!support || positions.includes(options.position)) position = options.position;
722
+ else adapt("dataLabels.position", "unsupported-position", `A '${kind2}'${target?.stacked ? " stacked" : ""} chart cannot place data labels '${options.position}'; the type's default position is used.`);
723
+ }
724
+ let separator = typeof options.separator === "string" ? options.separator : DEFAULT_CHART_LABEL_SEPARATOR;
725
+ if (/[\r\n]/.test(separator)) {
726
+ separator = separator.replace(/[\r\n]+/g, " ");
727
+ adapt("dataLabels.separator", "unsupported-content", "A data label is one line; the line break in the separator is replaced with a space.");
728
+ }
729
+ result.dataLabels = { content, position, separator };
730
+ }
731
+ }
732
+ result.active = result.legend !== void 0 || result.dataLabels !== void 0 || result.dataLabelsOff === true || result.axisTitles.category !== void 0 || result.axisTitles.value !== void 0;
733
+ return result;
734
+ }
735
+ function formatChartLabelNumber(value) {
736
+ return String(Number(value.toPrecision(12)));
737
+ }
738
+ function formatChartLabelPercent(share) {
739
+ return `${Math.round(Number((share * 100).toPrecision(12)))}%`;
740
+ }
741
+ function chartLabelText(parts, content, separator = DEFAULT_CHART_LABEL_SEPARATOR) {
742
+ return CONTENT_ORDER.filter((part) => content.includes(part) && parts[part] !== void 0).map((part) => parts[part]).join(separator);
743
+ }
744
+
745
+ // src/content-walk.ts
746
+ function isRecord(value) {
747
+ return typeof value === "object" && value !== null && !Array.isArray(value);
748
+ }
749
+ function pathFor(parentPath, key) {
750
+ return parentPath === "/" ? `/${key}` : `${parentPath}/${key.replaceAll("~", "~0").replaceAll("/", "~1")}`;
751
+ }
752
+ var bareIdPattern = /^[a-z0-9][a-z0-9-]*$/;
753
+ var promotedRegionKeys = [
754
+ "left",
755
+ "center",
756
+ "right",
757
+ "left+center",
758
+ "center+right",
759
+ "left+center+right",
760
+ "top",
761
+ "middle",
762
+ "bottom",
763
+ "top+middle",
764
+ "middle+bottom",
765
+ "top+middle+bottom",
766
+ "top:left",
767
+ "top:center",
768
+ "top:right",
769
+ "top:left+center",
770
+ "top:center+right",
771
+ "top:left+center+right",
772
+ "middle:left",
773
+ "middle:center",
774
+ "middle:right",
775
+ "middle:left+center",
776
+ "middle:center+right",
777
+ "middle:left+center+right",
778
+ "bottom:left",
779
+ "bottom:center",
780
+ "bottom:right",
781
+ "bottom:left+center",
782
+ "bottom:center+right",
783
+ "bottom:left+center+right",
784
+ "top+middle:left",
785
+ "top+middle:center",
786
+ "top+middle:right",
787
+ "top+middle:left+center",
788
+ "top+middle:center+right",
789
+ "top+middle:left+center+right",
790
+ "middle+bottom:left",
791
+ "middle+bottom:center",
792
+ "middle+bottom:right",
793
+ "middle+bottom:left+center",
794
+ "middle+bottom:center+right",
795
+ "middle+bottom:left+center+right",
796
+ "top+middle+bottom:left",
797
+ "top+middle+bottom:center",
798
+ "top+middle+bottom:right",
799
+ "top+middle+bottom:left+center",
800
+ "top+middle+bottom:center+right",
801
+ "top+middle+bottom:left+center+right"
802
+ ];
803
+ function visitContentPayloads(slide, slidePath, visit) {
804
+ const walk2 = (payload, path, depth, ancestors) => {
805
+ visit(payload, path);
806
+ if (!Array.isArray(payload.blocks) || depth >= MAX_COMPOSITION_DEPTH || ancestors.includes(payload)) return;
807
+ const nested = [...ancestors, payload];
808
+ const blocksPath = pathFor(path, "blocks");
809
+ payload.blocks.forEach((block, index) => {
810
+ if (isRecord(block)) walk2(block, `${blocksPath}/${index}`, depth + 1, nested);
811
+ });
812
+ };
813
+ if (Array.isArray(slide.blocks)) {
814
+ const blocksPath = pathFor(slidePath, "blocks");
815
+ slide.blocks.forEach((block, index) => {
816
+ if (isRecord(block)) walk2(block, `${blocksPath}/${index}`, 0, []);
817
+ });
818
+ }
819
+ for (const key of promotedRegionKeys) {
820
+ const region = slide[key];
821
+ if (isRecord(region)) walk2(region, pathFor(slidePath, key), 0, []);
822
+ }
823
+ }
824
+
825
+ // src/furniture-fields.ts
826
+ var DEFAULT_FURNITURE_DATE_FORMAT = "M/d/yyyy";
827
+ var DEFAULT_SLIDE_NUMBER_FORMAT = "{current}";
828
+ var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
829
+ var weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
830
+ var tokenWidths = { y: [1, 2, 4], M: [1, 2, 3, 4], d: [1, 2], E: [1, 2, 3, 4] };
831
+ function parseFurnitureDateFormat(pattern) {
832
+ if (!pattern) return "dateFormat must not be empty.";
833
+ const tokens = [];
834
+ const literal = (text2) => {
835
+ const last = tokens[tokens.length - 1];
836
+ if (last?.kind === "literal") last.text += text2;
837
+ else tokens.push({ kind: "literal", text: text2 });
838
+ };
839
+ for (let index = 0; index < pattern.length; ) {
840
+ const character = pattern.charAt(index);
841
+ if (character === "'") {
842
+ if (pattern[index + 1] === "'") {
843
+ literal("'");
844
+ index += 2;
845
+ continue;
846
+ }
847
+ let end = index + 1, text2 = "";
848
+ for (; ; ) {
849
+ if (end >= pattern.length) return "dateFormat has an unterminated quoted literal.";
850
+ if (pattern[end] === "'") {
851
+ if (pattern[end + 1] === "'") {
852
+ text2 += "'";
853
+ end += 2;
854
+ continue;
855
+ }
856
+ break;
857
+ }
858
+ text2 += pattern[end];
859
+ end++;
860
+ }
861
+ literal(text2);
862
+ index = end + 1;
863
+ continue;
864
+ }
865
+ if (/[A-Za-z]/.test(character)) {
866
+ let end = index;
867
+ while (pattern[end] === character) end++;
868
+ const width = end - index;
869
+ if (!tokenWidths[character]?.includes(width)) return `dateFormat token '${pattern.slice(index, end)}' is not supported; use yyyy, yy, MMMM, MMM, MM, M, dd, d, EEEE or EEE, and quote literal letters.`;
870
+ tokens.push({ kind: "field", letter: character, width });
871
+ index = end;
872
+ continue;
873
+ }
874
+ literal(character);
875
+ index++;
876
+ }
877
+ return tokens;
878
+ }
879
+ function parseIsoDate(value) {
880
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
881
+ if (!match) return null;
882
+ const year = Number(match[1]), month = Number(match[2]), day = Number(match[3]);
883
+ const date = new Date(Date.UTC(2e3, month - 1, day));
884
+ date.setUTCFullYear(year);
885
+ if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) return null;
886
+ return { year, month, day };
887
+ }
888
+ function formatFurnitureDate(iso, pattern) {
889
+ const date = parseIsoDate(iso);
890
+ if (!date) return { error: `'${iso}' is not an ISO YYYY-MM-DD calendar date.` };
891
+ const tokens = parseFurnitureDateFormat(pattern);
892
+ if (typeof tokens === "string") return { error: tokens };
893
+ const utc = new Date(Date.UTC(2e3, date.month - 1, date.day));
894
+ utc.setUTCFullYear(date.year);
895
+ const pad = (value, width) => String(value).padStart(width, "0");
896
+ let text2 = "";
897
+ for (const token of tokens) {
898
+ if (token.kind === "literal") {
899
+ text2 += token.text;
900
+ continue;
901
+ }
902
+ const { letter, width } = token;
903
+ if (letter === "y") text2 += width === 2 ? pad(date.year % 100, 2) : width === 4 ? pad(date.year, 4) : String(date.year);
904
+ else if (letter === "M") {
905
+ const name = months[date.month - 1] ?? "";
906
+ text2 += width >= 3 ? width === 4 ? name : name.slice(0, 3) : pad(date.month, width);
907
+ } else if (letter === "d") text2 += pad(date.day, width);
908
+ else {
909
+ const name = weekdays[utc.getUTCDay()] ?? "";
910
+ text2 += width === 4 ? name : name.slice(0, 3);
911
+ }
912
+ }
913
+ return { text: text2 };
914
+ }
915
+ function formatSlideNumber(format, current, total) {
916
+ if (!format.includes("{current}")) return { error: "slideNumberFormat must contain {current}." };
917
+ if (format.includes("{total}") && total === void 0) return { error: "slideNumberFormat uses {total}, which needs the rendered slide count." };
918
+ let text2 = "";
919
+ const fields2 = [];
920
+ for (const piece of format.split(/(\{current\}|\{total\})/)) {
921
+ if (piece === "{current}") {
922
+ const value = String(current);
923
+ fields2.push({ type: "slideNumber", start: text2.length, end: text2.length + value.length });
924
+ text2 += value;
925
+ } else if (piece === "{total}") text2 += String(total);
926
+ else text2 += piece;
927
+ }
928
+ return { text: text2, fields: fields2 };
929
+ }
930
+
931
+ // src/variables.ts
932
+ var VARIABLE_KINDS = ["color", "text", "number", "date", "image", "url", "list"];
933
+ var DEFAULT_VARIABLE_DATE_FORMAT = "MMMM d, yyyy";
934
+ var OPFVariableError = class extends Error {
935
+ diagnostics;
936
+ constructor(diagnostics) {
937
+ const first = diagnostics.find((entry) => entry.severity === "error") ?? diagnostics[0];
938
+ super(first ? `OPF variables: ${first.message}` : "OPF variables failed");
939
+ this.name = "OPFVariableError";
940
+ this.diagnostics = diagnostics;
941
+ }
942
+ };
943
+ var idPattern = /^[a-z][a-z0-9-]*$/;
944
+ var referencePattern = /^var:([a-z][a-z0-9-]*)$/;
945
+ var hexPattern = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
946
+ var urlPattern = /^(?:https?:\/\/|mailto:|tel:)\S+$/;
947
+ var datePrefixPattern = /^(\d{4}-\d{2}-\d{2})(?:[T ].*)?$/;
948
+ var decimalPattern = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
949
+ var tokenPattern = /\\\{\{|\{\{\s*([a-z][a-z0-9-]*)\s*(?:\|([^{}]*))?\}\}/g;
950
+ var SAMPLES = {
951
+ color: "#000000",
952
+ text: "Sample text",
953
+ number: 0,
954
+ date: "2000-01-01",
955
+ image: "https://example.invalid/sample.png",
956
+ url: "https://example.invalid/",
957
+ list: ["Sample item"]
958
+ };
959
+ var hasOwn = (value, key) => Object.hasOwn(value, key);
960
+ var clone = (value) => value === void 0 ? value : JSON.parse(JSON.stringify(value));
961
+ function isTemplate(presentation) {
962
+ return isRecord(presentation) && presentation.template === true;
963
+ }
964
+ function isKind(value) {
965
+ return typeof value === "string" && VARIABLE_KINDS.includes(value);
966
+ }
967
+ function coerceVariableValue(kind2, raw) {
968
+ const fail = (message) => ({ ok: false, message });
969
+ switch (kind2) {
970
+ case "color":
971
+ return typeof raw === "string" && hexPattern.test(raw.trim()) ? { ok: true, value: raw.trim() } : fail("expected a hex color such as #0F4C81");
972
+ case "text": {
973
+ if (typeof raw === "string") return { ok: true, value: raw };
974
+ if (typeof raw === "number" && Number.isFinite(raw)) return { ok: true, value: String(raw) };
975
+ if (typeof raw === "boolean") return { ok: true, value: String(raw) };
976
+ if (Array.isArray(raw) && raw.every((run) => typeof run === "string" || isRecord(run) && typeof run.text === "string")) return { ok: true, value: clone(raw) };
977
+ return fail("expected text: a string or an array of text runs");
978
+ }
979
+ case "number": {
980
+ if (typeof raw === "number" && Number.isFinite(raw)) return { ok: true, value: raw };
981
+ if (typeof raw === "string" && decimalPattern.test(raw.trim())) {
982
+ const value = Number(raw.trim());
983
+ if (Number.isFinite(value)) return { ok: true, value };
984
+ }
985
+ return fail("expected a finite number");
986
+ }
987
+ case "date": {
988
+ if (typeof raw === "string") {
989
+ const match = datePrefixPattern.exec(raw.trim());
990
+ if (match?.[1] && parseIsoDate(match[1])) return { ok: true, value: match[1] };
991
+ }
992
+ return fail("expected an ISO calendar date (YYYY-MM-DD)");
993
+ }
994
+ case "image": {
995
+ if (typeof raw === "string" && raw.trim()) return { ok: true, value: raw.trim() };
996
+ if (isRecord(raw) && typeof raw.src === "string" && raw.src.trim()) return { ok: true, value: clone(raw) };
997
+ return fail("expected an image source: a string or an object with src");
998
+ }
999
+ case "url":
1000
+ return typeof raw === "string" && urlPattern.test(raw.trim()) ? { ok: true, value: raw.trim() } : fail("expected an http, https, mailto or tel link");
1001
+ case "list": {
1002
+ if (typeof raw === "string") {
1003
+ const entries = raw.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
1004
+ return { ok: true, value: entries };
1005
+ }
1006
+ if (Array.isArray(raw) && raw.every((entry) => typeof entry === "string" || typeof entry === "number" && Number.isFinite(entry))) {
1007
+ return { ok: true, value: raw.map(String) };
1008
+ }
1009
+ return fail("expected a list of strings");
1010
+ }
1011
+ }
1012
+ }
1013
+ function isProvided(kind2, raw) {
1014
+ if (raw === void 0 || raw === null) return false;
1015
+ if (typeof raw === "string" && raw.trim() === "" && kind2 !== "text") return false;
1016
+ return true;
1017
+ }
1018
+ function variableDeclarations(presentation) {
1019
+ if (!isRecord(presentation) || !isRecord(presentation.variables)) return [];
1020
+ const out = [];
1021
+ for (const [id, entry] of Object.entries(presentation.variables)) {
1022
+ if (!idPattern.test(id)) continue;
1023
+ const path = pathFor("/variables", id);
1024
+ if (typeof entry === "string") {
1025
+ if (hexPattern.test(entry)) out.push({ id, kind: "color", path, required: true, value: entry });
1026
+ continue;
1027
+ }
1028
+ if (!isRecord(entry) || !isKind(entry.type)) continue;
1029
+ const kind2 = entry.type;
1030
+ const declaration = { id, kind: kind2, path, required: entry.required !== false };
1031
+ const value = entry.value !== void 0 ? coerceVariableValue(kind2, entry.value) : void 0;
1032
+ if (value?.ok) declaration.value = value.value;
1033
+ const example = entry.example !== void 0 ? coerceVariableValue(kind2, entry.example) : void 0;
1034
+ if (example?.ok) declaration.example = example.value;
1035
+ if (typeof entry.format === "string") declaration.format = entry.format;
1036
+ if (typeof entry.label === "string") declaration.label = entry.label;
1037
+ if (typeof entry.description === "string") declaration.description = entry.description;
1038
+ out.push(declaration);
1039
+ }
1040
+ return out;
1041
+ }
1042
+ function hasContentVariables(presentation) {
1043
+ return isTemplate(presentation) || variableDeclarations(presentation).some((declaration) => declaration.kind !== "color");
1044
+ }
1045
+ var POSITIVE_ZERO = "0";
1046
+ function formatVariableNumber(value, pattern) {
1047
+ if (!Number.isFinite(value)) return { error: `'${String(value)}' is not a finite number.` };
1048
+ if (pattern === void 0 || pattern === "") return { text: String(value) };
1049
+ const match = /^([^#0,.]*)((?:[#0,]*)(?:\.[#0]+)?)([\s\S]*)$/.exec(pattern);
1050
+ const numeric = match?.[2] ?? "";
1051
+ if (!match || !/[#0]/.test(numeric)) return { error: `Number format '${pattern}' needs a numeric part of '#' and '0' characters.` };
1052
+ const prefix = match[1] ?? "";
1053
+ const suffix = match[3] ?? "";
1054
+ const [integerPattern = "", decimalPattern2 = ""] = numeric.split(".");
1055
+ const grouping = integerPattern.includes(",");
1056
+ const minInteger = [...integerPattern].filter((char) => char === "0").length;
1057
+ const minDecimals = [...decimalPattern2].filter((char) => char === "0").length;
1058
+ const maxDecimals = decimalPattern2.length;
1059
+ const scaled = prefix.includes("%") || suffix.includes("%") ? value * 100 : value;
1060
+ const absolute = Math.abs(scaled);
1061
+ let fixed;
1062
+ const shifted = Number(`${absolute}e${maxDecimals}`);
1063
+ if (absolute >= 1e21) fixed = `${BigInt(absolute).toString()}${maxDecimals ? `.${"0".repeat(maxDecimals)}` : ""}`;
1064
+ else if (/e/i.test(String(absolute)) || !Number.isSafeInteger(Math.round(shifted))) fixed = absolute.toFixed(maxDecimals);
1065
+ else {
1066
+ const digits = String(Math.round(shifted)).padStart(maxDecimals + 1, "0");
1067
+ fixed = maxDecimals ? `${digits.slice(0, -maxDecimals)}.${digits.slice(-maxDecimals)}` : digits;
1068
+ }
1069
+ let [integer = POSITIVE_ZERO, decimals = ""] = fixed.split(".");
1070
+ while (decimals.length > minDecimals && decimals.endsWith("0")) decimals = decimals.slice(0, -1);
1071
+ integer = integer.replace(/^0+(?=\d)/, "");
1072
+ if (integer === POSITIVE_ZERO && minInteger === 0 && decimals) integer = "";
1073
+ integer = integer.padStart(minInteger, "0");
1074
+ if (grouping) integer = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
1075
+ const body = `${integer}${decimals ? `.${decimals}` : ""}` || POSITIVE_ZERO;
1076
+ const negative = scaled < 0 && /[1-9]/.test(body);
1077
+ return { text: `${negative ? "-" : ""}${prefix}${body}${suffix}` };
1078
+ }
1079
+ function diag(context, entry, dedupeKey) {
1080
+ if (dedupeKey) {
1081
+ if (context.seen.has(dedupeKey)) return;
1082
+ context.seen.add(dedupeKey);
1083
+ }
1084
+ context.diagnostics.push(entry);
1085
+ }
1086
+ function sampleValue(declaration) {
1087
+ return declaration.example !== void 0 ? declaration.example : declaration.value !== void 0 ? declaration.value : SAMPLES[declaration.kind];
1088
+ }
1089
+ function plainText(value) {
1090
+ if (typeof value === "string") return value;
1091
+ if (Array.isArray(value)) return value.map((run) => typeof run === "string" ? run : isRecord(run) && typeof run.text === "string" ? run.text : "").join("");
1092
+ return "";
1093
+ }
1094
+ function inlineText(effective, format, path, context) {
1095
+ const { declaration } = effective;
1096
+ const value = effective.status === "unfilled" ? sampleValue(declaration) : effective.value;
1097
+ switch (declaration.kind) {
1098
+ case "color":
1099
+ case "url":
1100
+ return String(value);
1101
+ case "text":
1102
+ if (Array.isArray(value)) {
1103
+ diag(context, { code: "variable-rich-flattened", severity: "info", path, id: declaration.id, message: `Rich text variable '${declaration.id}' is inserted as plain text inside a string; reference it whole as 'var:${declaration.id}' to keep its formatting.` }, `flat:${declaration.id}:${path}`);
1104
+ }
1105
+ return plainText(value);
1106
+ case "number": {
1107
+ const pattern = (format ?? declaration.format)?.trim();
1108
+ const formatted = formatVariableNumber(value, pattern === "" ? void 0 : pattern);
1109
+ if ("error" in formatted) {
1110
+ diag(context, { code: "variable-format", severity: "error", path, id: declaration.id, message: formatted.error }, `fmt:${declaration.id}:${format ?? ""}`);
1111
+ return String(value);
1112
+ }
1113
+ return formatted.text;
1114
+ }
1115
+ case "date": {
1116
+ const pattern = (format ?? declaration.format)?.trim() || DEFAULT_VARIABLE_DATE_FORMAT;
1117
+ const formatted = formatFurnitureDate(String(value), pattern);
1118
+ if ("error" in formatted) {
1119
+ diag(context, { code: "variable-format", severity: "error", path, id: declaration.id, message: formatted.error }, `fmt:${declaration.id}:${format ?? ""}`);
1120
+ return String(value);
1121
+ }
1122
+ return formatted.text;
1123
+ }
1124
+ case "image":
1125
+ return isRecord(value) ? String(value.src) : String(value);
1126
+ case "list":
1127
+ return value.join(format ?? ", ");
1128
+ }
1129
+ }
1130
+ function interpolate(text2, path, context) {
1131
+ let changed = false;
1132
+ const value = text2.replace(tokenPattern, (match, id, format) => {
1133
+ if (id === void 0) {
1134
+ if (context.unescape) {
1135
+ changed = true;
1136
+ return "{{";
1137
+ }
1138
+ return match;
1139
+ }
1140
+ context.uses.push({ id, path, form: "token" });
1141
+ const effective = context.effective.get(id);
1142
+ if (!effective) {
1143
+ diag(context, { code: "variable-unknown", severity: "warning", path, id, message: `'{{${id}}}' names no declared variable; declare '${id}' in the top-level variables map, or write '\\{{' for literal braces.` }, `unk:${id}:${path}`);
1144
+ return match;
1145
+ }
1146
+ if (effective.status === "unfilled" && !context.shape) {
1147
+ if (!effective.declaration.required) {
1148
+ changed = true;
1149
+ return "";
1150
+ }
1151
+ return match;
1152
+ }
1153
+ const result = inlineText(effective, effective.declaration.kind === "list" ? format : format?.trim(), path, context);
1154
+ if (result === void 0) return match;
1155
+ changed = true;
1156
+ return result;
1157
+ });
1158
+ return { value, changed };
1159
+ }
1160
+ var OMIT = /* @__PURE__ */ Symbol("omit");
1161
+ var Splice = class {
1162
+ constructor(values) {
1163
+ this.values = values;
1164
+ }
1165
+ values;
1166
+ };
1167
+ function reduceString(text2, path, parentIsArray, context) {
1168
+ const reference = referencePattern.exec(text2);
1169
+ if (reference) {
1170
+ const id = reference[1];
1171
+ const effective = context.effective.get(id);
1172
+ if (effective && effective.declaration.kind !== "color") {
1173
+ context.uses.push({ id, path, form: "reference" });
1174
+ if (effective.status === "unfilled" && !context.shape) {
1175
+ return effective.declaration.required ? { kind: "keep" } : { kind: "omit" };
1176
+ }
1177
+ const value = effective.status === "unfilled" ? sampleValue(effective.declaration) : effective.value;
1178
+ if (effective.declaration.kind === "list" && parentIsArray) {
1179
+ return context.shape ? { kind: "replace", value: value[0] ?? "" } : { kind: "splice", values: clone(value) };
1180
+ }
1181
+ return { kind: "replace", value: clone(value) };
1182
+ }
1183
+ if (effective) {
1184
+ context.uses.push({ id, path, form: "reference" });
1185
+ return { kind: "keep" };
1186
+ }
1187
+ return { kind: "keep" };
1188
+ }
1189
+ if (!text2.includes("{{")) return { kind: "keep" };
1190
+ const result = interpolate(text2, path, context);
1191
+ return result.changed ? { kind: "replace", value: result.value } : { kind: "keep" };
1192
+ }
1193
+ var skippedKeys = /* @__PURE__ */ new Set(["extensions"]);
1194
+ var skippedRootKeys = /* @__PURE__ */ new Set(["variables", "catalogs", "$schema", "template"]);
1195
+ function walk(value, path, parentIsArray, context) {
1196
+ if (typeof value === "string") {
1197
+ const reduced = reduceString(value, path, parentIsArray, context);
1198
+ if (reduced.kind === "keep") return value;
1199
+ if (reduced.kind === "omit") return OMIT;
1200
+ if (reduced.kind === "splice") return new Splice(reduced.values);
1201
+ return reduced.value;
1202
+ }
1203
+ if (Array.isArray(value)) {
1204
+ let changed = false;
1205
+ const out = [];
1206
+ value.forEach((entry, index) => {
1207
+ const next = walk(entry, `${path}/${index}`, true, context);
1208
+ if (next === OMIT) {
1209
+ changed = true;
1210
+ return;
1211
+ }
1212
+ if (next instanceof Splice) {
1213
+ changed = true;
1214
+ out.push(...next.values);
1215
+ return;
1216
+ }
1217
+ if (next !== entry) changed = true;
1218
+ out.push(next);
1219
+ });
1220
+ return changed ? out : value;
1221
+ }
1222
+ if (isRecord(value)) {
1223
+ let changed = false;
1224
+ const entries = [];
1225
+ for (const [key, entry] of Object.entries(value)) {
1226
+ if (skippedKeys.has(key) || path === "" && skippedRootKeys.has(key)) {
1227
+ entries.push([key, entry]);
1228
+ continue;
1229
+ }
1230
+ const next = walk(entry, pathFor(path || "/", key), false, context);
1231
+ if (next === OMIT) {
1232
+ changed = true;
1233
+ continue;
1234
+ }
1235
+ if (next !== entry) changed = true;
1236
+ entries.push([key, next]);
1237
+ }
1238
+ return changed ? Object.fromEntries(entries) : value;
1239
+ }
1240
+ return value;
1241
+ }
1242
+ function plan(presentation, values, options) {
1243
+ const declarations = variableDeclarations(presentation);
1244
+ const template = options.template ?? isTemplate(presentation);
1245
+ const lenient = template || options.partial === true;
1246
+ const context = {
1247
+ effective: /* @__PURE__ */ new Map(),
1248
+ diagnostics: [],
1249
+ uses: [],
1250
+ unescape: false,
1251
+ shape: options.shape === true,
1252
+ content: hasContentVariables(presentation) || template,
1253
+ seen: /* @__PURE__ */ new Set()
1254
+ };
1255
+ const unfilled = [];
1256
+ const examplesUsed = [];
1257
+ const providedColors = /* @__PURE__ */ new Map();
1258
+ const declared = new Set(declarations.map((declaration) => declaration.id));
1259
+ for (const key of Object.keys(values)) {
1260
+ if (!declared.has(key)) {
1261
+ context.diagnostics.push({ code: "variable-unknown-value", severity: "warning", path: "/variables", id: key, message: `A value was supplied for '${key}', which no variable declares.` });
1262
+ }
1263
+ }
1264
+ for (const declaration of declarations) {
1265
+ const raw = hasOwn(values, declaration.id) ? values[declaration.id] : void 0;
1266
+ let effective;
1267
+ if (isProvided(declaration.kind, raw)) {
1268
+ const coerced = coerceVariableValue(declaration.kind, raw);
1269
+ if (coerced.ok) {
1270
+ effective = { declaration, status: "filled", value: coerced.value, provided: true };
1271
+ if (declaration.kind === "color") providedColors.set(declaration.id, coerced.value);
1272
+ } else {
1273
+ context.diagnostics.push({ code: "variable-invalid-value", severity: "error", path: declaration.path, id: declaration.id, message: `Value for '${declaration.id}' (${declaration.kind}): ${coerced.message}.` });
1274
+ }
1275
+ }
1276
+ if (!effective && declaration.value !== void 0) effective = { declaration, status: "filled", value: declaration.value, provided: false };
1277
+ if (!effective && options.examples && declaration.example !== void 0) {
1278
+ effective = { declaration, status: "example", value: declaration.example, provided: false };
1279
+ examplesUsed.push(declaration.id);
1280
+ context.diagnostics.push({ code: "variable-example-used", severity: "info", path: declaration.path, id: declaration.id, message: `'${declaration.id}' is unfilled; its example is used.` });
1281
+ if (declaration.kind === "color") providedColors.set(declaration.id, declaration.example);
1282
+ }
1283
+ if (!effective) {
1284
+ effective = { declaration, status: "unfilled", provided: false };
1285
+ if (declaration.required) {
1286
+ unfilled.push(declaration.id);
1287
+ context.diagnostics.push({
1288
+ code: "variable-unfilled",
1289
+ severity: lenient ? "info" : "error",
1290
+ path: declaration.path,
1291
+ id: declaration.id,
1292
+ message: lenient ? `'${declaration.id}' (${declaration.kind}) is not filled yet.` : `Required variable '${declaration.id}' (${declaration.kind}) has no value. Provide one, or mark the document as a template ("template": true).`
1293
+ });
1294
+ }
1295
+ }
1296
+ context.effective.set(declaration.id, effective);
1297
+ }
1298
+ return { context, declarations, unfilled, examplesUsed, providedColors };
1299
+ }
1300
+ function rebuildVariables(presentation, built, keepAll) {
1301
+ const source = presentation.variables;
1302
+ if (!isRecord(source)) return void 0;
1303
+ const out = [];
1304
+ for (const [id, entry] of Object.entries(source)) {
1305
+ const effective = built.context.effective.get(id);
1306
+ if (!effective) {
1307
+ out.push([id, entry]);
1308
+ continue;
1309
+ }
1310
+ const { declaration } = effective;
1311
+ if (declaration.kind !== "color") {
1312
+ if (keepAll || effective.status === "unfilled" && declaration.required) out.push([id, entry]);
1313
+ continue;
1314
+ }
1315
+ const color = built.providedColors.get(id);
1316
+ if (color === void 0) {
1317
+ out.push([id, entry]);
1318
+ continue;
1319
+ }
1320
+ const base = isRecord(entry) ? entry : {};
1321
+ const { example: _example, required: _required, label: _label, ...rest } = base;
1322
+ out.push([id, { ...rest, type: "color", value: color }]);
1323
+ }
1324
+ return out.length ? Object.fromEntries(out) : void 0;
1325
+ }
1326
+ function finish(presentation, built, options) {
1327
+ const { context } = built;
1328
+ const complete = built.unfilled.length === 0;
1329
+ context.unescape = complete && context.content && !context.shape;
1330
+ const walked = context.content ? walk(presentation, "", false, context) : presentation;
1331
+ const result = { ...walked };
1332
+ const variables = rebuildVariables(presentation, built, context.shape);
1333
+ if (variables) result.variables = variables;
1334
+ else delete result.variables;
1335
+ if (!context.shape && complete && hasOwn(result, "template")) delete result.template;
1336
+ if (context.content) {
1337
+ const used = new Set(context.uses.map((use) => use.id));
1338
+ for (const declaration of built.declarations) {
1339
+ if (declaration.kind !== "color" && !used.has(declaration.id)) {
1340
+ context.diagnostics.push({ code: "variable-unused", severity: "warning", path: declaration.path, id: declaration.id, message: `Variable '${declaration.id}' is declared but never used; reference it as '{{${declaration.id}}}' or 'var:${declaration.id}'.` });
1341
+ }
1342
+ }
1343
+ }
1344
+ const diagnostics = context.diagnostics;
1345
+ if (options.strict && diagnostics.some((entry) => entry.severity === "error")) throw new OPFVariableError(diagnostics);
1346
+ return { presentation: result, diagnostics, unfilled: built.unfilled, examplesUsed: built.examplesUsed, complete };
1347
+ }
1348
+ function resolveVariables(presentation, values = {}, options = {}) {
1349
+ if (!isRecord(presentation)) throw new TypeError("resolveVariables expects a presentation object.");
1350
+ if (!isRecord(values)) throw new TypeError("resolveVariables values must be an object keyed by variable id.");
1351
+ const built = plan(presentation, values, options);
1352
+ if (!built.context.content && built.providedColors.size === 0 && built.context.diagnostics.length === 0) {
1353
+ return { presentation, diagnostics: [], unfilled: built.unfilled, examplesUsed: [], complete: built.unfilled.length === 0 };
1354
+ }
1355
+ return finish(presentation, built, options);
1356
+ }
1357
+ function instantiateForValidation(presentation, values = {}, template) {
1358
+ const options = { template, shape: true, partial: true };
1359
+ return finish(presentation, plan(presentation, values, options), options);
1360
+ }
1361
+ function listVariables(presentation, values = {}) {
1362
+ if (!isRecord(presentation)) return [];
1363
+ const built = plan(presentation, isRecord(values) ? values : {}, { examples: false, partial: true });
1364
+ built.context.shape = true;
1365
+ built.context.content = true;
1366
+ walk(presentation, "", false, built.context);
1367
+ return built.declarations.map((declaration) => {
1368
+ const effective = built.context.effective.get(declaration.id);
1369
+ return {
1370
+ ...declaration,
1371
+ ...effective?.status === "filled" && effective.provided ? { value: effective.value } : {},
1372
+ filled: effective?.status === "filled",
1373
+ uses: built.context.uses.filter((use) => use.id === declaration.id)
1374
+ };
1375
+ });
1376
+ }
1377
+
1378
+ // src/chart-data.ts
1379
+ var record3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1380
+ var own = (value, key) => Object.hasOwn(value, key);
1381
+ var segment = (key) => String(key).replaceAll("~", "~0").replaceAll("/", "~1");
1382
+ var at = (base, ...keys) => `${base}${keys.map((key) => `/${segment(key)}`).join("")}`;
1383
+ var STRICT_DECIMAL = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
1384
+ function chartNumber(value) {
1385
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
1386
+ if (typeof value !== "string") return null;
1387
+ const text2 = value.trim();
1388
+ if (!STRICT_DECIMAL.test(text2)) return null;
1389
+ const number = Number(text2);
1390
+ if (!Number.isFinite(number) || /^-?\d+$/.test(text2) && !Number.isSafeInteger(number)) return null;
1391
+ return number;
1392
+ }
1393
+ function numberFormatError(format) {
1394
+ if (format === void 0) return void 0;
1395
+ if (typeof format !== "string" || format === "") return 'A number format is a non-empty pattern such as "#,##0" or "0.0%".';
1396
+ const result = formatVariableNumber(0, format);
1397
+ return "error" in result ? result.error : void 0;
1398
+ }
1399
+ function formatDataNumber(value, format) {
1400
+ if (format === void 0 || format === "" || !Number.isFinite(value)) return String(value);
1401
+ const result = formatVariableNumber(value, format);
1402
+ return "text" in result ? result.text : String(value);
1403
+ }
1404
+ function formatParts(format) {
1405
+ const match = /^([^#0,.]*)((?:[#0,]*)(?:\.[#0]+)?)([\s\S]*)$/.exec(format);
1406
+ const numeric = match?.[2] ?? "";
1407
+ if (!match || !/[#0]/.test(numeric)) return void 0;
1408
+ const [integer = "", decimals = ""] = numeric.split(".");
1409
+ return { prefix: match[1] ?? "", integer, decimals, suffix: match[3] ?? "" };
1410
+ }
1411
+ var EXCEL_SAFE = /* @__PURE__ */ new Set([..."$-+():!^&'~{}<>= "]);
1412
+ function excelLiteral(text2, percent) {
1413
+ let out = "";
1414
+ let quoted = "";
1415
+ const flush = () => {
1416
+ if (quoted) {
1417
+ out += `"${quoted}"`;
1418
+ quoted = "";
1419
+ }
1420
+ };
1421
+ for (const char of text2) {
1422
+ if (char === "%" && !percent.used) {
1423
+ flush();
1424
+ out += "%";
1425
+ percent.used = true;
1426
+ continue;
1427
+ }
1428
+ if (char === '"') {
1429
+ flush();
1430
+ out += '\\"';
1431
+ continue;
1432
+ }
1433
+ if (EXCEL_SAFE.has(char) && quoted === "") {
1434
+ out += char;
1435
+ continue;
1436
+ }
1437
+ quoted += char;
1438
+ }
1439
+ flush();
1440
+ return out;
1441
+ }
1442
+ function excelInteger(integer) {
1443
+ const grouping = integer.includes(",");
1444
+ const digits = integer.replaceAll(",", "");
1445
+ if (!grouping) {
1446
+ const zeros2 = [...digits].filter((char) => char === "0").length;
1447
+ return "#".repeat(digits.length - zeros2) + "0".repeat(zeros2);
1448
+ }
1449
+ const zeros = [...digits].filter((char) => char === "0").length;
1450
+ const width = Math.max(4, digits.length);
1451
+ const placeholders = "#".repeat(width - zeros) + "0".repeat(zeros);
1452
+ return placeholders.replace(/\B(?=(.{3})+$)/g, ",");
1453
+ }
1454
+ function excelNumberFormat(format) {
1455
+ if (format === void 0 || format === "") return "General";
1456
+ const parts = formatParts(format);
1457
+ if (!parts) return "General";
1458
+ const percent = { used: false };
1459
+ const minDecimals = [...parts.decimals].filter((char) => char === "0").length;
1460
+ const decimals = parts.decimals ? `.${"0".repeat(minDecimals)}${"#".repeat(parts.decimals.length - minDecimals)}` : "";
1461
+ const prefix = excelLiteral(parts.prefix, percent);
1462
+ const body = `${excelInteger(parts.integer)}${decimals}`;
1463
+ return `${prefix}${body}${excelLiteral(parts.suffix, percent)}`;
1464
+ }
1465
+ function excelTokens(code) {
1466
+ const tokens = [];
1467
+ for (let i = 0; i < code.length; i++) {
1468
+ const char = code[i];
1469
+ if (char === '"') {
1470
+ const end = code.indexOf('"', i + 1);
1471
+ if (end < 0) return void 0;
1472
+ tokens.push({ kind: "literal", text: code.slice(i + 1, end), quoted: true });
1473
+ i = end;
1474
+ } else if (char === "\\") {
1475
+ if (i + 1 >= code.length) return void 0;
1476
+ tokens.push({ kind: "literal", text: code[i + 1], quoted: true });
1477
+ i++;
1478
+ } else if (char === "[") {
1479
+ const end = code.indexOf("]", i + 1);
1480
+ if (end < 0) return void 0;
1481
+ const currency = /^\$([^-\]]*)(?:-[0-9A-Fa-f]+)?$/.exec(code.slice(i + 1, end));
1482
+ if (!currency) return void 0;
1483
+ tokens.push({ kind: "literal", text: currency[1] ?? "", quoted: true });
1484
+ i = end;
1485
+ } else if (char === "%") tokens.push({ kind: "percent" });
1486
+ else if ("#0,.".includes(char)) tokens.push({ kind: "placeholder", text: char });
1487
+ else if (EXCEL_SAFE.has(char)) tokens.push({ kind: "literal", text: char, quoted: false });
1488
+ else return void 0;
1489
+ }
1490
+ return tokens;
1491
+ }
1492
+ function numberFormatFromExcel(code) {
1493
+ if (typeof code !== "string") return void 0;
1494
+ const trimmed = code.trim();
1495
+ if (trimmed === "" || /^general$/i.test(trimmed)) return void 0;
1496
+ const tokens = excelTokens(code);
1497
+ if (!tokens) return void 0;
1498
+ const first = tokens.findIndex((token) => token.kind === "placeholder");
1499
+ if (first < 0) return void 0;
1500
+ let last = first;
1501
+ while (last + 1 < tokens.length && tokens[last + 1].kind === "placeholder") last++;
1502
+ if (tokens.slice(last + 1).some((token) => token.kind === "placeholder")) return void 0;
1503
+ const numeric = tokens.slice(first, last + 1).map((token) => token.text).join("");
1504
+ const match = /^([#0,]*)(?:\.([#0]+))?$/.exec(numeric);
1505
+ if (!match || !/[#0]/.test(numeric)) return void 0;
1506
+ const integer = match[1] ?? "";
1507
+ if (/^,|,$/.test(integer) || integer.includes(",,")) return void 0;
1508
+ if (/,/.test(integer) && !/[#0]/.test(integer)) return void 0;
1509
+ if (/0[#,]*#/.test(integer) || /#0/.test(match[2] ?? "")) return void 0;
1510
+ const percentTokens = tokens.filter((token) => token.kind === "percent").length;
1511
+ const literalText = (list) => list.map((token) => token.kind === "percent" ? "%" : token.kind === "literal" ? token.text : "").join("");
1512
+ const quotedPercent = tokens.some((token) => token.kind === "literal" && token.quoted && token.text.includes("%"));
1513
+ if (percentTokens > 1 || percentTokens === 0 && quotedPercent) return void 0;
1514
+ const prefix = literalText(tokens.slice(0, first));
1515
+ const suffix = literalText(tokens.slice(last + 1));
1516
+ const format = `${prefix}${numeric}${suffix}`;
1517
+ const parts = formatParts(format);
1518
+ if (!parts || parts.prefix !== prefix || parts.suffix !== suffix) return void 0;
1519
+ return format;
1520
+ }
1521
+ function columnName(column) {
1522
+ if (typeof column === "string") return column;
1523
+ if (record3(column) && typeof column.name === "string") return column.name;
1524
+ return void 0;
1525
+ }
1526
+ function columnFormat(column) {
1527
+ return record3(column) && typeof column.format === "string" ? column.format : void 0;
1528
+ }
1529
+ function datasetsOf(document) {
1530
+ return record3(document) && record3(document.datasets) ? document.datasets : {};
1531
+ }
1532
+ function isDatasetRef(value) {
1533
+ return record3(value) && typeof value.dataset === "string";
1534
+ }
1535
+ function selectFields(columns2, fields2, fieldsPath, columnsPath, diagnostics) {
1536
+ const names = columns2.map((column) => columnName(column) ?? "");
1537
+ if (!Array.isArray(fields2)) return { names, formats: columns2.map(columnFormat), columns: columns2, indices: names.map((_, index) => index) };
1538
+ duplicateColumns(names, columnsPath, diagnostics);
1539
+ const indices = [];
1540
+ fields2.forEach((field, index) => {
1541
+ const found = typeof field === "string" ? names.indexOf(field) : -1;
1542
+ if (found < 0) diagnostics.push({ code: "dataset-field-unknown", severity: "error", path: at(fieldsPath, index), message: `field ${JSON.stringify(field)} is not a column of the dataset; use one of ${names.map((name) => JSON.stringify(name)).join(", ")}` });
1543
+ else indices.push(found);
1544
+ });
1545
+ return { names: indices.map((index) => names[index]), formats: indices.map((index) => columnFormat(columns2[index])), columns: indices.map((index) => columns2[index]), indices };
1546
+ }
1547
+ function duplicateColumns(names, columnsPath, diagnostics) {
1548
+ const seen = /* @__PURE__ */ new Set();
1549
+ names.forEach((name, index) => {
1550
+ if (seen.has(name)) diagnostics.push({ code: "data-column-duplicate", severity: "error", path: at(columnsPath, index), message: `column name ${JSON.stringify(name)} is used by an earlier column; column names must be unique where fields or a chart mapping address them` });
1551
+ seen.add(name);
1552
+ });
1553
+ }
1554
+ function formatDiagnostics(columns2, columnsPath, diagnostics) {
1555
+ columns2.forEach((column, index) => {
1556
+ if (!record3(column) || column.format === void 0) return;
1557
+ const error = numberFormatError(column.format);
1558
+ if (error) diagnostics.push({ code: "number-format-invalid", severity: "error", path: at(columnsPath, index, "format"), message: error });
1559
+ });
1560
+ }
1561
+ function datasetDiagnostics(document) {
1562
+ const diagnostics = [];
1563
+ for (const [id, dataset] of Object.entries(datasetsOf(document))) {
1564
+ if (!record3(dataset) || !Array.isArray(dataset.columns)) continue;
1565
+ const columnsPath = at("/datasets", id, "columns");
1566
+ duplicateColumns(dataset.columns.map((column) => columnName(column) ?? ""), columnsPath, diagnostics);
1567
+ formatDiagnostics(dataset.columns, columnsPath, diagnostics);
1568
+ }
1569
+ return diagnostics;
1570
+ }
1571
+ function inlineColumn(column) {
1572
+ const name = columnName(column) ?? "";
1573
+ const format = columnFormat(column);
1574
+ return format === void 0 ? name : { name, format };
1575
+ }
1576
+ function inlinedData(ref, datasets) {
1577
+ const id = ref.dataset;
1578
+ const dataset = own(datasets, id) ? datasets[id] : void 0;
1579
+ if (!record3(dataset) || !Array.isArray(dataset.columns) || !Array.isArray(dataset.rows)) return void 0;
1580
+ const diagnostics = [];
1581
+ const selection = selectFields(dataset.columns, ref.fields, "", "", diagnostics);
1582
+ if (diagnostics.some((entry) => entry.code === "dataset-field-unknown")) return void 0;
1583
+ const rows2 = dataset.rows.map((row) => selection.indices.map((index) => Array.isArray(row) && index < row.length ? structuredClone(row[index]) : null));
1584
+ return { columns: selection.columns.map(inlineColumn), rows: rows2, ...record3(dataset.source) ? { source: structuredClone(dataset.source) } : {} };
1585
+ }
1586
+ function inlineDatasets(document) {
1587
+ if (!record3(document)) return document;
1588
+ const copy = structuredClone(document);
1589
+ const datasets = datasetsOf(copy);
1590
+ if (!Object.keys(datasets).length) return copy;
1591
+ visitDataPayloads(copy, (payload) => {
1592
+ if (record3(payload.chart) && isDatasetRef(payload.chart.data)) {
1593
+ const inline = inlinedData(payload.chart.data, datasets);
1594
+ if (inline) payload.chart.data = inline;
1595
+ }
1596
+ if (record3(payload.table) && isDatasetRef(payload.table)) payload.table = inlineTable(payload.table, datasets) ?? payload.table;
1597
+ });
1598
+ return copy;
1599
+ }
1600
+ function inlineTable(table, datasets) {
1601
+ const inline = inlinedData(table, datasets);
1602
+ if (!inline) return void 0;
1603
+ const { dataset: _dataset, fields: _fields, ...rest } = table;
1604
+ return { ...rest, columns: inline.columns, rows: inline.rows };
1605
+ }
1606
+ function inlineTableData(table, document) {
1607
+ if (!record3(table) || !isDatasetRef(table)) return table;
1608
+ return inlineTable(table, datasetsOf(document)) ?? table;
1609
+ }
1610
+ function inlineChartData(chart, document) {
1611
+ if (!record3(chart) || !isDatasetRef(chart.data)) return chart;
1612
+ const inline = inlinedData(chart.data, datasetsOf(document));
1613
+ return inline ? { ...chart, data: inline } : chart;
1614
+ }
1615
+ function visitDataPayloads(document, visit) {
1616
+ if (!Array.isArray(document.slides)) return;
1617
+ const walk2 = (payload, depth, ancestors) => {
1618
+ visit(payload);
1619
+ if (depth >= 32 || ancestors.includes(payload) || !Array.isArray(payload.blocks)) return;
1620
+ const nested = [...ancestors, payload];
1621
+ for (const block of payload.blocks) if (record3(block)) walk2(block, depth + 1, nested);
1622
+ };
1623
+ for (const slide of document.slides) {
1624
+ if (!record3(slide)) continue;
1625
+ walk2(slide, 0, []);
1626
+ for (const [key, value] of Object.entries(slide)) if (record3(value) && /^(top|middle|bottom|left|center|right)([+:]|$)/.test(key)) walk2(value, 1, [slide]);
1627
+ }
1628
+ }
1629
+ function unusedDatasets(document) {
1630
+ const datasets = datasetsOf(document);
1631
+ const ids = Object.keys(datasets);
1632
+ if (!ids.length || !record3(document)) return [];
1633
+ const used = /* @__PURE__ */ new Set();
1634
+ visitDataPayloads(document, (payload) => {
1635
+ if (record3(payload.chart) && isDatasetRef(payload.chart.data)) used.add(payload.chart.data.dataset);
1636
+ if (record3(payload.table) && isDatasetRef(payload.table)) used.add(payload.table.dataset);
1637
+ });
1638
+ return ids.filter((id) => !used.has(id));
1639
+ }
1640
+ function isXYChartType(type) {
1641
+ return chartOptionTarget(type)?.kind === "scatter";
1642
+ }
1643
+ var VARIABLE_REFERENCE = /^var:([a-z][a-z0-9-]*)$/;
1644
+ function numberVariable(document, cell) {
1645
+ if (typeof cell !== "string") return false;
1646
+ const id = VARIABLE_REFERENCE.exec(cell)?.[1];
1647
+ if (!id || !record3(document) || !record3(document.variables)) return false;
1648
+ const variable = document.variables[id];
1649
+ return record3(variable) && variable.type === "number";
1650
+ }
1651
+ function resolveChartData(chart, document, options = {}) {
1652
+ const base = options.path ?? "";
1653
+ const diagnostics = [];
1654
+ const fail = (reason, message) => ({ ok: false, reason, message, diagnostics });
1655
+ const data = record3(chart) ? chart.data : void 0;
1656
+ if (!record3(data)) return fail("no-columns", "The chart has no data.");
1657
+ let columns2;
1658
+ let rows2;
1659
+ let columnsPath;
1660
+ let rowsPath;
1661
+ let source;
1662
+ let datasetId;
1663
+ let selected;
1664
+ if (isDatasetRef(data)) {
1665
+ datasetId = data.dataset;
1666
+ const datasets = datasetsOf(document);
1667
+ const dataset = own(datasets, datasetId) ? datasets[datasetId] : void 0;
1668
+ if (!record3(dataset) || !Array.isArray(dataset.columns) || !Array.isArray(dataset.rows)) {
1669
+ diagnostics.push({ code: "dataset-unknown", severity: "error", path: at(base, "data", "dataset"), message: `unknown dataset '${datasetId}'; add it to the top-level datasets map` });
1670
+ return fail("dataset-unknown", `The chart references dataset '${datasetId}', which the document does not hold.`);
1671
+ }
1672
+ columnsPath = at("/datasets", datasetId, "columns");
1673
+ rowsPath = at("/datasets", datasetId, "rows");
1674
+ formatDiagnostics(dataset.columns, columnsPath, diagnostics);
1675
+ const selection = selectFields(dataset.columns, data.fields, at(base, "data", "fields"), columnsPath, diagnostics);
1676
+ columns2 = selection.columns;
1677
+ selected = selection.indices;
1678
+ rows2 = dataset.rows;
1679
+ if (record3(dataset.source)) source = dataset.source;
1680
+ } else if (typeof data.src === "string") {
1681
+ diagnostics.push({ code: "chart-data-source-unresolved", severity: "warning", path: at(base, "data", "src"), message: `chart data source '${data.src}' is not loaded by any engine; the preview and export draw a placeholder. Import the data inline (columns and rows, with a 'source' for provenance) or reference a dataset` });
1682
+ return fail("data-not-inline", "The chart reads an external data source, which no engine resolves.");
1683
+ } else {
1684
+ if (!Array.isArray(data.columns)) return fail("no-columns", "The chart data has no columns.");
1685
+ columns2 = data.columns;
1686
+ rows2 = Array.isArray(data.rows) ? data.rows : [];
1687
+ columnsPath = at(base, "data", "columns");
1688
+ rowsPath = at(base, "data", "rows");
1689
+ formatDiagnostics(columns2, columnsPath, diagnostics);
1690
+ if (record3(data.source)) source = data.source;
1691
+ }
1692
+ if (!columns2.length) return fail("no-columns", "The chart data has no columns.");
1693
+ const names = columns2.map((column) => columnName(column) ?? "");
1694
+ const formats = columns2.map(columnFormat);
1695
+ const mapping = record3(chart) && record3(chart.mapping) ? chart.mapping : void 0;
1696
+ if (mapping && !selected) duplicateColumns(names, columnsPath, diagnostics);
1697
+ const mappingPath = at(base, "mapping");
1698
+ const xy = isXYChartType(record3(chart) ? chart.type : void 0);
1699
+ const known = (name, path) => {
1700
+ if (name === void 0) return void 0;
1701
+ const index = typeof name === "string" ? names.indexOf(name) : -1;
1702
+ if (index < 0) {
1703
+ diagnostics.push({ code: "chart-mapping-unknown-column", severity: "error", path, message: `mapping names column ${JSON.stringify(name)}, which the chart data does not have; use one of ${names.map((entry) => JSON.stringify(entry)).join(", ")}` });
1704
+ return void 0;
1705
+ }
1706
+ return index;
1707
+ };
1708
+ const category = known(mapping?.category, at(mappingPath, "category")) ?? 0;
1709
+ let x;
1710
+ if (xy) {
1711
+ let mapped = known(mapping?.x, at(mappingPath, "x"));
1712
+ if (mapped === category) {
1713
+ diagnostics.push({ code: "chart-mapping-adapted", severity: "warning", path: at(mappingPath, "x"), message: `the X column ${JSON.stringify(names[category])} is also the category column; the default X column is used` });
1714
+ mapped = void 0;
1715
+ }
1716
+ x = mapped ?? (names.length > 2 ? category === 1 ? 0 : 1 : void 0);
1717
+ if (x !== void 0 && (x >= names.length || x === category)) x = void 0;
1718
+ } else if (mapping?.x !== void 0) {
1719
+ diagnostics.push({ code: "chart-mapping-adapted", severity: "warning", path: at(mappingPath, "x"), message: `a '${String(record3(chart) ? chart.type : "")}' chart has no X value axis; mapping.x is ignored` });
1720
+ }
1721
+ let series;
1722
+ if (Array.isArray(mapping?.series)) {
1723
+ series = [];
1724
+ mapping.series.forEach((name, index) => {
1725
+ const path = at(mappingPath, "series", index);
1726
+ const found = known(name, path);
1727
+ if (found === void 0) return;
1728
+ if (found === category || found === x) {
1729
+ diagnostics.push({ code: "chart-mapping-adapted", severity: "warning", path, message: `series ${JSON.stringify(name)} is the ${found === category ? "category" : "X"} column; it is not plotted as a series` });
1730
+ return;
1731
+ }
1732
+ if (series.includes(found)) {
1733
+ diagnostics.push({ code: "chart-mapping-adapted", severity: "warning", path, message: `series ${JSON.stringify(name)} is listed twice; it is plotted once` });
1734
+ return;
1735
+ }
1736
+ series.push(found);
1737
+ });
1738
+ } else series = names.map((_, index) => index).filter((index) => index !== category && index !== x);
1739
+ if (x !== void 0 && !series.length) {
1740
+ diagnostics.push({ code: "chart-mapping-adapted", severity: "warning", path: at(mappingPath, mapping?.x !== void 0 ? "x" : "series"), message: `no series is left beside the X column ${JSON.stringify(names[x])}; it is plotted as the series against row numbers` });
1741
+ series = [x];
1742
+ x = void 0;
1743
+ }
1744
+ const order = [category, ...x === void 0 ? [] : [x], ...series];
1745
+ if (!rows2.length) return fail("no-rows", "The chart data has no rows.");
1746
+ const lone = names.length === 1 && !diagnostics.some((entry) => entry.code === "dataset-field-unknown");
1747
+ const out = rows2.map((row, rowIndex) => order.map((column, position) => {
1748
+ const sourceIndex = selected ? selected[column] : column;
1749
+ const cell = Array.isArray(row) && sourceIndex < row.length ? row[sourceIndex] : null;
1750
+ if (position === 0 && !lone) return cell === void 0 ? null : cell;
1751
+ const number = chartNumber(cell);
1752
+ if (number === null && cell !== null && cell !== void 0 && cell !== "" && !numberVariable(document, cell)) {
1753
+ diagnostics.push({ code: "chart-value-not-numeric", severity: "warning", path: at(rowsPath, rowIndex, sourceIndex), message: `chart value ${JSON.stringify(cell)} is not a number; it is plotted as a gap. Write numbers as plain decimals (12, -3.5, 1e6) and put units, currency and percent in the column format` });
1754
+ }
1755
+ return number;
1756
+ }));
1757
+ return {
1758
+ ok: true,
1759
+ columns: order.map((index) => names[index]),
1760
+ hasX: x !== void 0,
1761
+ formats: order.map((index) => formats[index]),
1762
+ rows: out,
1763
+ ...source ? { source } : {},
1764
+ ...datasetId !== void 0 ? { dataset: datasetId } : {},
1765
+ diagnostics
1766
+ };
1767
+ }
1768
+ var DISPLAY_NUMBER = /^(-?)([$€£¥]?)((?:[1-9]\d{0,2}(?:,\d{3})+)|0|[1-9]\d*)(?:\.(\d+))?(%?)$/;
1769
+ function chartNumberFixes(chart, document, options) {
1770
+ const fixes = /* @__PURE__ */ new Map();
1771
+ const resolved = resolveChartData(chart, document, options);
1772
+ if (!resolved.ok) return fixes;
1773
+ const candidates = /* @__PURE__ */ new Set();
1774
+ for (const diagnostic of resolved.diagnostics) {
1775
+ if (diagnostic.code !== "chart-value-not-numeric") continue;
1776
+ candidates.add(Number(diagnostic.path.slice(diagnostic.path.lastIndexOf("/") + 1)));
1777
+ }
1778
+ if (!candidates.size) return fixes;
1779
+ const data = chart.data;
1780
+ let columns2;
1781
+ let rows2;
1782
+ let columnsPath;
1783
+ let rowsPath;
1784
+ if (isDatasetRef(data)) {
1785
+ const dataset = datasetsOf(document)[data.dataset];
1786
+ columns2 = dataset.columns;
1787
+ rows2 = dataset.rows;
1788
+ columnsPath = at("/datasets", data.dataset, "columns");
1789
+ rowsPath = at("/datasets", data.dataset, "rows");
1790
+ } else {
1791
+ columns2 = data.columns;
1792
+ rows2 = data.rows;
1793
+ columnsPath = at(options.path ?? "", "data", "columns");
1794
+ rowsPath = at(options.path ?? "", "data", "rows");
1795
+ }
1796
+ for (const column of [...candidates].sort((a, b) => a - b)) {
1797
+ const header = columns2[column];
1798
+ const name = columnName(header);
1799
+ if (name === void 0 || columnFormat(header) !== void 0) continue;
1800
+ const fix = columnFix(rows2, column, rowsPath);
1801
+ if (!fix) continue;
1802
+ fixes.set(column, {
1803
+ patches: [...fix.patches, { op: "replace", path: at(columnsPath, column), value: record3(header) ? { ...header, format: fix.format } : { name, format: fix.format } }],
1804
+ column,
1805
+ name,
1806
+ format: fix.format
1807
+ });
1808
+ }
1809
+ return fixes;
1810
+ }
1811
+ function columnFix(rows2, column, rowsPath) {
1812
+ const cells = [];
1813
+ let prefix;
1814
+ let suffix;
1815
+ let grouping = false;
1816
+ let wide = false;
1817
+ const decimals = [];
1818
+ for (const [index, row] of rows2.entries()) {
1819
+ const cell = Array.isArray(row) && column < row.length ? row[column] : null;
1820
+ if (cell === null || cell === void 0 || cell === "") continue;
1821
+ const patch = chartNumber(cell) === null;
1822
+ if (typeof cell !== "number" && typeof cell !== "string") return void 0;
1823
+ const text2 = typeof cell === "number" ? String(cell) : cell.trim();
1824
+ const match = DISPLAY_NUMBER.exec(text2);
1825
+ if (!match) return void 0;
1826
+ const [, minus = "", currency = "", integer = "", fraction = "", percent = ""] = match;
1827
+ if ((prefix ?? currency) !== currency || (suffix ?? percent) !== percent) return void 0;
1828
+ prefix = currency;
1829
+ suffix = percent;
1830
+ if (integer.includes(",")) grouping = true;
1831
+ else if (integer.length > 3) wide = true;
1832
+ decimals.push(fraction);
1833
+ const digits = `${minus}${integer.replaceAll(",", "")}${fraction ? `.${fraction}` : ""}`;
1834
+ cells.push({ row: index, text: text2, value: Number(percent ? `${digits}e-2` : digits), patch });
1835
+ }
1836
+ if (!cells.some((cell) => cell.patch) || grouping && wide) return void 0;
1837
+ const fewest = Math.min(...decimals.map((fraction) => fraction.length));
1838
+ const most = Math.max(...decimals.map((fraction) => fraction.length));
1839
+ if (decimals.some((fraction) => fraction.length > fewest && fraction.endsWith("0"))) return void 0;
1840
+ const format = `${prefix ?? ""}${grouping ? "#,##0" : "0"}${most ? `.${"0".repeat(fewest)}${"#".repeat(most - fewest)}` : ""}${suffix ?? ""}`;
1841
+ if (cells.some((cell) => !Number.isFinite(cell.value) || formatDataNumber(cell.value, format) !== cell.text)) return void 0;
1842
+ return { format, patches: cells.filter((cell) => cell.patch).map((cell) => ({ op: "replace", path: at(rowsPath, cell.row, column), value: cell.value })) };
1843
+ }
1844
+ function suggestChartNumberFix(chart, document, options = {}) {
1845
+ const fixes = chartNumberFixes(chart, document, options);
1846
+ if (options.column === void 0) return fixes.values().next().value;
1847
+ for (const fix of fixes.values()) if (fix.column === options.column || fix.name === options.column) return fix;
1848
+ return void 0;
1849
+ }
1850
+ function chartNumberFixesByCell(chart, document, options) {
1851
+ const byCell = /* @__PURE__ */ new Map();
1852
+ for (const fix of chartNumberFixes(chart, document, options).values()) for (const patch of fix.patches.slice(0, -1)) byCell.set(patch.path, fix);
1853
+ return byCell;
1854
+ }
1855
+ function cellFormat(cell) {
1856
+ return record3(cell) && own(cell, "value") && typeof cell.format === "string" ? cell.format : void 0;
1857
+ }
1858
+ function resolveTableData(table, document, options = {}) {
1859
+ const base = options.path ?? "";
1860
+ const diagnostics = [];
1861
+ if (!record3(table)) return { rows: [], formats: [], diagnostics };
1862
+ if (isDatasetRef(table)) {
1863
+ const id = table.dataset;
1864
+ const datasets = datasetsOf(document);
1865
+ const dataset = own(datasets, id) ? datasets[id] : void 0;
1866
+ if (!record3(dataset) || !Array.isArray(dataset.columns) || !Array.isArray(dataset.rows)) {
1867
+ diagnostics.push({ code: "dataset-unknown", severity: "error", path: at(base, "dataset"), message: `unknown dataset '${id}'; add it to the top-level datasets map` });
1868
+ return { rows: [], formats: [], dataset: id, diagnostics };
1869
+ }
1870
+ const columnsPath = at("/datasets", id, "columns");
1871
+ formatDiagnostics(dataset.columns, columnsPath, diagnostics);
1872
+ const selection = selectFields(dataset.columns, table.fields, at(base, "fields"), columnsPath, diagnostics);
1873
+ const rows3 = dataset.rows.map((row) => selection.indices.map((index) => Array.isArray(row) && index < row.length ? row[index] : null));
1874
+ return { columns: selection.columns, rows: rows3, formats: selection.formats, dataset: id, diagnostics };
1875
+ }
1876
+ const columns2 = Array.isArray(table.columns) ? table.columns : void 0;
1877
+ const rows2 = Array.isArray(table.rows) ? table.rows.filter(Array.isArray) : [];
1878
+ const width = Math.max(columns2?.length ?? 0, ...rows2.map((row) => row.length), 0);
1879
+ const formats = Array.from({ length: width }, (_, index) => {
1880
+ const header = columns2?.[index];
1881
+ return columnFormat(header) ?? cellFormat(header);
1882
+ });
1883
+ const check = (cell, path) => {
1884
+ if (!record3(cell) || cell.format === void 0) return;
1885
+ const error = numberFormatError(cell.format);
1886
+ if (error) diagnostics.push({ code: "number-format-invalid", severity: "error", path: at(path, "format"), message: error });
1887
+ };
1888
+ columns2?.forEach((header, index) => {
1889
+ check(header, at(base, "columns", index));
1890
+ });
1891
+ if (Array.isArray(table.rows)) table.rows.forEach((row, rowIndex) => {
1892
+ if (Array.isArray(row)) row.forEach((cell, index) => {
1893
+ check(cell, at(base, "rows", rowIndex, index));
1894
+ });
1895
+ });
1896
+ return { ...columns2 ? { columns: columns2 } : {}, rows: rows2, formats, diagnostics };
1897
+ }
1898
+ function tableCellDisplayValue(cell, columnFormat2) {
1899
+ if (record3(cell) && own(cell, "value")) {
1900
+ const styled = cell;
1901
+ const format = typeof styled.format === "string" ? styled.format : columnFormat2;
1902
+ if (typeof styled.value !== "number" || format === void 0 || numberFormatError(format)) return cell;
1903
+ return { ...styled, value: formatDataNumber(styled.value, format) };
1904
+ }
1905
+ if (typeof cell !== "number" || columnFormat2 === void 0 || numberFormatError(columnFormat2)) return cell;
1906
+ return formatDataNumber(cell, columnFormat2);
1907
+ }
1908
+
1909
+ // src/image-aspect.ts
1910
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1911
+ function sourceOf(value) {
1912
+ if (typeof value === "string") return value;
1913
+ return isRecord2(value) && typeof value.src === "string" ? value.src : void 0;
1914
+ }
1915
+ var PREFIX_BYTES = 64 * 1024;
1916
+ var JPEG_PREFIX_BYTES = 1024 * 1024;
1917
+ function dataBytes(uri, limit) {
1918
+ const match = /^data:([^;,]*)((?:;[^;,]*)*),/i.exec(uri);
1919
+ if (!match) return void 0;
1920
+ const type = (match[1] ?? "").toLowerCase(), start = match[0].length;
1921
+ try {
1922
+ if (/;base64/i.test(match[2] ?? "")) {
1923
+ const chars = Math.ceil(limit / 3) * 4, text2 = uri.slice(start, start + chars + chars / 8).replace(/\s+/g, "").slice(0, chars);
1924
+ return { type, bytes: Uint8Array.from(atob(text2.slice(0, text2.length - text2.length % 4)), (char) => char.charCodeAt(0)) };
1925
+ }
1926
+ const prefix = uri.slice(start, start + limit).replace(/%[0-9a-f]?$/i, "");
1927
+ return { type, bytes: new TextEncoder().encode(decodeURIComponent(prefix)) };
1928
+ } catch {
1929
+ return void 0;
1930
+ }
1931
+ }
1932
+ var at2 = (b, i) => b[i] ?? 0;
1933
+ var u16be = (b, i) => at2(b, i) << 8 | at2(b, i + 1);
1934
+ var u32be = (b, i) => (at2(b, i) << 24 | at2(b, i + 1) << 16 | at2(b, i + 2) << 8 | at2(b, i + 3)) >>> 0;
1935
+ var u16le = (b, i) => at2(b, i) | at2(b, i + 1) << 8;
1936
+ var u24le = (b, i) => at2(b, i) | at2(b, i + 1) << 8 | at2(b, i + 2) << 16;
1937
+ var ascii = (b, i, n) => String.fromCharCode(...b.subarray(i, i + n));
1938
+ function rasterDimensions(b) {
1939
+ if (b.length >= 24 && u32be(b, 0) === 2303741511 && ascii(b, 12, 4) === "IHDR") return { width: u32be(b, 16), height: u32be(b, 20) };
1940
+ if (b.length >= 10 && (ascii(b, 0, 6) === "GIF87a" || ascii(b, 0, 6) === "GIF89a")) return { width: u16le(b, 6), height: u16le(b, 8) };
1941
+ if (b.length >= 4 && b[0] === 255 && b[1] === 216) {
1942
+ let i = 2;
1943
+ while (i + 9 < b.length) {
1944
+ if (at2(b, i) !== 255) {
1945
+ i++;
1946
+ continue;
1947
+ }
1948
+ const marker = at2(b, i + 1);
1949
+ if (marker === 255) {
1950
+ i++;
1951
+ continue;
1952
+ }
1953
+ if (marker === 216 || marker === 1 || marker >= 208 && marker <= 215) {
1954
+ i += 2;
1955
+ continue;
1956
+ }
1957
+ if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return { height: u16be(b, i + 5), width: u16be(b, i + 7) };
1958
+ i += 2 + u16be(b, i + 2);
1959
+ }
1960
+ return void 0;
1961
+ }
1962
+ if (b.length >= 30 && ascii(b, 0, 4) === "RIFF" && ascii(b, 8, 4) === "WEBP") {
1963
+ const chunk = ascii(b, 12, 4);
1964
+ if (chunk === "VP8X") return { width: u24le(b, 24) + 1, height: u24le(b, 27) + 1 };
1965
+ if (chunk === "VP8L" && at2(b, 20) === 47) {
1966
+ const bits = u16le(b, 21) | u16le(b, 23) << 16;
1967
+ return { width: (bits & 16383) + 1, height: (bits >>> 14 & 16383) + 1 };
1968
+ }
1969
+ if (chunk === "VP8 ") return { width: u16le(b, 26) & 16383, height: u16le(b, 28) & 16383 };
1970
+ }
1971
+ return void 0;
1972
+ }
1973
+ function svgDimensions(text2) {
1974
+ const root = /<svg\b([^>]*)>/i.exec(text2)?.[1];
1975
+ if (root === void 0) return void 0;
1976
+ const attribute = (name) => new RegExp(`\\s${name}\\s*=\\s*["']([^"']*)["']`, "i").exec(root)?.[1];
1977
+ const length = (value) => {
1978
+ const match = value && /^\s*([0-9]*\.?[0-9]+)\s*(px|pt|in|cm|mm|em)?\s*$/i.exec(value);
1979
+ return match ? Number(match[1]) : void 0;
1980
+ };
1981
+ const width = length(attribute("width")), height = length(attribute("height"));
1982
+ if (width && height) return { width, height };
1983
+ const box = attribute("viewBox")?.trim().split(/[\s,]+/).map(Number);
1984
+ if (box && box.length === 4 && box.every(Number.isFinite)) {
1985
+ const [, , width2 = 0, height2 = 0] = box;
1986
+ if (width2 > 0 && height2 > 0) return { width: width2, height: height2 };
1987
+ }
1988
+ return void 0;
1989
+ }
1990
+ var cache = /* @__PURE__ */ new Map();
1991
+ var CACHE_ENTRIES = 64;
1992
+ function readDimensions(source) {
1993
+ const first = dataBytes(source, PREFIX_BYTES);
1994
+ if (!first) return void 0;
1995
+ let dimensions;
1996
+ if (first.type.startsWith("image/svg")) dimensions = svgDimensions(new TextDecoder().decode(first.bytes));
1997
+ else {
1998
+ dimensions = rasterDimensions(first.bytes);
1999
+ if (!dimensions && first.bytes.length >= 2 && first.bytes[0] === 255 && first.bytes[1] === 216) {
2000
+ const more = dataBytes(source, JPEG_PREFIX_BYTES);
2001
+ if (more) dimensions = rasterDimensions(more.bytes);
2002
+ }
2003
+ }
2004
+ return dimensions && dimensions.width > 0 && dimensions.height > 0 ? dimensions : void 0;
2005
+ }
2006
+ function intrinsicImageSize(value, assets) {
2007
+ let source = sourceOf(value);
2008
+ for (let hop = 0; source?.startsWith("asset:") && hop < 8; hop++) source = sourceOf(isRecord2(assets) ? assets[source.slice(6)] : void 0);
2009
+ if (!source?.startsWith("data:")) return void 0;
2010
+ if (!cache.has(source)) {
2011
+ if (cache.size >= CACHE_ENTRIES) cache.delete(cache.keys().next().value);
2012
+ cache.set(source, readDimensions(source));
2013
+ }
2014
+ const size = cache.get(source);
2015
+ return size ? { ...size, svg: /^data:image\/svg/i.test(source) } : void 0;
2016
+ }
2017
+ function intrinsicImageAspect(value, assets) {
2018
+ const size = intrinsicImageSize(value, assets);
2019
+ return size ? size.width / size.height : void 0;
2020
+ }
2021
+
2022
+ // src/reading-order.ts
2023
+ function visualReadingOrder(items, direction = "ltr") {
2024
+ const index = new Map(items.map((item, i) => [item, i]));
2025
+ const along = direction === "rtl" ? (item) => -(item.box.x + item.box.width) : (item) => item.box.x;
2026
+ return readingRows(items).flatMap((row) => [...row].sort((a, b) => along(a) - along(b) || a.box.y - b.box.y || index.get(a) - index.get(b)));
2027
+ }
2028
+ function readingRows(items) {
2029
+ const index = new Map(items.map((item, i) => [item, i]));
2030
+ const sorted = [...items].sort((a, b) => a.box.y - b.box.y || a.box.x - b.box.x || index.get(a) - index.get(b));
2031
+ const rows2 = [];
2032
+ for (const item of sorted) {
2033
+ const centre = item.box.y + item.box.height / 2;
2034
+ const row = rows2.find((candidate) => centre >= candidate.top && centre <= candidate.bottom);
2035
+ if (row) {
2036
+ row.items.push(item);
2037
+ row.top = Math.min(row.top, item.box.y);
2038
+ row.bottom = Math.max(row.bottom, item.box.y + item.box.height);
2039
+ } else rows2.push({ top: item.box.y, bottom: item.box.y + item.box.height, items: [item] });
2040
+ }
2041
+ rows2.sort((a, b) => a.top - b.top);
2042
+ return rows2.map((row) => row.items.sort((a, b) => index.get(a) - index.get(b)));
2043
+ }
2044
+
2045
+ // src/direction.ts
2046
+ var strongRtl = /[\u200F\u061C]|(?=\p{L})[\p{Script=Arabic}\p{Script=Hebrew}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Nko}\p{Script=Adlam}\p{Script=Hanifi_Rohingya}\p{Script=Mandaic}\p{Script=Samaritan}\p{Script=Mende_Kikakui}\p{Script=Imperial_Aramaic}\p{Script=Phoenician}\p{Script=Kharoshthi}\p{Script=Old_South_Arabian}\p{Script=Old_North_Arabian}\p{Script=Avestan}\p{Script=Inscriptional_Parthian}\p{Script=Inscriptional_Pahlavi}\p{Script=Psalter_Pahlavi}\p{Script=Old_Turkic}\p{Script=Old_Hungarian}\p{Script=Nabataean}\p{Script=Palmyrene}\p{Script=Hatran}\p{Script=Manichaean}\p{Script=Sogdian}\p{Script=Old_Sogdian}\p{Script=Elymaic}\p{Script=Chorasmian}\p{Script=Yezidi}\p{Script=Cypriot}\p{Script=Lydian}\p{Script=Meroitic_Cursive}\p{Script=Meroitic_Hieroglyphs}\u{10F70}-\u{10FAF}\u{10D40}-\u{10D8F}]/u;
2047
+ var strongLtr = /[\u200E\p{L}\p{Nl}]/u;
2048
+ function paragraphDirection(text2, deckDirection) {
2049
+ if (deckDirection !== "rtl") return "ltr";
2050
+ let isolates = 0;
2051
+ for (const char of String(text2 ?? "")) {
2052
+ if (char === "\u2066" || char === "\u2067" || char === "\u2068") isolates += 1;
2053
+ else if (char === "\u2069") isolates = Math.max(0, isolates - 1);
2054
+ else if (isolates === 0) {
2055
+ if (strongRtl.test(char)) return "rtl";
2056
+ if (strongLtr.test(char)) return "ltr";
2057
+ }
2058
+ }
2059
+ return "rtl";
2060
+ }
2061
+ function physicalAlignment(alignment, direction) {
2062
+ const value = alignment ?? "left";
2063
+ if (direction !== "rtl" || value === "center") return value;
2064
+ return value === "left" ? "right" : "left";
2065
+ }
2066
+ function paragraphDirectionAt(text2, deckDirection) {
2067
+ if (deckDirection !== "rtl") return () => "ltr";
2068
+ const source = String(text2 ?? ""), starts = [], directions = [];
2069
+ let start = 0;
2070
+ const close = (end) => {
2071
+ starts.push(start);
2072
+ directions.push(paragraphDirection(source.slice(start, end), "rtl"));
2073
+ };
2074
+ for (const match of source.matchAll(/\r\n|\r|\n/g)) {
2075
+ close(match.index);
2076
+ start = match.index + match[0].length;
2077
+ }
2078
+ close(source.length);
2079
+ return (offset) => {
2080
+ for (let index = starts.length - 1; index >= 0; index--) if (offset >= starts[index]) return directions[index];
2081
+ return directions[0];
2082
+ };
2083
+ }
2084
+
2085
+ // src/script-fonts.ts
2086
+ var eastAsianScripts = /* @__PURE__ */ new Set(["Hani", "Hans", "Hant", "Hanb", "Jpan", "Kore", "Hang", "Jamo", "Hira", "Kana", "Hrkt", "Bopo", "Yiii"]);
2087
+ var complexScripts = /* @__PURE__ */ new Set([
2088
+ "Arab",
2089
+ "Hebr",
2090
+ "Syrc",
2091
+ "Thaa",
2092
+ "Nkoo",
2093
+ "Adlm",
2094
+ "Rohg",
2095
+ "Mand",
2096
+ "Samr",
2097
+ "Deva",
2098
+ "Beng",
2099
+ "Guru",
2100
+ "Gujr",
2101
+ "Orya",
2102
+ "Taml",
2103
+ "Telu",
2104
+ "Knda",
2105
+ "Mlym",
2106
+ "Sinh",
2107
+ "Thai",
2108
+ "Laoo",
2109
+ "Tibt",
2110
+ "Mymr",
2111
+ "Khmr",
2112
+ "Mong",
2113
+ "Bali",
2114
+ "Java",
2115
+ "Lana",
2116
+ "Tale",
2117
+ "Talu",
2118
+ "Cakm",
2119
+ "Olck"
2120
+ ]);
2121
+ var rtlScripts = /* @__PURE__ */ new Set(["Arab", "Hebr", "Syrc", "Thaa", "Nkoo", "Adlm", "Rohg", "Mand", "Samr"]);
2122
+ var latinSlotScripts = /* @__PURE__ */ new Set(["Latn", "Cyrl", "Grek", "Zyyy", "Zzzz"]);
2123
+ var deprecatedLanguages = { iw: "he", in: "id", ji: "yi" };
2124
+ var matchingLanguages = { ...deprecatedLanguages, no: "nb", zsm: "ms", ku: "kmr" };
2125
+ var likelyScripts = {
2126
+ zh: { script: "Hans", regions: { TW: "Hant", HK: "Hant", MO: "Hant" } },
2127
+ sr: { script: "Cyrl", regions: { ME: "Latn" } },
2128
+ pa: { script: "Guru", regions: { PK: "Arab" } },
2129
+ az: { script: "Latn", regions: { IR: "Arab" } },
2130
+ uz: { script: "Latn", regions: { AF: "Arab" } },
2131
+ bs: { script: "Latn" },
2132
+ mn: { script: "Cyrl", regions: { CN: "Mong" } },
2133
+ ms: { script: "Latn" }
1719
2134
  };
2135
+ var bareLanguageId = /^[a-z][a-z0-9-]*$/;
2136
+ function scriptFontRole(script) {
2137
+ const code = normalizeScript(script);
2138
+ if (code && eastAsianScripts.has(code)) return "eastAsian";
2139
+ if (code && complexScripts.has(code)) return "complexScript";
2140
+ return "latin";
2141
+ }
2142
+ function normalizeScript(value) {
2143
+ if (typeof value !== "string" || !/^[A-Za-z]{4}$/.test(value)) return void 0;
2144
+ return value.slice(0, 1).toUpperCase() + value.slice(1).toLowerCase();
2145
+ }
2146
+ function text(value) {
2147
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
2148
+ }
2149
+ var tagSyntax = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/;
2150
+ function splitTag(tag) {
2151
+ if (!tagSyntax.test(tag)) return void 0;
2152
+ const [first = "", ...rest] = tag.split("-");
2153
+ const language = first.toLowerCase();
2154
+ if (language === "und") return void 0;
2155
+ let script;
2156
+ let region;
2157
+ for (const subtag of rest) {
2158
+ if (subtag.length === 1) break;
2159
+ if (!script && !region && /^[A-Za-z]{4}$/.test(subtag)) script = normalizeScript(subtag);
2160
+ else if (!region && /^(?:[A-Za-z]{2}|\d{3})$/.test(subtag)) region = subtag.toUpperCase();
2161
+ }
2162
+ return { language, script, region };
2163
+ }
2164
+ function canonicalTag(tag) {
2165
+ let extension = false;
2166
+ return tag.split("-").map((subtag, index) => {
2167
+ const lower = subtag.toLowerCase();
2168
+ if (index === 0) return deprecatedLanguages[lower] ?? lower;
2169
+ if (extension) return lower;
2170
+ if (subtag.length === 1) {
2171
+ extension = true;
2172
+ return lower;
2173
+ }
2174
+ if (/^[A-Za-z]{4}$/.test(subtag)) return normalizeScript(subtag);
2175
+ if (/^[A-Za-z]{2}$/.test(subtag)) return subtag.toUpperCase();
2176
+ return lower;
2177
+ }).join("-");
2178
+ }
2179
+ function vendoredScript(language, region) {
2180
+ const entry = likelyScripts[language];
2181
+ return entry ? (region && entry.regions?.[region]) ?? entry.script : void 0;
2182
+ }
2183
+ function inferScript(tag) {
2184
+ const parts = splitTag(tag);
2185
+ if (!parts) return void 0;
2186
+ const known = parts.script ?? vendoredScript(matchingLanguages[parts.language] ?? parts.language, parts.region);
2187
+ if (known) return known;
2188
+ try {
2189
+ return normalizeScript(new Intl.Locale(tag).maximize().script);
2190
+ } catch {
2191
+ return void 0;
2192
+ }
2193
+ }
2194
+ function inlineRecords(document, kind2) {
2195
+ const kinds = isRecord(document.catalogs) ? document.catalogs : {};
2196
+ const entry = kinds[kind2];
2197
+ return isRecord(entry) && Array.isArray(entry.records) ? entry.records.filter(isRecord) : [];
2198
+ }
2199
+ function allRecords(document, kind2) {
2200
+ return [...inlineRecords(document, kind2), ...catalogs[kind2]];
2201
+ }
2202
+ function resolveReference(lookup, kind2, reference) {
2203
+ if (typeof reference === "string") return lookup(kind2, reference);
2204
+ if (!isRecord(reference)) return void 0;
2205
+ const base = typeof reference.id === "string" ? lookup(kind2, reference.id) : void 0;
2206
+ return { ...base, ...reference };
2207
+ }
2208
+ function matchLanguageTag(document, tag) {
2209
+ const wanted = splitTag(tag);
2210
+ if (!wanted) return void 0;
2211
+ const records = allRecords(document, "languages").filter((record6) => typeof record6.bcp47 === "string");
2212
+ const key = canonicalTag(tag).toLowerCase();
2213
+ const exact = records.find((record6) => canonicalTag(record6.bcp47).toLowerCase() === key);
2214
+ if (exact) return exact;
2215
+ const language = matchingLanguages[wanted.language] ?? wanted.language;
2216
+ const script = wanted.script ?? vendoredScript(language, wanted.region);
2217
+ const candidates = records.flatMap((record6) => {
2218
+ const parts = splitTag(record6.bcp47);
2219
+ if (!parts || (matchingLanguages[parts.language] ?? parts.language) !== language) return [];
2220
+ const recordScript = normalizeScript(record6.script) ?? parts.script ?? vendoredScript(language, parts.region);
2221
+ if (script && recordScript && script !== recordScript) return [];
2222
+ return [{ record: record6, region: parts.region }];
2223
+ });
2224
+ return candidates.find((candidate) => wanted.region && candidate.region === wanted.region)?.record ?? candidates.find((candidate) => !candidate.region)?.record ?? candidates[0]?.record;
2225
+ }
2226
+ function resolveLanguage(document, lookup, reference) {
2227
+ if (typeof reference === "string") {
2228
+ const value = reference.trim();
2229
+ const byId = bareLanguageId.test(value) ? lookup("languages", value) : void 0;
2230
+ if (byId) return { record: byId, languageId: value };
2231
+ if (/^[a-z][a-z0-9+.-]*:/i.test(value) || !splitTag(value)) return void 0;
2232
+ const matched = matchLanguageTag(document, value);
2233
+ if (matched) return { record: matched, authoredTag: value, languageId: text(matched.id) };
2234
+ return inferScript(value) ? { record: {}, authoredTag: value } : void 0;
2235
+ }
2236
+ if (!isRecord(reference)) return void 0;
2237
+ const authoredTag = text(reference.bcp47);
2238
+ if (authoredTag !== void 0 && !splitTag(authoredTag)) return void 0;
2239
+ const base = typeof reference.id === "string" ? lookup("languages", reference.id) : authoredTag ? matchLanguageTag(document, authoredTag) : void 0;
2240
+ const record6 = { ...base, ...reference };
2241
+ const tag = text(record6.bcp47);
2242
+ if (!tag || !splitTag(tag)) return void 0;
2243
+ return { record: record6, authoredTag, authoredOoxmlLang: text(reference.ooxmlLang), languageId: text(base?.id) };
2244
+ }
2245
+ function pairFamilies(value) {
2246
+ if (!isRecord(value)) return void 0;
2247
+ const major = text(value.major);
2248
+ const minor = text(value.minor);
2249
+ const heading = major ?? minor;
2250
+ const body = minor ?? major;
2251
+ return heading && body ? { heading, body } : void 0;
2252
+ }
2253
+ function schemeServesLanguage(scheme, language) {
2254
+ const entries = Array.isArray(scheme.languages) ? scheme.languages.filter((entry) => typeof entry === "string") : [];
2255
+ if (entries.length === 0) return true;
2256
+ const name = text(language.name)?.toLowerCase();
2257
+ if (!name) return false;
2258
+ const base = name.replace(/\s*\([^)]*\)\s*$/, "");
2259
+ return entries.some((entry) => {
2260
+ const value = entry.trim().toLowerCase();
2261
+ return value === name || value === base;
2262
+ });
2263
+ }
2264
+ function resolveScriptFonts(input, options = {}) {
2265
+ const document = isRecord(input) ? input : {};
2266
+ const lookup = (kind2, id) => inlineRecords(document, kind2).find((record7) => record7.id === id) ?? catalogs[kind2].find((record7) => record7.id === id);
2267
+ let design = isRecord(document.design) ? document.design : {};
2268
+ if (options.slideIndex !== void 0) {
2269
+ const slides = Array.isArray(document.slides) ? document.slides : [];
2270
+ if (!Number.isInteger(options.slideIndex) || options.slideIndex < 0 || options.slideIndex >= slides.length) {
2271
+ throw new RangeError(`slideIndex must be an integer between 0 and ${slides.length - 1}.`);
2272
+ }
2273
+ const slide = slides[options.slideIndex];
2274
+ if (isRecord(slide) && isRecord(slide.design)) design = { ...design, ...slide.design };
2275
+ }
2276
+ const theme = resolveReference(lookup, "themes", design.theme ?? "minimal") ?? {};
2277
+ const scheme = resolveFontSchemeReference(design.fontScheme ?? theme.fontScheme ?? DEFAULT_FONT_SCHEME, (id) => lookup("fontSchemes", id)).scheme;
2278
+ const latin = resolveFontFamilies(scheme);
2279
+ const fromOption = options.language !== void 0 ? resolveLanguage(document, lookup, options.language) : void 0;
2280
+ const fromDocument = fromOption ? void 0 : resolveLanguage(document, lookup, document.language);
2281
+ const requestedDefault = text(options.defaultLanguage);
2282
+ const fallbackTag = requestedDefault && splitTag(requestedDefault) ? requestedDefault : "en-US";
2283
+ const language = fromOption ?? fromDocument ?? resolveLanguage(document, lookup, fallbackTag) ?? { record: {}, authoredTag: fallbackTag };
2284
+ const languageSource = fromOption ? "option" : fromDocument ? "document" : "default";
2285
+ const record6 = language.record;
2286
+ const bcp47 = canonicalTag(language.authoredTag ?? text(record6.bcp47) ?? fallbackTag);
2287
+ const curated = text(record6.ooxmlLang);
2288
+ const lang = language.authoredOoxmlLang ? canonicalTag(language.authoredOoxmlLang) : language.authoredTag && splitTag(language.authoredTag)?.region ? canonicalTag(language.authoredTag) : curated ? canonicalTag(curated) : bcp47;
2289
+ const parts = splitTag(bcp47);
2290
+ const script = normalizeScript(record6.script) ?? (language.languageId ? parts?.script ?? (parts ? vendoredScript(matchingLanguages[parts.language] ?? parts.language, parts.region) : void 0) ?? normalizeScript(matchLanguageTag({}, bcp47)?.script) : inferScript(bcp47)) ?? "Zzzz";
2291
+ const scriptRole = scriptFontRole(script);
2292
+ const declaredDirection = record6.direction;
2293
+ const direction = declaredDirection === "rtl" || declaredDirection === "ltr" ? declaredDirection : rtlScripts.has(script) ? "rtl" : "ltr";
2294
+ const app = options.app ?? "PowerPoint";
2295
+ const preferred = app === "Google Slides" ? record6.googleFontScheme : record6.fontScheme;
2296
+ const alternate = app === "Google Slides" ? record6.fontScheme : record6.googleFontScheme;
2297
+ const languageScheme = resolveReference(lookup, "fontSchemes", preferred) ?? resolveReference(lookup, "fontSchemes", alternate);
2298
+ const languageFamilies = pairFamilies(languageScheme);
2299
+ const schemeAdmitsLanguage = schemeServesLanguage(scheme, record6);
2300
+ const slot = (role, family) => {
2301
+ const explicit = pairFamilies(scheme[role]);
2302
+ if (explicit) return { ...explicit, source: "fontScheme" };
2303
+ if (scheme.languageFamily === family && schemeAdmitsLanguage) return { ...latin, source: "schemeFamily" };
2304
+ if (scriptRole === role && languageFamilies) return { ...languageFamilies, source: "language" };
2305
+ return { heading: latin.heading, body: latin.body, source: "latin" };
2306
+ };
2307
+ const eastAsian = slot("eastAsian", "ea");
2308
+ const complexScript = slot("complexScript", "cs");
2309
+ const heading = { latin: latin.heading, eastAsian: eastAsian.heading, complexScript: complexScript.heading };
2310
+ const body = { latin: latin.body, eastAsian: eastAsian.body, complexScript: complexScript.body };
2311
+ let supplement;
2312
+ if (!latinSlotScripts.has(script)) {
2313
+ const chosen = complexScript.source === "fontScheme" || complexScript.source === "schemeFamily" ? complexScript : void 0;
2314
+ const families = scriptRole === "latin" ? chosen ? { heading: chosen.heading, body: chosen.body } : languageFamilies : (scriptRole === "eastAsian" ? eastAsian : complexScript).source === "latin" ? void 0 : { heading: heading[scriptRole], body: body[scriptRole] };
2315
+ if (families) supplement = { script: script === "Kore" ? "Hang" : script, heading: families.heading, body: families.body };
2316
+ }
2317
+ return {
2318
+ ...body,
2319
+ lang,
2320
+ bcp47,
2321
+ ...language.languageId ? { languageId: language.languageId } : {},
2322
+ languageSource,
2323
+ script,
2324
+ scriptRole,
2325
+ direction,
2326
+ rtl: direction === "rtl",
2327
+ heading,
2328
+ body,
2329
+ ...supplement ? { supplement } : {},
2330
+ sources: { eastAsian: eastAsian.source, complexScript: complexScript.source }
2331
+ };
2332
+ }
2333
+ function resolveSlideDirection(presentation, slideIndex) {
2334
+ if (!isRecord(presentation) || presentation.language === void 0) return "ltr";
2335
+ try {
2336
+ return resolveScriptFonts(presentation, Number.isInteger(slideIndex) ? { slideIndex } : {}).direction;
2337
+ } catch {
2338
+ try {
2339
+ return resolveScriptFonts(presentation).direction;
2340
+ } catch {
2341
+ return "ltr";
2342
+ }
2343
+ }
2344
+ }
2345
+
2346
+ // src/numbering.ts
2347
+ var NUMBERING_STYLES = ["arabic", "roman-upper", "roman-lower", "alpha-upper", "alpha-lower"];
2348
+ var NUMBERING_SUFFIXES = ["period", "paren", "paren-both"];
2349
+ var MAX_NUMBERING_VALUE = 32767;
2350
+ var MAX_ROMAN_VALUE = 3999;
2351
+ var MAX_NUMBERING_LEVELS = 9;
2352
+ var styleSet = new Set(NUMBERING_STYLES);
2353
+ var suffixSet = new Set(NUMBERING_SUFFIXES);
2354
+ var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2355
+ function resolveEntry(entry, label) {
2356
+ if (typeof entry === "string") {
2357
+ if (!styleSet.has(entry)) throw new RangeError(`${label} must be one of ${NUMBERING_STYLES.join(", ")}.`);
2358
+ return { style: entry, start: 1, suffix: "period" };
2359
+ }
2360
+ if (!isObject(entry)) throw new TypeError(`${label} must be a style name or a numbering object.`);
2361
+ const { style = "arabic", start = 1, suffix = "period" } = entry;
2362
+ if (typeof style !== "string" || !styleSet.has(style)) throw new RangeError(`${label}.style must be one of ${NUMBERING_STYLES.join(", ")}.`);
2363
+ if (typeof suffix !== "string" || !suffixSet.has(suffix)) throw new RangeError(`${label}.suffix must be one of ${NUMBERING_SUFFIXES.join(", ")}.`);
2364
+ if (typeof start !== "number" || !Number.isInteger(start) || start < 1 || start > MAX_NUMBERING_VALUE) throw new RangeError(`${label}.start must be an integer from 1 to ${MAX_NUMBERING_VALUE}.`);
2365
+ return { style, start, suffix };
2366
+ }
2367
+ function resolveNumbering(input) {
2368
+ if (Array.isArray(input)) {
2369
+ if (!input.length || input.length > MAX_NUMBERING_LEVELS) throw new RangeError(`numbering must list from 1 to ${MAX_NUMBERING_LEVELS} levels.`);
2370
+ return input.map((entry, index) => resolveEntry(entry, `numbering[${index}]`));
2371
+ }
2372
+ return [resolveEntry(input, "numbering")];
2373
+ }
2374
+ function numberingAtLevel(levels, level) {
2375
+ return levels[Math.min(Math.max(0, level), levels.length - 1)];
2376
+ }
2377
+ var ROMAN = [[1e3, "m"], [900, "cm"], [500, "d"], [400, "cd"], [100, "c"], [90, "xc"], [50, "l"], [40, "xl"], [10, "x"], [9, "ix"], [5, "v"], [4, "iv"], [1, "i"]];
2378
+ function bareNumber(value, style) {
2379
+ if (style === "arabic") return String(value);
2380
+ if (style === "roman-upper" || style === "roman-lower") {
2381
+ if (value > MAX_ROMAN_VALUE) return null;
2382
+ let rest = value, out = "";
2383
+ for (const [amount, glyphs] of ROMAN) while (rest >= amount) {
2384
+ out += glyphs;
2385
+ rest -= amount;
2386
+ }
2387
+ return style === "roman-upper" ? out.toUpperCase() : out;
2388
+ }
2389
+ const letter = String.fromCharCode(97 + (value - 1) % 26).repeat(Math.floor((value - 1) / 26) + 1);
2390
+ return style === "alpha-upper" ? letter.toUpperCase() : letter;
2391
+ }
2392
+ function numberingStyleDraws(style, value) {
2393
+ return bareNumber(value, style) !== null;
2394
+ }
2395
+ function formatListNumber(value, style = "arabic", suffix = "period") {
2396
+ if (!Number.isInteger(value) || value < 1) throw new RangeError("A list number must be an integer of at least 1.");
2397
+ if (!styleSet.has(style)) throw new RangeError(`Unknown numbering style '${String(style)}'.`);
2398
+ if (!suffixSet.has(suffix)) throw new RangeError(`Unknown numbering suffix '${String(suffix)}'.`);
2399
+ const bare = bareNumber(value, style) ?? String(value);
2400
+ return suffix === "period" ? `${bare}.` : suffix === "paren" ? `${bare})` : `(${bare})`;
2401
+ }
2402
+ function listNumbers(items, numbering) {
2403
+ const levels = resolveNumbering(numbering);
2404
+ const counters = [];
2405
+ return items.map((item, index) => {
2406
+ const object = isObject(item) ? item : void 0;
2407
+ const level = object?.level === void 0 ? 0 : object.level;
2408
+ if (typeof level !== "number" || !Number.isInteger(level) || level < 0) throw new RangeError("List levels must be nonnegative integers.");
2409
+ const own2 = object?.start;
2410
+ if (own2 !== void 0 && (typeof own2 !== "number" || !Number.isInteger(own2) || own2 < 1 || own2 > MAX_NUMBERING_VALUE)) throw new RangeError(`A list item start must be an integer from 1 to ${MAX_NUMBERING_VALUE}.`);
2411
+ const { style: authored, start, suffix } = numberingAtLevel(levels, level);
2412
+ const previous = counters[level];
2413
+ const value = own2 ?? (previous === void 0 ? start : previous + 1);
2414
+ counters[level] = value;
2415
+ counters.length = level + 1;
2416
+ const adapted = !numberingStyleDraws(authored, value);
2417
+ const style = adapted ? "arabic" : authored;
2418
+ return { index, level, value, style, suffix, text: formatListNumber(value, style, suffix), ...adapted ? { adapted: "roman-range" } : {} };
2419
+ });
2420
+ }
2421
+ function sliceNumberedItems(items, numbering, from, to = items.length) {
2422
+ const whole = listNumbers(items, numbering);
2423
+ const result = items.slice(from, to);
2424
+ for (let index = 0; index < result.length; index++) {
2425
+ const wanted = whole[from + index].value;
2426
+ if (listNumbers(result.slice(0, index + 1), numbering)[index].value === wanted) continue;
2427
+ const item = result[index];
2428
+ const object = isObject(item) ? item : { text: item };
2429
+ result[index] = { ...object, start: wanted };
2430
+ }
2431
+ return result;
2432
+ }
2433
+ function numberingFindings(payload) {
2434
+ const errors = [], warnings = [];
2435
+ const lists = ["items", "bullets"].filter((key) => Array.isArray(payload[key]));
2436
+ if (Object.hasOwn(payload, "numbering")) {
2437
+ if (!Object.hasOwn(payload, "items") && !Object.hasOwn(payload, "bullets")) {
2438
+ errors.push({ key: "numbering", message: "numbering applies to 'items' or 'bullets'; this payload has neither", params: {} });
2439
+ } else {
2440
+ for (const key of lists) {
2441
+ let numbers;
2442
+ try {
2443
+ numbers = listNumbers(payload[key], payload.numbering);
2444
+ } catch {
2445
+ continue;
2446
+ }
2447
+ const largest = numbers.reduce((max, number) => Math.max(max, number.value), 0);
2448
+ if (largest > MAX_NUMBERING_VALUE) errors.push({ key, message: `numbering counts up to ${largest}, past the native limit of ${MAX_NUMBERING_VALUE}`, params: { largest, limit: MAX_NUMBERING_VALUE } });
2449
+ }
2450
+ }
2451
+ } else {
2452
+ for (const key of lists) {
2453
+ if (payload[key].some((item) => isObject(item) && item.start !== void 0)) {
2454
+ warnings.push({ key, message: `'start' on ${key} entries has no effect without a 'numbering' field on the payload`, params: {} });
2455
+ }
2456
+ }
2457
+ }
2458
+ return { errors, warnings };
2459
+ }
1720
2460
 
1721
- // src/chart-options.ts
1722
- var END_POSITIONS = ["center", "inside-end", "inside-base", "outside-end"];
1723
- var STACKED_POSITIONS = ["center", "inside-end", "inside-base"];
1724
- var POINT_POSITIONS = ["above", "below", "left", "right", "center"];
1725
- var PIE_POSITIONS = ["center", "inside-end", "outside-end"];
1726
- var NONE = [];
1727
- var VALUE_CATEGORY = ["category", "value"];
1728
- var ALL_CONTENT = ["category", "value", "percent"];
1729
- var categoryKinds = /* @__PURE__ */ new Set(["bar", "line", "area", "scatter", "histogram", "pareto", "box", "waterfall", "funnel"]);
1730
- var valueKinds = /* @__PURE__ */ new Set(["bar", "line", "area", "scatter", "histogram", "pareto", "box", "waterfall"]);
1731
- var legendKinds = /* @__PURE__ */ new Set(["bar", "line", "area", "pie", "doughnut", "scatter", "radar", "box"]);
1732
- function labelSupport(target) {
1733
- const none = { supported: true, content: VALUE_CATEGORY, positions: NONE, defaultPosition: null, defaultOn: false };
1734
- switch (target.kind) {
1735
- case "bar":
1736
- return target.stacked ? { ...none, positions: STACKED_POSITIONS, defaultPosition: "center" } : { ...none, positions: END_POSITIONS, defaultPosition: "outside-end" };
1737
- case "line":
1738
- case "scatter":
1739
- return { ...none, positions: POINT_POSITIONS, defaultPosition: "above" };
1740
- case "pie":
1741
- return { ...none, content: ALL_CONTENT, positions: PIE_POSITIONS, defaultPosition: "outside-end" };
1742
- case "doughnut":
1743
- return { ...none, content: ALL_CONTENT };
1744
- // The funnel labels its bars with values and the treemap its tiles with category names by default.
1745
- case "funnel":
1746
- case "treemap":
1747
- return { ...none, defaultOn: true };
1748
- case "area":
1749
- case "radar":
1750
- return none;
1751
- case "histogram":
1752
- case "pareto":
1753
- case "waterfall":
1754
- return { ...none, positions: END_POSITIONS, defaultPosition: "outside-end" };
1755
- default:
1756
- return { ...none, supported: false, content: [] };
2461
+ // src/color.ts
2462
+ var schemeColorSlots = /* @__PURE__ */ new Set([
2463
+ "accent1",
2464
+ "accent2",
2465
+ "accent3",
2466
+ "accent4",
2467
+ "accent5",
2468
+ "accent6",
2469
+ "dark1",
2470
+ "dark2",
2471
+ "light1",
2472
+ "light2",
2473
+ "hyperlink",
2474
+ "followedHyperlink"
2475
+ ]);
2476
+ var schemeColorRoles = /* @__PURE__ */ new Set([
2477
+ "primary",
2478
+ "secondary",
2479
+ "accent",
2480
+ "background",
2481
+ "surface",
2482
+ "text",
2483
+ "textSecondary"
2484
+ ]);
2485
+ var defaultRoleSlots = {
2486
+ primary: "accent1",
2487
+ secondary: "accent2",
2488
+ accent: "accent3",
2489
+ background: "light1",
2490
+ surface: "light2",
2491
+ text: "dark1",
2492
+ textSecondary: "dark2"
2493
+ };
2494
+ function normalizeHexColor(value) {
2495
+ if (typeof value !== "string") return void 0;
2496
+ let hex = value.trim();
2497
+ if (/^#[0-9a-f]{3}$/i.test(hex)) hex = `#${[...hex.slice(1)].map((char) => char + char).join("")}`;
2498
+ if (/^#[0-9a-f]{6}([0-9a-f]{2})?$/i.test(hex)) return hex.length === 9 ? `#${hex.slice(1, 7).toUpperCase()}` : hex.toUpperCase();
2499
+ return void 0;
2500
+ }
2501
+ function schemeSlot(colorScheme, slot, fallback) {
2502
+ return normalizeHexColor(colorScheme[slot]) ?? fallback;
2503
+ }
2504
+ function resolveVariable(variables, id, fallback) {
2505
+ const entry = variables[id];
2506
+ if (typeof entry === "string") return normalizeHexColor(entry) ?? fallback;
2507
+ if (entry && typeof entry === "object" && entry.type === "color") {
2508
+ return normalizeHexColor(entry.value) ?? fallback;
1757
2509
  }
2510
+ return fallback;
1758
2511
  }
1759
- function chartOptionSupport(target) {
1760
- return {
1761
- axisTitles: { category: categoryKinds.has(target.kind), value: valueKinds.has(target.kind) },
1762
- legend: legendKinds.has(target.kind),
1763
- dataLabels: labelSupport(target)
1764
- };
2512
+ function resolveColorRef(reference, options) {
2513
+ const { colorScheme, roles, variables = {}, fallback } = options;
2514
+ const literal = normalizeHexColor(reference);
2515
+ if (literal) return literal;
2516
+ if (reference.startsWith("var:")) {
2517
+ const id = reference.slice("var:".length);
2518
+ if (/^[a-z][a-z0-9-]*$/.test(id)) return resolveVariable(variables, id, fallback);
2519
+ return fallback;
2520
+ }
2521
+ if (schemeColorRoles.has(reference)) {
2522
+ const roleKey = reference;
2523
+ const fromRoles = roles?.[roleKey];
2524
+ if (fromRoles) return normalizeHexColor(fromRoles) ?? fallback;
2525
+ const fromScheme = normalizeHexColor(colorScheme[reference]);
2526
+ if (fromScheme) return fromScheme;
2527
+ const slot = defaultRoleSlots[reference];
2528
+ return slot ? schemeSlot(colorScheme, slot, fallback) : fallback;
2529
+ }
2530
+ if (schemeColorSlots.has(reference)) return schemeSlot(colorScheme, reference, fallback);
2531
+ return fallback;
2532
+ }
2533
+ function luminance(value) {
2534
+ if (typeof value !== "string") return void 0;
2535
+ let hex = value.trim();
2536
+ if (/^#[0-9a-f]{3}$/i.test(hex)) hex = `#${[...hex.slice(1)].map((c) => c + c).join("")}`;
2537
+ if (/^#[0-9a-f]{6}ff$/i.test(hex)) hex = hex.slice(0, 7);
2538
+ if (!/^#[0-9a-f]{6}$/i.test(hex)) return void 0;
2539
+ return [0.2126, 0.7152, 0.0722].reduce((sum, weight, index) => {
2540
+ const offset = index * 2 + 1;
2541
+ const n = Number.parseInt(hex.slice(offset, offset + 2), 16) / 255;
2542
+ return sum + weight * (n <= 0.04045 ? n / 12.92 : ((n + 0.055) / 1.055) ** 2.4);
2543
+ }, 0);
2544
+ }
2545
+ function colorContrast(foreground, background) {
2546
+ const a = luminance(foreground), b = luminance(background);
2547
+ return a === void 0 || b === void 0 ? void 0 : (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
2548
+ }
2549
+ function textColorForFill(fill, preferred) {
2550
+ const background = luminance(fill), contrast = colorContrast(preferred, fill);
2551
+ if (background === void 0 || contrast === void 0 || contrast >= 4.5) return preferred;
2552
+ return (background + 0.05) / 0.05 >= 1.05 / (background + 0.05) ? "#000000" : "#FFFFFF";
2553
+ }
2554
+ function chartColorForFill(fill, preferred) {
2555
+ const contrast = colorContrast(preferred, fill);
2556
+ if (contrast === void 0 || contrast >= 3) return preferred;
2557
+ let hex = preferred.trim();
2558
+ if (hex.length === 4) hex = `#${[...hex.slice(1)].map((c) => c + c).join("")}`;
2559
+ const channels = [1, 3, 5].map((offset) => Number.parseInt(hex.slice(offset, offset + 2), 16));
2560
+ for (let step = 1; step <= 255; step++) {
2561
+ const candidates = [0, 255].map((target) => {
2562
+ const color = `#${channels.map((channel) => Math.round(channel + (target - channel) * step / 255).toString(16).padStart(2, "0")).join("")}`.toUpperCase();
2563
+ return { color, contrast: colorContrast(color, fill) ?? 0 };
2564
+ }).sort((a, b) => b.contrast - a.contrast);
2565
+ const best = candidates[0];
2566
+ if (best && best.contrast >= 3) return best.color;
2567
+ }
2568
+ return textColorForFill(fill, preferred);
2569
+ }
2570
+ var CHART_SERIES_MIN_LIGHTNESS_STEP = 11;
2571
+ var CHART_SERIES_MIN_DIFFERENCE = 12;
2572
+ var D65 = [0.95047, 1, 1.08883];
2573
+ function opaqueChannels(value) {
2574
+ if (luminance(value) === void 0) return void 0;
2575
+ let hex = value.trim();
2576
+ if (hex.length === 4) hex = `#${[...hex.slice(1)].map((c) => c + c).join("")}`;
2577
+ return [1, 3, 5].map((offset) => Number.parseInt(hex.slice(offset, offset + 2), 16));
2578
+ }
2579
+ var toLinear = (channel) => {
2580
+ const n = channel / 255;
2581
+ return n <= 0.04045 ? n / 12.92 : ((n + 0.055) / 1.055) ** 2.4;
2582
+ };
2583
+ var fromLinear = (value) => 255 * (value <= 31308e-7 ? value * 12.92 : 1.055 * value ** (1 / 2.4) - 0.055);
2584
+ function labOf(channels) {
2585
+ const [r, g, b] = channels.map(toLinear);
2586
+ const f = (t) => t > 216 / 24389 ? Math.cbrt(t) : (24389 / 27 * t + 16) / 116;
2587
+ const fx = f((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / D65[0]), fy = f(0.2126729 * r + 0.7151522 * g + 0.072175 * b), fz = f((0.0193339 * r + 0.119192 * g + 0.9503041 * b) / D65[2]);
2588
+ return [116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)];
2589
+ }
2590
+ function labToLinear([l, a, b]) {
2591
+ const fy = (l + 16) / 116, fx = fy + a / 500, fz = fy - b / 200;
2592
+ const inverse = (t) => t ** 3 > 216 / 24389 ? t ** 3 : (116 * t - 16) / (24389 / 27);
2593
+ const x = inverse(fx) * D65[0], y = l > 8 ? fy ** 3 : l / (24389 / 27), z = inverse(fz) * D65[2];
2594
+ const rgb = [
2595
+ 3.2404542 * x - 1.5371385 * y - 0.4985314 * z,
2596
+ -0.969266 * x + 1.8760108 * y + 0.041556 * z,
2597
+ 0.0556434 * x - 0.2040259 * y + 1.0572252 * z
2598
+ ];
2599
+ return rgb.every((channel) => channel >= -5e-4 && channel <= 1.0005) ? rgb : void 0;
2600
+ }
2601
+ var hexOfChannels = (channels) => `#${channels.map((channel) => Math.max(0, Math.min(255, Math.round(channel))).toString(16).padStart(2, "0")).join("").toUpperCase()}`;
2602
+ function labWithLightness(l, a, b) {
2603
+ const chroma = Math.hypot(a, b), hue = Math.atan2(b, a);
2604
+ const at3 = (scale) => labToLinear([l, Math.cos(hue) * chroma * scale, Math.sin(hue) * chroma * scale]);
2605
+ let linear = at3(1);
2606
+ if (!linear) {
2607
+ let low = 0, high = 1;
2608
+ for (let step = 0; step < 24; step++) {
2609
+ const mid = (low + high) / 2;
2610
+ if (at3(mid)) low = mid;
2611
+ else high = mid;
2612
+ }
2613
+ linear = at3(low) ?? [0, 0, 0];
2614
+ }
2615
+ return hexOfChannels(linear.map((channel) => fromLinear(Math.max(0, Math.min(1, channel)))));
2616
+ }
2617
+ function labDifference(a, b) {
2618
+ return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
2619
+ }
2620
+ var CHART_SERIES_SHORTFALL_WEIGHT = 8;
2621
+ function chartPaletteForFill(fill, palette) {
2622
+ const opaqueFill = colorContrast(fill, fill) !== void 0;
2623
+ const preferredLab = palette.map((color) => {
2624
+ const channels = opaqueChannels(color);
2625
+ return channels ? labOf(channels) : void 0;
2626
+ });
2627
+ const adjusted = palette.map((color) => chartColorForFill(fill, color));
2628
+ const kept = palette.map((color, index) => opaqueFill && preferredLab[index] !== void 0 && adjusted[index] === color);
2629
+ const placed = palette.map((_, index) => kept[index] || !opaqueFill || !preferredLab[index] ? adjusted[index] : void 0);
2630
+ const placedLab = placed.map((color) => {
2631
+ const channels = color === void 0 ? void 0 : opaqueChannels(color);
2632
+ return channels ? labOf(channels) : void 0;
2633
+ });
2634
+ palette.forEach((_, index) => {
2635
+ const wanted = preferredLab[index], base = adjusted[index];
2636
+ if (placed[index] !== void 0 || !wanted) return;
2637
+ const baseLab = labOf(opaqueChannels(base));
2638
+ const needs = palette.flatMap((_2, other) => {
2639
+ const before = preferredLab[other], lab = placedLab[other];
2640
+ return other !== index && before && lab && placed[other] !== void 0 ? [{ lab, adjacent: Math.abs(other - index) === 1, wide: Math.min(labDifference(before, wanted), CHART_SERIES_MIN_DIFFERENCE), step: Math.min(Math.abs(before[0] - wanted[0]), CHART_SERIES_MIN_LIGHTNESS_STEP) }] : [];
2641
+ });
2642
+ const shortfall = (candidate) => needs.reduce((worst, need) => {
2643
+ const lightness = Math.abs(need.lab[0] - candidate[0]) - need.step, difference = labDifference(need.lab, candidate) - need.wide;
2644
+ return need.adjacent ? { near: Math.min(worst.near, difference, lightness), far: worst.far } : { near: Math.min(worst.near, difference), far: Math.min(worst.far, lightness) };
2645
+ }, { near: Infinity, far: Infinity });
2646
+ const deficit = (value) => Math.max(0, -value - 1e-9);
2647
+ const cost = (candidate) => candidate.moved + CHART_SERIES_SHORTFALL_WEIGHT * deficit(candidate.near) + deficit(candidate.far);
2648
+ const start = shortfall(baseLab);
2649
+ if (deficit(start.near) === 0 && deficit(start.far) === 0) {
2650
+ placed[index] = base;
2651
+ placedLab[index] = baseLab;
2652
+ return;
2653
+ }
2654
+ let best = { color: base, lab: baseLab, cost: cost({ moved: 0, ...start }), moved: 0 };
2655
+ for (let step = 0; step <= 400; step++) {
2656
+ const color = labWithLightness(step / 4, wanted[1], wanted[2]), contrast = colorContrast(color, fill);
2657
+ if (contrast === void 0 || contrast < 3) continue;
2658
+ const lab = labOf(opaqueChannels(color)), moved = Math.abs(lab[0] - baseLab[0]), total = cost({ moved, ...shortfall(lab) });
2659
+ if (total < best.cost - 1e-9 || Math.abs(total - best.cost) <= 1e-9 && moved < best.moved) best = { color, lab, cost: total, moved };
2660
+ }
2661
+ placed[index] = best.color;
2662
+ placedLab[index] = best.lab;
2663
+ });
2664
+ return placed;
1765
2665
  }
1766
- var KEPT = {
1767
- column: { kind: "bar" },
1768
- "stacked-column-3x": { kind: "bar", stacked: true },
1769
- "100pct-stacked-column-3x": { kind: "bar", stacked: true },
1770
- bar: { kind: "bar" },
1771
- "stacked-bar-3x": { kind: "bar", stacked: true },
1772
- "100pct-stacked-bar-3x": { kind: "bar", stacked: true },
1773
- line: { kind: "line" },
1774
- "line-with-markers": { kind: "line" },
1775
- "stacked-line-3x": { kind: "line", stacked: true },
1776
- "stacked-line-with-markers-3x": { kind: "line", stacked: true },
1777
- area: { kind: "area" },
1778
- "stacked-area-3x": { kind: "area", stacked: true },
1779
- "100pct-stacked-area-3x": { kind: "area", stacked: true },
1780
- pie: { kind: "pie" },
1781
- doughnut: { kind: "doughnut" },
1782
- scatter: { kind: "scatter" },
1783
- radar: { kind: "radar" },
1784
- "radar-with-markers": { kind: "radar" },
1785
- "filled-radar": { kind: "radar" },
1786
- treemap: { kind: "treemap" },
1787
- histogram: { kind: "histogram" },
1788
- pareto: { kind: "pareto" },
1789
- "box-and-whisker": { kind: "box" },
1790
- waterfall: { kind: "waterfall" },
1791
- funnel: { kind: "funnel" },
1792
- world: { kind: "map" }
1793
- };
1794
- function chartOptionTarget(typeId) {
1795
- if (typeof typeId !== "string") return void 0;
1796
- const raw = typeId.trim().toLowerCase();
1797
- const id = raw === "donut" ? "doughnut" : raw;
1798
- const kept = deprecatedCatalogIds[`chartTypes/${id}`] ?? id;
1799
- const target = Object.hasOwn(KEPT, kept) ? KEPT[kept] : void 0;
1800
- return target ? { ...target } : void 0;
2666
+
2667
+ // src/annotations.ts
2668
+ var CITATION_MARKER_SCALE = 2 / 3;
2669
+ var CITATION_MARKER_RAISE = 0.3;
2670
+ var CAPTION_FONT_RATIO = 0.6;
2671
+ var CAPTION_MAX_RATIO = 0.35;
2672
+ var FOOTNOTE_MAX_RATIO = 0.35;
2673
+ var CAPTIONABLE_FIELDS = Object.freeze(["image", "chart", "table", "video"]);
2674
+ var record4 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
2675
+ var isRecord3 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
2676
+ var round = (value) => Math.round(value * 1e6) / 1e6 || value;
2677
+ var regionKey = /^(top|middle|bottom|left|center|right)([+:]|$)/;
2678
+ var TEXT_FIELDS = ["text", "items", "bullets"];
2679
+ function annotationText(value) {
2680
+ if (typeof value === "string") return value;
2681
+ if (Array.isArray(value)) return value.map((run) => typeof run === "string" ? run : record4(run).text ?? "").join("");
2682
+ if (isRecord3(value) && value.text !== void 0) return annotationText(value.text);
2683
+ return value === void 0 || value === null ? "" : String(value);
1801
2684
  }
1802
- var record3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1803
- var LEGEND_POSITIONS = /* @__PURE__ */ new Set(["none", "top", "bottom", "left", "right"]);
1804
- var CONTENT_ORDER = ["category", "value", "percent"];
1805
- var POSITIONS = /* @__PURE__ */ new Set(["auto", "center", "inside-end", "inside-base", "outside-end", "above", "below", "left", "right"]);
1806
- var DEFAULT_CHART_LABEL_SEPARATOR = ", ";
1807
- function resolveChartOptions(chart, target) {
1808
- const result = { active: false, axisTitles: {}, diagnostics: [] };
1809
- if (!record3(chart)) return result;
1810
- const support = target ? chartOptionSupport(target) : void 0;
1811
- const adapt = (option, reason, message) => result.diagnostics.push({ code: "chart-option-adapted", option, reason, message });
1812
- const kind2 = target?.kind ?? "unknown";
1813
- if (record3(chart.axisTitles)) {
1814
- for (const axis of ["category", "value"]) {
1815
- const raw = chart.axisTitles[axis];
1816
- if (typeof raw !== "string" || !raw.trim()) continue;
1817
- if (support && !support.axisTitles[axis]) {
1818
- adapt(`axisTitles.${axis}`, "unsupported-type", `A '${kind2}' chart has no ${axis} axis to title; the ${axis} axis title is not drawn or exported.`);
1819
- continue;
2685
+ function captionSettings(value) {
2686
+ if (value === void 0 || value === null) return void 0;
2687
+ if (typeof value === "string" || Array.isArray(value)) return { text: value, position: "below", align: "left" };
2688
+ if (!isRecord3(value) || value.text === void 0) return void 0;
2689
+ return { text: value.text, position: value.position === "above" ? "above" : "below", align: value.align === "center" || value.align === "right" ? value.align : "left" };
2690
+ }
2691
+ function walkCitationRuns(slide, basePath, visit) {
2692
+ const runs = (value2, path) => {
2693
+ if (!Array.isArray(value2)) return;
2694
+ value2.forEach((run, index) => {
2695
+ if (!isRecord3(run) || typeof run.text !== "string") return;
2696
+ const cite = run.cite === void 0 ? [] : Array.isArray(run.cite) ? run.cite.filter((id) => typeof id === "string") : typeof run.cite === "string" ? [run.cite] : [];
2697
+ const footnote = typeof run.footnote === "string" || Array.isArray(run.footnote) ? run.footnote : void 0;
2698
+ if (!cite.length && footnote === void 0) return;
2699
+ visit({ path: `${path}.${index}`, run, cite, footnote });
2700
+ });
2701
+ };
2702
+ const list = (items, path) => {
2703
+ if (!Array.isArray(items)) return;
2704
+ items.forEach((item, index) => {
2705
+ if (Array.isArray(item)) runs(item, `${path}.${index}`);
2706
+ else if (isRecord3(item)) {
2707
+ runs(item.text, `${path}.${index}.text`);
2708
+ runs(item.description, `${path}.${index}.description`);
1820
2709
  }
1821
- result.axisTitles[axis] = raw.trim();
2710
+ });
2711
+ };
2712
+ const host = (node, path, depth) => {
2713
+ const payload = record4(node);
2714
+ if (Array.isArray(payload.blocks)) {
2715
+ if (depth > 32) return;
2716
+ payload.blocks.forEach((block, index) => {
2717
+ host(block, `${path}.blocks.${index}`, depth + 1);
2718
+ });
2719
+ return;
1822
2720
  }
2721
+ for (const field of TEXT_FIELDS) {
2722
+ if (payload[field] === void 0) continue;
2723
+ if (field === "text") runs(payload.text, `${path}.text`);
2724
+ else list(payload[field], `${path}.${field}`);
2725
+ }
2726
+ };
2727
+ const value = record4(slide);
2728
+ const regions = Object.keys(value).filter((key) => regionKey.test(key)).sort();
2729
+ if (regions.length) {
2730
+ for (const key of regions) host(value[key], `${basePath}.${key}`, 0);
2731
+ return;
1823
2732
  }
1824
- if (typeof chart.legend === "string" && LEGEND_POSITIONS.has(chart.legend)) {
1825
- if (support && !support.legend) adapt("legend", "unsupported-type", `A '${kind2}' chart has no legend option; the legend setting is ignored.`);
1826
- else result.legend = chart.legend;
2733
+ if (Array.isArray(value.blocks)) {
2734
+ value.blocks.forEach((block, index) => {
2735
+ host(block, `${basePath}.blocks.${index}`, 0);
2736
+ });
2737
+ return;
1827
2738
  }
1828
- const labels = chart.dataLabels;
1829
- if (labels === false && support?.dataLabels.defaultOn) result.dataLabelsOff = true;
1830
- if (labels === true || record3(labels)) {
1831
- const options = record3(labels) ? labels : {};
1832
- if (support && !support.dataLabels.supported) {
1833
- adapt("dataLabels", "unsupported-type", `A '${kind2}' chart has no data label option; data labels are not drawn or exported.`);
1834
- } else {
1835
- const allowed = support?.dataLabels.content ?? ALL_CONTENT;
1836
- const requested = Array.isArray(options.content) ? options.content.filter((part) => typeof part === "string" && CONTENT_ORDER.includes(part)) : ["value"];
1837
- const content = CONTENT_ORDER.filter((part) => requested.includes(part) && allowed.includes(part));
1838
- for (const part of CONTENT_ORDER) {
1839
- if (requested.includes(part) && !allowed.includes(part)) adapt("dataLabels.content", "unsupported-content", `A '${kind2}' chart cannot label '${part}'${part === "percent" ? " (percent exists only on pie and doughnut charts)" : ""}; it is dropped from the data labels.`);
1840
- }
1841
- if (!content.length) content.push("value");
1842
- const positions = support?.dataLabels.positions ?? NONE;
1843
- let position = support?.dataLabels.defaultPosition ?? null;
1844
- if (typeof options.position === "string" && POSITIONS.has(options.position) && options.position !== "auto") {
1845
- if (!support || positions.includes(options.position)) position = options.position;
1846
- else adapt("dataLabels.position", "unsupported-position", `A '${kind2}'${target?.stacked ? " stacked" : ""} chart cannot place data labels '${options.position}'; the type's default position is used.`);
1847
- }
1848
- let separator = typeof options.separator === "string" ? options.separator : DEFAULT_CHART_LABEL_SEPARATOR;
1849
- if (/[\r\n]/.test(separator)) {
1850
- separator = separator.replace(/[\r\n]+/g, " ");
1851
- adapt("dataLabels.separator", "unsupported-content", "A data label is one line; the line break in the separator is replaced with a space.");
2739
+ host(value, basePath, 0);
2740
+ }
2741
+ var citationMarkerText = (numbers) => numbers.join(",");
2742
+ function referenceList(presentation) {
2743
+ const list = record4(presentation).references;
2744
+ return Array.isArray(list) ? list.filter((item) => isRecord3(item) && typeof item.id === "string") : [];
2745
+ }
2746
+ function numberSlide(slide, slideIndex, references, numbering) {
2747
+ const markers = /* @__PURE__ */ new Map(), marked = [], used = /* @__PURE__ */ new Map();
2748
+ walkCitationRuns(slide, `slides.${slideIndex}`, (entry) => {
2749
+ const numbers = [];
2750
+ for (const id of entry.cite) {
2751
+ let note = numbering.byId.get(id);
2752
+ if (!note) {
2753
+ const index = references.findIndex((item) => item.id === id), reference = references[index];
2754
+ note = { number: numbering.notes.length + 1, kind: "reference", id, text: reference?.text ?? id, sourcePath: index >= 0 ? `references.${index}` : entry.path, resolved: !!reference, ...typeof reference?.url === "string" ? { url: reference.url } : {} };
2755
+ numbering.notes.push(note);
2756
+ numbering.byId.set(id, note);
1852
2757
  }
1853
- result.dataLabels = { content, position, separator };
2758
+ if (!numbers.includes(note.number)) numbers.push(note.number);
2759
+ used.set(note.number, note);
1854
2760
  }
1855
- }
1856
- result.active = result.legend !== void 0 || result.dataLabels !== void 0 || result.dataLabelsOff === true || result.axisTitles.category !== void 0 || result.axisTitles.value !== void 0;
1857
- return result;
2761
+ if (entry.footnote !== void 0) {
2762
+ const note = { number: numbering.notes.length + 1, kind: "footnote", text: entry.footnote, sourcePath: entry.path, resolved: true };
2763
+ numbering.notes.push(note);
2764
+ numbers.push(note.number);
2765
+ used.set(note.number, note);
2766
+ }
2767
+ const text2 = citationMarkerText(numbers);
2768
+ markers.set(entry.path, text2);
2769
+ marked.push({ path: entry.path, numbers, text: text2 });
2770
+ });
2771
+ if (!marked.length) return void 0;
2772
+ return { markers, marked, notes: [...used.values()].sort((a, b) => a.number - b.number) };
1858
2773
  }
1859
- function formatChartLabelNumber(value) {
1860
- return String(Number(value.toPrecision(12)));
2774
+ function collectCitations(presentation) {
2775
+ const references = referenceList(presentation), numbering = { notes: [], byId: /* @__PURE__ */ new Map() }, slides = /* @__PURE__ */ new Map();
2776
+ const list = record4(presentation).slides;
2777
+ if (Array.isArray(list)) list.forEach((slide, index) => {
2778
+ const result = numberSlide(slide, index, references, numbering);
2779
+ if (result) slides.set(index, result);
2780
+ });
2781
+ const cited = new Set(numbering.notes.filter((note) => note.kind === "reference").map((note) => note.id));
2782
+ return { notes: numbering.notes, references: numbering.notes.filter((note) => note.kind === "reference"), slides, unused: references.map((item) => item.id).filter((id) => !cited.has(id)) };
1861
2783
  }
1862
- function formatChartLabelPercent(share) {
1863
- return `${Math.round(Number((share * 100).toPrecision(12)))}%`;
2784
+ function slideCitations(slide, slideIndex, presentation) {
2785
+ const references = referenceList(presentation), numbering = { notes: [], byId: /* @__PURE__ */ new Map() };
2786
+ const list = record4(presentation).slides;
2787
+ if (Array.isArray(list)) for (let index = 0; index < Math.min(slideIndex, list.length); index += 1) numberSlide(list[index], index, references, numbering);
2788
+ return numberSlide(slide, slideIndex, references, numbering);
1864
2789
  }
1865
- function chartLabelText(parts, content, separator = DEFAULT_CHART_LABEL_SEPARATOR) {
1866
- return CONTENT_ORDER.filter((part) => content.includes(part) && parts[part] !== void 0).map((part) => parts[part]).join(separator);
2790
+ var fitHeight = (fit) => Math.max(fit.placement?.height ?? 0, "richLines" in fit ? fit.height : fit.lines.length * fit.lineHeight);
2791
+ function layoutCaption(value, box, path, options) {
2792
+ const settings = captionSettings(value);
2793
+ if (!settings) return void 0;
2794
+ const fontSize = Math.max(options.minFontSize, CAPTION_FONT_RATIO * 25 * options.scale), gap = fontSize * 0.4;
2795
+ const maxHeight = Math.max(fontSize * 1.22, box.height * CAPTION_MAX_RATIO);
2796
+ const diagnostics = [];
2797
+ const probe = options.fit(settings.text, { x: box.x, y: box.y, width: box.width, height: maxHeight }, fontSize, fontSize, path, settings.align);
2798
+ const height = Math.min(maxHeight, Math.max(fontSize * 1.22, fitHeight(probe)));
2799
+ const captionBox = { x: box.x, y: settings.position === "above" ? box.y : box.y + box.height - height, width: box.width, height };
2800
+ const fit = options.fit(settings.text, captionBox, fontSize, fontSize, path, settings.align);
2801
+ const overflow = fit.overflow || fitHeight(fit) > height + 0.01;
2802
+ if (overflow) diagnostics.push({ code: "text-overflow", path, message: "The caption does not fit its band at the minimum font size; shorten it or give the block more space." });
2803
+ const mediaHeight = Math.max(options.scale, box.height - height - gap);
2804
+ const mediaBox = { x: box.x, y: settings.position === "above" ? box.y + height + gap : box.y, width: box.width, height: mediaHeight };
2805
+ for (const target of [captionBox, mediaBox]) for (const key of ["x", "y", "width", "height"]) target[key] = round(target[key]);
2806
+ return { path, value, text: settings.text, position: settings.position, alignment: settings.align, box: captionBox, mediaBox, fit, textStyle: options.textStyle(path), fontSize, overflow, diagnostics };
2807
+ }
2808
+ function layoutFootnotes(citations, options) {
2809
+ const fontSize = Math.max(options.minFontSize, 13 * options.scale), thickness = Math.max(1, round(options.scale));
2810
+ const gap = fontSize * 0.5, spacing = fontSize * 0.25, diagnostics = [];
2811
+ const width = Math.max(options.scale, options.width);
2812
+ const measured = citations.notes.map((note) => {
2813
+ const value = typeof note.text === "string" ? `${note.number} ${note.text}` : [`${note.number} `, ...note.text];
2814
+ const probe = options.fit(value, { x: options.x, y: 0, width, height: Number.MAX_SAFE_INTEGER }, fontSize, fontSize, note.sourcePath, "left");
2815
+ return { note, value, height: Math.max(fontSize * 1.22, fitHeight(probe)) };
2816
+ });
2817
+ const total = thickness + gap + measured.reduce((sum, item) => sum + item.height, 0) + spacing * Math.max(0, measured.length - 1);
2818
+ const height = round(Math.min(total, Math.max(fontSize * 1.22 + thickness + gap, options.maxHeight)));
2819
+ const top = round(options.bottom - height), box = { x: round(options.x), y: top, width: round(width), height };
2820
+ let cursor = top + thickness + gap, overflow = false;
2821
+ const entries = measured.map((item, index) => {
2822
+ const entryBox = { x: box.x, y: round(cursor), width: box.width, height: round(item.height) };
2823
+ const fit = options.fit(item.value, entryBox, fontSize, fontSize, item.note.sourcePath, "left");
2824
+ const exceeds = fit.overflow || entryBox.y + entryBox.height > options.bottom + 0.01;
2825
+ if (exceeds) {
2826
+ overflow = true;
2827
+ diagnostics.push({ code: "text-overflow", path: item.note.sourcePath, message: `Footnote ${item.note.number} does not fit the slide's footnote area; shorten the note or split the slide.` });
2828
+ }
2829
+ cursor += item.height + (index < measured.length - 1 ? spacing : 0);
2830
+ return { number: item.note.number, kind: item.note.kind, ...item.note.id !== void 0 ? { id: item.note.id } : {}, sourcePath: item.note.sourcePath, value: item.value, text: item.note.text, box: entryBox, fit, textStyle: options.textStyle(item.note.sourcePath), ...item.note.url !== void 0 ? { url: item.note.url } : {}, overflow: exceeds };
2831
+ });
2832
+ for (const note of citations.notes) if (!note.resolved) diagnostics.push({ code: "unresolved-content", path: note.sourcePath, message: `Reference '${note.id}' is not in the presentation's references list; its id is listed instead.` });
2833
+ return { algorithm: "footnote-area-v1", path: options.path, box, rule: { x: box.x, y: top, width: box.width, thickness }, entries, markers: citations.marked, fontSize, overflow, diagnostics };
2834
+ }
2835
+ function referencesSlide(presentation, options = {}) {
2836
+ const title = options.title ?? "References";
2837
+ const { references } = collectCitations(presentation);
2838
+ if (!references.length) return { title };
2839
+ const items = references.map((note) => {
2840
+ const label = `${note.number}. `;
2841
+ if (typeof note.text === "string" && note.url === void 0) return `${label}${note.text}`;
2842
+ const runs = [label, ...typeof note.text === "string" ? [note.text] : note.text];
2843
+ if (note.url !== void 0) runs.push(" ", { text: note.url, link: note.url });
2844
+ return runs;
2845
+ });
2846
+ return { title, items };
1867
2847
  }
1868
2848
 
1869
2849
  // src/composition.ts
@@ -1875,14 +2855,14 @@ function resolveFontSchemeReference(reference, lookup, path = "design.fontScheme
1875
2855
  const overrides = typeof reference === "object" && reference !== null && !Array.isArray(reference) ? reference : void 0;
1876
2856
  const id = typeof reference === "string" ? reference : typeof overrides?.id === "string" ? overrides.id : void 0;
1877
2857
  const found = id === void 0 ? void 0 : lookup(id);
1878
- const base = record4(found ?? lookup(DEFAULT_FONT_SCHEME));
2858
+ const base = record5(found ?? lookup(DEFAULT_FONT_SCHEME));
1879
2859
  const scheme = overrides ? { ...base, ...overrides } : { ...base };
1880
2860
  if (id === void 0 || found !== void 0) return { scheme };
1881
2861
  return { scheme, diagnostic: { code: "unresolved-font-scheme", path, id, fallback: DEFAULT_FONT_SCHEME, message: `Font scheme '${id}' is not in the inline or bundled catalogs; using the default font scheme '${DEFAULT_FONT_SCHEME}'.` } };
1882
2862
  }
1883
2863
  function resolveFontFamilies(input) {
1884
- const scheme = record4(input);
1885
- const family = (value) => typeof value === "string" ? value : record4(value).family;
2864
+ const scheme = record5(input);
2865
+ const family = (value) => typeof value === "string" ? value : record5(value).family;
1886
2866
  const accent = family(scheme.accent);
1887
2867
  return {
1888
2868
  heading: family(scheme.heading) ?? scheme.major ?? scheme.minor ?? DEFAULT_FONT_FAMILIES.heading,
@@ -1968,12 +2948,12 @@ var headings = /* @__PURE__ */ new Set(["title", "subtitle", "tag"]);
1968
2948
  var COVER_LAYOUT_IDS = /* @__PURE__ */ new Set(["title", "title-subtitle"]);
1969
2949
  var rows = ["top", "middle", "bottom"];
1970
2950
  var columns = ["left", "center", "right"];
1971
- var record4 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
2951
+ var record5 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
1972
2952
  var kind = (field) => field === "items" ? "list" : field === "bullets" ? "text" : field;
1973
2953
  var round2 = (value) => Math.round(value * 1e6) / 1e6 || value;
1974
2954
  var SLIDE_IMAGE_POSITIONS = ["background", "top", "bottom", "left", "right"];
1975
2955
  var SLIDE_IMAGE_BAND = 0.5;
1976
- var assetSource = (value) => typeof value === "string" ? value : record4(value).src;
2956
+ var assetSource = (value) => typeof value === "string" ? value : record5(value).src;
1977
2957
  var finite = (value, min, max) => typeof value === "number" && Number.isFinite(value) && value >= min && value <= max ? value : void 0;
1978
2958
  var pathNumber = (value) => String(round2(value));
1979
2959
  var SLIDE_IMAGE_SHAPES = { rectangle: "rect", rounded: "roundRect", circle: "ellipse", hexagon: "hexagon" };
@@ -1997,11 +2977,11 @@ function slideImageShape(kind2, box, cornerRadius = 1 / 6) {
1997
2977
  return { kind: "rectangle", preset: "rect", adjust: {}, path: rect };
1998
2978
  }
1999
2979
  function resolveSlideImage(slide, layout, presentation, width, height, path, padding, scale, diagnostics, rtl = false) {
2000
- const own = record4(slide.design), deck = record4(record4(presentation).design);
2001
- const local = own.slideImage !== void 0;
2002
- const configured = local ? own.slideImage : deck.slideImage;
2980
+ const own2 = record5(slide.design), deck = record5(record5(presentation).design);
2981
+ const local = own2.slideImage !== void 0;
2982
+ const configured = local ? own2.slideImage : deck.slideImage;
2003
2983
  if (!configured || typeof configured !== "object" && typeof configured !== "string") return void 0;
2004
- const treatment = typeof configured === "object" && !Array.isArray(configured) && "position" in configured ? record4(configured) : void 0;
2984
+ const treatment = typeof configured === "object" && !Array.isArray(configured) && "position" in configured ? record5(configured) : void 0;
2005
2985
  const alignment = typeof layout.slideImageAlignment === "string" ? layout.slideImageAlignment.toLowerCase() : void 0;
2006
2986
  const authoredPosition = treatment ? treatment.position : SLIDE_IMAGE_POSITIONS.find((value2) => value2 === alignment) ?? "background";
2007
2987
  if (!SLIDE_IMAGE_POSITIONS.includes(authoredPosition)) return void 0;
@@ -2015,7 +2995,7 @@ function resolveSlideImage(slide, layout, presentation, width, height, path, pad
2015
2995
  const source = assetSource(value);
2016
2996
  if (typeof source !== "string" || !source) return void 0;
2017
2997
  const t = treatment ?? {};
2018
- const fill = t.fill === "crop" || t.fill === "fit" ? t.fill : (own.imageFill ?? deck.imageFill) === "fit" ? "fit" : "crop";
2998
+ const fill = t.fill === "crop" || t.fill === "fit" ? t.fill : (own2.imageFill ?? deck.imageFill) === "fit" ? "fit" : "crop";
2019
2999
  const share = finite(t.size, 0.1, 0.9) ?? SLIDE_IMAGE_BAND;
2020
3000
  const band = { width: round2(width * share), height: round2(height * share) };
2021
3001
  const region = position === "left" ? { x: 0, y: 0, width: band.width, height } : position === "right" ? { x: round2(width - band.width), y: 0, width: band.width, height } : position === "top" ? { x: 0, y: 0, width, height: band.height } : position === "bottom" ? { x: 0, y: round2(height - band.height), width, height: band.height } : { x: 0, y: 0, width, height };
@@ -2040,13 +3020,13 @@ function resolveSlideImage(slide, layout, presentation, width, height, path, pad
2040
3020
  shape: slideImageShape(kind2, box, finite(t.cornerRadius, 0, 0.5))
2041
3021
  };
2042
3022
  if (typeof t.alt === "string") result.alt = t.alt;
2043
- const border = record4(t.border), borderWidth = finite(border.width, 0, 64);
3023
+ const border = record5(t.border), borderWidth = finite(border.width, 0, 64);
2044
3024
  if (border.color !== void 0 && borderWidth) result.border = { color: border.color, width: round2(borderWidth * scale) };
2045
3025
  const opacity = finite(t.opacity, 0, 1);
2046
3026
  if (opacity !== void 0 && opacity < 1) result.opacity = opacity;
2047
3027
  if (t.recolor === "grayscale") result.recolor = { type: "grayscale" };
2048
- else if (record4(t.recolor).dark !== void 0 && record4(t.recolor).light !== void 0) result.recolor = { type: "duotone", dark: t.recolor.dark, light: t.recolor.light };
2049
- const overlay = record4(t.overlay), overlayOpacity = finite(overlay.opacity, 0, 1);
3028
+ else if (record5(t.recolor).dark !== void 0 && record5(t.recolor).light !== void 0) result.recolor = { type: "duotone", dark: t.recolor.dark, light: t.recolor.light };
3029
+ const overlay = record5(t.overlay), overlayOpacity = finite(overlay.opacity, 0, 1);
2050
3030
  if (overlay.color !== void 0 && overlayOpacity !== void 0) {
2051
3031
  const edge = ["top", "bottom", "left", "right"].includes(overlay.edge) ? mirrorSide(overlay.edge) : void 0;
2052
3032
  if (edge && kind2 !== "rectangle") diagnostics.push({ code: "unsupported-image-treatment", path: `${designPath}.overlay.edge`, message: "An edge overlay needs a rectangle frame; a band cannot follow a rounded, circular or hexagonal mask as one native shape. Remove edge or use shape rectangle." });
@@ -2075,10 +3055,10 @@ function resolveLogo(presentation, slide, options = {}) {
2075
3055
  };
2076
3056
  const pick = (value, path) => {
2077
3057
  if (value === void 0 || value === null || value === false) return null;
2078
- if (typeof value === "string" || typeof value === "object" && !Array.isArray(value) && "src" in record4(value)) {
3058
+ if (typeof value === "string" || typeof value === "object" && !Array.isArray(value) && "src" in record5(value)) {
2079
3059
  return usable(value) ? { source: value, path, variant: "default", slot } : null;
2080
3060
  }
2081
- const set = record4(value);
3061
+ const set = record5(value);
2082
3062
  if (!Object.keys(set).some((key) => LOGO_SET_KEYS.has(key))) return null;
2083
3063
  for (const key of chain) {
2084
3064
  const variant = set[key];
@@ -2086,16 +3066,16 @@ function resolveLogo(presentation, slide, options = {}) {
2086
3066
  }
2087
3067
  return null;
2088
3068
  };
2089
- const deck = record4(presentation), slideIndex = Number.isSafeInteger(options.slideIndex) && options.slideIndex >= 0 ? options.slideIndex : 0;
2090
- const own = pick(record4(record4(slide).design).logo, `slides.${slideIndex}.design.logo`);
2091
- if (own) return own;
2092
- const shared = pick(record4(deck.design).logo, "design.logo");
3069
+ const deck = record5(presentation), slideIndex = Number.isSafeInteger(options.slideIndex) && options.slideIndex >= 0 ? options.slideIndex : 0;
3070
+ const own2 = pick(record5(record5(slide).design).logo, `slides.${slideIndex}.design.logo`);
3071
+ if (own2) return own2;
3072
+ const shared = pick(record5(deck.design).logo, "design.logo");
2093
3073
  if (shared) return shared;
2094
3074
  const organizations = Array.isArray(deck.organization) ? deck.organization : [deck.organization];
2095
- const primaryIndex = organizations.findIndex((item) => record4(item).role === "primary");
3075
+ const primaryIndex = organizations.findIndex((item) => record5(item).role === "primary");
2096
3076
  const index = primaryIndex >= 0 ? primaryIndex : organizations.findIndex(Boolean);
2097
3077
  if (index < 0) return null;
2098
- return pick(record4(organizations[index]).logo, Array.isArray(deck.organization) ? `organization.${index}.logo` : "organization.logo");
3078
+ return pick(record5(organizations[index]).logo, Array.isArray(deck.organization) ? `organization.${index}.logo` : "organization.logo");
2099
3079
  }
2100
3080
  var webUrl = /^https?:\/\/\S+$/i;
2101
3081
  function resolveSocialProfile(platform, value, records = [], owner = "organization") {
@@ -2113,8 +3093,8 @@ function resolveSocialProfile(platform, value, records = [], owner = "organizati
2113
3093
  return { text: display(url), href: encodeURI(url), resolved: true };
2114
3094
  }
2115
3095
  function layoutFurniture(input, options = {}) {
2116
- const slide = record4(input), width = options.width ?? 1280, height = options.height ?? 720, scale = Math.min(width, height) / 720;
2117
- const settings = { ...record4(record4(options.layout).composition), ...record4(slide.composition) };
3096
+ const slide = record5(input), width = options.width ?? 1280, height = options.height ?? 720, scale = Math.min(width, height) / 720;
3097
+ const settings = { ...record5(record5(options.layout).composition), ...record5(slide.composition) };
2118
3098
  assertComposition(settings);
2119
3099
  const furnitureRtl = (options.direction ?? resolveSlideDirection(options.presentation, options.slideIndex)) === "rtl";
2120
3100
  const minimum = snapFontSizeUp((settings.minFontSize ?? 16) * scale), size = gridFontSize(13 * scale, minimum), padding = (options.textRasterPadding ?? 2) * scale;
@@ -2126,14 +3106,14 @@ function layoutFurniture(input, options = {}) {
2126
3106
  if (options.date !== void 0 && (typeof options.date !== "string" || !parseIsoDate(options.date))) throw new RangeError("The furniture date option must be an ISO YYYY-MM-DD calendar date.");
2127
3107
  const outlines = options.textMeasurement?.outlineBounds !== void 0, parts = [], diagnostics = [];
2128
3108
  const sourceRoot = `slides.${options.slideIndex ?? 0}`, organizations = Array.isArray(options.presentation?.organization) ? options.presentation.organization : [options.presentation?.organization];
2129
- const primaryIndex = organizations.findIndex((item) => record4(item).role === "primary"), organizationIndex = primaryIndex >= 0 ? primaryIndex : organizations.findIndex(Boolean);
2130
- const organization = record4(organizations[organizationIndex]), organizationRoot = Array.isArray(options.presentation?.organization) ? `organization.${organizationIndex}` : "organization", organizationPath = `${organizationRoot}.name`;
2131
- const inlinePlatforms = record4(record4(options.presentation?.catalogs).socialPlatforms).records, platformRecords = [...Array.isArray(inlinePlatforms) ? inlinePlatforms : [], ...options.socialPlatforms ?? []];
3109
+ const primaryIndex = organizations.findIndex((item) => record5(item).role === "primary"), organizationIndex = primaryIndex >= 0 ? primaryIndex : organizations.findIndex(Boolean);
3110
+ const organization = record5(organizations[organizationIndex]), organizationRoot = Array.isArray(options.presentation?.organization) ? `organization.${organizationIndex}` : "organization", organizationPath = `${organizationRoot}.name`;
3111
+ const inlinePlatforms = record5(record5(options.presentation?.catalogs).socialPlatforms).records, platformRecords = [...Array.isArray(inlinePlatforms) ? inlinePlatforms : [], ...options.socialPlatforms ?? []];
2132
3112
  const fontFamily = options.fonts?.body ?? "sans-serif";
2133
3113
  let headerBottom = 0, footerTop = height, configured = false;
2134
3114
  const error = (path, message, code = "text-overflow") => diagnostics.push({ code, path, message });
2135
3115
  for (const kind2 of ["header", "footer"]) {
2136
- const local = record4(slide.design)[kind2] !== void 0, value = local ? record4(slide.design)[kind2] : options.presentation?.design?.[kind2];
3116
+ const local = record5(slide.design)[kind2] !== void 0, value = local ? record5(slide.design)[kind2] : options.presentation?.design?.[kind2];
2137
3117
  if (value === void 0 || value === false) continue;
2138
3118
  const root = local ? `${sourceRoot}.design.${kind2}` : `design.${kind2}`;
2139
3119
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("Header/footer content must be an object or false.");
@@ -2141,7 +3121,7 @@ function layoutFurniture(input, options = {}) {
2141
3121
  const zones = [];
2142
3122
  for (const [index, zone] of ["left", "center", "right"].entries()) {
2143
3123
  const side = furnitureRtl && zone !== "center" ? zone === "left" ? "right" : "left" : zone;
2144
- const content = record4(record4(value)[zone]), path = `${root}.${zone}`, x = width * (0.07 + (furnitureRtl ? 2 - index : index) * 0.3), zoneWidth = width * 0.26, zoneParts = [];
3124
+ const content = record5(record5(value)[zone]), path = `${root}.${zone}`, x = width * (0.07 + (furnitureRtl ? 2 - index : index) * 0.3), zoneWidth = width * 0.26, zoneParts = [];
2145
3125
  let y = 0;
2146
3126
  const add = (field, text2, generated = false, sourcePath, extras = {}) => {
2147
3127
  if (text2 === void 0) return;
@@ -2151,10 +3131,10 @@ function layoutFurniture(input, options = {}) {
2151
3131
  const ink = fit.sourceLines.map((line, index2) => {
2152
3132
  let outline = null;
2153
3133
  if (outlines) {
2154
- for (const segment of line.segments) if (segment.kind === "text") {
2155
- const bounds = measureTextOutline(text2.slice(segment.start, segment.end), size, style, options.textMeasurement);
3134
+ for (const segment2 of line.segments) if (segment2.kind === "text") {
3135
+ const bounds = measureTextOutline(text2.slice(segment2.start, segment2.end), size, style, options.textMeasurement);
2156
3136
  if (bounds) {
2157
- const next = { ...bounds, x: bounds.x + segment.x };
3137
+ const next = { ...bounds, x: bounds.x + segment2.x };
2158
3138
  if (!outline) outline = next;
2159
3139
  else {
2160
3140
  const right = Math.max(outline.x + outline.width, next.x + next.width), bottom = Math.max(outline.y + outline.height, next.y + next.height);
@@ -2178,7 +3158,7 @@ function layoutFurniture(input, options = {}) {
2178
3158
  y += box.height;
2179
3159
  };
2180
3160
  const imageBox = (source) => {
2181
- const imageHeight = Math.max(32 * scale, Math.min(height * 0.05, 72 * scale)), imageWidth = Math.min(zoneWidth, imageHeight * (intrinsicImageAspect(source, record4(options.presentation).assets) ?? 1));
3161
+ const imageHeight = Math.max(32 * scale, Math.min(height * 0.05, 72 * scale)), imageWidth = Math.min(zoneWidth, imageHeight * (intrinsicImageAspect(source, record5(options.presentation).assets) ?? 1));
2182
3162
  return { x: side === "left" ? x : side === "center" ? x + (zoneWidth - imageWidth) / 2 : x + zoneWidth - imageWidth, y, width: imageWidth, height: imageHeight };
2183
3163
  };
2184
3164
  if (content.logo === true) {
@@ -2200,7 +3180,7 @@ function layoutFurniture(input, options = {}) {
2200
3180
  else error(`${path}.organization`, "Generated organization name needs a named organization in the presentation.", "unresolved-content");
2201
3181
  }
2202
3182
  if (content.socials === true) {
2203
- const links = Object.entries(record4(organization.socials)).filter(([, value2]) => typeof value2 === "string" && value2.trim()).map(([platform, value2]) => ({ platform, sourcePath: `${organizationRoot}.socials.${platform}`, ...resolveSocialProfile(platform, value2, platformRecords, "organization") }));
3183
+ const links = Object.entries(record5(organization.socials)).filter(([, value2]) => typeof value2 === "string" && value2.trim()).map(([platform, value2]) => ({ platform, sourcePath: `${organizationRoot}.socials.${platform}`, ...resolveSocialProfile(platform, value2, platformRecords, "organization") }));
2204
3184
  if (links.length) add("socials", links.map((link) => link.text).join("\n"), true, `${organizationRoot}.socials`, { links });
2205
3185
  else error(`${path}.socials`, "Generated social profiles need a primary organization with socials.", "unresolved-content");
2206
3186
  }
@@ -2443,11 +3423,11 @@ function layoutMetric(value, box, options = {}) {
2443
3423
  const gap = 8 * scale, primaryGap = 12 * scale;
2444
3424
  const lineInk = (part, fit) => fit.sourceLines.map((line, index) => {
2445
3425
  let outline = null;
2446
- for (const segment of line.segments) {
2447
- if (segment.kind !== "text") continue;
2448
- const bounds = measureTextOutline(part.text.slice(segment.start, segment.end), fit.fontSize, part.style, options.textMeasurement);
3426
+ for (const segment2 of line.segments) {
3427
+ if (segment2.kind !== "text") continue;
3428
+ const bounds = measureTextOutline(part.text.slice(segment2.start, segment2.end), fit.fontSize, part.style, options.textMeasurement);
2449
3429
  if (!bounds) continue;
2450
- const next = { ...bounds, x: segment.x + bounds.x };
3430
+ const next = { ...bounds, x: segment2.x + bounds.x };
2451
3431
  if (!outline) outline = next;
2452
3432
  else {
2453
3433
  const right = Math.max(outline.x + outline.width, next.x + next.width), bottom = Math.max(outline.y + outline.height, next.y + next.height);
@@ -2602,13 +3582,13 @@ function layoutTimeline(value, box, options = {}) {
2602
3582
  const ink = fit.sourceLines.map((line, index) => {
2603
3583
  let left = Infinity, top = Infinity, right = -Infinity, bottom = -Infinity, hasInk = false;
2604
3584
  if (outlines) {
2605
- for (const segment of line.segments) if (segment.kind === "text") {
2606
- const outline = measureTextOutline(part.text.slice(segment.start, segment.end), size, part.style, options.textMeasurement);
3585
+ for (const segment2 of line.segments) if (segment2.kind === "text") {
3586
+ const outline = measureTextOutline(part.text.slice(segment2.start, segment2.end), size, part.style, options.textMeasurement);
2607
3587
  if (outline) {
2608
3588
  hasInk = true;
2609
- left = Math.min(left, outline.x + segment.x);
3589
+ left = Math.min(left, outline.x + segment2.x);
2610
3590
  top = Math.min(top, outline.y);
2611
- right = Math.max(right, outline.x + segment.x + outline.width);
3591
+ right = Math.max(right, outline.x + segment2.x + outline.width);
2612
3592
  bottom = Math.max(bottom, outline.y + outline.height);
2613
3593
  }
2614
3594
  }
@@ -2701,21 +3681,21 @@ function fitSourceText(text2, box, size, minimum, step, measure, prose = false,
2701
3681
  const layout = (fontSize) => {
2702
3682
  const tabWidth = measure(" ", fontSize) * 4;
2703
3683
  if (!Number.isFinite(tabWidth) || text2.includes(" ") && tabWidth <= 0) throw new RangeError("Text tabs require a positive finite measured space advance.");
2704
- const measureLine = (start2, end2, record5 = false) => {
3684
+ const measureLine = (start2, end2, record6 = false) => {
2705
3685
  let x = 0, offset = start2;
2706
3686
  const segments = [];
2707
- for (const [index, segment] of text2.slice(start2, end2).split(" ").entries()) {
3687
+ for (const [index, segment2] of text2.slice(start2, end2).split(" ").entries()) {
2708
3688
  if (index) {
2709
3689
  const next = (Math.floor(x / tabWidth + 1e-9) + 1) * tabWidth;
2710
- if (record5) segments.push({ kind: "tab", start: offset, end: offset + 1, x, width: next - x });
3690
+ if (record6) segments.push({ kind: "tab", start: offset, end: offset + 1, x, width: next - x });
2711
3691
  x = next;
2712
3692
  offset++;
2713
3693
  }
2714
- if (segment) {
2715
- const advance = measure(segment, fontSize);
2716
- if (record5) segments.push({ kind: "text", start: offset, end: offset + segment.length, x, width: advance });
3694
+ if (segment2) {
3695
+ const advance = measure(segment2, fontSize);
3696
+ if (record6) segments.push({ kind: "text", start: offset, end: offset + segment2.length, x, width: advance });
2717
3697
  x += advance;
2718
- offset += segment.length;
3698
+ offset += segment2.length;
2719
3699
  }
2720
3700
  }
2721
3701
  return { width: x, segments };
@@ -2757,9 +3737,9 @@ function fitSourceText(text2, box, size, minimum, step, measure, prose = false,
2757
3737
  end = b;
2758
3738
  continue;
2759
3739
  }
2760
- for (const segment of textSegments.segment(token[0])) {
2761
- const next = a + segment.index + segment.segment.length;
2762
- if (end > start && width(start, next) > box.width + 0.01) push(a + segment.index, a + segment.index, "soft");
3740
+ for (const segment2 of textSegments.segment(token[0])) {
3741
+ const next = a + segment2.index + segment2.segment.length;
3742
+ if (end > start && width(start, next) > box.width + 0.01) push(a + segment2.index, a + segment2.index, "soft");
2763
3743
  end = next;
2764
3744
  }
2765
3745
  }
@@ -2897,7 +3877,7 @@ function richTextLayouter(input, box, requestedSize, options, minimum = 0) {
2897
3877
  const size = script ? paint(normalSize * 0.7) : paint(normalSize), baselineShift = entry.run.superscript ? -normalSize * 0.35 : entry.run.subscript ? normalSize * 0.2 : 0;
2898
3878
  const measure = textWidthMeasurer(entry.style, options.textMeasurement);
2899
3879
  let cursor = 0;
2900
- for (const [index, segment] of text2.split(" ").entries()) {
3880
+ for (const [index, segment2] of text2.split(" ").entries()) {
2901
3881
  if (index) {
2902
3882
  const tabStart = a + cursor, tabWidth = measure(" ", size) * 4;
2903
3883
  if (!Number.isFinite(tabWidth) || tabWidth <= 0) throw new RangeError("Rich text tabs require a positive finite measured space advance.");
@@ -2906,11 +3886,11 @@ function richTextLayouter(input, box, requestedSize, options, minimum = 0) {
2906
3886
  x = next;
2907
3887
  cursor++;
2908
3888
  }
2909
- if (segment) {
2910
- const width2 = measure(segment, size), segmentStart = a + cursor;
2911
- result.push({ text: segment, runIndex: entry.runIndex, start: segmentStart - entry.start, end: segmentStart - entry.start + segment.length, x, width: width2, fontSize: size, baselineShift, style: entry.style, run: entry.run });
3889
+ if (segment2) {
3890
+ const width2 = measure(segment2, size), segmentStart = a + cursor;
3891
+ result.push({ text: segment2, runIndex: entry.runIndex, start: segmentStart - entry.start, end: segmentStart - entry.start + segment2.length, x, width: width2, fontSize: size, baselineShift, style: entry.style, run: entry.run });
2912
3892
  x += width2;
2913
- cursor += segment.length;
3893
+ cursor += segment2.length;
2914
3894
  }
2915
3895
  }
2916
3896
  if (entry.marker && b === entry.end) {
@@ -2953,8 +3933,8 @@ function richTextLayouter(input, box, requestedSize, options, minimum = 0) {
2953
3933
  lineEnd = end;
2954
3934
  continue;
2955
3935
  }
2956
- for (const { segment, index } of textSegments.segment(match[0])) {
2957
- const next = start + index + segment.length;
3936
+ for (const { segment: segment2, index } of textSegments.segment(match[0])) {
3937
+ const next = start + index + segment2.length;
2958
3938
  if (lineEnd > lineStart && width(lineStart, next) > box.width + WRAP_TOLERANCE) push(start + index);
2959
3939
  lineEnd = next;
2960
3940
  }
@@ -3069,19 +4049,26 @@ function fitList(input, box, requestedSize = 25, minFontSize = 16, options = { s
3069
4049
  function contentText(field, value) {
3070
4050
  if (field === "text" || headings.has(field)) return flatten(value);
3071
4051
  if (field === "items" || field === "bullets") return (Array.isArray(value) ? value : [value]).map((item) => {
3072
- const entry = record4(item);
4052
+ const entry = record5(item);
3073
4053
  return `\u2022 ${flatten(item)}${entry.description ? `
3074
4054
  ${flatten(entry.description)}` : ""}`;
3075
4055
  }).join("\n");
3076
- if (field === "code") return flatten(record4(value).source ?? value);
3077
- if (field === "quote") return flatten(record4(value).text ?? value);
4056
+ if (field === "code") return flatten(record5(value).source ?? value);
4057
+ if (field === "quote") return flatten(record5(value).text ?? value);
3078
4058
  return void 0;
3079
4059
  }
3080
4060
  function layoutTable(value, box, options = {}) {
3081
4061
  const scale = options.scale ?? 1, minimum = snapFontSizeUp((options.minFontSize ?? 16) * scale), requested = gridFontSize(15 * scale, minimum);
3082
4062
  if (![box.x, box.y, box.width, box.height, scale, minimum].every(Number.isFinite) || box.width <= 0 || box.height <= 0 || scale <= 0 || minimum <= 0) throw new RangeError("Table dimensions, scale and font sizes must be finite and positive.");
3083
- const grid = tableGrid(value, options.path ?? "table");
4063
+ const inline = inlineTableData(value, options.presentation);
4064
+ const grid = tableGrid(inline, options.path ?? "table");
3084
4065
  if (grid.issues.length) throw new RangeError(`${grid.issues[0].path}: ${grid.issues[0].message}`);
4066
+ const formats = resolveTableData(inline).formats;
4067
+ for (const row of grid.rows) for (const cell of row) {
4068
+ if (cell.header) continue;
4069
+ const shown = tableCellDisplayValue(cell.input, formats[cell.column]);
4070
+ if (shown !== cell.input) cell.value = typeof shown === "object" && shown !== null && !Array.isArray(shown) ? shown.value : shown;
4071
+ }
3085
4072
  const columnCount = grid.columnCount, cellWidth = box.width / columnCount;
3086
4073
  const cells = grid.rows.flat().map((cell) => {
3087
4074
  const padding = { top: 8, right: 10, bottom: 4, left: 10, ...cell.style.padding };
@@ -3132,13 +4119,13 @@ function layoutTable(value, box, options = {}) {
3132
4119
  return { rows: rows2, columnCount, height: sum(heights), overflow };
3133
4120
  }
3134
4121
  function tableOverflows(value, box, scale, settings, options, path) {
3135
- return box.width <= 0 || box.height <= 0 || layoutTable(value, box, { scale, minFontSize: settings.minFontSize, fontFamily: options.fonts?.body, textMeasurement: options.textMeasurement, path }).overflow;
4122
+ return box.width <= 0 || box.height <= 0 || layoutTable(value, box, { scale, minFontSize: settings.minFontSize, fontFamily: options.fonts?.body, textMeasurement: options.textMeasurement, path, presentation: options.presentation }).overflow;
3136
4123
  }
3137
4124
  function flatten(value) {
3138
4125
  if (value == null) return "";
3139
4126
  if (typeof value !== "object") return String(value);
3140
4127
  if (Array.isArray(value)) return value.map(flatten).join("");
3141
- const item = record4(value);
4128
+ const item = record5(value);
3142
4129
  return flatten(item.text ?? item.value ?? item.source ?? JSON.stringify(value));
3143
4130
  }
3144
4131
  function regionParts(key) {
@@ -3194,14 +4181,14 @@ function assertComposition(value) {
3194
4181
  if (value.overflow !== void 0 && !["warn", "error"].includes(value.overflow)) throw new RangeError("Invalid composition.overflow.");
3195
4182
  }
3196
4183
  function composeSlide(input, options = {}) {
3197
- const slide = record4(input), layout = record4(options.layout);
4184
+ const slide = record5(input), layout = record5(options.layout);
3198
4185
  const width = options.width ?? 1280, height = options.height ?? 720;
3199
4186
  if (![width, height].every((value) => Number.isFinite(value) && value > 0)) throw new RangeError("Canvas dimensions must be finite and positive.");
3200
4187
  const scale = Math.min(width, height) / 720;
3201
4188
  const deckDirection = options.direction ?? resolveSlideDirection(options.presentation, options.slideIndex);
3202
4189
  const rtl = deckDirection === "rtl", textDirection = rtl ? "rtl" : void 0;
3203
- const hasCards = record4(slide.design).contentBox ?? options.contentBox ?? false;
3204
- const composition = { ...record4(layout.composition), ...record4(slide.composition) };
4190
+ const hasCards = record5(slide.design).contentBox ?? options.contentBox ?? false;
4191
+ const composition = { ...record5(layout.composition), ...record5(slide.composition) };
3205
4192
  assertComposition(composition);
3206
4193
  const padding = (composition.padding ?? 0.08) * Math.min(width, height);
3207
4194
  const gap = (composition.gap ?? 1 / 30) * Math.min(width, height);
@@ -3209,13 +4196,13 @@ function composeSlide(input, options = {}) {
3209
4196
  const rasterPadding = (options.textRasterPadding ?? 1) * scale;
3210
4197
  if (!Number.isFinite(rasterPadding) || rasterPadding < 0) throw new RangeError("Text raster padding must be finite and nonnegative.");
3211
4198
  let coverGroup = false;
3212
- const alignmentFor = (field) => (field === "title" || coverGroup && (field === "tag" || field === "subtitle") && record4(slide.design).contentAlignment === void 0 ? record4(slide.design).titleAlignment ?? options.titleAlignment : record4(slide.design).contentAlignment ?? options.contentAlignment) ?? "left";
4199
+ const alignmentFor = (field) => (field === "title" || coverGroup && (field === "tag" || field === "subtitle") && record5(slide.design).contentAlignment === void 0 ? record5(slide.design).titleAlignment ?? options.titleAlignment : record5(slide.design).contentAlignment ?? options.contentAlignment) ?? "left";
3213
4200
  const styleFor = (field, path2) => resolveTextStyle({
3214
4201
  fontFamily: (field === "title" ? options.fonts?.heading : field === "code" ? options.fonts?.code : field === "tag" ? options.fonts?.accent ?? options.fonts?.body : options.fonts?.body) ?? (field === "code" ? "monospace" : "sans-serif"),
3215
4202
  fontWeight: field === "title" ? 700 : 400,
3216
4203
  path: path2
3217
4204
  }, options.textMeasurement);
3218
- const slideDesign = record4(slide.design), deckDesign = record4(record4(options.presentation).design);
4205
+ const slideDesign = record5(slide.design), deckDesign = record5(record5(options.presentation).design);
3219
4206
  const designHint = (field) => slideDesign[field] !== void 0 ? { value: slideDesign[field], path: `slides.${options.slideIndex ?? 0}.design.${field}` } : deckDesign[field] !== void 0 ? { value: deckDesign[field], path: `design.${field}` } : void 0;
3220
4207
  const listBullet = designHint("listBullet");
3221
4208
  const bulletImage = listBullet?.value === "image" ? (() => {
@@ -3255,11 +4242,11 @@ function composeSlide(input, options = {}) {
3255
4242
  return { width: line.width, y: line.y, baseline: line.baseline, height: line.height, outline };
3256
4243
  }) : fit.sourceLines.map((line, index) => {
3257
4244
  let outline = null;
3258
- for (const segment of line.segments) {
3259
- if (segment.kind === "tab") continue;
3260
- const bounds = measureTextOutline(text2.slice(segment.start, segment.end), fontSize, style, options.textMeasurement);
4245
+ for (const segment2 of line.segments) {
4246
+ if (segment2.kind === "tab") continue;
4247
+ const bounds = measureTextOutline(text2.slice(segment2.start, segment2.end), fontSize, style, options.textMeasurement);
3261
4248
  if (!bounds) continue;
3262
- const next = { ...bounds, x: segment.x + bounds.x };
4249
+ const next = { ...bounds, x: segment2.x + bounds.x };
3263
4250
  if (!outline) outline = next;
3264
4251
  else {
3265
4252
  const right = Math.max(outline.x + outline.width, next.x + next.width), bottom = Math.max(outline.y + outline.height, next.y + next.height);
@@ -3304,7 +4291,7 @@ function composeSlide(input, options = {}) {
3304
4291
  const regions = Object.keys(slide).filter((key) => regionParts(key)).sort();
3305
4292
  const emptyPayload = (value) => value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
3306
4293
  const emptyHost = (host) => emptyPayload(host.blocks) && fields.every((field) => emptyPayload(host[field]));
3307
- const hasBodyPayload = regions.some((key) => !emptyHost(record4(slide[key]))) || !emptyPayload(slide.blocks) || fields.some((field) => !emptyPayload(slide[field]));
4294
+ const hasBodyPayload = regions.some((key) => !emptyHost(record5(slide[key]))) || !emptyPayload(slide.blocks) || fields.some((field) => !emptyPayload(slide[field]));
3308
4295
  const isCover = !hasBodyPayload && (headingOnlyLayout || !layout.id && !layoutPlaceholders.some((placeholder) => !headings.has(placeholder.type ?? "")));
3309
4296
  coverGroup = isCover;
3310
4297
  let logo;
@@ -3347,7 +4334,7 @@ function composeSlide(input, options = {}) {
3347
4334
  const collect = (host, basePath, depth = 0, ancestors = []) => {
3348
4335
  if (Array.isArray(host.blocks)) {
3349
4336
  if (depth >= MAX_COMPOSITION_DEPTH || ancestors.includes(host)) throw new RangeError(`Content groups must be acyclic and nest at most ${MAX_COMPOSITION_DEPTH} levels.`);
3350
- const settings = record4(host.composition);
4337
+ const settings = record5(host.composition);
3351
4338
  assertComposition(settings);
3352
4339
  return [{
3353
4340
  field: "blocks",
@@ -3356,27 +4343,28 @@ function composeSlide(input, options = {}) {
3356
4343
  path: basePath,
3357
4344
  payload: host,
3358
4345
  composition: settings,
3359
- children: host.blocks.flatMap((block, index) => collect(record4(block), `${basePath}.blocks.${index}`, depth + 1, [...ancestors, host]))
4346
+ children: host.blocks.flatMap((block, index) => collect(record5(block), `${basePath}.blocks.${index}`, depth + 1, [...ancestors, host]))
3360
4347
  }];
3361
4348
  }
3362
4349
  const captioned = host.caption !== void 0 ? fields.filter((field) => host[field] !== void 0 && CAPTIONABLE_FIELDS.includes(field)) : [];
4350
+ const fieldValue = (field) => field === "table" ? inlineTableData(host[field], options.presentation) : field === "chart" ? inlineChartData(host[field], options.presentation) : host[field];
3363
4351
  return fields.filter((field) => host[field] !== void 0).map((field) => ({
3364
4352
  field,
3365
4353
  type: host.type ?? kind(field),
3366
- value: host[field],
4354
+ value: fieldValue(field),
3367
4355
  path: `${basePath}.${field}`,
3368
- payload: { type: host.type ?? kind(field), [field]: host[field], ...host.numbering !== void 0 && (field === "items" || field === "bullets") ? { numbering: host.numbering } : {} },
4356
+ payload: { type: host.type ?? kind(field), [field]: fieldValue(field), ...host.numbering !== void 0 && (field === "items" || field === "bullets") ? { numbering: host.numbering } : {} },
3369
4357
  ...captioned.length === 1 && captioned[0] === field ? { caption: host.caption, captionPath: `${basePath}.caption` } : {}
3370
4358
  }));
3371
4359
  };
3372
4360
  const regionsInReadingOrder = visualReadingOrder(regions.map((key) => ({ key, box: regionBox(regionParts(key), contentBox, gap) }))).map((entry) => entry.key);
3373
- const pending = regions.length ? regionsInReadingOrder.flatMap((key) => collect(record4(slide[key]), `${path}.${key}`).map((item) => ({ ...item, region: regionParts(key) }))) : Array.isArray(slide.blocks) ? slide.blocks.flatMap((block, index) => collect(record4(block), `${path}.blocks.${index}`)) : collect(slideImage?.replacesContent ? { ...slide, image: void 0 } : slide, path);
4361
+ const pending = regions.length ? regionsInReadingOrder.flatMap((key) => collect(record5(slide[key]), `${path}.${key}`).map((item) => ({ ...item, region: regionParts(key) }))) : Array.isArray(slide.blocks) ? slide.blocks.flatMap((block, index) => collect(record5(block), `${path}.blocks.${index}`)) : collect(slideImage?.replacesContent ? { ...slide, image: void 0 } : slide, path);
3374
4362
  const groups = [], flows = [];
3375
4363
  const decisions = options.explain ? [] : void 0;
3376
- const inheritedSettings = (parent, own = {}) => ({
4364
+ const inheritedSettings = (parent, own2 = {}) => ({
3377
4365
  minFontSize: parent.minFontSize,
3378
- ...own,
3379
- overflow: parent.overflow === "error" ? "error" : own.overflow ?? parent.overflow
4366
+ ...own2,
4367
+ overflow: parent.overflow === "error" ? "error" : own2.overflow ?? parent.overflow
3380
4368
  });
3381
4369
  const inset = (box, settings) => {
3382
4370
  const amount = (settings.padding ?? 0) * Math.min(box.width, box.height);
@@ -3467,10 +4455,10 @@ function composeSlide(input, options = {}) {
3467
4455
  };
3468
4456
  const scoreNode = (node, box, settings, penalties) => {
3469
4457
  if (!node.children) return leafScore(node, box, settings, penalties);
3470
- const own = inheritedSettings(settings, node.composition), area2 = inset(box, own);
3471
- const cols = defaultColumns(node.children.length, area2, own);
3472
- const boxes = gridBoxes(node.children.length, area2, cols, (own.gap ?? 1 / 30) * Math.min(box.width, box.height), own.weights ?? [], modeFor(own) === "column");
3473
- return node.children.reduce((score, child, index) => score + scoreNode(child, boxes[index], own, penalties), 0);
4458
+ const own2 = inheritedSettings(settings, node.composition), area2 = inset(box, own2);
4459
+ const cols = defaultColumns(node.children.length, area2, own2);
4460
+ const boxes = gridBoxes(node.children.length, area2, cols, (own2.gap ?? 1 / 30) * Math.min(box.width, box.height), own2.weights ?? [], modeFor(own2) === "column");
4461
+ return node.children.reduce((score, child, index) => score + scoreNode(child, boxes[index], own2, penalties), 0);
3474
4462
  };
3475
4463
  const arrange = (nodes, area2, settings, reserved = 0, gapOverride, containerPath = path, recorded = true) => {
3476
4464
  const count = Math.max(nodes.length, reserved);
@@ -3508,9 +4496,9 @@ function composeSlide(input, options = {}) {
3508
4496
  nodes.forEach((node, index) => {
3509
4497
  let box = node.region ? mirrored(regionBox(node.region, area2, actualGap)) : boxes[index];
3510
4498
  if (node.children) {
3511
- const own = inheritedSettings(settings, node.composition), inner = inset(box, own);
3512
- if (!node.synthetic) groups.push({ path: node.path, box, contentBox: inner, composition: own });
3513
- arrange(node.children, inner, own, 0, (own.gap ?? 1 / 30) * Math.min(box.width, box.height), node.path, !node.synthetic);
4499
+ const own2 = inheritedSettings(settings, node.composition), inner = inset(box, own2);
4500
+ if (!node.synthetic) groups.push({ path: node.path, box, contentBox: inner, composition: own2 });
4501
+ arrange(node.children, inner, own2, 0, (own2.gap ?? 1 / 30) * Math.min(box.width, box.height), node.path, !node.synthetic);
3514
4502
  } else {
3515
4503
  const frameBox = hasCards ? acceptedBox(box) : void 0;
3516
4504
  box = payloadBox(box);
@@ -3554,7 +4542,7 @@ function composeSlide(input, options = {}) {
3554
4542
  const picture = placeholders.findIndex((p) => p.type === "picture");
3555
4543
  if (picture >= 0) placeholders.splice(picture, 1);
3556
4544
  }
3557
- const ownMode = record4(slide.composition).mode;
4545
+ const ownMode = record5(slide.composition).mode;
3558
4546
  const direction = designHint("contentDirection")?.value;
3559
4547
  const directionMode = direction === "vertical" ? "column" : direction === "horizontal" ? "row" : void 0;
3560
4548
  const layoutDirectionMode = layout.slideLayoutDirection === "Vertical" ? "column" : layout.slideLayoutDirection === "Horizontal" ? "row" : "auto";
@@ -3619,7 +4607,7 @@ function resolveCanvasDimensions(input) {
3619
4607
  letter: [11, 8.5],
3620
4608
  a4: [11.69, 8.27]
3621
4609
  };
3622
- const value = record4(input);
4610
+ const value = record5(input);
3623
4611
  const preset = presets[typeof input === "string" ? input : value.preset] ?? presets.widescreen;
3624
4612
  const width = (value.widthInches ?? preset[0]) * 96;
3625
4613
  const height = (value.heightInches ?? preset[1]) * 96;
@@ -3627,4 +4615,4 @@ function resolveCanvasDimensions(input) {
3627
4615
  return { width, height };
3628
4616
  }
3629
4617
 
3630
- export { CAPTIONABLE_FIELDS, CAPTION_FONT_RATIO, CAPTION_MAX_RATIO, CHART_SERIES_MIN_DIFFERENCE, CHART_SERIES_MIN_LIGHTNESS_STEP, CITATION_MARKER_RAISE, CITATION_MARKER_SCALE, DEFAULT_CHART_LABEL_SEPARATOR, DEFAULT_FONT_SCHEME, DEFAULT_FURNITURE_DATE_FORMAT, DEFAULT_SLIDE_NUMBER_FORMAT, FONT_SIZE_GRID_PER_PX, FOOTNOTE_MAX_RATIO, MAX_COMPOSITION_DEPTH, MAX_NUMBERING_LEVELS, MAX_NUMBERING_VALUE, MAX_ROMAN_VALUE, NUMBERING_STYLES, NUMBERING_SUFFIXES, OPFCompositionError, PICTURE_BULLET_SCALE, annotationText, bareIdPattern, captionSettings, catalogIds, chartColorForFill, chartLabelText, chartOptionSupport, chartOptionTarget, chartPaletteForFill, citationMarkerText, collectCitations, colorContrast, composeSlide, deprecatedCatalogIds, fitList, fitRichText, fitText, formatChartLabelNumber, formatChartLabelPercent, formatFurnitureDate, formatListNumber, formatSlideNumber, intrinsicImageSize, isRecord2 as isRecord, layoutCaption, layoutCode, layoutFootnotes, layoutFurniture, layoutMetric, layoutQuote, layoutTable, layoutTimeline, listNumbers, measureText, measureTextOutline, normalizeHexColor, numberingAtLevel, numberingFindings, numberingStyleDraws, paragraphDirection, paragraphDirectionAt, parseIsoDate, pathFor, physicalAlignment, placeTextLines, promotedRegionKeys, readingRows, referencesSlide, resolveCanvasDimensions, resolveChartOptions, resolveColorRef, resolveFontFamilies, resolveFontSchemeReference, resolveLogo, resolveNumbering, resolveScriptFonts, resolveSlideDirection, resolveSocialProfile, resolveTextStyle, scriptFontRole, sliceNumberedItems, slideCitations, slideImageShape, snapFontSizeDown, snapFontSizeUp, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, visitContentPayloads, visualReadingOrder, walkCitationRuns, wrapText };
4618
+ export { CAPTIONABLE_FIELDS, CAPTION_FONT_RATIO, CAPTION_MAX_RATIO, CHART_SERIES_MIN_DIFFERENCE, CHART_SERIES_MIN_LIGHTNESS_STEP, CITATION_MARKER_RAISE, CITATION_MARKER_SCALE, DEFAULT_CHART_LABEL_SEPARATOR, DEFAULT_FONT_SCHEME, DEFAULT_FURNITURE_DATE_FORMAT, DEFAULT_SLIDE_NUMBER_FORMAT, DEFAULT_VARIABLE_DATE_FORMAT, FONT_SIZE_GRID_PER_PX, FOOTNOTE_MAX_RATIO, MAX_COMPOSITION_DEPTH, MAX_NUMBERING_LEVELS, MAX_NUMBERING_VALUE, MAX_ROMAN_VALUE, NUMBERING_STYLES, NUMBERING_SUFFIXES, OPFCompositionError, OPFVariableError, PICTURE_BULLET_SCALE, VARIABLE_KINDS, annotationText, bareIdPattern, captionSettings, catalogIds, chartColorForFill, chartLabelText, chartNumber, chartNumberFixesByCell, chartOptionSupport, chartOptionTarget, chartPaletteForFill, citationMarkerText, coerceVariableValue, collectCitations, colorContrast, composeSlide, datasetDiagnostics, deprecatedCatalogIds, excelNumberFormat, fitList, fitRichText, fitText, formatChartLabelNumber, formatChartLabelPercent, formatDataNumber, formatFurnitureDate, formatListNumber, formatSlideNumber, formatVariableNumber, hasContentVariables, inlineChartData, inlineDatasets, inlineTableData, instantiateForValidation, intrinsicImageSize, isDatasetRef, isRecord, isTemplate, isXYChartType, layoutCaption, layoutCode, layoutFootnotes, layoutFurniture, layoutMetric, layoutQuote, layoutTable, layoutTimeline, listNumbers, listVariables, measureText, measureTextOutline, normalizeHexColor, numberFormatError, numberFormatFromExcel, numberingAtLevel, numberingFindings, numberingStyleDraws, paragraphDirection, paragraphDirectionAt, pathFor, physicalAlignment, placeTextLines, promotedRegionKeys, readingRows, referencesSlide, resolveCanvasDimensions, resolveChartData, resolveChartOptions, resolveColorRef, resolveFontFamilies, resolveFontSchemeReference, resolveLogo, resolveNumbering, resolveScriptFonts, resolveSlideDirection, resolveSocialProfile, resolveTableData, resolveTextStyle, resolveVariables, scriptFontRole, sliceNumberedItems, slideCitations, slideImageShape, snapFontSizeDown, snapFontSizeUp, suggestChartNumberFix, tableCellDisplayValue, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, unusedDatasets, variableDeclarations, visitContentPayloads, visualReadingOrder, walkCitationRuns, wrapText };