@openpresentation/opf 0.16.0 → 0.17.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 (66) hide show
  1. package/README.md +16 -2
  2. package/dist/{catalog-refs-_anZs-Qz.d.ts → catalog-refs-C4K4y-vh.d.ts} +1 -1
  3. package/dist/{catalog-schemas-BZCjgAxu.d.ts → catalog-schemas-g7RFQBdc.d.ts} +1 -1
  4. package/dist/catalog.d.ts +2 -2
  5. package/dist/catalog.js +3 -23712
  6. package/dist/{chart-data-BnNqgnC-.d.ts → chart-data-DTxdRRT1.d.ts} +1 -1
  7. package/dist/{chunk-FQSIIADJ.js → chunk-BIC3SWPK.js} +4 -4
  8. package/dist/{chunk-5PDN7BN6.js → chunk-CW7GEAV3.js} +151 -49
  9. package/dist/{chunk-NAWQR7MN.js → chunk-ESRYBYLY.js} +1 -1
  10. package/dist/chunk-IPW5BS3X.js +973 -0
  11. package/dist/{chunk-5UEUAW57.js → chunk-IS5H5JKH.js} +1 -1
  12. package/dist/{chunk-UM24JFUE.js → chunk-M4UR3NPN.js} +2 -2
  13. package/dist/{chunk-KINQFU6X.js → chunk-N4EVOFEA.js} +19 -12
  14. package/dist/{chunk-PX5AWL7Y.js → chunk-NLNYNBTS.js} +79 -10
  15. package/dist/{chunk-SMLMI6IZ.js → chunk-O263APNI.js} +1 -1
  16. package/dist/{chunk-BSYQDWT4.js → chunk-O2CX5NEH.js} +26 -14
  17. package/dist/{chunk-3XDRQYWZ.js → chunk-O3CVNJTL.js} +1 -1
  18. package/dist/{chunk-5TO6I3GB.js → chunk-PPROJP2E.js} +1 -1
  19. package/dist/{chunk-WEDGVC5W.js → chunk-RA64UQKI.js} +18 -42
  20. package/dist/{chunk-K4DQQIT4.js → chunk-TCD2FB2K.js} +5 -5
  21. package/dist/chunk-WCG3THTD.js +23712 -0
  22. package/dist/chunk-WWYWFVOI.js +793 -0
  23. package/dist/{chunk-6OR326EA.js → chunk-YT7FMQER.js} +1 -1
  24. package/dist/{composition-D4ADF5c-.d.ts → composition-BbZ_9C-c.d.ts} +152 -132
  25. package/dist/composition.d.ts +3 -3
  26. package/dist/composition.js +1 -1
  27. package/dist/conversion-SBpTOwdg.d.ts +444 -0
  28. package/dist/convert.d.ts +4 -4
  29. package/dist/convert.js +7 -7
  30. package/dist/data.d.ts +2 -2
  31. package/dist/data.js +2 -2
  32. package/dist/deck-report-Bx3naFlF.d.ts +40 -0
  33. package/dist/deck.d.ts +23 -53
  34. package/dist/deck.js +10 -10
  35. package/dist/diff.d.ts +1 -1
  36. package/dist/diff.js +5 -5
  37. package/dist/docs.js +29 -23
  38. package/dist/examples.d.ts +1 -1
  39. package/dist/examples.js +237 -237
  40. package/dist/{finding-CT_f_vbD.d.ts → finding-CDQ_wH8j.d.ts} +1 -1
  41. package/dist/format.js +2 -2
  42. package/dist/index.d.ts +42 -12
  43. package/dist/index.js +15 -807
  44. package/dist/markdown.d.ts +6 -6
  45. package/dist/markdown.js +7 -7
  46. package/dist/node-engine.d.ts +94 -0
  47. package/dist/node-engine.js +21 -0
  48. package/dist/node.d.ts +60 -0
  49. package/dist/node.js +54 -0
  50. package/dist/{pagination-DzZPFheM.d.ts → pagination-BhX4M48Y.d.ts} +21 -7
  51. package/dist/pagination.d.ts +4 -4
  52. package/dist/pagination.js +4 -4
  53. package/dist/patch.d.ts +1 -1
  54. package/dist/patch.js +4 -4
  55. package/dist/{presentation-COrFuj2g.d.ts → presentation-fb8CZmRq.d.ts} +12 -75
  56. package/dist/repo-readme.js +1 -1
  57. package/dist/schemas.js +1 -1
  58. package/dist/spec/catalogs/manifest.json +1 -1
  59. package/dist/spec/schemas/opf.schema.json +16 -37
  60. package/dist/types.d.ts +3 -3
  61. package/dist/{validation-types-HxIgJfKx.d.ts → validation-types-iZbXfES6.d.ts} +19 -8
  62. package/dist/validator.d.ts +8 -8
  63. package/dist/validator.js +3 -3
  64. package/dist/yaml.d.ts +6 -6
  65. package/dist/yaml.js +7 -7
  66. package/package.json +34 -1
