@openpresentation/opf 0.19.0 → 0.20.2

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 (59) hide show
  1. package/dist/browser.js +16 -16
  2. package/dist/{chunk-SIQWYDFF.js → chunk-2XIMI2V5.js} +6 -6
  3. package/dist/{chunk-65GDO6TJ.js → chunk-5YTTTLL2.js} +3 -3
  4. package/dist/{chunk-A6COIVTD.js → chunk-AXUY4BHG.js} +1 -1
  5. package/dist/{chunk-LRPSMQ62.js → chunk-E4VBQW2Z.js} +1 -1
  6. package/dist/{chunk-HBFRNOP4.js → chunk-E662FTHI.js} +1 -1
  7. package/dist/{chunk-AK4UXIRP.js → chunk-J4NYP5JC.js} +1 -1
  8. package/dist/{chunk-ZPCKGGI6.js → chunk-K66K65UQ.js} +108 -43
  9. package/dist/{chunk-GRN3U5JT.js → chunk-MSWNRD4C.js} +5 -5
  10. package/dist/{chunk-XWMKAJSS.js → chunk-NVNK5BLL.js} +10 -9
  11. package/dist/{chunk-WHKFIFN7.js → chunk-NXYHBBVV.js} +1 -1
  12. package/dist/{chunk-OOT5AY4V.js → chunk-OGVRAMHE.js} +2 -2
  13. package/dist/{chunk-SEUJIS55.js → chunk-QEISIGFD.js} +2 -2
  14. package/dist/{chunk-R5T4H4ZV.js → chunk-QOQATI37.js} +45 -14
  15. package/dist/{chunk-FGUQHD37.js → chunk-SPSAQKNB.js} +5 -5
  16. package/dist/{chunk-YAHPQRVU.js → chunk-SZALQLZP.js} +1 -1
  17. package/dist/{chunk-OVBGGGKO.js → chunk-XGR5HL7R.js} +2 -2
  18. package/dist/{chunk-V3RGXJJS.js → chunk-XWDAXVFZ.js} +4 -4
  19. package/dist/{composition-DilAHoWM.d.ts → composition-QtS1rnhv.d.ts} +13 -2
  20. package/dist/composition.d.ts +1 -1
  21. package/dist/composition.js +1 -1
  22. package/dist/conversion-6YXKHUAU.js +19 -0
  23. package/dist/{conversion-k9l5pQsq.d.ts → conversion-DRBKSRAB.d.ts} +5 -5
  24. package/dist/convert.d.ts +2 -2
  25. package/dist/convert.js +7 -7
  26. package/dist/data.d.ts +1 -1
  27. package/dist/data.js +2 -2
  28. package/dist/{deck-report-D19fyuNu.d.ts → deck-report-4CNhJYk7.d.ts} +2 -2
  29. package/dist/deck.d.ts +5 -5
  30. package/dist/deck.js +10 -10
  31. package/dist/diff.js +5 -5
  32. package/dist/docs.js +21 -15
  33. package/dist/examples.d.ts +1 -1
  34. package/dist/examples.js +4854 -1497
  35. package/dist/format.js +2 -2
  36. package/dist/index.d.ts +11 -11
  37. package/dist/index.js +20 -20
  38. package/dist/markdown.d.ts +3 -3
  39. package/dist/markdown.js +7 -7
  40. package/dist/node-engine.d.ts +8 -7
  41. package/dist/node-engine.js +20 -20
  42. package/dist/{pagination-BNj5UQn5.d.ts → pagination-CAX6mHG6.d.ts} +1 -1
  43. package/dist/pagination.d.ts +2 -2
  44. package/dist/pagination.js +4 -4
  45. package/dist/patch.js +4 -4
  46. package/dist/{presentation-B_e2Djga.d.ts → presentation-CKbo-axq.d.ts} +1 -1
  47. package/dist/repo-readme.js +1 -1
  48. package/dist/schemas.js +1 -1
  49. package/dist/spec/schemas/layout.schema.json +27 -8
  50. package/dist/spec/schemas/opf.schema.json +4 -4
  51. package/dist/types.d.ts +4 -4
  52. package/dist/{validation-types-CWhPuYUt.d.ts → validation-types-CxbvWnKh.d.ts} +1 -1
  53. package/dist/{validator-Cse-AD32.d.ts → validator-Rq4en736.d.ts} +3 -3
  54. package/dist/validator.d.ts +4 -4
  55. package/dist/validator.js +3 -3
  56. package/dist/yaml.d.ts +3 -3
  57. package/dist/yaml.js +7 -7
  58. package/package.json +6 -6
  59. package/dist/conversion-NJQHH4QW.js +0 -19
package/dist/browser.js CHANGED
@@ -1,20 +1,20 @@
1
- import { OPFApiError } from './chunk-V3RGXJJS.js';
2
- export { OPFApiError, OPFExportError, OPFImportError, OPFMoveToCustomError, copySlides, edit, embed, fill, fillRecords, moveToCustom, parseSlideSelection, sameRecord, stats, updateFromCatalog } from './chunk-V3RGXJJS.js';
1
+ import { OPFApiError } from './chunk-XWDAXVFZ.js';
2
+ export { OPFApiError, OPFExportError, OPFImportError, OPFMoveToCustomError, copySlides, edit, embed, fill, fillRecords, moveToCustom, parseSlideSelection, sameRecord, stats, updateFromCatalog } from './chunk-XWDAXVFZ.js';
3
3
  export { specFileEntries, specFileKinds, specFilePaths } from './chunk-UJSAR6GF.js';
