@openpresentation/opf 0.27.0 → 0.28.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 (100) hide show
  1. package/dist/browser.js +18 -18
  2. package/dist/{catalog-refs-BSfGELpt.d.ts → catalog-refs-kAefsZWT.d.ts} +2 -1
  3. package/dist/{catalog-schemas-DoxA2BgY.d.ts → catalog-schemas-CC1d_h8s.d.ts} +3 -2
  4. package/dist/{chart-data-BnNqgnC-.d.ts → chart-data-Ci9s_NDN.d.ts} +2 -1
  5. package/dist/{chunk-LRTQXGJO.js → chunk-2CUCYENF.js} +523 -89
  6. package/dist/{chunk-4ZM5G3XX.js → chunk-5Q4IYLMW.js} +30 -14
  7. package/dist/{chunk-OSM3LZ2Y.js → chunk-BQUWRXTR.js} +4 -4
  8. package/dist/{chunk-4ACS57MG.js → chunk-CR72ZJ6W.js} +2 -2
  9. package/dist/{chunk-ZGTOZA2S.js → chunk-F6DCEGB3.js} +6 -6
  10. package/dist/{chunk-O3SA5SJ4.js → chunk-KELC4K62.js} +1 -1
  11. package/dist/{chunk-WJ4XTCOD.js → chunk-KXKN3W5Z.js} +3 -3
  12. package/dist/{chunk-TMREBZBQ.js → chunk-LLEXM7TQ.js} +3 -3
  13. package/dist/{chunk-GNKESMZT.js → chunk-MTFK7QEY.js} +6 -6
  14. package/dist/{chunk-JSHMFTSY.js → chunk-N77N4AOD.js} +1 -1
  15. package/dist/{chunk-7XUEKIXH.js → chunk-OAIAFXMS.js} +2 -2
  16. package/dist/{chunk-EO3JWEO5.js → chunk-P6PKYJTS.js} +1 -1
  17. package/dist/{chunk-353VBJ6K.js → chunk-PCU4XGKV.js} +5 -5
  18. package/dist/{chunk-SYW4BCGZ.js → chunk-SEZ4EPW6.js} +2 -2
  19. package/dist/{chunk-ABFDFZIW.js → chunk-TNUTRV6E.js} +1 -1
  20. package/dist/{chunk-CXPBU5W4.js → chunk-UOHWG4OS.js} +1 -1
  21. package/dist/{chunk-APLNGCEZ.js → chunk-VGGPAMP7.js} +2 -2
  22. package/dist/{chunk-PAGVSTVF.js → chunk-WPKCWQ6W.js} +149 -26
  23. package/dist/{chunk-KZPZRHL6.js → chunk-XZ23FORL.js} +6 -6
  24. package/dist/chunk-ZROVYWGU.js +1 -0
  25. package/dist/{composition-BGmpe5e4.d.ts → composition-C-hd_Ci3.d.ts} +84 -10
  26. package/dist/composition.d.ts +3 -3
  27. package/dist/composition.js +1 -1
  28. package/dist/conversion-XJTDWQ5W.js +24 -0
  29. package/dist/{conversion-8SQD4dqu.d.ts → conversion-hbZeS5kD.d.ts} +9 -8
  30. package/dist/convert.d.ts +6 -5
  31. package/dist/convert.js +7 -7
  32. package/dist/data.d.ts +4 -3
  33. package/dist/data.js +2 -2
  34. package/dist/{deck-report-BaKgPh_N.d.ts → deck-report-DQMsvyBI.d.ts} +5 -4
  35. package/dist/deck.d.ts +10 -9
  36. package/dist/deck.js +11 -11
  37. package/dist/diff.d.ts +3 -2
  38. package/dist/diff.js +4 -4
  39. package/dist/docs.d.ts +2 -1
  40. package/dist/docs.js +29 -17
  41. package/dist/examples.d.ts +3 -2
  42. package/dist/examples.js +90 -50
  43. package/dist/{explain-JSuESV-Y.d.ts → explain-iZeNOUc6.d.ts} +2 -1
  44. package/dist/{finding-Bh56sGtF.d.ts → finding-l9dlQ2NF.d.ts} +1 -1
  45. package/dist/font-policy.d.ts +2 -1
  46. package/dist/format.d.ts +2 -1
  47. package/dist/format.js +2 -2
  48. package/dist/ids.d.ts +3 -2
  49. package/dist/ids.js +4 -4
  50. package/dist/index.d.ts +38 -17
  51. package/dist/index.js +22 -22
  52. package/dist/markdown.d.ts +8 -7
  53. package/dist/markdown.js +8 -8
  54. package/dist/node-engine.d.ts +34 -11
  55. package/dist/node-engine.js +35 -29
  56. package/dist/{pagination-BsUTsQvk.d.ts → pagination-DyAKYpXu.d.ts} +4 -3
  57. package/dist/pagination.d.ts +4 -4
  58. package/dist/pagination.js +4 -4
  59. package/dist/patch.d.ts +4 -2
  60. package/dist/patch.js +3 -3
  61. package/dist/precompiled.d.ts +1 -1
  62. package/dist/precompiled.js +2 -2
  63. package/dist/{presentation-C-nHZyMw.d.ts → presentation-8jLSTFqL.d.ts} +10 -10
  64. package/dist/repo-readme.js +1 -1
  65. package/dist/schema-engine-ajv.js +1 -1
  66. package/dist/schema-engine-precompiled.js +2 -2
  67. package/dist/schema.d.ts +4 -3
  68. package/dist/schema.js +3 -3
  69. package/dist/schemas.d.ts +2 -1
  70. package/dist/schemas.js +1 -1
  71. package/dist/source.d.ts +5 -4
  72. package/dist/source.js +10 -10
  73. package/dist/spec/README.md +1 -1
  74. package/dist/spec/schemas/audience.schema.json +1 -0
  75. package/dist/spec/schemas/catalog-index.schema.json +1 -0
  76. package/dist/spec/schemas/catalog-manifest.schema.json +1 -0
  77. package/dist/spec/schemas/chart-type.schema.json +1 -0
  78. package/dist/spec/schemas/color-scheme.schema.json +1 -0
  79. package/dist/spec/schemas/finding.schema.json +1 -0
  80. package/dist/spec/schemas/font-scheme.schema.json +1 -0
  81. package/dist/spec/schemas/language.schema.json +1 -0
  82. package/dist/spec/schemas/layout-preview-index.schema.json +1 -0
  83. package/dist/spec/schemas/layout.schema.json +8 -6
  84. package/dist/spec/schemas/narrative.schema.json +1 -0
  85. package/dist/spec/schemas/opf.schema.json +8 -7
  86. package/dist/spec/schemas/purpose.schema.json +1 -0
  87. package/dist/spec/schemas/social-platform.schema.json +1 -0
  88. package/dist/spec/schemas/theme.schema.json +3 -1
  89. package/dist/spec/schemas/tone.schema.json +1 -0
  90. package/dist/spec-files.d.ts +2 -1
  91. package/dist/symbol-font-encodings.d.ts +2 -1
  92. package/dist/types.d.ts +10 -10
  93. package/dist/{validation-types-6_jEaHeE.d.ts → validation-types-mNmfcY9A.d.ts} +5 -4
  94. package/dist/validator.d.ts +8 -8
  95. package/dist/validator.js +3 -3
  96. package/dist/yaml.d.ts +8 -7
  97. package/dist/yaml.js +7 -7
  98. package/package.json +7 -7
  99. package/dist/chunk-GJO5NUVT.js +0 -1
  100. package/dist/conversion-LN6PFRZL.js +0 -24