@@ -185,4 +185,4 @@ declare function resolveTableData(table: unknown, document?: unknown, options?:
185
185
  /** Display value of a table cell: a number with a format (cell, else column) becomes formatted text; anything else is returned unchanged. */
186
186
  declare function tableCellDisplayValue(cell: DataTableCell, columnFormat?: string): DataTableCell;
187
187
 
188
- export { resolveChartData as A, resolveTableData as B, type ChartMapping as C, type DataCellValue as D, suggestChartNumberFix as E, tableCellDisplayValue as F, toExcelNumberFormat as G, unusedDatasets as H, type ChartComboSeries as I, type ResolvedChartData as R, type ChartNumberFix as a, type ChartNumberFixOperation as b, type ChartNumberFixOptions as c, type DataColumn as d, type DataDiagnostic as e, type DataDiagnosticCode as f, type DataResolveOptions as g, type DataSourceRef as h, type DataStyledCell as i, type DataTableCell as j, type DataTableHeader as k, type DataTableValue as l, type DataTextRun as m, type Dataset as n, type DatasetRef as o, type ResolvedTableData as p, chartNumber as q, datasetDiagnostics as r, formatDataNumber as s, fromExcelNumberFormat as t, inlineChartData as u, inlineDatasets as v, inlineTableData as w, isDatasetRef as x, isXYChartType as y, numberFormatError as z };
188
+ export { numberFormatError as A, resolveChartData as B, type ChartComboSeries as C, type DataCellValue as D, resolveTableData as E, suggestChartNumberFix as F, tableCellDisplayValue as G, toExcelNumberFormat as H, unusedDatasets as I, type ResolvedChartData as R, type ChartMapping as a, type ChartNumberFix as b, type ChartNumberFixOperation as c, type ChartNumberFixOptions as d, type DataColumn as e, type DataDiagnostic as f, type DataDiagnosticCode as g, type DataResolveOptions as h, type DataSourceRef as i, type DataStyledCell as j, type DataTableCell as k, type DataTableHeader as l, type DataTableValue as m, type DataTextRun as n, type Dataset as o, type DatasetRef as p, type ResolvedTableData as q, chartNumber as r, datasetDiagnostics as s, formatDataNumber as t, fromExcelNumberFormat as u, inlineChartData as v, inlineDatasets as w, inlineTableData as x, isDatasetRef as y, isXYChartType as z };
@@ -1,7 +1,7 @@
1
- import { Ctx, splitLines, lineRange, readYamlMapping, NOT_CHECKED, OPFMarkdownError, writeYaml, readCsv, DECIMAL, csvField, scalarText } from './chunk-5UEUAW57.js';
2
- import { parseQuoteLines, levelsFromIndents, parseCodeFence, splitWhen, fenceCode } from './chunk-NAWQR7MN.js';
3
- import { same, isRecord, runText, isPlainRun, FORMATTING } from './chunk-5TO6I3GB.js';
4
- import { validate } from './chunk-PX5AWL7Y.js';
1
+ import { Ctx, splitLines, lineRange, readYamlMapping, NOT_CHECKED, OPFMarkdownError, writeYaml, readCsv, DECIMAL, csvField, scalarText } from './chunk-IS5H5JKH.js';
2
+ import { parseQuoteLines, levelsFromIndents, parseCodeFence, splitWhen, fenceCode } from './chunk-ESRYBYLY.js';
3
+ import { same, isRecord, runText, isPlainRun, FORMATTING } from './chunk-PPROJP2E.js';
4
+ import { validate } from './chunk-NLNYNBTS.js';
5
5
  import { checkCatalogsOption } from './chunk-SPFV27O5.js';
6
6
 
7
7
  // src/markdown/canon.ts
@@ -319,7 +319,6 @@ function visitContentPayloads(slide, slidePath, visit) {
319
319
 
320
320
  // src/furniture-fields.ts
321
321
  var DEFAULT_FURNITURE_DATE_FORMAT = "M/d/yyyy";
322
- var DEFAULT_SLIDE_NUMBER_FORMAT = "{current}";
323
322
  var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
324
323
  var weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
325
324
  var tokenWidths = { y: [1, 2, 4], M: [1, 2, 3, 4], d: [1, 2], E: [1, 2, 3, 4] };
@@ -407,21 +406,6 @@ function formatFurnitureDate(iso, pattern) {
407
406
  }
408
407
  return { text: text2 };
409
408
  }
410
- function formatSlideNumber(format, current, total) {
411
- if (!format.includes("{current}")) return { error: "slideNumberFormat must contain {current}." };
412
- if (format.includes("{total}") && total === void 0) return { error: "slideNumberFormat uses {total}, which needs the rendered slide count." };
413
- let text2 = "";
414
- const fields2 = [];
415
- for (const piece of format.split(/(\{current\}|\{total\})/)) {
416
- if (piece === "{current}") {
417
- const value = String(current);
418
- fields2.push({ type: "slideNumber", start: text2.length, end: text2.length + value.length });
419
- text2 += value;
420
- } else if (piece === "{total}") text2 += String(total);
421
- else text2 += piece;
422
- }
423
- return { text: text2, fields: fields2 };
424
- }
425
409
 
426
410
  // src/deck-metadata.ts
427
411
  function recordsOf(value) {
@@ -441,6 +425,111 @@ function primaryOrganization(presentation) {
441
425
  return organizations.find((item) => item.role === "primary") ?? organizations[0];
442
426
  }
443
427
 
428
+ // src/slide-variables.ts
429
+ var SLIDE_SCOPED_BUILTINS = ["slide.number", "slide.section", "deck.slideCount"];
430
+ function isSlideScopedBuiltin(name) {
431
+ return SLIDE_SCOPED_BUILTINS.includes(name);
432
+ }
433
+ function isSlideScopedName(name) {
434
+ return name === "deck.slideCount" || name.startsWith("slide.");
435
+ }
436
+ var SLIDE_TOKEN_BODY = String.raw`\s*(?:slide(?:\.[A-Za-z0-9_-]+){1,2}|deck\.slideCount)\s*(?:\|[^{}]*)?\}\}`;
437
+ var slideTokenAhead = new RegExp(`^${SLIDE_TOKEN_BODY}`);
438
+ var slideTokenPattern = new RegExp(String.raw`\\\{\{(?=${SLIDE_TOKEN_BODY})|\{\{\s*(slide\.number|slide\.section|deck\.slideCount)\s*(?:\|[^{}]*)?\}\}`, "g");
439
+ var slideTokenSpanPattern = new RegExp(String.raw`\\?\{\{${SLIDE_TOKEN_BODY}`, "g");
440
+ var quickCheck = /\{\{\s*(?:slide\.|deck\.slideCount)/;
441
+ function slideTokenFollows(text2) {
442
+ return slideTokenAhead.test(text2);
443
+ }
444
+ function checkValues(values) {
445
+ if (!isRecord(values)) throw new TypeError("Slide variable values must be an object with slideNumber and slideCount.");
446
+ if (!Number.isSafeInteger(values.slideNumber) || values.slideNumber < 1) throw new RangeError("slideNumber must be a positive safe integer.");
447
+ if (values.slideCount !== void 0 && (!Number.isSafeInteger(values.slideCount) || values.slideCount < 1)) throw new RangeError("slideCount must be a positive safe integer.");
448
+ }
449
+ function substituteSlideTokens(text2, values) {
450
+ if (!text2.includes("{{")) return { text: text2, fields: [], changed: false };
451
+ const fields2 = [];
452
+ let out = "";
453
+ let last = 0;
454
+ let changed = false;
455
+ slideTokenPattern.lastIndex = 0;
456
+ for (let match = slideTokenPattern.exec(text2); match; match = slideTokenPattern.exec(text2)) {
457
+ const name = match[1];
458
+ let value;
459
+ if (name === void 0) value = "{{";
460
+ else if (name === "slide.number") value = String(values.slideNumber);
461
+ else if (name === "deck.slideCount") {
462
+ if (values.slideCount === void 0) continue;
463
+ value = String(values.slideCount);
464
+ } else value = typeof values.section === "string" ? values.section : "";
465
+ out += text2.slice(last, match.index);
466
+ if (name === "slide.number") fields2.push({ type: "slideNumber", start: out.length, end: out.length + value.length });
467
+ out += value;
468
+ last = match.index + match[0].length;
469
+ changed = true;
470
+ }
471
+ if (!changed) return { text: text2, fields: fields2, changed };
472
+ return { text: out + text2.slice(last), fields: fields2, changed };
473
+ }
474
+ function slideTokenSpans(text2) {
475
+ if (!text2.includes("{{")) return [];
476
+ return Array.from(text2.matchAll(slideTokenSpanPattern), (match) => [match.index, match.index + match[0].length]);
477
+ }
478
+ var skippedKeys = /* @__PURE__ */ new Set(["extensions"]);
479
+ function substitute(value, values, root) {
480
+ if (typeof value === "string") {
481
+ const result = substituteSlideTokens(value, values);
482
+ return result.changed ? result.text : value;
483
+ }
484
+ if (Array.isArray(value)) {
485
+ let changed2 = false;
486
+ const out2 = value.map((entry) => {
487
+ const next = substitute(entry, values, false);
488
+ if (next !== entry) changed2 = true;
489
+ return next;
490
+ });
491
+ return changed2 ? out2 : value;
492
+ }
493
+ if (!isRecord(value)) return value;
494
+ let changed = false;
495
+ const out = {};
496
+ for (const [key, entry] of Object.entries(value)) {
497
+ let next = entry;
498
+ if (skippedKeys.has(key)) next = entry;
499
+ else if (root && key === "design" && isRecord(entry)) {
500
+ const design = {};
501
+ let designChanged = false;
502
+ for (const [designKey, designEntry] of Object.entries(entry)) {
503
+ const designNext = designKey === "header" || designKey === "footer" ? designEntry : substitute(designEntry, values, false);
504
+ if (designNext !== designEntry) designChanged = true;
505
+ design[designKey] = designNext;
506
+ }
507
+ next = designChanged ? design : entry;
508
+ } else next = substitute(entry, values, false);
509
+ if (next !== entry) changed = true;
510
+ out[key] = next;
511
+ }
512
+ return changed ? out : value;
513
+ }
514
+ function resolveSlideVariables(slide, values) {
515
+ if (!isRecord(slide)) return slide;
516
+ const full = { slideNumber: values?.slideNumber, ...values?.slideCount !== void 0 ? { slideCount: values.slideCount } : {}, section: slide.section };
517
+ checkValues(full);
518
+ if (!quickCheck.test(JSON.stringify(slide))) return slide;
519
+ return substitute(slide, full, true);
520
+ }
521
+ function usesSlideBuiltin(value, name) {
522
+ const pattern = new RegExp(String.raw`(?:^|[^\\])\{\{\s*${name.replace(".", String.raw`\.`)}\s*(?:\|[^{}]*)?\}\}`);
523
+ const visit = (entry) => {
524
+ if (typeof entry === "string") return entry.includes("{{") && pattern.test(entry);
525
+ if (Array.isArray(entry)) return entry.some(visit);
526
+ if (!isRecord(entry)) return false;
527
+ for (const [key, child] of Object.entries(entry)) if (!skippedKeys.has(key) && visit(child)) return true;
528
+ return false;
529
+ };
530
+ return visit(value);
531
+ }
532
+
444
533
  // src/variables.ts
445
534
  var VARIABLE_KINDS = ["color", "text", "number", "date", "image", "url", "list"];
446
535
  var DEFAULT_VARIABLE_DATE_FORMAT = "MMMM d, yyyy";
@@ -454,10 +543,10 @@ var OPFVariableError = class extends Error {
454
543
  }
455
544
  };
456
545
  var idPattern = /^[a-z][a-z0-9-]*$/;
457
- var nameSource = String.raw`[a-z][a-z0-9-]*|(?:deck|speaker|organization)(?:\.[A-Za-z0-9_-]+){1,2}`;
546
+ var nameSource = String.raw`[a-z][a-z0-9-]*|(?:deck|speaker|organization|slide)(?:\.[A-Za-z0-9_-]+){1,2}`;
458
547
  var referencePattern = new RegExp(String.raw`^var:(${nameSource})$`);
459
- var builtinNamePattern = /^(?:speakers|(?:deck|speaker|organization)(?:\.[A-Za-z0-9_-]+){1,2})$/;
460
- var builtinUsePattern = /\{\{\s*(?:speakers\b|(?:deck|speaker|organization)\.)|^var:(?:speakers$|(?:deck|speaker|organization)\.)/;
548
+ var builtinNamePattern = /^(?:speakers|(?:deck|speaker|organization|slide)(?:\.[A-Za-z0-9_-]+){1,2})$/;
549
+ var builtinUsePattern = /\{\{\s*(?:speakers\b|(?:deck|speaker|organization|slide)\.)|^var:(?:speakers$|(?:deck|speaker|organization|slide)\.)/;
461
550
  var hexPattern = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
462
551
  var urlPattern = /^(?:https?:\/\/|mailto:|tel:)\S+$/;
463
552
  var datePrefixPattern = /^(\d{4}-\d{2}-\d{2})(?:[T ].*)?$/;
@@ -560,7 +649,7 @@ function usesBuiltins(value, root = true) {
560
649
  if (Array.isArray(value)) return value.some((entry) => usesBuiltins(entry, false));
561
650
  if (!isRecord(value)) return false;
562
651
  for (const [key, entry] of Object.entries(value)) {
563
- if (skippedKeys.has(key) || root && skippedRootKeys.has(key)) continue;
652
+ if (skippedKeys2.has(key) || root && skippedRootKeys.has(key)) continue;
564
653
  if (usesBuiltins(entry, false)) return true;
565
654
  }
566
655
  return false;
@@ -672,13 +761,17 @@ function builtinSource(name, doc) {
672
761
  const names = speakersOf(doc).map((speaker) => present(speaker.name)).filter((entry) => typeof entry === "string");
673
762
  return { ok: true, kind: "list", raw: names.length ? names : void 0 };
674
763
  }
764
+ const list = (names) => names.map((entry) => `'${entry}'`).join(", ");
765
+ if (isSlideScopedName(name)) {
766
+ if (isSlideScopedBuiltin(name)) return { ok: false, message: `'var:${name}' is not supported: '${name}' is a slide-scoped built-in, written only as the inline token '{{${name}}}'.` };
767
+ return { ok: false, message: `'${name}' is not a built-in variable; the slide-scoped built-ins are ${list(SLIDE_SCOPED_BUILTINS)}.` };
768
+ }
675
769
  const parts = name.split(".");
676
770
  const root = parts[0];
677
771
  const fields2 = builtinFields(root);
678
- const list = (names) => names.map((entry) => `'${entry}'`).join(", ");
679
772
  if (root === "deck") {
680
773
  const field2 = parts[1];
681
- if (parts.length !== 2 || !Object.hasOwn(fields2, field2)) return { ok: false, message: `'${name}' is not a built-in variable; the deck built-ins are ${list(Object.keys(fields2).map((key) => `deck.${key}`))}.` };
774
+ if (parts.length !== 2 || !Object.hasOwn(fields2, field2)) return { ok: false, message: `'${name}' is not a built-in variable; the deck built-ins are ${list(Object.keys(fields2).map((key) => `deck.${key}`))} and the slide-scoped 'deck.slideCount'.` };
682
775
  const value = doc[field2];
683
776
  return { ok: true, kind: "text", raw: field2 === "author" && Array.isArray(value) ? present(value.filter((entry) => typeof entry === "string").join(", ")) : present(value) };
684
777
  }
@@ -742,15 +835,16 @@ function lookupVariable(id, path, context) {
742
835
  }
743
836
  function interpolate(text2, path, context) {
744
837
  let changed = false;
745
- const value = text2.replace(tokenPattern, (match, id, format) => {
838
+ const value = text2.replace(tokenPattern, (match, id, format, offset) => {
746
839
  if (id === void 0) {
747
- if (context.unescape) {
840
+ if (context.unescape && !slideTokenFollows(text2.slice(offset + match.length))) {
748
841
  changed = true;
749
842
  return "{{";
750
843
  }
751
844
  return match;
752
845
  }
753
846
  context.uses.push({ id, path, form: "token" });
847
+ if (isSlideScopedBuiltin(id)) return match;
754
848
  const effective = lookupVariable(id, path, context);
755
849
  if (!effective) {
756
850
  if (!isBuiltinName(id)) 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}`);
@@ -803,7 +897,7 @@ function reduceString(text2, path, parentIsArray, context) {
803
897
  const result = interpolate(text2, path, context);
804
898
  return result.changed ? { kind: "replace", value: result.value } : { kind: "keep" };
805
899
  }
806
- var skippedKeys = /* @__PURE__ */ new Set(["extensions"]);
900
+ var skippedKeys2 = /* @__PURE__ */ new Set(["extensions"]);
807
901
  var skippedRootKeys = /* @__PURE__ */ new Set(["variables", "catalogs", "$schema", "template"]);
808
902
  function walk(value, path, parentIsArray, context) {
809
903
  if (typeof value === "string") {
@@ -836,7 +930,7 @@ function walk(value, path, parentIsArray, context) {
836
930
  let changed = false;
837
931
  const entries = [];
838
932
  for (const [key, entry] of Object.entries(value)) {
839
- if (skippedKeys.has(key) || path === "" && skippedRootKeys.has(key)) {
933
+ if (skippedKeys2.has(key) || path === "" && skippedRootKeys.has(key)) {
840
934
  entries.push([key, entry]);
841
935
  continue;
842
936
  }
@@ -940,6 +1034,21 @@ function rebuildVariables(presentation, built, keepAll) {
940
1034
  }
941
1035
  return out.length ? Object.fromEntries(out) : void 0;
942
1036
  }
1037
+ function slideSectionDiagnostics(presentation, uses) {
1038
+ const paths = uses.filter((use) => use.id === "slide.section").map((use) => use.path);
1039
+ if (!paths.length || !Array.isArray(presentation.slides)) return [];
1040
+ const out = [];
1041
+ presentation.slides.forEach((slide, index) => {
1042
+ if (!isRecord(slide) || typeof slide.section === "string" && slide.section !== "") return;
1043
+ const own2 = `/slides/${index}/`;
1044
+ const design = isRecord(slide.design) ? slide.design : {};
1045
+ const inherited = ["header", "footer"].filter((kind2) => design[kind2] === void 0).map((kind2) => `/design/${kind2}/`);
1046
+ const where = paths.find((path) => path.startsWith(own2) || inherited.some((prefix) => path.startsWith(prefix)));
1047
+ if (where === void 0) return;
1048
+ out.push({ code: "variable-builtin-missing", severity: "warning", path: `/slides/${index}`, id: "slide.section", message: `Slide ${index + 1} has no section, so '{{slide.section}}' (used at ${where}) resolves to nothing on it; give the slide a section or remove the token.` });
1049
+ });
1050
+ return out;
1051
+ }
943
1052
  function finish(presentation, built, options) {
944
1053
  const { context } = built;
945
1054
  const complete = built.unfilled.length === 0;
@@ -958,6 +1067,7 @@ function finish(presentation, built, options) {
958
1067
  }
959
1068
  }
960
1069
  }
1070
+ if (context.shape) context.diagnostics.push(...slideSectionDiagnostics(presentation, context.uses));
961
1071
  const diagnostics = context.diagnostics;
962
1072
  if (options.strict && diagnostics.some((entry) => entry.severity === "error")) throw new OPFVariableError(diagnostics);
963
1073
  return { presentation: result, diagnostics, unfilled: built.unfilled, examplesUsed: built.examplesUsed, complete };
@@ -991,6 +1101,7 @@ function listVariables(presentation, values = {}) {
991
1101
  };
992
1102
  });
993
1103
  }
1104
+ var SLIDE_SCOPED_LABELS = { "slide.number": "Slide number", "slide.section": "Section", "deck.slideCount": "Slide count" };
994
1105
  var FIELD_LABELS = { name: "name", legalName: "legal name", tagline: "tagline", domain: "domain", email: "email", phone: "phone", logo: "logo", title: "title", bio: "bio", photo: "photo", description: "description", author: "author" };
995
1106
  function listBuiltinVariables(presentation) {
996
1107
  if (!isRecord(presentation)) return [];
@@ -1013,7 +1124,12 @@ function listBuiltinVariables(presentation) {
1013
1124
  const source = builtinSource(name, presentation);
1014
1125
  if (!source.ok) continue;
1015
1126
  const coerced = source.raw === void 0 ? void 0 : coerceVariableValue(source.kind, source.raw);
1016
- out.push({ name, kind: source.kind, label, ...coerced?.ok ? { value: coerced.value } : {}, available: coerced?.ok === true, uses: built.context.uses.filter((use) => use.id === name) });
1127
+ out.push({ name, kind: source.kind, label, scope: "deck", ...coerced?.ok ? { value: coerced.value } : {}, available: coerced?.ok === true, uses: built.context.uses.filter((use) => use.id === name) });
1128
+ }
1129
+ const slides = Array.isArray(presentation.slides) ? presentation.slides : [];
1130
+ for (const name of SLIDE_SCOPED_BUILTINS) {
1131
+ const available = name !== "slide.section" || slides.some((slide) => isRecord(slide) && typeof slide.section === "string" && slide.section !== "");
1132
+ out.push({ name, kind: "text", label: SLIDE_SCOPED_LABELS[name], scope: "slide", available, uses: built.context.uses.filter((use) => use.id === name) });
1017
1133
  }
1018
1134
  return out;
1019
1135
  }
@@ -4181,8 +4297,7 @@ function layoutFurniture(input, options = {}) {
4181
4297
  const outlines = options.textMeasurement?.outlineBounds !== void 0, parts = [], diagnostics = [];
4182
4298
  const sourceRoot = `slides.${options.slideIndex ?? 0}`, organizations = Array.isArray(options.presentation?.organization) ? options.presentation.organization : [options.presentation?.organization];
4183
4299
  const primaryIndex = organizations.findIndex((item) => record6(item).role === "primary"), organizationIndex = primaryIndex >= 0 ? primaryIndex : organizations.findIndex(Boolean);
4184
- const organization = record6(organizations[organizationIndex]), organizationRoot = Array.isArray(options.presentation?.organization) ? `organization.${organizationIndex}` : "organization", organizationPath = `${organizationRoot}.name`;
4185
- const speakers = Array.isArray(options.presentation?.speaker) ? options.presentation.speaker : [options.presentation?.speaker], speaker = record6(speakers[0]), speakerRoot = Array.isArray(options.presentation?.speaker) ? "speaker.0" : "speaker";
4300
+ const organization = record6(organizations[organizationIndex]), organizationRoot = Array.isArray(options.presentation?.organization) ? `organization.${organizationIndex}` : "organization";
4186
4301
  const fontFamily = options.fontFamilies?.body ?? "sans-serif";
4187
4302
  let headerBottom = 0, footerTop = height, configured = false;
4188
4303
  const error = (path, message, code = "text-overflow") => diagnostics.push({ code, path, message });
@@ -4248,30 +4363,17 @@ function layoutFurniture(input, options = {}) {
4248
4363
  zoneParts.push({ type: "image", kind: kind2, zone, field: "image", path: `${path}.image`, sourcePath: `${path}.image`, generated: false, image: content.image, box, alignment: side });
4249
4364
  y += box.height;
4250
4365
  }
4251
- add("text", content.text);
4252
- if (content.organization === true) {
4253
- if (typeof organization.name === "string") add("organization", organization.name, true, organizationPath);
4254
- else error(`${path}.organization`, "Generated organization name needs a named organization in the presentation.", "unresolved-content");
4255
- }
4256
- if (content.speaker === true) {
4257
- if (typeof speaker.name === "string" && speaker.name.trim()) add("speaker", typeof speaker.title === "string" && speaker.title.trim() ? `${speaker.name}, ${speaker.title}` : speaker.name, true, `${speakerRoot}.name`);
4258
- else error(`${path}.speaker`, "Generated speaker needs a named speaker in the presentation.", "unresolved-content");
4259
- }
4366
+ if (typeof content.text === "string") {
4367
+ const resolved = substituteSlideTokens(content.text, { slideNumber: number, ...slideCount !== void 0 ? { slideCount } : {}, section: slide.section });
4368
+ if (slideCount === void 0 && usesSlideBuiltin(content.text, "deck.slideCount")) error(`${path}.text`, "{{deck.slideCount}} needs the rendered slide count (the slideCount option or the presentation slides).", "unresolved-content");
4369
+ add("text", resolved.text, false, void 0, { fields: resolved.fields });
4370
+ } else add("text", content.text);
4260
4371
  if (content.socials === true) {
4261
4372
  const links = Object.entries(record6(organization.socials)).filter(([, value2]) => typeof value2 === "string" && value2.trim()).map(([platform, value2]) => ({ platform, sourcePath: `${organizationRoot}.socials.${platform}`, ...resolveSocialProfile(platform, value2, "organization") }));
4262
4373
  if (links.length) add("socials", links.map((link) => link.text).join("\n"), true, `${organizationRoot}.socials`, { links });
4263
4374
  else error(`${path}.socials`, "Generated social profiles need a primary organization with socials.", "unresolved-content");
4264
4375
  }
4265
- if (content.section === true) {
4266
- if (typeof slide.section === "string") add("section", slide.section, true, `${sourceRoot}.section`);
4267
- else error(`${path}.section`, "Generated section needs a literal slide section.", "unresolved-content");
4268
- }
4269
- for (const setting of ["slideNumberFormat", "dateFormat"]) if (content[setting] !== void 0 && typeof content[setting] !== "string") throw new TypeError(`Furniture setting ${path}.${setting} requires a string.`);
4270
- if (content.slideNumber === true) {
4271
- const resolved = formatSlideNumber(content.slideNumberFormat ?? DEFAULT_SLIDE_NUMBER_FORMAT, number, slideCount);
4272
- if ("error" in resolved) error(`${path}.slideNumberFormat`, resolved.error, "unresolved-content");
4273
- else add("slideNumber", resolved.text, true, void 0, { fields: resolved.fields });
4274
- }
4376
+ if (content.dateFormat !== void 0 && typeof content.dateFormat !== "string") throw new TypeError(`Furniture setting ${path}.dateFormat requires a string.`);
4275
4377
  if (content.date === true) {
4276
4378
  const format = content.dateFormat ?? DEFAULT_FURNITURE_DATE_FORMAT;
4277
4379
  if (options.date === void 0) error(`${path}.date`, "A current date needs a host-supplied ISO date option. For fixed content use a literal date, or an ISO date with dateFormat.", "unresolved-content");
@@ -5851,4 +5953,4 @@ function resolveCanvasDimensions(input) {
5851
5953
  return { width, height };
5852
5954
  }
5853
5955
 
5854
- export { CAPTIONABLE_FIELDS, CAPTION_FONT_RATIO, CAPTION_MAX_RATIO, CHART_HIGHLIGHT_MUTED_MIN_CONTRAST, CHART_HIGHLIGHT_MUTED_MIX, CHART_SERIES_MIN_DIFFERENCE, CHART_SERIES_MIN_LIGHTNESS_STEP, CHART_TYPES, CITATION_MARKER_RAISE, CITATION_MARKER_SCALE, CODE_HIGHLIGHT_LANGUAGES, CODE_HIGHLIGHT_MAX_LENGTH, CODE_PANEL_BACKGROUND, CODE_PANEL_FOREGROUND, CODE_SYNTAX_MIN_CONTRAST, DARK_BACKGROUND_LUMINANCE, DEFAULT_CHART_LABEL_SEPARATOR, DEFAULT_FURNITURE_DATE_FORMAT, DEFAULT_SLIDE_NUMBER_FORMAT, DEFAULT_VARIABLE_DATE_FORMAT, DESIGN_HINT_KEYS, ENGINE_DEFAULT_CHART_TYPES, ENGINE_DEFAULT_COLOR_SCHEME, ENGINE_DEFAULT_FONT_SCHEME, ENGINE_DEFAULT_THEME, FONT_SIZE_GRID_PER_PX, FOOTNOTE_MAX_RATIO, LANGUAGES, LAYOUT_BODY_KINDS, MAX_COMPOSITION_DEPTH, MAX_NUMBERING_LEVELS, MAX_NUMBERING_VALUE, MAX_PLACEHOLDER_GROUP_DEPTH, MAX_ROMAN_VALUE, METRIC_TREND_MIN_CONTRAST, METRIC_TREND_SHAPES, NUMBERING_STYLES, NUMBERING_SUFFIXES, OPFCompositionError, OPFVariableError, PATTERN_PRESETS, PATTERN_PRESET_ALIASES, PATTERN_TILE_SIZE, PICTURE_BULLET_SCALE, SOCIAL_PLATFORMS, SURFACE_ALT_MIN_CONTRAST, SURFACE_ALT_MIX, TIMELINE_OUTLINE_MIN_CONTRAST, TIMELINE_STATUSES, TIMELINE_TEXT_MIN_CONTRAST, VARIABLE_KINDS, annotationText, captionSettings, chartColorForFill, chartHighlightColors, chartHighlightMarks, chartLabelText, chartNumber, chartNumberFixesByCell, chartOptionSupport, chartOptionTarget, chartPaletteForFill, chartPrimaryLayout, citationMarkerText, codeHighlightBands, codeHighlightColors, codeHighlightLines, codeHighlightSlice, codeLineNumbers, codeLineRuns, codeSyntaxPalette, codeSyntaxPaletteForScheme, coerceVariableValue, collectCitations, colorContrast, composeSlide, datasetDiagnostics, defaultSlideBackground, fitImage, fitList, fitRichText, fitText, formatChartLabelNumber, formatChartLabelPercent, formatDataNumber, formatFurnitureDate, formatListNumber, formatSlideNumber, formatVariableNumber, fromExcelNumberFormat, hasContentVariables, hasPlaceholderGroups, imageBackground, imageShape, inlineChartData, inlineDatasets, inlineTableData, instantiateForValidation, intrinsicImageAspect, intrinsicImageSize, isDarkColor, isDatasetRef, isPlaceholderGroup, isRecord, isTemplate, isXYChartType, layoutCaption, layoutCode, layoutContent, layoutFootnotes, layoutFurniture, layoutLeaves, layoutMetric, layoutQuote, layoutSlots, layoutStructure, layoutTable, layoutTimeline, layoutWatermark, listBuiltinVariables, listNumbers, listVariables, measureText, measureTextOutline, metricTrendColor, metricTrendMark, metricTrendPoints, normalizeHexColor, normalizeLanguageFamily, numberFormatError, numberingAtLevel, numberingFindings, numberingStyleDraws, paragraphDirection, paragraphDirectionAt, pathFor, patternBitmap, patternRuns, physicalAlignment, placeTextLines, promotedRegionKeys, readingRows, referencesSlide, resolveCanvasDimensions, resolveChartData, resolveChartOptions, resolveCodeLanguage, resolveColorRef, resolveColorRoles, resolveDesignHints, resolveDesignRecords, resolveFontFamilies, resolveFontScheme, resolveLogo, resolveNumbering, resolvePatternPreset, resolveScriptFonts, resolveSlideDirection, resolveSocialProfile, resolveTableData, resolveTextStyle, resolveVariables, scriptFontRole, sliceNumberedItems, slideCitations, snapFontSizeDown, snapFontSizeUp, suggestChartNumberFix, surfaceAltColor, tableCellDisplayValue, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, timelineMarkerShapes, timelineTextColor, toExcelNumberFormat, tokenizeCode, unusedDatasets, variableDeclarations, visitContentPayloads, visualReadingOrder, walkCitationRuns, wrapText };
5956
+ export { CAPTIONABLE_FIELDS, CAPTION_FONT_RATIO, CAPTION_MAX_RATIO, CHART_HIGHLIGHT_MUTED_MIN_CONTRAST, CHART_HIGHLIGHT_MUTED_MIX, CHART_SERIES_MIN_DIFFERENCE, CHART_SERIES_MIN_LIGHTNESS_STEP, CHART_TYPES, CITATION_MARKER_RAISE, CITATION_MARKER_SCALE, CODE_HIGHLIGHT_LANGUAGES, CODE_HIGHLIGHT_MAX_LENGTH, CODE_PANEL_BACKGROUND, CODE_PANEL_FOREGROUND, CODE_SYNTAX_MIN_CONTRAST, DARK_BACKGROUND_LUMINANCE, DEFAULT_CHART_LABEL_SEPARATOR, DEFAULT_FURNITURE_DATE_FORMAT, DEFAULT_VARIABLE_DATE_FORMAT, DESIGN_HINT_KEYS, ENGINE_DEFAULT_CHART_TYPES, ENGINE_DEFAULT_COLOR_SCHEME, ENGINE_DEFAULT_FONT_SCHEME, ENGINE_DEFAULT_THEME, FONT_SIZE_GRID_PER_PX, FOOTNOTE_MAX_RATIO, LANGUAGES, LAYOUT_BODY_KINDS, MAX_COMPOSITION_DEPTH, MAX_NUMBERING_LEVELS, MAX_NUMBERING_VALUE, MAX_PLACEHOLDER_GROUP_DEPTH, MAX_ROMAN_VALUE, METRIC_TREND_MIN_CONTRAST, METRIC_TREND_SHAPES, NUMBERING_STYLES, NUMBERING_SUFFIXES, OPFCompositionError, OPFVariableError, PATTERN_PRESETS, PATTERN_PRESET_ALIASES, PATTERN_TILE_SIZE, PICTURE_BULLET_SCALE, SLIDE_SCOPED_BUILTINS, SOCIAL_PLATFORMS, SURFACE_ALT_MIN_CONTRAST, SURFACE_ALT_MIX, TIMELINE_OUTLINE_MIN_CONTRAST, TIMELINE_STATUSES, TIMELINE_TEXT_MIN_CONTRAST, VARIABLE_KINDS, annotationText, captionSettings, chartColorForFill, chartHighlightColors, chartHighlightMarks, chartLabelText, chartNumber, chartNumberFixesByCell, chartOptionSupport, chartOptionTarget, chartPaletteForFill, chartPrimaryLayout, citationMarkerText, codeHighlightBands, codeHighlightColors, codeHighlightLines, codeHighlightSlice, codeLineNumbers, codeLineRuns, codeSyntaxPalette, codeSyntaxPaletteForScheme, coerceVariableValue, collectCitations, colorContrast, composeSlide, datasetDiagnostics, defaultSlideBackground, fitImage, fitList, fitRichText, fitText, formatChartLabelNumber, formatChartLabelPercent, formatDataNumber, formatFurnitureDate, formatListNumber, formatVariableNumber, fromExcelNumberFormat, hasContentVariables, hasPlaceholderGroups, imageBackground, imageShape, inlineChartData, inlineDatasets, inlineTableData, instantiateForValidation, intrinsicImageAspect, intrinsicImageSize, isDarkColor, isDatasetRef, isPlaceholderGroup, isRecord, isTemplate, isXYChartType, layoutCaption, layoutCode, layoutContent, layoutFootnotes, layoutFurniture, layoutLeaves, layoutMetric, layoutQuote, layoutSlots, layoutStructure, layoutTable, layoutTimeline, layoutWatermark, listBuiltinVariables, listNumbers, listVariables, measureText, measureTextOutline, metricTrendColor, metricTrendMark, metricTrendPoints, normalizeHexColor, normalizeLanguageFamily, numberFormatError, numberingAtLevel, numberingFindings, numberingStyleDraws, paragraphDirection, paragraphDirectionAt, pathFor, patternBitmap, patternRuns, physicalAlignment, placeTextLines, promotedRegionKeys, readingRows, referencesSlide, resolveCanvasDimensions, resolveChartData, resolveChartOptions, resolveCodeLanguage, resolveColorRef, resolveColorRoles, resolveDesignHints, resolveDesignRecords, resolveFontFamilies, resolveFontScheme, resolveLogo, resolveNumbering, resolvePatternPreset, resolveScriptFonts, resolveSlideDirection, resolveSlideVariables, resolveSocialProfile, resolveTableData, resolveTextStyle, resolveVariables, scriptFontRole, sliceNumberedItems, slideCitations, slideTokenSpans, snapFontSizeDown, snapFontSizeUp, suggestChartNumberFix, surfaceAltColor, tableCellDisplayValue, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, timelineMarkerShapes, timelineTextColor, toExcelNumberFormat, tokenizeCode, unusedDatasets, usesSlideBuiltin, variableDeclarations, visitContentPayloads, visualReadingOrder, walkCitationRuns, wrapText };
@@ -1,4 +1,4 @@
1
- import { runText } from './chunk-5TO6I3GB.js';
1
+ import { runText } from './chunk-PPROJP2E.js';
2
2
 
3
3
  // src/convert/text-lines.ts
4
4
  var LIST_PREFIX = /^([ \t]*)(?:([-*•+]|\d{1,3}[.)])[ \t]+)?/;