4
- export { OPFDataImportError, ingest, parseTabularData } from './chunk-AK4UXIRP.js';
5
- export { DECK_FORMATS, deckFormatOf, parse, stringify } from './chunk-GRN3U5JT.js';
6
- export { fromMarkdown, toMarkdown } from './chunk-FGUQHD37.js';
7
- import './chunk-SIQWYDFF.js';
8
- export { OPFMarkdownError } from './chunk-LRPSMQ62.js';
9
- export { diff, merge } from './chunk-SEUJIS55.js';
10
- export { OPFPatchError, OPFPatchValidationError, applyPatch } from './chunk-WHKFIFN7.js';
11
- export { OPFFormatError, format } from './chunk-A6COIVTD.js';
12
- export { OPFPaginationError, paginate, paginateSlide } from './chunk-OVBGGGKO.js';
13
- import './chunk-YAHPQRVU.js';
14
- import './chunk-HBFRNOP4.js';
15
- export { CATALOG_SCHEMA, DEFAULT_CHART_PALETTE, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, catalogDisplayKinds, catalogSchemaNames, findValidationRule, resolveSlideContext, validate, validateCatalogRecord, validationCategories, validationRules } from './chunk-65GDO6TJ.js';
16
- export { CATALOG_GROUP_PATTERN, CATALOG_REFERENCE_PATTERN, CHART_TYPES, DEFAULT_VARIABLE_DATE_FORMAT, ENGINE_DEFAULT_CHART_TYPES, ENGINE_DEFAULT_COLOR_SCHEME, ENGINE_DEFAULT_FONT_SCHEME, ENGINE_DEFAULT_THEME, LANGUAGES, LOGO_SHAPES, OPFCatalogsOptionError, OPFUnresolvedReferenceError, OPFVariableError, SLIDE_SCOPED_BUILTINS, SOCIAL_PLATFORMS, VARIABLE_KINDS, catalogGroupSource, catalogKinds, catalogRecords, catalogReferenceSites, chartNumber, coerceVariableValue, datasetDiagnostics, formatDataNumber, formatVariableNumber, fromExcelNumberFormat, hasContentVariables, inlineChartData, inlineDatasets, inlineTableData, isDatasetRef, isTemplate, isXYChartType, listBuiltinVariables, listVariables, normalizeLanguageFamily, numberFormatError, parseReference, resolveChartData, resolveDesignRecords, resolveFontScheme, resolveReference, resolveSlideVariables, resolveTableData, resolveVariables, suggestChartNumberFix, tableCellDisplayValue, toExcelNumberFormat, unresolvedReference, unusedDatasets, variableDeclarations } from './chunk-ZPCKGGI6.js';
17
- export { audience, chartType, colorScheme, fontScheme, language, layout, narrative, presentation, purpose, schemaEntries, schemaNames, schemas, socialPlatform, theme, tone } from './chunk-R5T4H4ZV.js';
4
+ export { OPFDataImportError, ingest, parseTabularData } from './chunk-J4NYP5JC.js';
5
+ export { DECK_FORMATS, deckFormatOf, parse, stringify } from './chunk-MSWNRD4C.js';
6
+ export { fromMarkdown, toMarkdown } from './chunk-SPSAQKNB.js';
7
+ import './chunk-2XIMI2V5.js';
8
+ export { OPFMarkdownError } from './chunk-E4VBQW2Z.js';
9
+ export { diff, merge } from './chunk-QEISIGFD.js';
10
+ export { OPFPatchError, OPFPatchValidationError, applyPatch } from './chunk-NXYHBBVV.js';
11
+ export { OPFFormatError, format } from './chunk-AXUY4BHG.js';
12
+ export { OPFPaginationError, paginate, paginateSlide } from './chunk-XGR5HL7R.js';
13
+ import './chunk-SZALQLZP.js';
14
+ import './chunk-E662FTHI.js';
15
+ export { CATALOG_SCHEMA, DEFAULT_CHART_PALETTE, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, catalogDisplayKinds, catalogSchemaNames, findValidationRule, resolveSlideContext, validate, validateCatalogRecord, validationCategories, validationRules } from './chunk-5YTTTLL2.js';
16
+ export { CATALOG_GROUP_PATTERN, CATALOG_REFERENCE_PATTERN, CHART_TYPES, DEFAULT_VARIABLE_DATE_FORMAT, ENGINE_DEFAULT_CHART_TYPES, ENGINE_DEFAULT_COLOR_SCHEME, ENGINE_DEFAULT_FONT_SCHEME, ENGINE_DEFAULT_THEME, LANGUAGES, LOGO_SHAPES, OPFCatalogsOptionError, OPFUnresolvedReferenceError, OPFVariableError, SLIDE_SCOPED_BUILTINS, SOCIAL_PLATFORMS, VARIABLE_KINDS, catalogGroupSource, catalogKinds, catalogRecords, catalogReferenceSites, chartNumber, coerceVariableValue, datasetDiagnostics, formatDataNumber, formatVariableNumber, fromExcelNumberFormat, hasContentVariables, inlineChartData, inlineDatasets, inlineTableData, isDatasetRef, isTemplate, isXYChartType, listBuiltinVariables, listVariables, normalizeLanguageFamily, numberFormatError, parseReference, resolveChartData, resolveDesignRecords, resolveFontScheme, resolveReference, resolveSlideVariables, resolveTableData, resolveVariables, suggestChartNumberFix, tableCellDisplayValue, toExcelNumberFormat, unresolvedReference, unusedDatasets, variableDeclarations } from './chunk-K66K65UQ.js';
17
+ export { audience, chartType, colorScheme, fontScheme, language, layout, narrative, presentation, purpose, schemaEntries, schemaNames, schemas, socialPlatform, theme, tone } from './chunk-QOQATI37.js';
18
18
  export { FONT_POLICY, applyFontPolicyDecisions, fontAvailabilityDiagnostics, fontPolicyFor } from './chunk-F7PLNWHQ.js';
19
19
 
20
20
  // src/browser.ts
@@ -1,9 +1,9 @@
1
- import { NOT_CHECKED, Ctx } from './chunk-LRPSMQ62.js';
2
- import { sortPresentationKeys } from './chunk-A6COIVTD.js';
3
- import { same } from './chunk-HBFRNOP4.js';
4
- import { validate } from './chunk-65GDO6TJ.js';
5
- import { checkCatalogsOption } from './chunk-ZPCKGGI6.js';
6
- import { presentation } from './chunk-R5T4H4ZV.js';
1
+ import { NOT_CHECKED, Ctx } from './chunk-E4VBQW2Z.js';
2
+ import { sortPresentationKeys } from './chunk-AXUY4BHG.js';
3
+ import { same } from './chunk-E662FTHI.js';
4
+ import { validate } from './chunk-5YTTTLL2.js';
5
+ import { checkCatalogsOption } from './chunk-K66K65UQ.js';
6
+ import { presentation } from './chunk-QOQATI37.js';
7
7
  import { Parser, parseDocument, isMap, Document, visit, isScalar, isAlias, isSeq } from 'yaml';
8
8
 
9
9
  var MAX_ALIAS_COUNT = 100;
@@ -1,11 +1,11 @@
1
- import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, imageBackground, tableGrid, timelineTextColor, LOGO_SHAPES, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, resolveReference, textWidthMeasurer, normalizeHexColor, resolveColorRef, resolveDesignRecords, resolveFontFamilies, checkCatalogsOption, provenanceOf, unresolvedReference, OPFUnresolvedReferenceError, resolveColorRoles, resolveCanvasDimensions, resolveSlideVariables, defaultSlideBackground, isRecord, hasContentVariables, isTemplate, instantiateForValidation, layoutTemplateIssues, resolveScriptFonts, 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-ZPCKGGI6.js';
2
- import { schemaEntries, schemas } from './chunk-R5T4H4ZV.js';
1
+ import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, imageBackground, tableGrid, timelineTextColor, LOGO_SHAPES, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, resolveReference, textWidthMeasurer, normalizeHexColor, resolveColorRef, resolveDesignRecords, resolveFontFamilies, checkCatalogsOption, provenanceOf, unresolvedReference, OPFUnresolvedReferenceError, resolveColorRoles, resolveCanvasDimensions, resolveSlideVariables, defaultSlideBackground, isRecord, hasContentVariables, isTemplate, instantiateForValidation, layoutTemplateIssues, resolveScriptFonts, 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-K66K65UQ.js';
2
+ import { schemaEntries, schemas } from './chunk-QOQATI37.js';
3
3
  import Ajv2020 from 'ajv/dist/2020.js';
4
4
  import addFormats from 'ajv-formats';