@@ -1,6 +1,6 @@
1
1
  import { original } from './chunk-JBARNYFR.js';
2
- import { schemas, schemaEntries } from './chunk-4ZM5G3XX.js';
3
- import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, chartSeriesPalette, chartLeadFromScheme, normalizeHexColor, resolveChartColors, colorContrast, imageBackground, tableGrid, timelineTextColor, LOGO_SHAPES, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, resolveReference, textWidthMeasurer, resolveColorRef, resolveDesignRecords, resolveFontFamilies, checkCatalogsOption, provenanceOf, unresolvedReference, OPFUnresolvedReferenceError, resolveColorRoles, resolveCanvasDimensions, resolveSlideVariables, normalizeColorVariant, isRecord, hasContentVariables, isTemplate, instantiateForValidation, layoutTemplateIssues, resolveScriptFonts, defaultSlideBackground, catalogReferenceSites, parseReference, catalogGroupDeclared, pointerPath, unusedDatasets, MAX_COMPOSITION_DEPTH, datasetDiagnostics, pathFor, catalogRecords, collectCitations, numberingFindings, walkCitationRuns, resolveTableData, resolveChartOptions, chartOptionTarget, codeHighlightLines, chartNumberFixesByCell, catalogKinds, catalogGroupSource, registeredCatalog, OPFCompositionError, composeSlide } from './chunk-LRTQXGJO.js';
2
+ import { schemas, schemaEntries } from './chunk-5Q4IYLMW.js';
3
+ import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, chartSeriesPalette, chartLeadFromScheme, normalizeHexColor, resolveChartColors, colorContrast, imageBackground, tableGrid, timelineTextColor, LOGO_SHAPES, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, resolveReference, textWidthMeasurer, resolveColorRef, resolveDesignRecords, resolveFontFamilies, checkCatalogsOption, provenanceOf, unresolvedReference, OPFUnresolvedReferenceError, resolveColorRoles, resolveCanvasDimensions, resolveSlideVariables, normalizeColorVariant, isRecord, hasContentVariables, isTemplate, instantiateForValidation, layoutTemplateIssues, resolveScriptFonts, defaultSlideBackground, catalogReferenceSites, parseReference, catalogGroupDeclared, pointerPath, unusedDatasets, MAX_COMPOSITION_DEPTH, datasetDiagnostics, pathFor, catalogRecords, collectCitations, numberingFindings, walkCitationRuns, resolveTableData, resolveChartOptions, chartOptionTarget, codeHighlightLines, chartNumberFixesByCell, catalogKinds, catalogGroupSource, registeredCatalog, OPFCompositionError, composeSlide } from './chunk-2CUCYENF.js';
4
4
  import { schemaEngine } from '#schema-engine';
5
5
  import { parseTree, printParseErrorCode, getNodePath } from 'jsonc-parser';
6
6
 
