@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
@@ -1,4 +1,4 @@
1
- import { isRecord, same } from './chunk-5TO6I3GB.js';
1
+ import { isRecord, same } from './chunk-PPROJP2E.js';
2
2
  import { parseDocument, YAMLMap, Scalar, stringify } from 'yaml';
3
3
 
4
4
  // src/not-checked.ts
@@ -1,5 +1,5 @@
1
- import { jsonEqual, formatPointer } from './chunk-3XDRQYWZ.js';
2
- import { annotationText } from './chunk-5PDN7BN6.js';
1
+ import { jsonEqual, formatPointer } from './chunk-O3CVNJTL.js';
2
+ import { annotationText } from './chunk-CW7GEAV3.js';
3
3
 
4
4
  // src/diff-match.ts
5
5
  var DEFAULT_MATCH_THRESHOLD = 0.5;
@@ -1,10 +1,10 @@
1
- import { fromMarkdown, toMarkdown } from './chunk-FQSIIADJ.js';
2
- import { fromYaml, toYaml } from './chunk-K4DQQIT4.js';
3
- import { NOT_CHECKED } from './chunk-5UEUAW57.js';
4
- import { parseSource, validate } from './chunk-PX5AWL7Y.js';
1
+ import { fromMarkdown, toMarkdown } from './chunk-BIC3SWPK.js';
2
+ import { fromYaml, toYaml } from './chunk-TCD2FB2K.js';
3
+ import { NOT_CHECKED } from './chunk-IS5H5JKH.js';
4
+ import { parseSource, validate, OPFValidationError } from './chunk-NLNYNBTS.js';
5
5
  import { checkCatalogsOption } from './chunk-SPFV27O5.js';
6
6
 
7
- // src/deck.ts
7
+ // src/deck-report.ts
8
8
  var DECK_FORMATS = ["json", "yaml", "markdown"];