5
5
  import { parseTree, printParseErrorCode, getNodePath } from 'jsonc-parser';
6
6
 
7
7
  // src/catalog-schemas.ts
8
- var CATALOG_SCHEMA = 2;
8
+ var CATALOG_SCHEMA = 3;
9
9
  var catalogDisplayKinds = ["chartTypes", "languages", "socialPlatforms"];
10
10
  var catalogSchemaNames = {
11
11
  layouts: "layout",
@@ -1,4 +1,4 @@
1
- import { presentation } from './chunk-R5T4H4ZV.js';
1
+ import { presentation } from './chunk-QOQATI37.js';
2
2
 
3
3
  // src/format.ts
4
4
  var OPFFormatError = class extends Error {
@@ -1,4 +1,4 @@
1
- import { isRecord, same } from './chunk-HBFRNOP4.js';
1
+ import { isRecord, same } from './chunk-E662FTHI.js';
2
2
  import { parseDocument, YAMLMap, Scalar, stringify } from 'yaml';
3
3
 
4
4
  // src/not-checked.ts
@@ -1,4 +1,4 @@
1
- import { validate } from './chunk-65GDO6TJ.js';
1
+ import { validate } from './chunk-5YTTTLL2.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"];
@@ -1,4 +1,4 @@
1
- import { chartNumber } from './chunk-ZPCKGGI6.js';
1
+ import { chartNumber } from './chunk-K66K65UQ.js';
2
2
 
3
3
  // src/data.ts
4
4
  var OPFDataImportError = class extends Error {
@@ -2101,13 +2101,19 @@ function parse(layout) {
2101
2101
  }
2102
2102
  if (areas.length > MAX_TEMPLATE_TRACKS) grid("layout-template", "/areas", `a template has at most ${MAX_TEMPLATE_TRACKS} rows; this one has ${areas.length}`);
2103
2103
  const cells = areas.map((row, index) => {
2104
- if (typeof row !== "string") {
2105
- grid("layout-template", `/areas/${index}`, "each row is a string of area names");
2104
+ if (!Array.isArray(row)) {
2105
+ grid("layout-template", `/areas/${index}`, "each row is an array of area names; use nested arrays instead of space-separated strings");
2106
2106
  return [];
2107
2107
  }
2108
- const words = row.trim().split(/\s+/).filter(Boolean);
2109
- if (!words.length) grid("layout-template", `/areas/${index}`, 'a row needs at least one area name or "."');
2110
- for (const word of words) if (word !== "." && !NAME.test(word)) grid("layout-template", `/areas/${index}`, `'${word}' is not an area name (lowercase letters, digits and hyphens, starting with a letter) or "."`);
2108
+ const words = [];
2109
+ if (!row.length) grid("layout-template", `/areas/${index}`, 'a row needs at least one area name or "."');
2110
+ row.forEach((cell, column) => {
2111
+ if (typeof cell !== "string") grid("layout-template", `/areas/${index}/${column}`, 'each cell is one area name or "."');
2112
+ else {
2113
+ words.push(cell);
2114
+ if (cell !== "." && !NAME.test(cell)) grid("layout-template", `/areas/${index}/${column}`, `'${cell}' is not an area name (lowercase letters, digits and hyphens, starting with a letter) or "."`);
2115
+ }
2116
+ });
2111
2117
  return words;
2112
2118
  });
2113
2119
  const width = cells[0]?.length ?? 0;
@@ -3299,6 +3305,7 @@ function resolveColorRoles(colorScheme, options = {}) {
3299
3305
  background,
3300
3306
  surface,
3301
3307
  surfaceAlt: surfaceAltColor(surface, text2),
3308
+ border: dark ? "#475569" : "#CBD5E1",
3302
3309
  text: text2,
3303
3310
  textSecondary: keepAlpha(colorScheme.textSecondary) ?? (dark ? slot("light2", "#E2E8F0") : slot("dark2", "#334155")),
3304
3311
  hyperlink: readableLink(slot("hyperlink", DEFAULT_HYPERLINK), background, text2),
@@ -4749,9 +4756,9 @@ function layoutContent(layout) {
4749
4756
  }
4750
4757
  function templateAreas(layout) {
4751
4758
  const value = record7(layout), regions = record7(value.regions);
4752
- const rows3 = Array.isArray(value.areas) ? value.areas.filter((row) => typeof row === "string") : [];
4759
+ const rows3 = Array.isArray(value.areas) ? value.areas.filter((row) => Array.isArray(row) && row.every((cell) => typeof cell === "string")) : [];
4753
4760
  const seen = /* @__PURE__ */ new Set(), out = [];
4754
- for (const row of rows3) for (const name of row.trim().split(/\s+/)) {
4761
+ for (const row of rows3) for (const name of row) {
4755
4762
  if (!name || name === "." || seen.has(name)) continue;
4756
4763
  seen.add(name);
4757
4764
  out.push(name === "title" || name === "subtitle" || !Object.hasOwn(regions, name) ? { name } : { name, region: layoutRegion(name, regions[name]) });
@@ -5338,6 +5345,22 @@ function layoutQuote(value, box, options = {}) {
5338
5345
  photoFits = selected.photoFits;
5339
5346
  }
5340
5347
  }
5348
+ if (body.fit && !body.fit.overflow && photoFits && (!attribution || attribution.fit && !attribution.fit.overflow)) {
5349
+ body.box.height = body.fit.placement?.height ?? heightOf(body.fit);
5350
+ const footerTop = body.box.y + body.box.height + 18;
5351
+ const previousTop = Math.min(attribution?.box.y ?? Infinity, photo?.box.y ?? Infinity);
5352
+ if (Number.isFinite(previousTop)) {
5353
+ const shift = footerTop - previousTop;
5354
+ if (attribution) {
5355
+ attribution.box.y += shift;
5356
+ attribution.fit = translateTextFit(attribution.fit, shift);
5357
+ }
5358
+ if (photo) {
5359
+ photo.box.y += shift;
5360
+ photo.shape = imageShape("circle", photo.box);
5361
+ }
5362
+ }
5363
+ }
5341
5364
  if (hasPhoto && !photo) photoFits = false;
5342
5365
  for (const part of parts) {
5343
5366
  const area = part.box;
@@ -5357,7 +5380,11 @@ function layoutQuote(value, box, options = {}) {
5357
5380
  report("part-overlap", path, ["body", "footer"], "Quote body and photo overlap; do not accept this layout without more space.");
5358
5381
  }
5359
5382
  if (diagnostics.length && options.overflow === "error") throw new OPFCompositionError(diagnostics);
5360
- return { algorithm: "quote-flow-v1", textMeasurement: options.textMeasurement ? "provided" : "estimated", parts, ...photo ? { photo } : {}, diagnostics, overflow: diagnostics.length > 0 };
5383
+ const occupiedParts = parts.filter((part) => part.fit).map((part) => ({ ...part.box, height: part.fit.placement?.height ?? heightOf(part.fit) }));
5384
+ if (photo) occupiedParts.push(photo.box);
5385
+ const bottom = Math.max(box.y, ...occupiedParts.map((area) => area.y + area.height));
5386
+ const bounds = { x: box.x, y: box.y, width: box.width, height: bottom - box.y };
5387
+ return { bounds, algorithm: "quote-flow-v1", textMeasurement: options.textMeasurement ? "provided" : "estimated", parts, ...photo ? { photo } : {}, diagnostics, overflow: diagnostics.length > 0 };
5361
5388
  }
5362
5389
  function layoutMetric(value, box, options = {}) {
5363
5390
  const scalar = typeof value === "string" || typeof value === "number";
@@ -5373,7 +5400,7 @@ function layoutMetric(value, box, options = {}) {
5373
5400
  }
5374
5401
  if (!Number.isFinite(rasterPadding) || rasterPadding < 0) throw new RangeError("Metric raster padding must be finite and nonnegative.");
5375
5402
  if (options.overflow !== void 0 && !["warn", "error"].includes(options.overflow)) throw new RangeError("Invalid metric overflow policy.");
5376
- const requestedAlignment = options.align ?? "left";
5403
+ const requestedAlignment = options.align ?? "center";
5377
5404
  if (!["left", "center", "right"].includes(requestedAlignment)) throw new RangeError("Invalid metric alignment.");
5378
5405
  const sourcePath = options.path ?? "metric", parts = [], diagnostics = [];
5379
5406
  for (const role of ["value", "unit", "label", "description", "delta", "trend"]) {
@@ -5408,16 +5435,16 @@ function layoutMetric(value, box, options = {}) {
5408
5435
  let outline = null;
5409
5436
  for (const segment2 of line.segments) {
5410
5437
  if (segment2.kind !== "text") continue;
5411
- const bounds = measureTextOutline(part.text.slice(segment2.start, segment2.end), fit.fontSize, part.style, options.textMeasurement);
5412
- if (!bounds) continue;
5413
- const next = { ...bounds, x: segment2.x + bounds.x };
5438
+ const bounds2 = measureTextOutline(part.text.slice(segment2.start, segment2.end), fit.fontSize, part.style, options.textMeasurement);
5439
+ if (!bounds2) continue;
5440
+ const next = { ...bounds2, x: segment2.x + bounds2.x };
5414
5441
  if (!outline) outline = next;
5415
5442
  else {
5416
- const right = Math.max(outline.x + outline.width, next.x + next.width), bottom = Math.max(outline.y + outline.height, next.y + next.height);
5443
+ const right = Math.max(outline.x + outline.width, next.x + next.width), bottom2 = Math.max(outline.y + outline.height, next.y + next.height);
5417
5444
  outline.x = Math.min(outline.x, next.x);
5418
5445
  outline.y = Math.min(outline.y, next.y);
5419
5446
  outline.width = right - outline.x;
5420
- outline.height = bottom - outline.y;
5447
+ outline.height = bottom2 - outline.y;
5421
5448
  }
5422
5449
  }
5423
5450
  return { width: line.width, y: index * fit.lineHeight, baseline: fit.fontSize + index * fit.lineHeight, height: fit.lineHeight, outline };
@@ -5527,7 +5554,9 @@ function layoutMetric(value, box, options = {}) {
5527
5554
  }
5528
5555
  }
5529
5556
  if (diagnostics.length && options.overflow === "error") throw new OPFCompositionError(diagnostics);
5530
- return { algorithm: "metric-flow-v1", alignment, textMeasurement: options.textMeasurement ? "provided" : "estimated", arrangement: selected.arrangement, ...metric.sentiment === void 0 ? {} : { sentiment: metric.sentiment }, attempts, parts, diagnostics, overflow: diagnostics.length > 0 };
5557
+ const bottom = Math.max(box.y, ...visible.map((part) => part.box.y + occupied(part.fit)));
5558
+ const bounds = { x: box.x, y: box.y, width: box.width, height: bottom - box.y };
5559
+ return { bounds, algorithm: "metric-flow-v1", alignment, textMeasurement: options.textMeasurement ? "provided" : "estimated", arrangement: selected.arrangement, ...metric.sentiment === void 0 ? {} : { sentiment: metric.sentiment }, attempts, parts, diagnostics, overflow: diagnostics.length > 0 };
5531
5560
  }
5532
5561
  var TIMELINE_STATUS_VALUES = ["done", "current", "planned"];
5533
5562
  var TIMELINE_RING_RATIO = 1.6;
@@ -5553,9 +5582,9 @@ function layoutTimeline(value, box, options = {}) {
5553
5582
  add("name", timeline.name, `${path}.name`, 24, 700);
5554
5583
  add("description", timeline.description, `${path}.description`, 18, 400);
5555
5584
  timeline.events.forEach((event, index) => {
5556
- add("when", event.when, `${eventPath(index)}.when`, 16, 500, index);
5557
- add("what", event.what, `${eventPath(index)}.what`, 16, 500, index);
5558
- add("event-description", event.description, `${eventPath(index)}.description`, 16, 500, index);
5585
+ add("when", event.when, `${eventPath(index)}.when`, 18, 500, index);
5586
+ add("what", event.what, `${eventPath(index)}.what`, 22, 600, index);
5587
+ add("event-description", event.description, `${eventPath(index)}.description`, 18, 400, index);
5559
5588
  });
5560
5589
  const metadata = [], events = timeline.events.map(() => []);
5561
5590
  for (const part of source) if (part.eventIndex === void 0) metadata.push(part);
@@ -5609,6 +5638,7 @@ function layoutTimeline(value, box, options = {}) {
5609
5638
  const accepted = { ...part, box: area, alignment, fit };
5610
5639
  parts.push(accepted);
5611
5640
  if (fit.overflow) report(accepted, "text-fit", "Timeline field exceeds its readable width; increase its space or split the timeline.");
5641
+ if (breaksInsideWord(part.text, fit)) report(accepted, "text-fit", "Timeline field would split a word; use a wider vertical arrangement or paginate events.");
5612
5642
  if (area.x < box.x - 0.01 || area.y < box.y - 0.01 || area.x + area.width > box.x + box.width + 0.01 || area.y + area.height > box.y + box.height + 0.01) report(accepted, "part-outside-cell", "Timeline field extends outside its cell; increase the cell or paginate events.", Math.max(area.x + area.width - box.x - box.width, area.y + area.height - box.y - box.height));
5613
5643
  return accepted;
5614
5644
  };
@@ -5626,27 +5656,35 @@ function layoutTimeline(value, box, options = {}) {
5626
5656
  };
5627
5657
  if (arrangement === "alternating") {
5628
5658
  const width = box.width / Math.max(2, count), step = (box.width - width) / Math.max(1, count - 1), start = count === 1 ? box.x + box.width / 2 : box.x + width / 2;
5629
- const lineY = y + Math.max(scale, available) * 0.46;
5659
+ const fieldGap = 4 * scale, clearance = 24 * scale;
5660
+ const heights = events.map((fields2) => fields2.reduce((height, part) => height + measure(part, gridFontSize(part.requestedFontSize - reduction * scale, minimum), width, "center").height, 0) + Math.max(0, fields2.length - 1) * fieldGap);
5661
+ const markerExtent = hasRing ? ringRadius + strokeWidth / 2 : radius;
5662
+ const upper = Math.max(markerExtent, ...heights.filter((_, index) => index % 2 === 0).map((height) => height + clearance));
5663
+ const lower = Math.max(markerExtent, ...heights.filter((_, index) => index % 2 === 1).map((height) => height + clearance));
5664
+ const lineY = y + Math.max(0, (available - upper - lower) / 2) + upper;
5630
5665
  events.forEach((fields2, index) => {
5631
- const x = start + (rtl ? count - 1 - index : index) * step, top = index % 2 === 0 ? y : lineY + 24 * scale, bottom = index % 2 === 0 ? lineY - 24 * scale : box.y + box.height;
5666
+ const x = start + (rtl ? count - 1 - index : index) * step, top = index % 2 === 0 ? lineY - clearance - heights[index] : lineY + clearance, bottom = index % 2 === 0 ? lineY - clearance : box.y + box.height;
5632
5667
  markers.push(marker(index, x, lineY));
5633
5668
  let cursor = top;
5634
- for (const part of fields2) {
5669
+ for (const [fieldIndex, part] of fields2.entries()) {
5635
5670
  const placed = place(part, x - width / 2, cursor, width, "center");
5636
5671
  cursor += placed.box.height;
5637
5672
  if (cursor > bottom + 0.01) report(placed, "event-space", "Timeline event labels exceed their side of the connector; change the arrangement or paginate events.", cursor - bottom);
5673
+ if (fieldIndex < fields2.length - 1) cursor += fieldGap;
5638
5674
  }
5639
5675
  });
5640
5676
  } else {
5641
- const lead = hasRing ? ringRadius + strokeWidth / 2 : radius, gap = 24 * scale + (lead - radius);
5677
+ const outlined = timeline.events.some((event) => event.status === "planned");
5678
+ const lead = hasRing ? ringRadius + strokeWidth / 2 : radius + (outlined ? strokeWidth / 2 : 0), gap = 2 * lead + 12 * scale;
5642
5679
  const x = rtl ? box.x + box.width - lead : box.x + lead, textX = rtl ? box.x : box.x + gap, width = box.width - gap;
5643
5680
  events.forEach((fields2, index) => {
5644
5681
  const top = y;
5645
5682
  let first2;
5646
- for (const part of fields2) {
5683
+ for (const [fieldIndex, part] of fields2.entries()) {
5647
5684
  const placed = place(part, textX, y, width, "left");
5648
5685
  first2 ??= placed;
5649
5686
  y += placed.box.height;
5687
+ if (fieldIndex < fields2.length - 1) y += 4 * scale;
5650
5688
  }
5651
5689
  markers.push(marker(index, x, top + Math.min(first2?.box.height ?? 2 * radius, 2 * radius) / 2));
5652
5690
  y += 16 * scale;
@@ -5969,7 +6007,10 @@ function richTextLayouter(input, box, requestedSize, options, minimum = 0) {
5969
6007
  return layout;
5970
6008
  }
5971
6009
  var PICTURE_BULLET_SCALE = 0.65;
5972
- function fitList(input, box, requestedSize = 25, minFontSize = 16, options = { style: { fontFamily: "sans-serif", fontWeight: 400 } }) {
6010
+ function contentNominalSize(field) {
6011
+ return field === "items" || field === "bullets" ? 32 : 25;
6012
+ }
6013
+ function fitList(input, box, requestedSize = contentNominalSize("items"), minFontSize = 16, options = { style: { fontFamily: "sans-serif", fontWeight: 400 } }) {
5973
6014
  if (!Array.isArray(input) || ![box.width, box.height, requestedSize, minFontSize].every((value) => Number.isFinite(value) && value > 0)) throw new RangeError("List content, dimensions and font sizes must be valid.");
5974
6015
  const source = input.map((value, index) => {
5975
6016
  const object2 = typeof value === "object" && !Array.isArray(value) ? value : void 0;
@@ -6061,6 +6102,11 @@ function layoutTable(value, box, options = {}) {
6061
6102
  const inline = inlineTableData(value, options.presentation);
6062
6103
  const grid = tableGrid(inline, options.path ?? "table");
6063
6104
  if (grid.issues.length) throw new RangeError(`${grid.issues[0].path}: ${grid.issues[0].message}`);
6105
+ const numeric = new Set(grid.rows.flat().filter((cell) => !cell.header && typeof cell.value === "number" && Number.isFinite(cell.value)));
6106
+ const numericColumns = new Set(Array.from({ length: grid.columnCount }, (_, column) => column).filter((column) => {
6107
+ const body = grid.rows.flat().filter((cell) => !cell.header && cell.column === column && cell.value !== null && cell.value !== void 0);
6108
+ return body.length > 0 && body.every((cell) => cell.colSpan === 1 && numeric.has(cell));
6109
+ }));
6064
6110
  const formats = resolveTableData(inline).formats;
6065
6111
  for (const row of grid.rows) for (const cell of row) {
6066
6112
  if (cell.header) continue;
@@ -6072,7 +6118,9 @@ function layoutTable(value, box, options = {}) {
6072
6118
  const padding = { top: 8, right: 10, bottom: 4, left: 10, ...cell.style.padding };
6073
6119
  const width = cellWidth * cell.colSpan - (padding.left + padding.right) * scale;
6074
6120
  const style = { fontFamily: options.fontFamily ?? "sans-serif", fontWeight: cell.header ? 700 : 400, italic: false, path: cell.valuePath };
6075
- return { ...cell, padding, width, textStyle: style };
6121
+ const numericDefault = numeric.has(cell) || cell.header && cell.colSpan === 1 && numericColumns.has(cell.column);
6122
+ const alignment = cell.style.align ?? options.alignment ?? (numericDefault ? options.direction === "rtl" ? "left" : "right" : "left");
6123
+ return { ...cell, padding, width, textStyle: style, alignment };
6076
6124
  });
6077
6125
  const cellMinimum = (cell) => Array.isArray(cell.value) ? richMinimum(cell.value, requested, minimum) : minimum;
6078
6126
  const fitCell = (cell, height, min) => {
@@ -6112,7 +6160,7 @@ function layoutTable(value, box, options = {}) {
6112
6160
  const offset = cell.style.verticalAlign === "bottom" ? Math.max(0, available - height) : cell.style.verticalAlign === "middle" ? Math.max(0, (available - height) / 2) : 0;
6113
6161
  const textBox = { x: cellBox.x + cell.padding.left * scale, y: cellBox.y + cell.padding.top * scale + offset, width: Math.max(scale, cell.width), height: Math.max(scale, available - offset) };
6114
6162
  overflow ||= cell.width <= 0 || available <= 0 || fit.overflow;
6115
- rows3[cell.row].cells.push({ value: cell.value, input: cell.input, path: cell.valuePath, sourcePath: cell.path, style: cell.style, row: cell.row, column: cell.column, rowSpan: cell.rowSpan, colSpan: cell.colSpan, header: cell.header, rich: Array.isArray(cell.value), box: cellBox, textBox, textStyle: resolveTextStyle(cell.textStyle, options.textMeasurement), fit, ...fit.directions ? { direction: fit.directions[0] } : {} });
6163
+ rows3[cell.row].cells.push({ value: cell.value, input: cell.input, path: cell.valuePath, sourcePath: cell.path, style: cell.style, alignment: cell.alignment, row: cell.row, column: cell.column, rowSpan: cell.rowSpan, colSpan: cell.colSpan, header: cell.header, rich: Array.isArray(cell.value), box: cellBox, textBox, textStyle: resolveTextStyle(cell.textStyle, options.textMeasurement), fit, ...fit.directions ? { direction: fit.directions[0] } : {} });
6116
6164
  }
6117
6165
  return { rows: rows3, columnCount, height: sum(heights), overflow };
6118
6166
  }
@@ -6222,7 +6270,7 @@ function composeSlide(input, options = {}) {
6222
6270
  const rasterPadding = (options.textRasterPadding ?? 1) * scale;
6223
6271
  if (!Number.isFinite(rasterPadding) || rasterPadding < 0) throw new RangeError("Text raster padding must be finite and nonnegative.");
6224
6272
  let coverGroup = false;
6225
- const alignmentFor = (field) => (field === "title" || coverGroup && (field === "tag" || field === "subtitle") && hints.sources.contentAlignment !== "slide" ? hints.titleAlignment : hints.contentAlignment) ?? "left";
6273
+ const alignmentFor = (field) => (field === "title" || coverGroup && (field === "tag" || field === "subtitle") && hints.sources.contentAlignment !== "slide" ? hints.titleAlignment : hints.contentAlignment) ?? (field === "metric" ? "center" : "left");
6226
6274
  const styleFor = (field, path2) => resolveTextStyle({
6227
6275
  fontFamily: (field === "title" ? options.fontFamilies?.heading : field === "code" ? options.fontFamilies?.code : field === "tag" ? options.fontFamilies?.accent ?? options.fontFamilies?.body : options.fontFamilies?.body) ?? (field === "code" ? "monospace" : "sans-serif"),
6228
6276
  fontWeight: field === "title" ? 700 : 400,
@@ -6502,8 +6550,8 @@ function composeSlide(input, options = {}) {
6502
6550
  penalties.textOverflow += internal.overflow ? 1e3 : 0;
6503
6551
  }
6504
6552
  } else if (text2) {
6505
- const fit = fitContent(node.field, node.value, text2, box, 25 * scale, snapFontSizeUp((settings.minFontSize ?? 16) * scale), node.path, node.payload.numbering);
6506
- const reduction = Math.max(0, snapFontSizeDown(25 * scale) - fit.fontSize) / scale;
6553
+ const fit = fitContent(node.field, node.value, text2, box, contentNominalSize(node.field) * scale, snapFontSizeUp((settings.minFontSize ?? 16) * scale), node.path, node.payload.numbering);
6554
+ const reduction = Math.max(0, snapFontSizeDown(contentNominalSize(node.field) * scale) - fit.fontSize) / scale;
6507
6555
  score += reduction + (fit.overflow ? 1e3 : 0);
6508
6556
  if (penalties) {
6509
6557
  penalties.fontReduction += reduction;
@@ -6571,7 +6619,7 @@ function composeSlide(input, options = {}) {
6571
6619
  });
6572
6620
  };
6573
6621
  let regionTag, templateCell = false;
6574
- const placeNode = (node, box, settings, cards = hasCards, listColumns) => {
6622
+ const placeNode = (node, box, settings, cards = hasCards, listColumns, metricReferenceHeight) => {
6575
6623
  if (node.children) {
6576
6624
  const own3 = inheritedSettings(settings, node.composition), inner = inset(box, own3);
6577
6625
  if (!node.synthetic) groups.push({ path: node.path, box, contentBox: inner, composition: own3 });
@@ -6579,6 +6627,7 @@ function composeSlide(input, options = {}) {
6579
6627
  return;
6580
6628
  }
6581
6629
  const frameBox = cards ? acceptedBox(box) : void 0;
6630
+ const metricHeight = metricReferenceHeight === void 0 ? void 0 : payloadBox({ ...box, height: metricReferenceHeight }, cards).height;
6582
6631
  box = payloadBox(box, cards);
6583
6632
  const caption = captionOf(node, box);
6584
6633
  if (caption) {
@@ -6588,11 +6637,11 @@ function composeSlide(input, options = {}) {
6588
6637
  const textValue = contentText(node.field, node.value);
6589
6638
  const quoteLayout = node.field === "quote" ? measureQuote(node, box, settings) : void 0;
6590
6639
  const codeLayout = node.field === "code" ? measureCode(node, box, settings) : void 0;
6591
- const metricLayout = node.field === "metric" ? measureMetric(node, box, settings) : void 0;
6640
+ const metricLayout = node.field === "metric" ? measureMetric(node, { ...box, height: metricHeight ?? box.height }, settings) : void 0;
6592
6641
  const timelineLayout = node.field === "timeline" ? measureTimeline(node, box, settings) : void 0;
6593
6642
  const internal = quoteLayout ?? codeLayout ?? metricLayout ?? timelineLayout, body = internal?.parts.find((part) => part.role === "body" || part.role === "value");
6594
6643
  const columns2 = textValue !== void 0 && !internal ? listColumnsFor(node, box, settings, listColumns) : void 0;
6595
- const text2 = internal ? body?.fit : columns2 ? columns2.length > 1 ? mergeListColumns(columns2) : columns2[0].text : textValue !== void 0 ? fitContent(node.field, node.value, textValue, box, 25 * scale, snapFontSizeUp((settings.minFontSize ?? 16) * scale), node.path, node.payload.numbering) : void 0;
6644
+ const text2 = internal ? body?.fit : columns2 ? columns2.length > 1 ? mergeListColumns(columns2) : columns2[0].text : textValue !== void 0 ? fitContent(node.field, node.value, textValue, box, contentNominalSize(node.field) * scale, snapFontSizeUp((settings.minFontSize ?? 16) * scale), node.path, node.payload.numbering) : void 0;
6596
6645
  const image = node.field === "image" ? composeImage(node.host ?? {}, node.hostPath ?? node.path, box, box, imageContext()) : void 0;
6597
6646
  items.push({
6598
6647
  path: node.path,
@@ -6625,7 +6674,7 @@ function composeSlide(input, options = {}) {
6625
6674
  const own3 = node.payload.columns;
6626
6675
  const mode = own3 === "auto" || own3 === 1 || own3 === 2 || own3 === 3 ? own3 : regionMode;
6627
6676
  if (mode === void 0 || mode === 1) return void 0;
6628
- const start = 25 * scale, floor = snapFontSizeUp((settings.minFontSize ?? 16) * scale);
6677
+ const start = contentNominalSize(node.field) * scale, floor = snapFontSizeUp((settings.minFontSize ?? 16) * scale);
6629
6678
  const numbering = node.payload.numbering;
6630
6679
  const listOptions = { ...richOptions(styleFor(node.field, node.path)), ...numbering !== void 0 ? { numbering } : bulletImage ? { bulletImage } : {} };
6631
6680
  const columnGap = (count2) => Math.min(gap, box.width / (count2 * 2));
@@ -6669,12 +6718,13 @@ function composeSlide(input, options = {}) {
6669
6718
  if (node.children) return node.children.some((child) => isLong(child, cellWidth));
6670
6719
  if (node.field === "text") return estimatedLines(node.value, cellWidth, startSize) > LONG_LINES;
6671
6720
  if (node.field === "items" || node.field === "bullets") {
6721
+ const listSize = contentNominalSize(node.field) * scale;
6672
6722
  let lines = 0;
6673
6723
  for (const entry of Array.isArray(node.value) ? node.value : []) {
6674
6724
  const object2 = entry !== null && typeof entry === "object" && !Array.isArray(entry) ? entry : void 0;
6675
- const indent = startSize * 1.1 * (1 + (typeof object2?.level === "number" ? object2.level : 0));
6676
- lines += estimatedLines(object2 ? object2.text : entry, cellWidth - indent, startSize);
6677
- if (object2?.description !== void 0) lines += estimatedLines(object2.description, cellWidth - indent, startSize * 0.82);
6725
+ const indent = listSize * 1.1 * (1 + (typeof object2?.level === "number" ? object2.level : 0));
6726
+ lines += estimatedLines(object2 ? object2.text : entry, cellWidth - indent, listSize);
6727
+ if (object2?.description !== void 0) lines += estimatedLines(object2.description, cellWidth - indent, listSize * 0.82);
6678
6728
  }
6679
6729
  return lines > LONG_LINES;
6680
6730
  }
@@ -6686,16 +6736,17 @@ function composeSlide(input, options = {}) {
6686
6736
  if (node.field === "code") return String(typeof node.value === "string" ? node.value : record8(node.value).source ?? "").split("\n").length > LONG_CODE_LINES;
6687
6737
  return false;
6688
6738
  };
6689
- const naturalHeight = (node, cellWidth, cards) => {
6739
+ const naturalHeight = (node, cellWidth, cards, metricReferenceHeight, naturalSettings = composition) => {
6690
6740
  const pad = cards ? 2 * Math.min(12 * scale, cellWidth / 4) : 0, inner = Math.max(scale, cellWidth - pad);
6691
6741
  if (node.children) return node.children.reduce((sum, child) => sum + naturalHeight(child, cellWidth, false), 0) + gap * Math.max(0, node.children.length - 1) + pad;
6692
6742
  let content;
6693
6743
  if (node.field === "text" || node.field === "items" || node.field === "bullets") {
6694
- const fit = fitContent(node.field, node.value, contentText(node.field, node.value) ?? "", { x: 0, y: 0, width: inner, height: 1e6 }, startSize, startSize, node.path, node.payload.numbering);
6744
+ const size = contentNominalSize(node.field) * scale;
6745
+ const fit = fitContent(node.field, node.value, contentText(node.field, node.value) ?? "", { x: 0, y: 0, width: inner, height: 1e6 }, size, size, node.path, node.payload.numbering);
6695
6746
  content = "height" in fit && typeof fit.height === "number" ? fit.height : fit.lines.length * fit.lineHeight;
6696
- } else if (node.field === "quote") content = estimatedLines(typeof node.value === "string" ? node.value : record8(node.value).text, inner, 32 * scale) * 32 * 1.22 * scale + 2 * 20 * 1.22 * scale;
6747
+ } else if (node.field === "quote") content = measureQuote(node, { x: 0, y: 0, width: inner, height: 1e6 }, naturalSettings).bounds.height + 18;
6697
6748
  else if (node.field === "code") content = String(typeof node.value === "string" ? node.value : record8(node.value).source ?? "").split("\n").length * 18 * 1.4 * scale + 24 * scale;
6698
- else if (node.field === "metric") content = 150 * scale;
6749
+ else if (node.field === "metric") content = measureMetric(node, { x: 0, y: 0, width: inner, height: metricReferenceHeight === void 0 ? 1e6 : Math.max(scale, metricReferenceHeight - pad) }, naturalSettings).bounds.height;
6699
6750
  else if (node.field === "timeline") content = 170 * scale;
6700
6751
  else if (node.field === "table") {
6701
6752
  const value = record8(node.value);
@@ -6866,8 +6917,9 @@ function composeSlide(input, options = {}) {
6866
6917
  }
6867
6918
  const arrangement = arrangementOf(region, units, cell);
6868
6919
  let boxes = flowCells(flowShape(arrangement, units.length, cell.width >= cell.height), cell, gap2).cells;
6869
- if (arrangement === "column" && region.anchor !== "top" && units.every((unit) => !unit.children && (unit.field === "text" || unit.field === "items" || unit.field === "bullets"))) {
6870
- const naturals = units.map((unit) => naturalHeight(unit, cell.width, cards) + 2 * rasterPadding + scale);
6920
+ const referenceHeights = boxes.map((box) => box.height);
6921
+ if (arrangement === "column" && region.anchor !== "top" && units.every((unit) => !unit.children && (unit.field === "text" || unit.field === "items" || unit.field === "bullets" || !unit.caption && (unit.field === "quote" || unit.field === "metric")))) {
6922
+ const naturals = units.map((unit, index) => naturalHeight(unit, cell.width, cards, referenceHeights[index], settings) + 2 * rasterPadding + scale);
6871
6923
  const used = naturals.reduce((a, b) => a + b, 0) + gap2 * (units.length - 1);
6872
6924
  if (used < cell.height) {
6873
6925
  let y2 = cell.y + (region.anchor === "middle" ? (cell.height - used) / 2 : cell.height - used);
@@ -6878,9 +6930,22 @@ function composeSlide(input, options = {}) {
6878
6930
  });
6879
6931
  }
6880
6932
  }
6933
+ if ((arrangement === "grid" || arrangement === "row") && region.anchor !== "top") {
6934
+ const naturals = boxes.map((box, index) => {
6935
+ const unit = units[index];
6936
+ return unit.children || unit.caption || !["text", "items", "bullets", "quote", "metric"].includes(unit.field) ? void 0 : naturalHeight(unit, box.width, cards, referenceHeights[index], settings) + 2 * rasterPadding + scale;
6937
+ });
6938
+ boxes = boxes.map((box, index) => {
6939
+ const height2 = naturals[index];
6940
+ if (height2 === void 0 || height2 >= box.height) return box;
6941
+ const rowHeight = Math.max(...boxes.flatMap((peer, peerIndex) => peer.y === box.y && naturals[peerIndex] !== void 0 ? [naturals[peerIndex]] : []));
6942
+ const spare = Math.max(0, box.height - rowHeight);
6943
+ return { ...box, y: box.y + (region.anchor === "middle" ? spare / 2 : spare), height: height2 };
6944
+ });
6945
+ }
6881
6946
  if (rtl) boxes = boxes.map((box) => ({ ...box, x: cell.x + cell.width - (box.x - cell.x) - box.width }));
6882
6947
  units.forEach((unit, index) => {
6883
- placeNode(unit, boxes[index], settings, cards, region.listColumns);
6948
+ placeNode(unit, boxes[index], settings, cards, region.listColumns, region.anchor === "top" ? void 0 : referenceHeights[index]);
6884
6949
  });
6885
6950
  return arrangement;
6886
6951
  } finally {
@@ -1,8 +1,8 @@
1
- import { fromMarkdown, toMarkdown } from './chunk-FGUQHD37.js';
2
- import { fromYaml, toYaml } from './chunk-SIQWYDFF.js';
3
- import { NOT_CHECKED } from './chunk-LRPSMQ62.js';
4
- import { parseSource, validate, OPFValidationError } from './chunk-65GDO6TJ.js';
5
- import { checkCatalogsOption } from './chunk-ZPCKGGI6.js';
1
+ import { fromMarkdown, toMarkdown } from './chunk-SPSAQKNB.js';
2
+ import { fromYaml, toYaml } from './chunk-2XIMI2V5.js';
3
+ import { NOT_CHECKED } from './chunk-E4VBQW2Z.js';
4
+ import { parseSource, validate, OPFValidationError } from './chunk-5YTTTLL2.js';
5
+ import { checkCatalogsOption } from './chunk-K66K65UQ.js';
6
6
 
7
7
  // src/deck-report.ts
8
8
  var DECK_FORMATS = ["json", "yaml", "markdown"];
@@ -1,8 +1,8 @@
1
- import { defaultCatalogs } from './chunk-OOT5AY4V.js';
2
- import { OPFApiError, parseSlideSelection, OPFExportError, asApiError, OPFImportError } from './chunk-V3RGXJJS.js';
3
- import { deckFormatOf, readDeckReport, stringify } from './chunk-GRN3U5JT.js';
4
- import { paginate } from './chunk-OVBGGGKO.js';
5
- import { validate, OPFValidationError } from './chunk-65GDO6TJ.js';
1
+ import { defaultCatalogs } from './chunk-OGVRAMHE.js';
2
+ import { OPFApiError, parseSlideSelection, OPFExportError, asApiError, OPFImportError } from './chunk-XWDAXVFZ.js';
3
+ import { deckFormatOf, readDeckReport, stringify } from './chunk-MSWNRD4C.js';
4
+ import { paginate } from './chunk-XGR5HL7R.js';
5
+ import { validate, OPFValidationError } from './chunk-5YTTTLL2.js';
6
6
  import { stat, readdir, lstat, mkdir, writeFile, rename, unlink, readFile } from 'fs/promises';
7
7
  import path6 from 'path';
8
8
  import { readFileSync, realpathSync, statSync } from 'fs';
@@ -93,7 +93,7 @@ function createImageResolver(root, reporter, passDirectory = "pass --asset-dir")
93
93
  }
94
94
  var RENDER_PACKAGE = "@openpresentation/opf-render";
95
95
  var PPTX_PACKAGE = "@openpresentation/opf-pptx";
96
- var PEER_RANGES = { [RENDER_PACKAGE]: "^0.19.0", [PPTX_PACKAGE]: "^0.19.0" };
96
+ var PEER_RANGES = { [RENDER_PACKAGE]: "^0.20.0", [PPTX_PACKAGE]: "^0.20.0" };
97
97
  var RENDER_EXTRAS = {
98
98
  /** The faces of the office pack, which the CLI and `convert` always load (every format). */
99
99
  fonts: ["@expo-google-fonts/roboto@0.4.3", "@expo-google-fonts/roboto-mono@0.4.2", "@expo-google-fonts/caladea@0.4.2", "@expo-google-fonts/arimo@0.4.3", "@expo-google-fonts/tinos@0.4.2", "@expo-google-fonts/cousine@0.4.3", "@expo-google-fonts/gelasio@0.4.1", "@expo-google-fonts/noto-sans@0.4.2"],
@@ -724,11 +724,12 @@ async function runImport(bytes, options, reporter, loaded) {
724
724
  const pptx = loaded ?? await loadPptx();
725
725
  try {
726
726
  const onDiagnostic = (diagnostic) => reporter.add("import", diagnostic);
727
+ const catalogOptions = options.catalogs === void 0 ? {} : { catalogs: options.catalogs };
727
728
  if (options.signals) {
728
- const result = await pptx.module.fromPptx(bytes, { onDiagnostic, signals: true });
729
+ const result = await pptx.module.fromPptx(bytes, { ...catalogOptions, onDiagnostic, signals: true });
729
730
  return { presentation: result.presentation, signals: result.signals, pptx };
730
731
  }
731
- return { presentation: await pptx.module.fromPptx(bytes, { onDiagnostic }), pptx };
732
+ return { presentation: await pptx.module.fromPptx(bytes, { ...catalogOptions, onDiagnostic }), pptx };
732
733
  } catch (error) {
733
734
  reportThrown(reporter, "import", error);
734
735
  const failure = error;
@@ -773,7 +774,7 @@ async function runPreparedExport(prepared, presentation, checked) {
773
774
  async function importPresentation(bytes, options, loaded) {
774
775
  const pptx = loaded ?? await loadPptx();
775
776
  const reporter = new Reporter();
776
- const run = await runImport(bytes, { signals: options.signals === true }, reporter, pptx);
777
+ const run = await runImport(bytes, { catalogs: options.catalogs ?? defaultCatalogs(), signals: options.signals === true }, reporter, pptx);
777
778
  if (run.failure || !run.presentation) throw new OPFApiError(run.failure?.message ?? "The file could not be read.", "import-failed", { details: { reason: run.failure?.code }, findings: reporter.findings });
778
779
  const check = validate(run.presentation, checkOptions(options.catalogs));
779
780
  for (const found of check.findings) reporter.addFinding(found);
@@ -1,4 +1,4 @@
1
- import { validate } from './chunk-65GDO6TJ.js';
1
+ import { validate } from './chunk-5YTTTLL2.js';
2
2
 
3
3
  // src/patch.ts
4
4
  var OPFPatchError = class extends Error {
@@ -1,5 +1,5 @@
1
- import { OPFApiError } from './chunk-V3RGXJJS.js';
2
- import { CATALOG_SCHEMA } from './chunk-65GDO6TJ.js';
1
+ import { OPFApiError } from './chunk-XWDAXVFZ.js';
2
+ import { CATALOG_SCHEMA } from './chunk-5YTTTLL2.js';
3
3
  import { CATALOG_SCHEMA as CATALOG_SCHEMA$1, gallery } from '@openpresentation/gallery';
4
4
 
5
5
  function galleryMismatch(declared, reads = CATALOG_SCHEMA) {
@@ -1,5 +1,5 @@
1
- import { jsonEqual, formatPointer } from './chunk-WHKFIFN7.js';
2
- import { annotationText } from './chunk-ZPCKGGI6.js';
1
+ import { jsonEqual, formatPointer } from './chunk-NXYHBBVV.js';
2
+ import { annotationText } from './chunk-K66K65UQ.js';
3
3
 
4
4
  // src/diff-match.ts
5
5
  var DEFAULT_MATCH_THRESHOLD = 0.5;