@@ -1572,7 +1572,7 @@ var formatRules = [
1572
1572
  "error",
1573
1573
  "The document breaks the OPF schema or a structural rule the schema cannot express.",
1574
1574
  "The schema is the contract every engine, exporter and editor relies on. A field of the wrong type, a missing required field, an unknown property or content fields that cannot be mixed make the document ambiguous or undrawable.",
1575
- { standard: SCHEMA, approximations: "All alternatives of a union are reported, with the failing schema location in `validation`. A required variable with no value is reported by `opf/variable-unfilled`, in the same category." }
1575
+ { standard: SCHEMA, approximations: "All alternatives of a union are reported, with the failing schema location in `validation`. A required variable with no value is reported by `opf/variable-unfilled`, in the same category. A chart written in the Chart.js or ECharts shape (`categories` and `series`, or `labels` and `datasets`, and no `data`) is one finding on the chart whose hint shows its own values as `data: { columns, rows }`, with a patch fix; the additional-property findings that repeat it are dropped." }
1576
1576
  ),
1577
1577
  rule("reference-id-duplicate", "format", "error", "Two references share an id.", "A citation names a reference by id; a repeated id makes it ambiguous which entry is cited.", { standard: SCHEMA }),
1578
1578
  rule("cite-unknown-reference", "format", "error", "A citation names a reference that does not exist.", "A cited id with no entry in `references` has nothing to show in the footnote area.", { standard: SCHEMA }),
@@ -1654,7 +1654,7 @@ var referenceRules = [
1654
1654
  "warning",
1655
1655
  "A content reference resolves nowhere.",
1656
1656
  "A layout, theme, colour scheme, font scheme, narrative, audience, purpose or tone reference that neither the document embeds nor a registered catalog defines falls back: a slide composes automatically, a design uses the engine default. A strict export fails instead. A slide with no layout is automatic composition and is never reported.",
1657
- { standard: SCHEMA, approximations: "Resolved like every engine resolves it: catalogs.custom, then the records embedded under catalogs.default, then the catalog registered for its source (the first registered catalog when `default` is omitted); `name:id` in catalogs.<name>, then the catalog registered for its source. Nothing is fetched. Free-form audience and purpose text is not a reference." }
1657
+ { standard: SCHEMA, approximations: 'Resolved like every engine resolves it: catalogs.custom, then the records embedded under catalogs.default, then the catalog registered for its source (the first registered catalog when `default` is omitted); `name:id` in catalogs.<name>, then the catalog registered for its source. Nothing is fetched. Free-form audience and purpose text is not a reference. It is a warning, not an error, because catalogs are open and a host may register the id; `--fail-on warning` is the check that a deck is done. With a catalog registered, the hint names the nearest records that exist ("Did you mean `two-column`?").' }
1658
1658
  ),