9
9
  function deckFormatOf(filename) {
10
10
  if (filename === void 0) return "json";
@@ -17,10 +17,10 @@ function resolveFormat(caller, options) {
17
17
  if (!DECK_FORMATS.includes(options.format)) throw new TypeError(`${caller}: unknown format ${JSON.stringify(options.format)}. Formats: ${DECK_FORMATS.join(", ")}.`);
18
18
  return options.format;
19
19
  }
20
- function readDeck(text, options = {}) {
21
- if (typeof text !== "string") throw new TypeError("readDeck expects a string.");
22
- checkCatalogsOption(options.catalogs, "readDeck");
23
- const format = resolveFormat("readDeck", options);
20
+ function readDeckReport(text, options = {}, caller = "parse") {
21
+ if (typeof text !== "string") throw new TypeError(`${caller} expects a string.`);
22
+ checkCatalogsOption(options.catalogs, caller);
23
+ const format = resolveFormat(caller, options);
24
24
  const { catalogs } = options;
25
25
  const check = options.validate ?? true;
26
26
  const validateOptions = typeof check === "object" ? { ...catalogs && check.catalogs === void 0 ? { catalogs } : {}, ...check } : { only: ["format", "references"], ...catalogs ? { catalogs } : {} };
@@ -40,12 +40,19 @@ function report(findings) {
40
40
  for (const found of findings) counts[found.severity]++;
41
41
  return { valid: counts.error === 0, schemaValid: null, findings, counts, checks: NOT_CHECKED };
42
42
  }
43
- function writeDeck(presentation, options = {}) {
44
- const format = resolveFormat("writeDeck", options);
43
+
44
+ // src/deck.ts
45
+ function parse(text, options = {}) {
46
+ const { presentation, format: _format, ...result } = readDeckReport(text, { ...options, validate: true }, "parse");
47
+ if (!result.valid) throw new OPFValidationError(result);
48
+ return presentation;
49
+ }
50
+ function stringify(presentation, options = {}) {
51
+ const format = resolveFormat("stringify", options);
45
52
  if (format === "yaml") return toYaml(presentation, { schemaComment: options.schemaComment === true }).yaml;
46
53
  if (format === "markdown") return toMarkdown(presentation).markdown;
47
54
  return `${JSON.stringify(presentation, null, 2)}
48
55
  `;
49
56
  }
50
57
 
51
- export { DECK_FORMATS, deckFormatOf, readDeck, writeDeck };
58
+ export { DECK_FORMATS, deckFormatOf, parse, readDeckReport, stringify };
@@ -1,7 +1,7 @@
1
- import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, imageBackground, tableGrid, timelineTextColor, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, textWidthMeasurer, normalizeHexColor, resolveColorRef, resolveDesignRecords, resolveColorRoles, resolveFontFamilies, resolveCanvasDimensions, defaultSlideBackground, isRecord, hasContentVariables, isTemplate, instantiateForValidation, unusedDatasets, MAX_COMPOSITION_DEPTH, datasetDiagnostics, pathFor, collectCitations, numberingFindings, walkCitationRuns, resolveTableData, resolveChartOptions, chartOptionTarget, codeHighlightLines, chartNumberFixesByCell, MAX_PLACEHOLDER_GROUP_DEPTH, OPFCompositionError, composeSlide, resolveScriptFonts } from './chunk-5PDN7BN6.js';
1
+ import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, imageBackground, tableGrid, timelineTextColor, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, textWidthMeasurer, normalizeHexColor, resolveColorRef, resolveDesignRecords, resolveFontFamilies, resolveColorRoles, resolveCanvasDimensions, resolveSlideVariables, defaultSlideBackground, isRecord, hasContentVariables, isTemplate, instantiateForValidation, resolveScriptFonts, unusedDatasets, MAX_COMPOSITION_DEPTH, datasetDiagnostics, pathFor, collectCitations, numberingFindings, walkCitationRuns, resolveTableData, resolveChartOptions, chartOptionTarget, codeHighlightLines, chartNumberFixesByCell, MAX_PLACEHOLDER_GROUP_DEPTH, OPFCompositionError, composeSlide } from './chunk-CW7GEAV3.js';
2
2
  import { catalogSchemaNames } from './chunk-TRDXRRQK.js';
3
3
  import { resolveReference, checkCatalogsOption, provenanceOf, unresolvedReference, OPFUnresolvedReferenceError, catalogReferenceSites, parseReference, catalogGroupDeclared, pointerPath, catalogRecords, catalogKinds, catalogGroupSource, registeredCatalog } from './chunk-SPFV27O5.js';
4
- import { schemaEntries, schemas } from './chunk-WEDGVC5W.js';
4
+ import { schemaEntries, schemas } from './chunk-RA64UQKI.js';
5
5
  import Ajv2020 from 'ajv/dist/2020.js';
6
6
  import addFormats from 'ajv-formats';
7
7
  import { parseTree, printParseErrorCode, getNodePath } from 'jsonc-parser';
@@ -2421,7 +2421,7 @@ var formatRules = [
2421
2421
  rule("chart-mapping-unknown-column", "format", "error", "A chart mapping names a column the data does not have.", "The series cannot be built from a column that is not there.", { standard: SCHEMA }),
2422
2422
  rule("number-format-invalid", "format", "error", "A number format cannot be read.", "A format code that no engine can parse is drawn as the plain number or fails to export.", { standard: "Excel number format codes, as far as OPF supports them" }),
2423
2423
  rule("variable-invalid-value", "format", "error", "A variable value does not match its declared type.", "A number variable given text, or a date given a non-date, cannot be filled into the deck.", { standard: SCHEMA }),
2424
- rule("variable-unknown-builtin", "format", "error", "A token names a built-in variable that does not exist.", "The `deck.`, `speaker.` and `organization.` names (and `speakers`) are reserved for built-in variables; a name under them that is not a built-in can never be filled, so the token would show literally.", { standard: SCHEMA }),
2424
+ rule("variable-unknown-builtin", "format", "error", "A token names a built-in variable that does not exist.", "The `deck.`, `speaker.`, `organization.` and `slide.` names (and `speakers`) are reserved for built-in variables; a name under them that is not a built-in can never be filled, so the token would show literally. The slide-scoped `slide.number`, `slide.section` and `deck.slideCount` are inline tokens only, so `var:slide.*` and `var:deck.slideCount` are reported too.", { standard: SCHEMA }),
2425
2425
  rule("chart-highlight-unknown-name", "format", "error", "A chart highlight names a series or category the chart does not have.", "`chart.highlight` emphasises marks by name; a name that matches no series or category in the chart data highlights nothing, so the chart is not drawn as authored.", { standard: SCHEMA }),
2426
2426
  rule("variable-format", "format", "error", "A variable cannot be formatted.", "A number or date format that does not apply to the value leaves the token unreadable.", { standard: SCHEMA }),
2427
2427
  rule(
@@ -2505,7 +2505,7 @@ var dataRules = [
2505
2505
  rule("chart-highlight-adapted", "content", "warning", "A chart highlight is adapted by the chart type.", "A highlight the chart type cannot show as authored (a category highlight on a chart without categories, for example) is adapted by every engine, so the emphasis is not exactly what was written.", { standard: SCHEMA }),
2506
2506
  rule("code-highlight-out-of-range", "content", "warning", "A code highlight marks a line the code does not have.", "Marked lines count from 1 by line break in code.source; an entry past the last line marks nothing.", { standard: SCHEMA }),
2507
2507
  rule("code-highlight-range-reversed", "content", "warning", "A code highlight range ends before it starts.", "A range written end before start marks nothing; write the smaller line number first.", { standard: SCHEMA }),
2508
- rule("variable-builtin-missing", "content", "warning", "A built-in variable has no value in this document.", "A `{{deck.*}}`, `{{speaker.*}}` or `{{organization.*}}` token whose field the document does not set resolves to nothing, so the text shows a gap.", { standard: SCHEMA }),
2508
+ rule("variable-builtin-missing", "content", "warning", "A built-in variable has no value in this document.", "A `{{deck.*}}`, `{{speaker.*}}` or `{{organization.*}}` token whose field the document does not set resolves to nothing, so the text shows a gap. `{{slide.section}}` is checked per slide: a slide without a `section` that uses it, in its own text or in the header or footer it inherits, is reported at that slide.", { standard: SCHEMA }),
2509
2509
  rule("duration-outside-narrative", "content", "warning", "The target duration is outside the range the narrative suits.", "A narrative records the talk lengths it suits. A deck whose duration is outside that range probably needs a different narrative or a different length; nothing is drawn or exported differently.", { standard: SCHEMA }),
2510
2510
  rule("narrative-duration-range", "content", "warning", "An inline narrative duration range has min greater than max.", "A range with its bounds swapped matches no duration, so the duration check cannot help.", { standard: "spec/schemas/narrative.schema.json" })
2511
2511
  ];
@@ -3245,6 +3245,67 @@ function parseSource(source) {
3245
3245
  };
3246
3246
  }
3247
3247
 
3248
+ // src/script-measurement.ts
3249
+ function scriptProfile(presentation, slideIndex, options, language) {
3250
+ let resolved;
3251
+ try {
3252
+ resolved = resolveScriptFonts(presentation, { slideIndex, ...options.catalogs !== void 0 ? { catalogs: options.catalogs } : {}, ...language === void 0 ? {} : { language } });
3253
+ } catch {
3254
+ resolved = void 0;
3255
+ }
3256
+ const slots = (role) => {
3257
+ const latin = options.fontFamilies[role];
3258
+ const slot = (key) => !resolved || resolved.sources[key] === "latin" || resolved.sources[key] === "schemeFamily" ? latin : resolved[role][key] ?? latin;
3259
+ return { latin, eastAsian: slot("eastAsian"), complexScript: slot("complexScript") };
3260
+ };
3261
+ return {
3262
+ heading: slots("heading"),
3263
+ body: slots("body"),
3264
+ ...resolved?.supplement ? { supplement: resolved.supplement } : {},
3265
+ script: resolved?.script ?? "Zzzz",
3266
+ ...resolved ? { scriptRole: resolved.scriptRole, bcp47: resolved.bcp47, lang: resolved.lang, languageSource: resolved.languageSource, direction: resolved.direction } : {},
3267
+ rtl: resolved?.rtl === true,
3268
+ serif: options.serif
3269
+ };
3270
+ }
3271
+ function slideScriptMeasurement(presentation, slideIndex, measurement, options) {
3272
+ const forScripts = measurement.forScripts;
3273
+ if (typeof forScripts !== "function") return measurement;
3274
+ const deckProfile = scriptProfile(presentation, slideIndex, options);
3275
+ const deck = forScripts.call(measurement, deckProfile);
3276
+ const languages = /* @__PURE__ */ new Map();
3277
+ const forLanguage = (lang) => {
3278
+ if (typeof lang !== "string" || !lang) return void 0;
3279
+ if (!languages.has(lang)) {
3280
+ let own2;
3281
+ try {
3282
+ const profile = scriptProfile(presentation, slideIndex, options, lang);
3283
+ own2 = profile.languageSource === "option" && profile.bcp47?.toLowerCase() !== deckProfile.bcp47?.toLowerCase() ? forScripts.call(measurement, profile) : void 0;
3284
+ } catch {
3285
+ own2 = void 0;
3286
+ }
3287
+ languages.set(lang, own2);
3288
+ }
3289
+ return languages.get(lang);
3290
+ };
3291
+ const pick = (style) => forLanguage(style?.lang) ?? deck;
3292
+ const scripted = { ...deck, measure: (text, size, style) => pick(style).measure(text, size, style) };
3293
+ if (typeof deck.resolveStyle === "function") scripted.resolveStyle = (style) => deck.resolveStyle.call(deck, style);
3294
+ if (typeof deck.outlineBounds === "function")
3295
+ scripted.outlineBounds = (text, size, style) => {
3296
+ const own2 = pick(style);
3297
+ return typeof own2.outlineBounds === "function" ? own2.outlineBounds(text, size, style) : deck.outlineBounds.call(deck, text, size, style);
3298
+ };
3299
+ return scripted;
3300
+ }
3301
+ function resolveSlideScriptMeasurement(presentation, slideIndex, measurement, options = {}) {
3302
+ if (typeof measurement.forScripts !== "function") return measurement;
3303
+ const { fontScheme } = resolveDesignRecords(presentation, slideIndex, options.catalogs !== void 0 ? { catalogs: options.catalogs } : {});
3304
+ const resolved = resolveFontFamilies(fontScheme);
3305
+ const fontFamilies = { heading: options.fontFamilies?.heading ?? resolved.heading, body: options.fontFamilies?.body ?? resolved.body };
3306
+ return slideScriptMeasurement(presentation, slideIndex, measurement, { ...options.catalogs !== void 0 ? { catalogs: options.catalogs } : {}, fontFamilies, serif: fontScheme.type === "serif" });
3307
+ }
3308
+
3248
3309
  // src/slide-context.ts
3249
3310
  var rec2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
3250
3311
  function resolveSlideContext(presentation, index, options = {}) {
@@ -3280,7 +3341,8 @@ function resolveSlideContext(presentation, index, options = {}) {
3280
3341
  ...options.fonts?.textMeasurement ? { textMeasurement: options.fonts.textMeasurement } : {},
3281
3342
  ...options.date !== void 0 ? { date: options.date } : {}
3282
3343
  };
3283
- return { options: composeOptions, diagnostics, resolved: { theme, colorScheme, fontScheme, fontSchemePath: records.fontSchemePath, ...layout ? { layout } : {}, provenance: { ...layoutProvenance ? { layout: layoutProvenance } : {}, ...records.provenance } } };
3344
+ const resolvedSlide = resolveSlideVariables(slide, { slideNumber: composeOptions.slideNumber, slideCount: composeOptions.slideCount });
3345
+ return { slide: resolvedSlide, options: composeOptions, diagnostics, resolved: { theme, colorScheme, fontScheme, fontSchemePath: records.fontSchemePath, ...layout ? { layout } : {}, provenance: { ...layoutProvenance ? { layout: layoutProvenance } : {}, ...records.provenance } } };
3284
3346
  }
3285
3347
 
3286
3348
  // src/rule-slides.ts
@@ -3316,10 +3378,17 @@ function buildSlides(document, options) {
3316
3378
  const slides = Array.isArray(document.slides) ? document.slides : [];
3317
3379
  return slides.map((slide, index) => {
3318
3380
  const path = pointer2("slides", index);
3319
- const measurement = lazy(() => measurementFor(options, index));
3381
+ const baseContext = lazy(() => resolveSlideContext(document, index, { catalogs: options.catalogs, date: "2000-01-01" }));
3382
+ const measurement = lazy(() => {
3383
+ const host = measurementFor(options, index);
3384
+ if (!host) return void 0;
3385
+ const { resolved: resolved2 } = baseContext();
3386
+ return slideScriptMeasurement(document, index, host, { catalogs: options.catalogs, fontFamilies: resolveFontFamilies(resolved2.fontScheme), serif: resolved2.fontScheme.type === "serif" });
3387
+ });
3320
3388
  const resolved = lazy(() => {
3321
3389
  const textMeasurement = measurement();
3322
- return resolveSlideContext(document, index, { catalogs: options.catalogs, ...textMeasurement ? { fonts: { textMeasurement } } : {}, date: "2000-01-01" });
3390
+ const slideContext = baseContext();
3391
+ return textMeasurement ? { ...slideContext, options: { ...slideContext.options, textMeasurement } } : slideContext;
3323
3392
  });
3324
3393
  const design = lazy(() => resolveDesign(document, index, resolved()));
3325
3394
  const layout = lazy(() => resolved().options.layout);
@@ -3327,14 +3396,14 @@ function buildSlides(document, options) {
3327
3396
  const result = { layoutDiagnostics: [] };
3328
3397
  const compose = (input, withLayout) => composeSlide(input, { ...resolved().options, layout: withLayout });
3329
3398
  try {
3330
- result.composition = compose(slide, layout());
3399
+ result.composition = compose(resolved().slide, layout());
3331
3400
  result.layoutDiagnostics.push(...result.composition.diagnostics.map((diagnostic) => ({ diagnostic, strict: false })));
3332
3401
  } catch (error) {
3333
3402
  if (error instanceof OPFCompositionError) {
3334
3403
  result.layoutDiagnostics.push(...error.diagnostics.map((diagnostic) => ({ diagnostic, strict: true })));
3335
3404
  try {
3336
3405
  const relaxedLayout = layout();
3337
- result.composition = compose(relax(slide), relaxedLayout ? relax(relaxedLayout) : void 0);
3406
+ result.composition = compose(relax(resolved().slide), relaxedLayout ? relax(relaxedLayout) : void 0);
3338
3407
  } catch (second) {
3339
3408
  result.layoutError = second instanceof Error ? second.message : String(second);
3340
3409
  }
@@ -3640,4 +3709,4 @@ function assertValidCatalogRecord(kind, value) {
3640
3709
  if (!report2.valid) throw new OPFValidationError(report2);
3641
3710
  }
3642
3711
 
3643
- export { DEFAULT_CHART_PALETTE, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, findValidationRule, parseSource, resolveSlideContext, validate, validateCatalogRecord, validationCategories, validationRules };
3712
+ export { DEFAULT_CHART_PALETTE, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, findValidationRule, parseSource, resolveSlideContext, resolveSlideScriptMeasurement, validate, validateCatalogRecord, validationCategories, validationRules };
@@ -1,4 +1,4 @@
1
- import { chartNumber } from './chunk-5PDN7BN6.js';
1
+ import { chartNumber } from './chunk-CW7GEAV3.js';
2
2
 
3
3
  // src/data.ts
4
4
  var OPFDataImportError = class extends Error {
@@ -1,5 +1,5 @@
1
- import { assertValid, resolveSlideContext } from './chunk-PX5AWL7Y.js';
2
- import { composeSlide, codeHighlightSlice, tableRowBoundaries, visitContentPayloads, sliceNumberedItems } from './chunk-5PDN7BN6.js';
1
+ import { assertValid, resolveSlideScriptMeasurement, resolveSlideContext } from './chunk-NLNYNBTS.js';
2
+ import { resolveSlideVariables, usesSlideBuiltin, composeSlide, codeHighlightSlice, tableRowBoundaries, visitContentPayloads, slideTokenSpans, sliceNumberedItems } from './chunk-CW7GEAV3.js';
3
3
  import { checkCatalogsOption } from './chunk-SPFV27O5.js';
4
4
 
5
5
  // src/pagination.ts
@@ -39,6 +39,10 @@ var deckContext = (presentation) => {
39
39
  const deck = isRecord(presentation) ? presentation : {};
40
40
  return { ...Array.isArray(deck.references) ? { references: deck.references } : {}, ...isRecord(deck.datasets) ? { datasets: deck.datasets } : {} };
41
41
  };
42
+ function keepTokensWhole(boundaries, text) {
43
+ const spans = slideTokenSpans(text);
44
+ return spans.length ? boundaries.filter((boundary) => !spans.some(([start, end]) => boundary > start && boundary < end)) : boundaries;
45
+ }
42
46
  function leafFor(path, field, value) {
43
47
  const leaf = { path, field, value };
44
48
  let text;
@@ -62,7 +66,7 @@ function leafFor(path, field, value) {
62
66
  }
63
67
  if (text !== void 0) {
64
68
  leaf.unit = "utf16";
65
- leaf.boundaries = [0, ...Array.from(graphemes.segment(text), (segment) => segment.index + segment.segment.length)];
69
+ leaf.boundaries = keepTokensWhole([0, ...Array.from(graphemes.segment(text), (segment) => segment.index + segment.segment.length)], text);
66
70
  } else if (field === "items" || field === "bullets") {
67
71
  leaf.unit = "items";
68
72
  leaf.boundaries = Array.from({ length: value.length + 1 }, (_, i) => i);
@@ -103,8 +107,9 @@ function paginateSlide(input, options = {}) {
103
107
  };
104
108
  source = withReadability(source);
105
109
  let evaluations = 0;
106
- const { fonts, minFontSize: _minFontSize, maxSlides: _maxSlides, reservedIds: _reservedIds, ...engineOptions } = options;
107
- const composeOptions = { ...engineOptions, ...fonts?.textMeasurement ? { textMeasurement: fonts.textMeasurement } : {} };
110
+ const { fonts, catalogs, minFontSize: _minFontSize, maxSlides: _maxSlides, reservedIds: _reservedIds, ...engineOptions } = options;
111
+ const textMeasurement = fonts?.textMeasurement && options.presentation !== void 0 ? resolveSlideScriptMeasurement(options.presentation, sourceIndex, fonts.textMeasurement, { ...catalogs !== void 0 ? { catalogs } : {}, ...options.fontFamilies ? { fontFamilies: options.fontFamilies } : {} }) : fonts?.textMeasurement;
112
+ const composeOptions = { ...engineOptions, ...textMeasurement ? { textMeasurement } : {} };
108
113
  const readableLayout = (layout) => {
109
114
  if (!isRecord(layout) || !Array.isArray(layout.placeholders) || !layout.placeholders.some((entry) => isRecord(entry) && entry.type === "group")) return layout;
110
115
  const visit = (entries) => entries.map((entry) => {
@@ -115,9 +120,12 @@ function paginateSlide(input, options = {}) {
115
120
  return { ...layout, placeholders: visit(layout.placeholders) };
116
121
  };
117
122
  if (options.layout !== void 0) composeOptions.layout = readableLayout(options.layout);
123
+ const presentationSlides = options.presentation?.slides;
124
+ const slideCount = options.slideCount ?? (Array.isArray(presentationSlides) && presentationSlides.length ? presentationSlides.length : void 0);
118
125
  const geometry = (slide, pageIndex = 0) => {
119
126
  if (++evaluations > 2e4) throw new OPFPaginationError("Pagination exceeded its layout evaluation limit. Split the input into smaller sections.");
120
- return composeSlide(withReadability(slide, true), { ...composeOptions, slideNumber: (options.slideNumber ?? sourceIndex + 1) + pageIndex });
127
+ const slideNumber = (options.slideNumber ?? sourceIndex + 1) + pageIndex;
128
+ return composeSlide(resolveSlideVariables(withReadability(slide, true), { slideNumber, ...slideCount !== void 0 ? { slideCount } : {} }), { ...composeOptions, slideNumber });
121
129
  };
122
130
  const initial = geometry(source);
123
131
  const fit = (diagnostics) => diagnostics.filter((issue) => issue.code === "text-overflow" || issue.code === "small-cell" || !!initial.furniture?.diagnostics.includes(issue));
@@ -133,7 +141,14 @@ function paginateSlide(input, options = {}) {
133
141
  if (!fit(initial.diagnostics).length) return { slides: [source], pages: [{ slideIndex: sourceIndex, mappings: initial.items.map((item) => ({ sourcePath: item.path, outputPath: item.path })), ...initial.furniture ? { repeatedMappings: repeatedMappings(0) } : {} }] };
134
142
  const headerIssues = initial.diagnostics.filter((issue) => headingFields.has(issue.path.slice(sourceBase.length + 1)) || initial.furniture?.diagnostics.includes(issue));
135
143
  if (headerIssues.length) throw new OPFPaginationError("Repeated headings or header/footer content cannot fit or resolve. Change the repeated content or slide design before pagination.", headerIssues);
136
- const leaves = initial.items.filter((item) => !headingFields.has(item.field)).map((item) => leafFor(item.path, item.field, item.value));
144
+ const tokens = resolveSlideVariables(source, { slideNumber: 1, slideCount: 1 }) !== source;
145
+ const sourceValue = (path, fallback) => {
146
+ if (!tokens || !path.startsWith(`${sourceBase}.`)) return fallback;
147
+ let node = source;
148
+ for (const key of path.slice(sourceBase.length + 1).split(".")) node = isRecord(node) || Array.isArray(node) ? node[key] : void 0;
149
+ return node === void 0 ? fallback : node;
150
+ };
151
+ const leaves = initial.items.filter((item) => !headingFields.has(item.field)).map((item) => leafFor(item.path, item.field, sourceValue(item.path, item.value)));
137
152
  const leafPaths = new Set(leaves.map((leaf) => leaf.path));
138
153
  const slides = [], pages = [];
139
154
  let selected = /* @__PURE__ */ new Map();
@@ -256,10 +271,6 @@ function paginateSlide(input, options = {}) {
256
271
  if (!slides.length) throw new OPFPaginationError("No body content can be paginated.", initial.diagnostics);
257
272
  return { slides, pages };
258
273
  }
259
- var usesSlideTotal = (presentation) => [presentation, ...presentation.slides].some((owner) => ["header", "footer"].some((kind) => ["left", "center", "right"].some((zone) => {
260
- const content = owner?.design?.[kind]?.[zone];
261
- return content?.slideNumber === true && typeof content.slideNumberFormat === "string" && content.slideNumberFormat.includes("{total}");
262
- })));
263
274
  function paginate(input, options = {}) {
264
275
  checkCatalogsOption(options.catalogs, "paginate");
265
276
  assertValid(input, { only: ["format"] });
@@ -280,7 +291,7 @@ function paginate(input, options = {}) {
280
291
  }
281
292
  }
282
293
  if (output2.length >= maxSlides) throw new OPFPaginationError(`Pagination needs more than ${maxSlides} slides. No partial result was returned.`);
283
- const result2 = paginateSlide(slide, { ...context.options, textRasterPadding: options.textRasterPadding, fonts: options.fonts, presentation, slideIndex: index, slideNumber: output2.length + 1, slideCount: slideCount2, date: options.date, maxSlides: maxSlides - output2.length, minFontSize: options.minFontSize, reservedIds });
294
+ const result2 = paginateSlide(slide, { ...context.options, textRasterPadding: options.textRasterPadding, fonts: options.fonts, ...options.catalogs ? { catalogs: options.catalogs } : {}, presentation, slideIndex: index, slideNumber: output2.length + 1, slideCount: slideCount2, date: options.date, maxSlides: maxSlides - output2.length, minFontSize: options.minFontSize, reservedIds });
284
295
  const outputStart = output2.length;
285
296
  result2.pages.forEach((page, pageIndex) => {
286
297
  const remap = (mapping) => ({ ...mapping, outputPath: mapping.outputPath.replace(/^slides\.\d+/, `slides.${outputStart + pageIndex}`) });
@@ -291,9 +302,10 @@ function paginate(input, options = {}) {
291
302
  });
292
303
  return { output: output2, pages: pages2 };
293
304
  };
305
+ const usesSlideCount = usesSlideBuiltin(presentation, "deck.slideCount");
294
306
  let slideCount = presentation.slides.length, result = run(slideCount);
295
- for (let attempt = 0; usesSlideTotal(presentation) && result.output.length !== slideCount; attempt++) {
296
- if (attempt >= 3) throw new OPFPaginationError("Slide-number totals did not converge. Change the slide-number format or split the input.");
307
+ for (let attempt = 0; usesSlideCount && result.output.length !== slideCount; attempt++) {
308
+ if (attempt >= 3) throw new OPFPaginationError("The slide count did not converge. Change the text around {{deck.slideCount}} or split the input.");
297
309
  slideCount = result.output.length;
298
310
  result = run(slideCount);
299
311
  }
@@ -1,4 +1,4 @@
1
- import { validate } from './chunk-PX5AWL7Y.js';
1
+ import { validate } from './chunk-NLNYNBTS.js';
2
2
 
3
3
  // src/patch.ts
4
4
  var OPFPatchError = class extends Error {
@@ -1,4 +1,4 @@
1
- import { validate } from './chunk-PX5AWL7Y.js';
1
+ import { validate } from './chunk-NLNYNBTS.js';
2
2
 
3
3
  // src/convert/shared.ts
4
4
  var CONTENT_KEYS = ["text", "items", "bullets", "image", "video", "chart", "table", "code", "metric", "quote", "timeline"];
@@ -52,7 +52,7 @@ var presentation = {
52
52
  "minItems": 1
53
53
  }
54
54
  ],
55
- "description": "Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (Organization.role 'primary', else the first item) supplies the default deck logo, the 'organization' and 'socials' header/footer fields and the 'organization.<field>' built-in variables; the others are available by id as 'organization.<id>.<field>' and are not drawn automatically."
55
+ "description": "Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (Organization.role 'primary', else the first item) supplies the default deck logo, the 'socials' header/footer field and the 'organization.<field>' built-in variables; the others are available by id as 'organization.<id>.<field>' and are not drawn automatically."
56
56
  },
57
57
  "speaker": {
58
58
  "oneOf": [
@@ -67,7 +67,7 @@ var presentation = {
67
67
  "minItems": 1
68
68
  }
69
69
  ],
70
- "description": "Person presenting the deck. Array form supports panels and multi-speaker decks. The first speaker is the primary speaker: the built-in variables 'speaker.<field>' and the 'speaker' header/footer field read it, 'speakers' lists every name, and 'speaker.<id>.<field>' addresses one by id. A speaker is drawn nowhere else."
70
+ "description": "Person presenting the deck. Array form supports panels and multi-speaker decks. The first speaker is the primary speaker: the built-in variables 'speaker.<field>' read it, 'speakers' lists every name, and 'speaker.<id>.<field>' addresses one by id. A speaker is drawn nowhere else."
71
71
  },
72
72
  "author": {
73
73
  "oneOf": [
@@ -775,7 +775,7 @@ var presentation = {
775
775
  },
776
776
  "Organization": {
777
777
  "type": "object",
778
- "description": "An organization associated with the presentation, typically the presenting company, but also hosts, partners, clients, or sponsors. The primary organization (role 'primary', else the first one) supplies the default deck logo (unless design.logo overrides it), the 'organization' header/footer field (its name) and the 'socials' field (its socials). Every field is also a built-in variable ('{{organization.name}}', 'var:organization.logo', 'organization.<id>.<field>'). Nothing else about an organization is drawn automatically. See docs/templates-and-variables.md.",
778
+ "description": "An organization associated with the presentation, typically the presenting company, but also hosts, partners, clients, or sponsors. The primary organization (role 'primary', else the first one) supplies the default deck logo (unless design.logo overrides it) and the 'socials' header/footer field (its socials). Every field is also a built-in variable ('{{organization.name}}', 'var:organization.logo', 'organization.<id>.<field>'). Nothing else about an organization is drawn automatically. See docs/templates-and-variables.md.",
779
779
  "required": [
780
780
  "id",
781
781
  "name"
@@ -793,7 +793,7 @@ var presentation = {
793
793
  },
794
794
  "name": {
795
795
  "type": "string",
796
- "description": "Display name. Drawn by the 'organization' header/footer field for the primary organization; the built-in variable 'organization.name' (or 'organization.<id>.name') carries it into any string.",
796
+ "description": "Display name. The built-in variable 'organization.name' (primary organization) or 'organization.<id>.name' carries it into any string, such as a header or footer text of '{{organization.name}}'.",
797
797
  "examples": [
798
798
  "Acme Corp",
799
799
  "DevConf 2026",
@@ -863,7 +863,7 @@ var presentation = {
863
863
  "sponsor",
864
864
  "host"
865
865
  ],
866
- "description": "Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (deck logo, 'organization' and 'socials' header/footer fields, and the 'organization.<field>' built-ins). When no organization has role 'primary', the first one is primary. 'partner', 'client', 'sponsor' and 'host' are descriptive labels that change nothing."
866
+ "description": "Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (deck logo, the 'socials' header/footer field, and the 'organization.<field>' built-ins). When no organization has role 'primary', the first one is primary. 'partner', 'client', 'sponsor' and 'host' are descriptive labels that change nothing."
867
867
  },
868
868
  "socials": {
869
869
  "$ref": "#/$defs/Socials",
@@ -873,7 +873,7 @@ var presentation = {
873
873
  },
874
874
  "Speaker": {
875
875
  "type": "object",
876
- "description": "A person presenting the deck. A speaker is drawn only through built-in variables ('{{speaker.name}}' inside any string, 'var:speaker.photo' as a whole image field) and the 'speaker' header/footer field (the first speaker's name and title). No layout, cover or bio slide places a speaker on its own. See docs/templates-and-variables.md.",
876
+ "description": "A person presenting the deck. A speaker is drawn only through built-in variables ('{{speaker.name}}' inside any string, a header or footer text included, and 'var:speaker.photo' as a whole image field). No layout, cover or bio slide places a speaker on its own. See docs/templates-and-variables.md.",
877
877
  "required": [
878
878
  "id",
879
879
  "name"
@@ -891,7 +891,7 @@ var presentation = {
891
891
  },
892
892
  "name": {
893
893
  "type": "string",
894
- "description": "Display name. Built-in variable 'speaker.name' (first speaker) or 'speaker.<id>.name'; the first speaker's name is also drawn by the 'speaker' header/footer field and every name is listed by 'speakers'.",
894
+ "description": "Display name. Built-in variable 'speaker.name' (first speaker) or 'speaker.<id>.name'; every name is listed by 'speakers'.",
895
895
  "examples": [
896
896
  "Alice Chen",
897
897
  "Bob Lee",
@@ -900,7 +900,7 @@ var presentation = {
900
900
  },
901
901
  "title": {
902
902
  "type": "string",
903
- "description": "Role or title. Built-in variable 'speaker.title'; the 'speaker' header/footer field draws it after the name ('Alice Chen, VP of Engineering').",
903
+ "description": "Role or title. Built-in variable 'speaker.title' (or 'speaker.<id>.title'), for example in a footer text of '{{speaker.name}}, {{speaker.title}}'.",
904
904
  "examples": [
905
905
  "VP of Engineering",
906
906
  "Founder & CEO",
@@ -1144,7 +1144,7 @@ var presentation = {
1144
1144
  false,
1145
1145
  {
1146
1146
  "left": {
1147
- "section": true
1147
+ "text": "{{slide.section}}"
1148
1148
  },
1149
1149
  "right": {
1150
1150
  "image": "asset:acme-logo"
@@ -1173,7 +1173,7 @@ var presentation = {
1173
1173
  "text": "Confidential"
1174
1174
  },
1175
1175
  "right": {
1176
- "slideNumber": true
1176
+ "text": "{{slide.number}}"
1177
1177
  }
1178
1178
  }
1179
1179
  ]
@@ -1666,7 +1666,7 @@ var presentation = {
1666
1666
  "additionalProperties": {
1667
1667
  "$ref": "#/$defs/Variable"
1668
1668
  },
1669
- "description": "Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: expected in a template (root 'template': true), an error in a normal deck. Engines resolve variables with resolveVariables() before composing, rendering or exporting, so a template plus its values previews and exports exactly like the equivalent hand-written deck. Read-only built-in variables come from the deck's own metadata and use dotted names that a user id can never take: 'deck.name', 'deck.description', 'deck.author', 'speaker.name|title|email|phone|bio|photo' (first speaker), 'speakers' (every speaker's name, a list), 'organization.name|legalName|tagline|domain|email|phone|logo' (primary organization), and 'speaker.<id>.<field>' / 'organization.<id>.<field>' by id. They are written the same two ways ('{{speaker.name}}', 'var:speaker.photo'), are never declared, and an unknown built-in path is a validation error; a known one with no source value resolves to nothing with a warning. The id 'speakers' is reserved. See docs/templates-and-variables.md.",
1669
+ "description": "Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: expected in a template (root 'template': true), an error in a normal deck. Engines resolve variables with resolveVariables() before composing, rendering or exporting, so a template plus its values previews and exports exactly like the equivalent hand-written deck. Read-only built-in variables come from the deck's own metadata and use dotted names that a user id can never take: 'deck.name', 'deck.description', 'deck.author', 'speaker.name|title|email|phone|bio|photo' (first speaker), 'speakers' (every speaker's name, a list), 'organization.name|legalName|tagline|domain|email|phone|logo' (primary organization), and 'speaker.<id>.<field>' / 'organization.<id>.<field>' by id. They are written the same two ways ('{{speaker.name}}', 'var:speaker.photo'), are never declared, and an unknown built-in path is a validation error; a known one with no source value resolves to nothing with a warning. The id 'speakers' is reserved. Three built-ins are slide-scoped and written only as inline tokens: '{{slide.number}}' (the displayed slide number after pagination), '{{slide.section}}' (the slide's section) and '{{deck.slideCount}}' (the number of slides after pagination). resolveVariables() leaves them as written; they resolve per slide as it is composed (resolveSlideVariables(), and header/footer text in layoutFurniture()), so every 'slide.' name is reserved and 'var:slide.*' or 'var:deck.slideCount' is a validation error. See docs/templates-and-variables.md.",
1670
1670
  "examples": [
1671
1671
  {
1672
1672
  "risk": "#B42318",
@@ -2625,7 +2625,8 @@ var presentation = {
2625
2625
  },
2626
2626
  "HeaderFooterItem": {
2627
2627
  "type": "object",
2628
- "description": "One header/footer zone. Every configured field renders; fields in one zone stack top to bottom in the order logo, image, text, organization, speaker, socials, section, slide number, date. Put a date and a slide number in different zones to keep each on the zone's single line.",
2628
+ "description": "One header/footer zone. Every configured field renders; fields in one zone stack top to bottom in the order logo, image, text, socials, date. Generated values such as the slide number, the slide count, the section, the organization or the speaker are variables inside text ('{{slide.number}} / {{deck.slideCount}}', '{{organization.name}}'), so one text writes several values in the author's order; a line break is '\\n'. Put a date and a slide number in different zones to keep each on the zone's single line. See docs/templates-and-variables.md.",
2629
+ "additionalProperties": false,
2629
2630
  "properties": {
2630
2631
  "logo": {
2631
2632
  "type": "boolean",
@@ -2633,10 +2634,12 @@ var presentation = {
2633
2634
  },
2634
2635
  "text": {
2635
2636
  "type": "string",
2636
- "description": "Literal text rendered in this zone.",
2637
+ "description": "Text rendered in this zone. Variables resolve inside it like any string: deck-wide ones ('{{organization.name}}', '{{speaker.name}}, {{speaker.title}}', '{{customer}}') before composition, and the slide-scoped built-ins '{{slide.number}}', '{{slide.section}}' and '{{deck.slideCount}}' for each slide as it is laid out. Each '{{slide.number}}' is a live slide-number field: PPTX export writes it as a native field when its value fits within one accepted text line, and as static text with a diagnostic otherwise. '{{deck.slideCount}}' is fixed text, because PowerPoint has no slide-count field. Write '\\{{' for literal braces.",
2637
2638
  "examples": [
2638
2639
  "Confidential",
2639
- "Q4 Business Review"
2640
+ "Q4 Business Review",
2641
+ "{{slide.number}} / {{deck.slideCount}}",
2642
+ "{{organization.name}}\n{{slide.section}}"
2640
2643
  ]
2641
2644
  },
2642
2645
  "image": {
@@ -2647,21 +2650,6 @@ var presentation = {
2647
2650
  "./assets/footer-badge.svg"
2648
2651
  ]
2649
2652
  },
2650
- "slideNumber": {
2651
- "type": "boolean",
2652
- "description": "Whether to render the current slide number in this zone. PPTX export writes a native slide-number field when its value fits within one accepted text line; a value split across lines exports as static text with a diagnostic."
2653
- },
2654
- "slideNumberFormat": {
2655
- "type": "string",
2656
- "pattern": "[{]current[}]",
2657
- "description": 'Template for the slide number when slideNumber is true. {current} is the displayed slide number (a native PPTX field when its value fits within one accepted text line); {total} is the number of slides in the rendered or exported deck, written as fixed text because PowerPoint has no slide-count field. Other characters are literal. Defaults to "{current}".',
2658
- "examples": [
2659
- "{current}",
2660
- "{current} / {total}",
2661
- "A-{current}",
2662
- "Page {current} of {total}"
2663
- ]
2664
- },
2665
2653
  "date": {
2666
2654
  "oneOf": [
2667
2655
  {
@@ -2689,18 +2677,6 @@ var presentation = {
2689
2677
  "MMMM d, yyyy"
2690
2678
  ]
2691
2679
  },
2692
- "organization": {
2693
- "type": "boolean",
2694
- "description": "Whether to render the primary organization's name from organization (role 'primary', else the first organization)."
2695
- },
2696
- "speaker": {
2697
- "type": "boolean",
2698
- "description": "Whether to render the primary (first) speaker's name and title from speaker, joined as 'Ada Lovelace, CTO' (just the name when the speaker has no title). It is generated text: without a named speaker the engine reports unresolved-content at this field. PPTX export writes it as static text and import restores the flag from the file's furniture provenance."
2699
- },
2700
- "section": {
2701
- "type": "boolean",
2702
- "description": "Whether to render the current slide section label."
2703
- },
2704
2680
  "socials": {
2705
2681
  "type": "boolean",
2706
2682
  "description": "Whether to render the primary organization's social profiles from organization.socials, one line per platform in key order. A handle is formatted through the platform's socialPlatforms record (companyUrlPattern, else profileUrlPattern, else baseUrl, with handlePrefix stripped) and shown as that profile URL without its 'https://' scheme; a value that is already a URL is shown without an 'https://' scheme; an unknown platform key shows the raw value. Exporters link each line to its full URL when one exists. Speaker socials are not rendered by this field."
@@ -3100,7 +3076,7 @@ var presentation = {
3100
3076
  },
3101
3077
  "section": {
3102
3078
  "type": "string",
3103
- "description": "PowerPoint-style slide section label. Consecutive slides with the same value belong to the same section in presenter view, outlines, and PowerPoint section-aware exports.",
3079
+ "description": "PowerPoint-style slide section label. Consecutive slides with the same value belong to the same section in presenter view, outlines, and PowerPoint section-aware exports. Also the slide-scoped built-in variable '{{slide.section}}', for example in a header text.",
3104
3080
  "examples": [
3105
3081
  "Intro",
3106
3082
  "Problem",
@@ -1,9 +1,9 @@
1
- import { NOT_CHECKED, Ctx } from './chunk-5UEUAW57.js';
2
- import { sortPresentationKeys } from './chunk-6OR326EA.js';
3
- import { same } from './chunk-5TO6I3GB.js';
4
- import { validate } from './chunk-PX5AWL7Y.js';
1
+ import { NOT_CHECKED, Ctx } from './chunk-IS5H5JKH.js';
2
+ import { sortPresentationKeys } from './chunk-YT7FMQER.js';
3
+ import { same } from './chunk-PPROJP2E.js';
4
+ import { validate } from './chunk-NLNYNBTS.js';
5
5
  import { checkCatalogsOption } from './chunk-SPFV27O5.js';
6
- import { presentation } from './chunk-WEDGVC5W.js';
6
+ import { presentation } from './chunk-RA64UQKI.js';
7
7
  import { Parser, parseDocument, isMap, Document, visit, isScalar, isAlias, isSeq } from 'yaml';
8
8
 
9
9
  var MAX_ALIAS_COUNT = 100;