1659
1659
  rule(
1660
1660
  "catalog-record-not-in-source",
@@ -1772,7 +1772,7 @@ var readingOrderRule = rule(
1772
1772
  {
1773
1773
  standard: 'WCAG 2.2 SC 1.3.2 Meaningful Sequence, level A (PowerPoint: "Check reading order")',
1774
1774
  cost: "composition",
1775
- approximations: "Compares the composed content order with a visual order recomputed from the composed boxes: items whose vertical centres fall in the same row are ordered along the reading direction (a right-to-left deck is checked by rows only), rows from top to bottom. Headings are expected first. Free-form overlap is not analysed. On a slide with a template layout (OPF 0.19) the expected order is the binding order instead (the regions in reading order, then the blocks of each region), and items outside a region (placed images, the row below the grid) are not compared."
1775
+ approximations: "Compares the composed content order with a visual order recomputed from the composed boxes: items whose vertical centres fall in the same row are ordered along the reading direction (a right-to-left deck is checked by rows only), rows from top to bottom. Headings are expected first. Content drawn entirely above the title, over the columns of the title, is reported; content in a column beside the title is read after it. Free-form overlap is not analysed. On a slide with a template layout (OPF 0.19) the expected order is the binding order instead (the regions in reading order, then the blocks of each region), and items outside a region (placed images, the row below the grid) are not compared."
1776
1776
  }
1777
1777
  );
1778
1778
  var linkRule = rule(
@@ -2305,8 +2305,9 @@ var readingOrderRules = [
2305
2305
  if (!composition) continue;
2306
2306
  const body = composition.items.filter((item) => !["title", "subtitle", "tag"].includes(item.field));
2307
2307
  const title = composition.items.find((item) => item.field === "title");
2308
- if (title && body.some((item) => item.box.y + item.box.height <= title.box.y + 1)) {
2309
- const above = body.find((item) => item.box.y + item.box.height <= title.box.y + 1);
2308
+ const isAbove = (item) => item.box.y + item.box.height <= title.box.y + 1 && item.box.x < title.box.x + title.box.width && title.box.x < item.box.x + item.box.width;
2309
+ if (title && body.some(isAbove)) {
2310
+ const above = body.find(isAbove);
2310
2311
  context.report(readingOrderRule, {
2311
2312
  path: pointerOfDotted(above.path),
2312
2313
  slide,
@@ -2845,6 +2846,60 @@ var layoutContentRules = [
2845
2846
  // src/validation-rules.ts
2846
2847
  var ruleImplementations = [...accessibilityRules, ...layoutContentRules, ...languageTagRules];
2847
2848
 
2849
+ // src/chart-shape.ts
2850
+ var FOREIGN_CHART_KEYS = ["categories", "series", "labels", "datasets"];
2851
+ var CHART_DEFINITION = "https://openpresentation.org/schema/opf/v1#/$defs/Chart";
2852
+ var isRecord2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
2853
+ var isCell = (value) => value === null || ["string", "number", "boolean"].includes(typeof value);
2854
+ function foreignChartKeys(chart) {
2855
+ if (!isRecord2(chart) || Object.hasOwn(chart, "data")) return [];
2856
+ return FOREIGN_CHART_KEYS.filter((key2) => Object.hasOwn(chart, key2));
2857
+ }
2858
+ function convertChartData(chart) {
2859
+ const labels = chart.categories ?? chart.labels;
2860
+ const series = chart.series ?? chart.datasets;
2861
+ if (!Array.isArray(labels) || !labels.length || !labels.every(isCell) || !Array.isArray(series) || !series.length) return void 0;
2862
+ const columns = ["Category"];
2863
+ const values = [];
2864
+ for (const [index, entry2] of series.entries()) {
2865
+ if (!isRecord2(entry2)) return void 0;
2866
+ const list = entry2.values ?? entry2.data;
2867
+ const name = entry2.name ?? entry2.label;
2868
+ if (!Array.isArray(list) || list.length !== labels.length || !list.every(isCell)) return void 0;
2869
+ columns.push(typeof name === "string" && name ? name : `Series ${index + 1}`);
2870
+ values.push(list);
2871
+ }
2872
+ if (new Set(columns).size !== columns.length) return void 0;
2873
+ return { columns, rows: labels.map((label, row) => [label, ...values.map((list) => list[row])]) };
2874
+ }
2875
+ function chartShapePatch(chart, path) {
2876
+ if (!isRecord2(chart)) return void 0;
2877
+ const data = convertChartData(chart);
2878
+ const keys = foreignChartKeys(chart);
2879
+ if (!data || !keys.length) return void 0;
2880
+ return [{ op: "add", path: `${path}/data`, value: data }, ...keys.map((key2) => ({ op: "remove", path: `${path}/${key2}` }))];
2881
+ }
2882
+ var SHOWN_ROWS = 3;
2883
+ var json = (value) => JSON.stringify(value);
2884
+ function dataExample(chart) {
2885
+ const data = convertChartData(chart);
2886
+ if (!data) return '"data": { "columns": ["Category", "Series name"], "rows": [["Q1", 3], ["Q2", 8]] }';
2887
+ const rows = data.rows.slice(0, SHOWN_ROWS).map(json);
2888
+ return `"data": { "columns": ${json(data.columns)}, "rows": [${rows.join(",")}${data.rows.length > SHOWN_ROWS ? ",..." : ""}] }`;
2889
+ }
2890
+ function chartShapeHelp(chart) {
2891
+ const keys = foreignChartKeys(chart);
2892
+ if (!keys.length || !isRecord2(chart)) return void 0;
2893
+ const names2 = keys.map((key2) => `"${key2}"`).join(" and ");
2894
+ const example = dataExample(chart);
2895
+ const next = `replace ${names2} with ${example}. The first column holds the category labels and each further column is one series.`;
2896
+ return {
2897
+ message: `The chart has ${names2} (the Chart.js and ECharts shape), but an OPF chart keeps its values in "data": { "columns", "rows" }.`,
2898
+ next,
2899
+ help: `OPF charts use "data": { "columns", "rows" }, not ${names2}. Replace ${names2} with ${example}; the first column holds the category labels and each further column is one series. See the Chart definition: ${CHART_DEFINITION} (opf schema presentation '/$defs/Chart').`
2900
+ };
2901
+ }
2902
+
2848
2903
  // src/check-document.ts
2849
2904
  var own = (value, key2) => Object.hasOwn(value, key2);
2850
2905
  var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -3167,12 +3222,42 @@ var HELP = {
3167
3222
  "numbering-start-ignored": "Add a 'numbering' field to the payload, or remove 'start'.",
3168
3223
  "slide-size-out-of-range": "Use a custom widthInches and heightInches between 1 and 56, or a preset. PowerPoint stores the slide size in 914400 to 51206400 EMU per side."
3169
3224
  };
3225
+ function collapseChartShape(document, findings) {
3226
+ const chartAt = (path) => {
3227
+ let node = document;
3228
+ for (const segment of splitPointer(path)) node = Array.isArray(node) ? node[Number(segment)] : isRecord(node) ? node[segment] : void 0;
3229
+ return node;
3230
+ };
3231
+ const collapsed = /* @__PURE__ */ new Set();
3232
+ for (const [index, entry2] of findings.entries()) {
3233
+ if (entry2.ruleId !== "opf/schema" || entry2.validation?.keyword !== "required" || entry2.validation.params.missingProperty !== "data") continue;
3234
+ const chart = chartAt(entry2.path);
3235
+ const hint = chartShapeHelp(chart);
3236
+ if (!hint) continue;
3237
+ const patch2 = chartShapePatch(chart, entry2.path);
3238
+ findings[index] = {
3239
+ ...entry2,
3240
+ message: hint.message,
3241
+ help: hint.help,
3242
+ ...patch2 ? { fixes: [{ id: "convert-chart-data", title: "Convert to data: { columns, rows }", kind: "patch", safe: false, patch: patch2 }] } : {}
3243
+ };
3244
+ collapsed.add(entry2.path);
3245
+ }
3246
+ if (!collapsed.size) return;
3247
+ for (let index = findings.length - 1; index >= 0; index--) {
3248
+ const entry2 = findings[index];
3249
+ if (entry2?.ruleId !== "opf/schema" || entry2.validation?.keyword !== "additionalProperties") continue;
3250
+ const at2 = entry2.path.lastIndexOf("/");
3251
+ if (collapsed.has(entry2.path.slice(0, at2)) && FOREIGN_CHART_KEYS.includes(entry2.path.slice(at2 + 1))) findings.splice(index, 1);
3252
+ }
3253
+ }
3170
3254
  function engineFindings(document, engine, enabled) {
3171
3255
  const findings = [];
3172
3256
  for (const issue2 of engine.errors) {
3173
3257
  const ruleId = issueRuleId(issue2);
3174
3258
  if (enabled(ruleId)) findings.push(issueFinding(issue2, ruleId));
3175
3259
  }
3260
+ collapseChartShape(document, findings);
3176
3261
  let numberFixes;
3177
3262
  for (const issue2 of engine.warnings) {
3178
3263
  const code2 = issue2.params.code;
@@ -3274,6 +3359,13 @@ function undeclaredCatalogFindings(document) {
3274
3359
  }
3275
3360
  return findings;
3276
3361
  }
3362
+ var KIND_PLURAL = { layouts: "layouts", themes: "themes", colorSchemes: "color schemes", fontSchemes: "font schemes", narratives: "narratives", audiences: "audiences", purposes: "purposes", tones: "tones" };
3363
+ function nearestRecords(kind, reference, candidates) {
3364
+ if (!candidates.length) return "";
3365
+ const close = candidates.filter((value) => distance(reference, value) <= Math.max(2, Math.floor(reference.length / 4))).slice(0, 3);
3366
+ const quote = (values) => values.map((value) => `'${value}'`).join(", ");
3367
+ return close.length ? `Did you mean ${quote(close)}? ` : `The nearest ${KIND_PLURAL[kind]} that exist: ${quote(candidates.slice(0, 3))}. `;
3368
+ }
3277
3369
  function contentReferenceFindings(document, options2, findings) {
3278
3370
  const invalid = embeddedRecordFindings(document, options2, findings);
3279
3371
  const registeredChecked = /* @__PURE__ */ new Set();
@@ -3294,7 +3386,7 @@ function contentReferenceFindings(document, options2, findings) {
3294
3386
  finding("opf/unresolved-reference", {
3295
3387
  path,
3296
3388
  message: diagnostic.message,
3297
- help: site.kind === "layouts" ? "Embed the layout record (embed(), or opf embed), register the catalog that defines it, choose one of the suggested layouts, or remove the reference to compose the slide automatically." : "Embed the record (embed(), or opf embed), register the catalog that defines it, or choose one of the suggested records.",
3389
+ help: `${nearestRecords(site.kind, site.reference, suggestions.map((entry2) => entry2.value))}${site.kind === "layouts" ? "Embed the layout record (embed(), or opf embed), register the catalog that defines it, choose one of the suggested layouts, or remove the reference to compose the slide automatically." : "Embed the record (embed(), or opf embed), register the catalog that defines it, or choose one of the suggested records."}`,
3298
3390
  definition: `${schemas.presentation.$id}#/$defs/Catalogs`,
3299
3391
  lookup: ["opf", "catalog", site.kind],
3300
3392
  suggestions
@@ -5724,12 +5816,12 @@ function asNext(text2) {
5724
5816
  const trimmed = text2.trim();
5725
5817
  return /^[A-Z][a-z]/.test(trimmed) ? trimmed.charAt(0).toLowerCase() + trimmed.slice(1) : trimmed;
5726
5818
  }
5727
- var isRecord2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
5819
+ var isRecord3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
5728
5820
  function readAt(document, segments) {
5729
5821
  let value = document;
5730
5822
  for (const segment of segments) {
5731
5823
  if (Array.isArray(value)) value = value[Number(segment)];
5732
- else if (isRecord2(value)) value = value[segment];
5824
+ else if (isRecord3(value)) value = value[segment];
5733
5825
  else return void 0;
5734
5826
  }
5735
5827
  return value;
@@ -5794,11 +5886,11 @@ function dotted(segments) {
5794
5886
  }
5795
5887
  function keysAt(helpers, path) {
5796
5888
  const value = helpers.valueAt(path);
5797
- return isRecord2(value) ? Object.keys(value) : [];
5889
+ return isRecord3(value) ? Object.keys(value) : [];
5798
5890
  }
5799
5891
  function idsAt(helpers, path) {
5800
5892
  const value = helpers.valueAt(path);
5801
- return Array.isArray(value) ? value.flatMap((entry2) => isRecord2(entry2) && typeof entry2.id === "string" ? [entry2.id] : []) : [];
5893
+ return Array.isArray(value) ? value.flatMap((entry2) => isRecord3(entry2) && typeof entry2.id === "string" ? [entry2.id] : []) : [];
5802
5894
  }
5803
5895
  function codeList(names2, max = 5, joiner = "and") {
5804
5896
  const shown = names2.slice(0, max).map(code);
@@ -5839,7 +5931,7 @@ function freshName(base, taken = []) {
5839
5931
  }
5840
5932
  var BLOCK_KINDS = ["chart", "image", "video", "table", "code", "metric", "quote", "timeline", "items", "bullets", "text"];
5841
5933
  function blockKind(node) {
5842
- if (!isRecord2(node)) return void 0;
5934
+ if (!isRecord3(node)) return void 0;
5843
5935
  if (typeof node.type === "string" && node.type !== "group") return node.type;
5844
5936
  const found = BLOCK_KINDS.find((key2) => node[key2] !== void 0);
5845
5937
  if (found === "items" || found === "bullets") return "list";
@@ -6048,13 +6140,13 @@ function regionNamesOf(slideLayout, helpers) {
6048
6140
  const colon = slideLayout.indexOf(":");
6049
6141
  const group = colon > 0 ? slideLayout.slice(0, colon) : void 0;
6050
6142
  const id = colon > 0 ? slideLayout.slice(colon + 1) : slideLayout;
6051
- const record = (group ? [group] : ["custom", "default"]).map((name) => helpers.valueAt(["catalogs", name, "layouts", id])).find(isRecord2);
6143
+ const record = (group ? [group] : ["custom", "default"]).map((name) => helpers.valueAt(["catalogs", name, "layouts", id])).find(isRecord3);
6052
6144
  if (!record) return void 0;
6053
6145
  const names2 = [];
6054
6146
  const walk2 = (list) => {
6055
6147
  if (!Array.isArray(list)) return;
6056
6148
  for (const entry2 of list) {
6057
- if (!isRecord2(entry2)) continue;
6149
+ if (!isRecord3(entry2)) continue;
6058
6150
  if (typeof entry2.name === "string" && !["title", "subtitle", "tag"].includes(entry2.name)) names2.push(entry2.name);
6059
6151
  walk2(entry2.regions);
6060
6152
  }
@@ -6168,7 +6260,7 @@ var codeHighlightRangeReversed = (finding2) => {
6168
6260
  };
6169
6261
  };
6170
6262
  var variableBuiltinMissing = (finding2, _context, helpers) => {
6171
- const idParam = isRecord2(finding2.validation?.params) ? finding2.validation.params.id : void 0;
6263
+ const idParam = isRecord3(finding2.validation?.params) ? finding2.validation.params.id : void 0;
6172
6264
  const id = (typeof idParam === "string" ? idParam : void 0) ?? match(finding2.message, /^Built-in '(.+?)' has no value/)?.[1] ?? match(finding2.message, /'\{\{(.+?)\}\}'/)?.[1];
6173
6265
  if (!id) return void 0;
6174
6266
  const root = id.split(".")[0];
@@ -6190,7 +6282,7 @@ var durationOutsideNarrative2 = (finding2) => {
6190
6282
  };
6191
6283
  var narrativeDurationRange = (_finding, _context, helpers) => {
6192
6284
  const range = helpers.valueAt(helpers.segments);
6193
- if (isRecord2(range) && typeof range.min === "number" && typeof range.max === "number") {
6285
+ if (isRecord3(range) && typeof range.min === "number" && typeof range.max === "number") {
6194
6286
  return {
6195
6287
  message: `The duration range has min ${range.min} greater than max ${range.max}.`,
6196
6288
  next: `swap them: ${code(`min: ${range.max}`)} and ${code(`max: ${range.min}`)}.`
@@ -6283,7 +6375,7 @@ var AUDIT_TEXT = {
6283
6375
  };
6284
6376
 
6285
6377
  // src/finding-text-format.ts
6286
- var params = (finding2) => isRecord2(finding2.validation?.params) ? finding2.validation.params : {};
6378
+ var params = (finding2) => isRecord3(finding2.validation?.params) ? finding2.validation.params : {};
6287
6379
  var text = (value) => typeof value === "string" ? value : void 0;
6288
6380
  var jsonSyntax = (finding2) => ({
6289
6381
  message: keep(finding2.message),
@@ -6404,7 +6496,7 @@ var dataColumnDuplicate = (finding2, _context, helpers) => {
6404
6496
  const name = unquote(match(finding2.message, /^column name ("(?:[^"\\]|\\.)*") is used by an earlier column/)?.[1]);
6405
6497
  if (!name) return void 0;
6406
6498
  const columns = helpers.valueAt(helpers.segments.slice(0, -1));
6407
- const taken = Array.isArray(columns) ? columns.map((entry2) => isRecord2(entry2) ? String(entry2.name) : String(entry2)) : [];
6499
+ const taken = Array.isArray(columns) ? columns.map((entry2) => isRecord3(entry2) ? String(entry2.name) : String(entry2)) : [];
6408
6500
  return {
6409
6501
  message: `Two columns are named ${code(name)}. Series and mappings address columns by name.`,
6410
6502
  next: `rename the second one (for example ${code(freshName(name, taken))}).`
@@ -6682,7 +6774,7 @@ var unresolvedReference2 = (finding2, _context, helpers) => {
6682
6774
  const group = colon > 0 ? reference.slice(0, colon) : void 0;
6683
6775
  const bare = colon > 0 ? reference.slice(colon + 1) : reference;
6684
6776
  const embeddedIn = group ? [group] : ["custom", "default"];
6685
- const embedded = kind && embeddedIn.some((name) => isRecord2(helpers.valueAt(["catalogs", name, kind, bare])));
6777
+ const embedded = kind && embeddedIn.some((name) => isRecord3(helpers.valueAt(["catalogs", name, kind, bare])));
6686
6778
  const suggestions = (finding2.suggestions ?? []).slice(0, 5).map((entry2) => String(entry2.value));
6687
6779
  if (embedded) {
6688
6780
  return {
@@ -7047,6 +7139,8 @@ function describeSchema(finding2, context, helpers) {
7047
7139
  case "required": {
7048
7140
  const property = String(params2.missingProperty ?? "");
7049
7141
  const where = containerName(segments);
7142
+ const shape = property === "data" ? chartShapeHelp(actual) : void 0;
7143
+ if (shape) return { message: shape.message, next: shape.next };
7050
7144
  const schema = context.schemaAt ? context.schemaAt([...segments, property]) : attempt(() => schemaAtPath(context.document, [...segments, property]), void 0);
7051
7145
  if (format === "json") {
7052
7146
  const sample2 = property === "slides" ? '[ { "title": "\u2026" } ]' : starterText(schema, property, "json");
@@ -7187,7 +7281,7 @@ function schemaFixes(finding2, document) {
7187
7281
  switch (validation.keyword) {
7188
7282
  case "required": {
7189
7283
  const property = String(params2.missingProperty ?? "");
7190
- if (!property || !isRecord2(readAt(document, segments)) || Object.hasOwn(readAt(document, segments), property)) return [];
7284
+ if (!property || !isRecord3(readAt(document, segments)) || Object.hasOwn(readAt(document, segments), property)) return [];
7191
7285
  const value = starterValue(document, segments, property);
7192
7286
  if (value === void 0) return [];
7193
7287
  return [patch(`add:${property}`, `Add \`${property}\``, [{ op: "add", path: pointerOf([...segments, property]), value }])];
@@ -7203,7 +7297,7 @@ function schemaFixes(finding2, document) {
7203
7297
  const name = String(params2.additionalProperty ?? segments.at(-1) ?? "");
7204
7298
  const parent = segments.slice(0, -1);
7205
7299
  const owner = readAt(document, parent);
7206
- if (!name || !isRecord2(owner) || !Object.hasOwn(owner, name)) return [];
7300
+ if (!name || !isRecord3(owner) || !Object.hasOwn(owner, name)) return [];
7207
7301
  const fixes = [];
7208
7302
  for (const suggestion of finding2.suggestions ?? []) {
7209
7303
  const target = String(suggestion.value);
@@ -7212,14 +7306,14 @@ function schemaFixes(finding2, document) {
7212
7306
  if (allowsKey(document, parent, "extensions") && name !== "extensions") {
7213
7307
  const existing = owner.extensions;
7214
7308
  if (existing === void 0) fixes.push(patch("move-to-extensions", "Move into extensions", [{ op: "add", path: pointerOf([...parent, "extensions"]), value: {} }, { op: "move", from: finding2.path, path: pointerOf([...parent, "extensions", name]) }]));
7215
- else if (isRecord2(existing) && !Object.hasOwn(existing, name)) fixes.push(patch("move-to-extensions", "Move into extensions", [{ op: "move", from: finding2.path, path: pointerOf([...parent, "extensions", name]) }]));
7309
+ else if (isRecord3(existing) && !Object.hasOwn(existing, name)) fixes.push(patch("move-to-extensions", "Move into extensions", [{ op: "move", from: finding2.path, path: pointerOf([...parent, "extensions", name]) }]));
7216
7310
  }
7217
7311
  return fixes;
7218
7312
  }
7219
7313
  case "type": {
7220
7314
  const current = readAt(document, segments);
7221
7315
  if (current === void 0) return [];
7222
- if (params2.type === "array" && (typeof current === "string" || isRecord2(current))) return [patch("wrap-in-list", "Wrap in a list", [{ op: "replace", path: finding2.path, value: [current] }], typeof current === "string")];
7316
+ if (params2.type === "array" && (typeof current === "string" || isRecord3(current))) return [patch("wrap-in-list", "Wrap in a list", [{ op: "replace", path: finding2.path, value: [current] }], typeof current === "string")];
7223
7317
  if (params2.type === "string" && (typeof current === "number" || typeof current === "boolean")) return [patch("convert-to-string", "Convert to string", [{ op: "replace", path: finding2.path, value: String(current) }], true)];
7224
7318
  return [];
7225
7319
  }
@@ -7238,6 +7332,30 @@ function schemaFixes(finding2, document) {
7238
7332
  }
7239
7333
  }
7240
7334
 
7335
+ // src/drawn-font-families.ts
7336
+ function resolveDrawnFontFamilies(families, measurement, keepsName) {
7337
+ const resolve2 = measurement?.resolveStyle;
7338
+ if (typeof resolve2 !== "function") return families;
7339
+ const drawn = { ...families };
7340
+ for (const role of ["heading", "body", "code", "accent"]) {
7341
+ const named = families[role];
7342
+ if (typeof named !== "string" || !named) continue;
7343
+ try {
7344
+ const resolved = resolve2.call(measurement, { fontFamily: named, fontWeight: role === "heading" ? 700 : 400 });
7345
+ let keep3;
7346
+ if (keepsName) keep3 = keepsName(named, resolved.fontFamily);
7347
+ else {
7348
+ const regular = role === "heading" ? resolve2.call(measurement, { fontFamily: named, fontWeight: 400 }) : resolved;
7349
+ const encoded = resolved.symbolEncoding != null;
7350
+ keep3 = encoded || regular.fontWeight !== void 0 && regular.fontWeight !== 400 && resolved.fontFamily.toLowerCase() !== named.toLowerCase();
7351
+ }
7352
+ if (!keep3) drawn[role] = resolved.fontFamily;
7353
+ } catch {
7354
+ }
7355
+ }
7356
+ return drawn;
7357
+ }
7358
+
7241
7359
  // src/script-measurement.ts
7242
7360
  function scriptProfile(presentation, slideIndex, options2, language) {
7243
7361
  let resolved;
@@ -7390,7 +7508,12 @@ function buildSlides(document, options2) {
7390
7508
  const layout = lazy(() => resolved().options.layout);
7391
7509
  const composed = lazy(() => {
7392
7510
  const result = { layoutDiagnostics: [] };
7393
- const compose = (input, withLayout) => composeSlide(input, { ...resolved().options, layout: withLayout });
7511
+ let fontFamilies;
7512
+ const compose = (input, withLayout) => {
7513
+ const options3 = resolved().options;
7514
+ fontFamilies ??= options3.fontFamilies && resolveDrawnFontFamilies(options3.fontFamilies, options3.textMeasurement);
7515
+ return composeSlide(input, { ...options3, ...fontFamilies ? { fontFamilies } : {}, layout: withLayout });
7516
+ };
7394
7517
  try {
7395
7518
  result.composition = compose(resolved().slide, layout());
7396
7519
  result.layoutDiagnostics.push(...result.composition.diagnostics.map((diagnostic) => ({ diagnostic, strict: false })));
@@ -7708,4 +7831,4 @@ function assertValidCatalogRecord(kind, value) {
7708
7831
  if (!report2.valid) throw new OPFValidationError(report2);
7709
7832
  }
7710
7833
 
7711
- export { BLOCK_AS_VALUES, BLOCK_OPTION_KEYS, CATALOG_SCHEMA, COMMON_KEYS, DEFAULT_VALIDATION_THRESHOLDS, FENCE_ATTRIBUTES, FENCE_KINDS, LINE_KEYWORDS, METRIC_KEYS, OPFPatchError, OPFPatchHashError, OPFPatchValidationError, OPFValidationError, PATTERN_PHRASES, SLIDE_OPTION_KEYS, SOURCE_EXPLAIN, SPAN_FLAGS, SPAN_VALUES, UNRANKED_PRIORITY, activeSchema, analyzeJsonSyntax, applyPatch, applyPatchWithInverse, applyTextEdits, assertValid, assertValidCatalogRecord, catalogDisplayKinds, catalogKindAt, catalogSchemaNames, createLocator, createSchemaValue, describeField, describeFinding, distance2 as distance, documentHash, escapePointerToken, findValidationRule, formatPointer, getAtPointer, hasPointer, idToken, invertPatch, isIdToken, isPromotedRegionKey, jsonEqual, listSchemaFields, nearest, normalizePatch, parsePointer, parseSource, pointerFromPath, readPointer, rememberByText, resolveIdPatch, resolveIdPointer, resolveSchema, resolveSlideContext, resolveSlideScriptMeasurement, ruleDocumentation, schemaAtPath, schemaCatalogKinds, schemaLabel, schemaType, schemaVariants, segmentsOf, splitOpfPath, splitPointer2 as splitPointer, starterValue, toIdPatch, toIdPointer, unescapePointerToken, us, validate, validateCatalogRecord, validationCategories, validationRules, walkPatch };
7834
+ export { BLOCK_AS_VALUES, BLOCK_OPTION_KEYS, CATALOG_SCHEMA, COMMON_KEYS, DEFAULT_VALIDATION_THRESHOLDS, FENCE_ATTRIBUTES, FENCE_KINDS, LINE_KEYWORDS, METRIC_KEYS, OPFPatchError, OPFPatchHashError, OPFPatchValidationError, OPFValidationError, PATTERN_PHRASES, SLIDE_OPTION_KEYS, SOURCE_EXPLAIN, SPAN_FLAGS, SPAN_VALUES, UNRANKED_PRIORITY, activeSchema, analyzeJsonSyntax, applyPatch, applyPatchWithInverse, applyTextEdits, assertValid, assertValidCatalogRecord, catalogDisplayKinds, catalogKindAt, catalogSchemaNames, createLocator, createSchemaValue, describeField, describeFinding, distance2 as distance, documentHash, escapePointerToken, findValidationRule, formatPointer, getAtPointer, hasPointer, idToken, invertPatch, isIdToken, isPromotedRegionKey, jsonEqual, listSchemaFields, nearest, normalizePatch, parsePointer, parseSource, pointerFromPath, readPointer, rememberByText, resolveDrawnFontFamilies, resolveIdPatch, resolveIdPointer, resolveSchema, resolveSlideContext, resolveSlideScriptMeasurement, ruleDocumentation, schemaAtPath, schemaCatalogKinds, schemaLabel, schemaType, schemaVariants, segmentsOf, splitOpfPath, splitPointer2 as splitPointer, starterValue, toIdPatch, toIdPointer, unescapePointerToken, us, validate, validateCatalogRecord, validationCategories, validationRules, walkPatch };
@@ -1,10 +1,10 @@
1
- import { sortPresentationKeys } from './chunk-O3SA5SJ4.js';
1
+ import { sortPresentationKeys } from './chunk-KELC4K62.js';
2
2
  import { NOT_CHECKED } from './chunk-RYZPBJLB.js';
3
- import { Ctx, describeYamlErrors } from './chunk-SYW4BCGZ.js';
4
- import { same } from './chunk-ABFDFZIW.js';
5
- import { validate } from './chunk-PAGVSTVF.js';
6
- import { presentation } from './chunk-4ZM5G3XX.js';
7
- import { checkCatalogsOption } from './chunk-LRTQXGJO.js';
3
+ import { Ctx, describeYamlErrors } from './chunk-SEZ4EPW6.js';
4
+ import { same } from './chunk-TNUTRV6E.js';
5
+ import { validate } from './chunk-WPKCWQ6W.js';
6
+ import { presentation } from './chunk-5Q4IYLMW.js';
7
+ import { checkCatalogsOption } from './chunk-2CUCYENF.js';
8
8
  import { Parser, parseDocument, isMap, isSeq, Document, visit, isScalar, isAlias } from 'yaml';
9
9
 
10
10
  var MAX_ALIAS_COUNT = 100;