@openpresentation/opf 0.15.1 → 0.16.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 (73) hide show
  1. package/README.md +4 -0
  2. package/dist/{catalog-refs-BSfGELpt.d.ts → catalog-refs-_anZs-Qz.d.ts} +1 -1
  3. package/dist/{catalog-schemas-CinPh5WA.d.ts → catalog-schemas-BZCjgAxu.d.ts} +1 -1
  4. package/dist/catalog.d.ts +2 -2
  5. package/dist/catalog.js +609 -121
  6. package/dist/{chunk-5FTUIDUH.js → chunk-3XDRQYWZ.js} +1 -1
  7. package/dist/{chunk-O744SXXA.js → chunk-5PDN7BN6.js} +202 -95
  8. package/dist/{chunk-VGAOJEY7.js → chunk-5TO6I3GB.js} +1 -1
  9. package/dist/{chunk-3RKB4RDJ.js → chunk-5UEUAW57.js} +4 -2
  10. package/dist/{chunk-ZPU46TFA.js → chunk-6OR326EA.js} +1 -1
  11. package/dist/{chunk-UBFJ2QHH.js → chunk-BSYQDWT4.js} +12 -2
  12. package/dist/{chunk-3JG4YXWF.js → chunk-FQSIIADJ.js} +12 -5
  13. package/dist/chunk-K4DQQIT4.js +319 -0
  14. package/dist/chunk-KINQFU6X.js +51 -0
  15. package/dist/{chunk-374R35TJ.js → chunk-NAWQR7MN.js} +1 -1
  16. package/dist/{chunk-QDI75P2M.js → chunk-PX5AWL7Y.js} +141 -13
  17. package/dist/{chunk-U4J75TRG.js → chunk-SMLMI6IZ.js} +5 -2
  18. package/dist/{chunk-TWQJLI6E.js → chunk-UM24JFUE.js} +2 -2
  19. package/dist/{chunk-2CJPC4HI.js → chunk-WEDGVC5W.js} +185 -11
  20. package/dist/{composition-D2tghM82.d.ts → composition-D4ADF5c-.d.ts} +88 -9
  21. package/dist/composition.d.ts +2 -2
  22. package/dist/composition.js +1 -1
  23. package/dist/convert.d.ts +3 -3
  24. package/dist/convert.js +7 -7
  25. package/dist/data.d.ts +27 -10
  26. package/dist/data.js +2 -2
  27. package/dist/deck.d.ts +77 -0
  28. package/dist/deck.js +12 -0
  29. package/dist/diff.js +5 -5
  30. package/dist/docs.js +24 -24
  31. package/dist/examples.d.ts +1 -1
  32. package/dist/examples.js +2992 -748
  33. package/dist/format.js +2 -2
  34. package/dist/index.d.ts +9 -8
  35. package/dist/index.js +15 -13
  36. package/dist/markdown.d.ts +6 -7
  37. package/dist/markdown.js +7 -7
  38. package/dist/{pagination-wedbe7eq.d.ts → pagination-DzZPFheM.d.ts} +2 -2
  39. package/dist/pagination.d.ts +3 -3
  40. package/dist/pagination.js +4 -4
  41. package/dist/patch.js +4 -4
  42. package/dist/{presentation-CDKQVkyI.d.ts → presentation-COrFuj2g.d.ts} +16 -8
  43. package/dist/repo-readme.js +1 -1
  44. package/dist/schemas.d.ts +14 -0
  45. package/dist/schemas.js +1 -1
  46. package/dist/spec/catalogs/layouts/business-model-canvas.json +66 -1
  47. package/dist/spec/catalogs/layouts/capability-map.json +53 -3
  48. package/dist/spec/catalogs/layouts/chart-2x.json +32 -8
  49. package/dist/spec/catalogs/layouts/chart-3x-bottom-vertical-title-left.json +16 -10
  50. package/dist/spec/catalogs/layouts/chart-3x-left-title-center.json +16 -10
  51. package/dist/spec/catalogs/layouts/chart-3x-left.json +16 -10
  52. package/dist/spec/catalogs/layouts/chart-3x-right-title-center.json +16 -10
  53. package/dist/spec/catalogs/layouts/chart-3x-right.json +16 -10
  54. package/dist/spec/catalogs/layouts/chart-3x-top-vertical-title-left.json +16 -10
  55. package/dist/spec/catalogs/layouts/chart-3x.json +48 -12
  56. package/dist/spec/catalogs/layouts/dashboard.json +37 -5
  57. package/dist/spec/catalogs/layouts/image-2x.json +32 -8
  58. package/dist/spec/catalogs/layouts/image-3x.json +48 -12
  59. package/dist/spec/catalogs/layouts/index.json +1 -1
  60. package/dist/spec/catalogs/layouts/kpi-scorecard.json +48 -3
  61. package/dist/spec/catalogs/layouts/logic-tree.json +69 -0
  62. package/dist/spec/catalogs/layouts/market-size.json +26 -2
  63. package/dist/spec/catalogs/layouts/org-chart.json +53 -6
  64. package/dist/spec/catalogs/manifest.json +3 -3
  65. package/dist/spec/schemas/layout.schema.json +172 -5
  66. package/dist/spec/schemas/opf.schema.json +11 -6
  67. package/dist/types.d.ts +9 -31
  68. package/dist/{validation-types-BOcY9GTs.d.ts → validation-types-HxIgJfKx.d.ts} +2 -2
  69. package/dist/validator.d.ts +6 -6
  70. package/dist/validator.js +3 -3
  71. package/dist/yaml.d.ts +4 -4
  72. package/dist/yaml.js +8 -321
  73. package/package.json +5 -1
@@ -1771,6 +1771,75 @@ function readingRows(items) {
1771
1771
  return rows3.map((row) => row.items.sort((a, b) => index.get(a) - index.get(b)));
1772
1772
  }
1773
1773
 
1774
+ // src/layout-content.ts
1775
+ var LAYOUT_BODY_KINDS = ["text", "list", "image", "video", "chart", "table", "code", "metric", "quote", "timeline"];
1776
+ var MAX_PLACEHOLDER_GROUP_DEPTH = 3;
1777
+ var bodyKinds = new Set(LAYOUT_BODY_KINDS);
1778
+ var record4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
1779
+ function isPlaceholderGroup(placeholder) {
1780
+ return record4(placeholder).type === "group";
1781
+ }
1782
+ function layoutSlots(layout, base = "layout.placeholders") {
1783
+ const walk2 = (entries, path, depth) => (Array.isArray(entries) ? entries : []).map((entry, index) => {
1784
+ const placeholder = record4(entry), slotPath = `${path}.${index}`;
1785
+ const type = typeof placeholder.type === "string" ? placeholder.type : "";
1786
+ if (type !== "group") {
1787
+ if (depth > 0 && !bodyKinds.has(type)) throw new RangeError(`${slotPath}: a placeholder group holds body regions only, not '${type}'.`);
1788
+ return { type, path: slotPath, depth, placeholder };
1789
+ }
1790
+ if (depth >= MAX_PLACEHOLDER_GROUP_DEPTH) throw new RangeError(`${slotPath}: placeholder groups nest at most ${MAX_PLACEHOLDER_GROUP_DEPTH} levels inside a layout record.`);
1791
+ if (!Array.isArray(placeholder.placeholders) || !placeholder.placeholders.length) throw new RangeError(`${slotPath}: a placeholder group needs at least one placeholder.`);
1792
+ return { type, path: slotPath, depth, placeholder, children: walk2(placeholder.placeholders, `${slotPath}.placeholders`, depth + 1) };
1793
+ });
1794
+ return walk2(layout?.placeholders, base, 0);
1795
+ }
1796
+ function layoutLeaves(layout) {
1797
+ const leaves = [];
1798
+ const visit = (slots) => {
1799
+ for (const slot of slots) slot.children ? visit(slot.children) : leaves.push({ type: slot.type, path: slot.path, depth: slot.depth, placeholder: slot.placeholder });
1800
+ };
1801
+ visit(layoutSlots(layout));
1802
+ return leaves;
1803
+ }
1804
+ function layoutStructure(layout) {
1805
+ const describe = (slots) => slots.map((slot) => {
1806
+ if (!slot.children) return slot.type;
1807
+ const mode = record4(slot.placeholder.composition).mode;
1808
+ return `${typeof mode === "string" && mode !== "auto" ? mode : "group"} (${describe(slot.children)})`;
1809
+ }).join(", ");
1810
+ return describe(layoutSlots(layout));
1811
+ }
1812
+ function hasPlaceholderGroups(layout) {
1813
+ return Array.isArray(layout?.placeholders) && layout.placeholders.some(isPlaceholderGroup);
1814
+ }
1815
+ function layoutContent(layout) {
1816
+ const heading = { title: false, subtitle: false, tag: false };
1817
+ const counts = /* @__PURE__ */ new Map();
1818
+ let count = 0;
1819
+ const visit = (entries, depth) => {
1820
+ for (const entry of Array.isArray(entries) ? entries : []) {
1821
+ const type = record4(entry).type;
1822
+ if (type === "group") {
1823
+ if (depth < MAX_PLACEHOLDER_GROUP_DEPTH) visit(record4(entry).placeholders, depth + 1);
1824
+ continue;
1825
+ }
1826
+ if (type === "title" || type === "subtitle" || type === "tag") {
1827
+ if (depth === 0) heading[type] = true;
1828
+ } else if (typeof type === "string" && bodyKinds.has(type)) {
1829
+ counts.set(type, (counts.get(type) ?? 0) + 1);
1830
+ count += 1;
1831
+ }
1832
+ }
1833
+ };
1834
+ visit(layout?.placeholders, 0);
1835
+ let kind2 = "title", best = 0;
1836
+ for (const [candidate, total] of counts) if (total > best) {
1837
+ kind2 = candidate;
1838
+ best = total;
1839
+ }
1840
+ return { kind: kind2, count, heading };
1841
+ }
1842
+
1774
1843
  // src/direction.ts
1775
1844
  var strongRtl = /[\u200F\u061C]|(?=\p{L})[\p{Script=Arabic}\p{Script=Hebrew}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Nko}\p{Script=Adlam}\p{Script=Hanifi_Rohingya}\p{Script=Mandaic}\p{Script=Samaritan}\p{Script=Mende_Kikakui}\p{Script=Imperial_Aramaic}\p{Script=Phoenician}\p{Script=Kharoshthi}\p{Script=Old_South_Arabian}\p{Script=Old_North_Arabian}\p{Script=Avestan}\p{Script=Inscriptional_Parthian}\p{Script=Inscriptional_Pahlavi}\p{Script=Psalter_Pahlavi}\p{Script=Old_Turkic}\p{Script=Old_Hungarian}\p{Script=Nabataean}\p{Script=Palmyrene}\p{Script=Hatran}\p{Script=Manichaean}\p{Script=Sogdian}\p{Script=Old_Sogdian}\p{Script=Elymaic}\p{Script=Chorasmian}\p{Script=Yezidi}\p{Script=Cypriot}\p{Script=Lydian}\p{Script=Meroitic_Cursive}\p{Script=Meroitic_Hieroglyphs}\u{10F70}-\u{10FAF}\u{10D40}-\u{10D8F}]/u;
1776
1845
  var strongLtr = /[\u200E\p{L}\p{Nl}]/u;
@@ -3283,8 +3352,8 @@ function codeSyntaxPalette(theme = {}, options = {}) {
3283
3352
  return palette;
3284
3353
  }
3285
3354
  function codeSyntaxPaletteForScheme(scheme, options = {}) {
3286
- const record6 = scheme && typeof scheme === "object" ? scheme : {};
3287
- const pick = (role, slot, fallback) => normalize(record6[role]) ?? normalize(record6[slot]) ?? fallback;
3355
+ const record7 = scheme && typeof scheme === "object" ? scheme : {};
3356
+ const pick = (role, slot, fallback) => normalize(record7[role]) ?? normalize(record7[slot]) ?? fallback;
3288
3357
  return codeSyntaxPalette({ primary: pick("primary", "accent1", "#2563EB"), secondary: pick("secondary", "accent2", "#0F766E"), accent: pick("accent", "accent3", "#F59E0B") }, options);
3289
3358
  }
3290
3359
 
@@ -3436,8 +3505,8 @@ var blend = (from, to, amount) => {
3436
3505
  return toHex(a.map((value, index) => value + (b[index] - value) * amount));
3437
3506
  };
3438
3507
  function codeHighlightColors(scheme, palette = codeSyntaxPaletteForScheme(scheme)) {
3439
- const record6 = scheme && typeof scheme === "object" ? scheme : {};
3440
- const primary = normalize(record6.primary) ?? normalize(record6.accent1) ?? "#2563EB";
3508
+ const record7 = scheme && typeof scheme === "object" ? scheme : {};
3509
+ const primary = normalize(record7.primary) ?? normalize(record7.accent1) ?? "#2563EB";
3441
3510
  const [hue, saturation] = toHsl(primary);
3442
3511
  const tint = fromHsl(hue, Math.min(saturation, 0.7), 0.45);
3443
3512
  const band = blend(CODE_PANEL_BACKGROUND, tint, CODE_HIGHLIGHT_BAND_TINT);
@@ -3634,7 +3703,7 @@ var CAPTION_FONT_RATIO = 0.6;
3634
3703
  var CAPTION_MAX_RATIO = 0.35;
3635
3704
  var FOOTNOTE_MAX_RATIO = 0.35;
3636
3705
  var CAPTIONABLE_FIELDS = Object.freeze(["image", "chart", "table", "video"]);
3637
- var record4 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
3706
+ var record5 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
3638
3707
  var isRecord3 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
3639
3708
  var round = (value) => Math.round(value * 1e6) / 1e6 || value;
3640
3709
  var regionKey = /^(top|middle|bottom|left|center|right)([+:]|$)/;
@@ -3642,7 +3711,7 @@ var TEXT_FIELDS = ["text", "items", "bullets"];
3642
3711
  var HEADING_FIELDS = ["tag", "title", "subtitle"];
3643
3712
  function annotationText(value) {
3644
3713
  if (typeof value === "string") return value;
3645
- if (Array.isArray(value)) return value.map((run) => typeof run === "string" ? run : record4(run).text ?? "").join("");
3714
+ if (Array.isArray(value)) return value.map((run) => typeof run === "string" ? run : record5(run).text ?? "").join("");
3646
3715
  if (isRecord3(value) && value.text !== void 0) return annotationText(value.text);
3647
3716
  return value === void 0 || value === null ? "" : String(value);
3648
3717
  }
@@ -3674,7 +3743,7 @@ function walkCitationRuns(slide, basePath, visit) {
3674
3743
  });
3675
3744
  };
3676
3745
  const host = (node, path, depth) => {
3677
- const payload = record4(node);
3746
+ const payload = record5(node);
3678
3747
  if (Array.isArray(payload.blocks)) {
3679
3748
  if (depth > 32) return;
3680
3749
  payload.blocks.forEach((block, index) => {
@@ -3689,7 +3758,7 @@ function walkCitationRuns(slide, basePath, visit) {
3689
3758
  }
3690
3759
  if (isRecord3(payload.quote)) runs(payload.quote.text, `${path}.quote.text`);
3691
3760
  };
3692
- const value = record4(slide);
3761
+ const value = record5(slide);
3693
3762
  for (const field of HEADING_FIELDS) runs(value[field], `${basePath}.${field}`);
3694
3763
  const regions = Object.keys(value).filter((key) => regionKey.test(key)).sort();
3695
3764
  if (regions.length) {
@@ -3706,7 +3775,7 @@ function walkCitationRuns(slide, basePath, visit) {
3706
3775
  }
3707
3776
  var citationMarkerText = (numbers) => numbers.join(",");
3708
3777
  function referenceList(presentation) {
3709
- const list = record4(presentation).references;
3778
+ const list = record5(presentation).references;
3710
3779
  return Array.isArray(list) ? list.filter((item) => isRecord3(item) && typeof item.id === "string") : [];
3711
3780
  }
3712
3781
  function numberSlide(slide, slideIndex, references, numbering) {
@@ -3739,7 +3808,7 @@ function numberSlide(slide, slideIndex, references, numbering) {
3739
3808
  }
3740
3809
  function collectCitations(presentation) {
3741
3810
  const references = referenceList(presentation), numbering = { notes: [], byId: /* @__PURE__ */ new Map() }, slides = /* @__PURE__ */ new Map();
3742
- const list = record4(presentation).slides;
3811
+ const list = record5(presentation).slides;
3743
3812
  if (Array.isArray(list)) list.forEach((slide, index) => {
3744
3813
  const result = numberSlide(slide, index, references, numbering);
3745
3814
  if (result) slides.set(index, result);
@@ -3749,7 +3818,7 @@ function collectCitations(presentation) {
3749
3818
  }
3750
3819
  function slideCitations(slide, slideIndex, presentation) {
3751
3820
  const references = referenceList(presentation), numbering = { notes: [], byId: /* @__PURE__ */ new Map() };
3752
- const list = record4(presentation).slides;
3821
+ const list = record5(presentation).slides;
3753
3822
  if (Array.isArray(list)) for (let index = 0; index < Math.min(slideIndex, list.length); index += 1) numberSlide(list[index], index, references, numbering);
3754
3823
  return numberSlide(slide, slideIndex, references, numbering);
3755
3824
  }
@@ -3812,35 +3881,12 @@ function referencesSlide(presentation, options = {}) {
3812
3881
  return { title, items };
3813
3882
  }
3814
3883
 
3815
- // src/layout-content.ts
3816
- var LAYOUT_BODY_KINDS = ["text", "list", "image", "video", "chart", "table", "code", "metric", "quote", "timeline"];
3817
- var bodyKinds = new Set(LAYOUT_BODY_KINDS);
3818
- function layoutContent(record6) {
3819
- const heading = { title: false, subtitle: false, tag: false };
3820
- const counts = /* @__PURE__ */ new Map();
3821
- let count = 0;
3822
- for (const placeholder of Array.isArray(record6?.placeholders) ? record6.placeholders : []) {
3823
- const type = placeholder?.type;
3824
- if (type === "title" || type === "subtitle" || type === "tag") heading[type] = true;
3825
- else if (typeof type === "string" && bodyKinds.has(type)) {
3826
- counts.set(type, (counts.get(type) ?? 0) + 1);
3827
- count += 1;
3828
- }
3829
- }
3830
- let kind2 = "title", best = 0;
3831
- for (const [candidate, total] of counts) if (total > best) {
3832
- kind2 = candidate;
3833
- best = total;
3834
- }
3835
- return { kind: kind2, count, heading };
3836
- }
3837
-
3838
3884
  // src/composition.ts
3839
3885
  var MAX_COMPOSITION_DEPTH = 32;
3840
3886
  var FALLBACK_CODE_FAMILY = "Roboto Mono";
3841
3887
  var DEFAULT_FONT_FAMILIES = { heading: String(ENGINE_DEFAULT_FONT_SCHEME.major), body: String(ENGINE_DEFAULT_FONT_SCHEME.minor) };
3842
3888
  function resolveFontFamilies(input) {
3843
- const scheme = record5(input);
3889
+ const scheme = record6(input);
3844
3890
  const family = (value) => typeof value === "string" && value ? value : void 0;
3845
3891
  const accent = family(scheme.accent);
3846
3892
  return {
@@ -3911,6 +3957,12 @@ function translateTextFit(fit, dy) {
3911
3957
  }
3912
3958
  };
3913
3959
  }
3960
+ var FIELD_KINDS = { text: "text", bullets: "text", items: "list", image: "image", video: "video", chart: "chart", table: "table", code: "code", metric: "metric", quote: "quote", timeline: "timeline" };
3961
+ function chartPrimaryLayout(side, kinds) {
3962
+ const first = side === "left" || side === "top";
3963
+ const group = { type: "group", composition: {}, placeholders: kinds.map((type) => ({ type })) };
3964
+ return { composition: { mode: side === "left" || side === "right" ? "row" : "column", weights: first ? [3, 2] : [2, 3] }, placeholders: first ? [{ type: "chart" }, group] : [group, { type: "chart" }] };
3965
+ }
3914
3966
  var OPFCompositionError = class extends Error {
3915
3967
  constructor(diagnostics, explanation) {
3916
3968
  super("Slide content does not fit its composition.");
@@ -3926,7 +3978,7 @@ var fields = ["text", "items", "bullets", "image", "video", "chart", "table", "c
3926
3978
  var headings = /* @__PURE__ */ new Set(["title", "subtitle", "tag"]);
3927
3979
  var rows2 = ["top", "middle", "bottom"];
3928
3980
  var columns = ["left", "center", "right"];
3929
- var record5 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
3981
+ var record6 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
3930
3982
  var kind = (field) => field === "items" ? "list" : field === "bullets" ? "text" : field;
3931
3983
  var round2 = (value) => Math.round(value * 1e6) / 1e6 || value;
3932
3984
  var PLACEMENT_BAND = 0.5;
@@ -3940,13 +3992,13 @@ var imagePayload = (host) => {
3940
3992
  for (const key of IMAGE_OPTION_KEYS) if (host[key] !== void 0) payload[key] = host[key];
3941
3993
  return payload;
3942
3994
  };
3943
- var assetSource = (value) => typeof value === "string" ? value : record5(value).src;
3995
+ var assetSource = (value) => typeof value === "string" ? value : record6(value).src;
3944
3996
  var finite = (value, min, max) => typeof value === "number" && Number.isFinite(value) && value >= min && value <= max ? value : void 0;
3945
3997
  var pathNumber = (value) => String(round2(value));
3946
3998
  var IMAGE_SHAPES = { rectangle: "rect", rounded: "roundRect", circle: "ellipse", hexagon: "hexagon" };
3947
3999
  var roundBox = (box) => ({ x: round2(box.x), y: round2(box.y), width: round2(box.width), height: round2(box.height) });
3948
4000
  var imageFocus = (value) => {
3949
- const focus = record5(value), x = finite(focus.x, 0, 1), y = finite(focus.y, 0, 1);
4001
+ const focus = record6(value), x = finite(focus.x, 0, 1), y = finite(focus.y, 0, 1);
3950
4002
  return x !== void 0 && y !== void 0 ? { x, y } : { x: 0.5, y: 0.5 };
3951
4003
  };
3952
4004
  function imageShape(kind2, box, cornerRadius = 1 / 6) {
@@ -3987,7 +4039,7 @@ function fitImage(frame, fit, aspect, focus = { x: 0.5, y: 0.5 }) {
3987
4039
  }
3988
4040
  function imageBackground(value) {
3989
4041
  if (typeof value === "string") return IMAGE_SOURCE.test(value) ? { src: value, fit: "cover", focus: { x: 0.5, y: 0.5 } } : void 0;
3990
- const background = record5(value);
4042
+ const background = record6(value);
3991
4043
  if (background.type !== "image" || typeof background.src !== "string" || !background.src) return void 0;
3992
4044
  const opacity = finite(background.opacity, 0, 1);
3993
4045
  return {
@@ -4002,11 +4054,11 @@ function imageBackground(value) {
4002
4054
  }
4003
4055
  function composeRecolor(value) {
4004
4056
  if (value === "grayscale") return { type: "grayscale" };
4005
- const duotone = record5(value);
4057
+ const duotone = record6(value);
4006
4058
  return duotone.dark !== void 0 && duotone.light !== void 0 ? { type: "duotone", dark: duotone.dark, light: duotone.light } : void 0;
4007
4059
  }
4008
4060
  function composeOverlay(value, frame, shape, path, mirrorSide, diagnostics) {
4009
- const overlay = record5(value), opacity = finite(overlay.opacity, 0, 1);
4061
+ const overlay = record6(value), opacity = finite(overlay.opacity, 0, 1);
4010
4062
  if (overlay.color === void 0 || opacity === void 0) return void 0;
4011
4063
  const edge = IMAGE_EDGES.includes(overlay.edge) ? mirrorSide(overlay.edge) : void 0;
4012
4064
  if (edge && shape.kind !== "rectangle") {
@@ -4031,7 +4083,7 @@ function composeImage(host, hostPath, region, frameArea, context) {
4031
4083
  const focus = imageFocus(host.focus);
4032
4084
  const shape = imageShape(kindName, box, finite(host.cornerRadius, 0, 0.5));
4033
4085
  const result = { region: roundBox(region), box, fit, focus, shape };
4034
- const border = record5(host.border), borderWidth = finite(border.width, 0, 64);
4086
+ const border = record6(host.border), borderWidth = finite(border.width, 0, 64);
4035
4087
  if (border.color !== void 0 && borderWidth) result.border = { color: border.color, width: round2(borderWidth * scale) };
4036
4088
  const opacity = finite(host.opacity, 0, 1);
4037
4089
  if (opacity !== void 0 && opacity < 1) result.opacity = opacity;
@@ -4045,7 +4097,7 @@ function composeImage(host, hostPath, region, frameArea, context) {
4045
4097
  return result;
4046
4098
  }
4047
4099
  function resolveBackgroundImage(slide, presentation, themeBackground, width, height, path, mirrorSide, diagnostics) {
4048
- const own2 = record5(slide.design), deck = record5(record5(presentation).design);
4100
+ const own2 = record6(slide.design), deck = record6(record6(presentation).design);
4049
4101
  const [value, at3] = own2.background !== void 0 ? [own2.background, `${path}.design.background`] : deck.background !== void 0 ? [deck.background, "design.background"] : [themeBackground, "theme"];
4050
4102
  const background = imageBackground(value);
4051
4103
  if (!background) return void 0;
@@ -4056,7 +4108,7 @@ function resolveBackgroundImage(slide, presentation, themeBackground, width, hei
4056
4108
  if (recolor) result.recolor = recolor;
4057
4109
  const overlay = composeOverlay(background.overlay, box, imageShape("rectangle", box), `${at3}.overlay`, mirrorSide, diagnostics);
4058
4110
  if (overlay) result.overlay = overlay;
4059
- const aspect = background.fit === "tile" ? void 0 : intrinsicImageAspect(background.src, record5(presentation).assets);
4111
+ const aspect = background.fit === "tile" ? void 0 : intrinsicImageAspect(background.src, record6(presentation).assets);
4060
4112
  if (aspect) result.picture = fitImage(box, background.fit, aspect, background.focus);
4061
4113
  return result;
4062
4114
  }
@@ -4077,10 +4129,10 @@ function resolveLogo(presentation, slide, options = {}) {
4077
4129
  };
4078
4130
  const pick = (value, path) => {
4079
4131
  if (value === void 0 || value === null || value === false) return null;
4080
- if (typeof value === "string" || typeof value === "object" && !Array.isArray(value) && "src" in record5(value)) {
4132
+ if (typeof value === "string" || typeof value === "object" && !Array.isArray(value) && "src" in record6(value)) {
4081
4133
  return usable(value) ? { source: value, path, variant: "default", slot } : null;
4082
4134
  }
4083
- const set2 = record5(value);
4135
+ const set2 = record6(value);
4084
4136
  if (!Object.keys(set2).some((key) => LOGO_SET_KEYS.has(key))) return null;
4085
4137
  for (const key of chain) {
4086
4138
  const variant = set2[key];
@@ -4088,16 +4140,16 @@ function resolveLogo(presentation, slide, options = {}) {
4088
4140
  }
4089
4141
  return null;
4090
4142
  };
4091
- const deck = record5(presentation), slideIndex = Number.isSafeInteger(options.slideIndex) && options.slideIndex >= 0 ? options.slideIndex : 0;
4092
- const own2 = pick(record5(record5(slide).design).logo, `slides.${slideIndex}.design.logo`);
4143
+ const deck = record6(presentation), slideIndex = Number.isSafeInteger(options.slideIndex) && options.slideIndex >= 0 ? options.slideIndex : 0;
4144
+ const own2 = pick(record6(record6(slide).design).logo, `slides.${slideIndex}.design.logo`);
4093
4145
  if (own2) return own2;
4094
- const shared = pick(record5(deck.design).logo, "design.logo");
4146
+ const shared = pick(record6(deck.design).logo, "design.logo");
4095
4147
  if (shared) return shared;
4096
4148
  const organizations = Array.isArray(deck.organization) ? deck.organization : [deck.organization];
4097
- const primaryIndex = organizations.findIndex((item) => record5(item).role === "primary");
4149
+ const primaryIndex = organizations.findIndex((item) => record6(item).role === "primary");
4098
4150
  const index = primaryIndex >= 0 ? primaryIndex : organizations.findIndex(Boolean);
4099
4151
  if (index < 0) return null;
4100
- return pick(record5(organizations[index]).logo, Array.isArray(deck.organization) ? `organization.${index}.logo` : "organization.logo");
4152
+ return pick(record6(organizations[index]).logo, Array.isArray(deck.organization) ? `organization.${index}.logo` : "organization.logo");
4101
4153
  }
4102
4154
  var webUrl = /^https?:\/\/\S+$/i;
4103
4155
  function resolveSocialProfile(platform, value, owner = "organization") {
@@ -4115,8 +4167,8 @@ function resolveSocialProfile(platform, value, owner = "organization") {
4115
4167
  return { text: display(url), href: encodeURI(url), resolved: true };
4116
4168
  }
4117
4169
  function layoutFurniture(input, options = {}) {
4118
- const slide = record5(input), width = options.width ?? 1280, height = options.height ?? 720, scale = Math.min(width, height) / 720;
4119
- const settings = { ...record5(record5(options.layout).composition), ...record5(slide.composition) };
4170
+ const slide = record6(input), width = options.width ?? 1280, height = options.height ?? 720, scale = Math.min(width, height) / 720;
4171
+ const settings = { ...record6(record6(options.layout).composition), ...record6(slide.composition) };
4120
4172
  assertComposition(settings);
4121
4173
  const furnitureRtl = (options.direction ?? resolveSlideDirection(options.presentation, options.slideIndex)) === "rtl";
4122
4174
  const minimum = snapFontSizeUp((settings.minFontSize ?? 16) * scale), size = gridFontSize(13 * scale, minimum), padding = (options.textRasterPadding ?? 2) * scale;
@@ -4128,14 +4180,14 @@ function layoutFurniture(input, options = {}) {
4128
4180
  if (options.date !== void 0 && (typeof options.date !== "string" || !parseIsoDate(options.date))) throw new RangeError("The furniture date option must be an ISO YYYY-MM-DD calendar date.");
4129
4181
  const outlines = options.textMeasurement?.outlineBounds !== void 0, parts = [], diagnostics = [];
4130
4182
  const sourceRoot = `slides.${options.slideIndex ?? 0}`, organizations = Array.isArray(options.presentation?.organization) ? options.presentation.organization : [options.presentation?.organization];
4131
- const primaryIndex = organizations.findIndex((item) => record5(item).role === "primary"), organizationIndex = primaryIndex >= 0 ? primaryIndex : organizations.findIndex(Boolean);
4132
- const organization = record5(organizations[organizationIndex]), organizationRoot = Array.isArray(options.presentation?.organization) ? `organization.${organizationIndex}` : "organization", organizationPath = `${organizationRoot}.name`;
4133
- const speakers = Array.isArray(options.presentation?.speaker) ? options.presentation.speaker : [options.presentation?.speaker], speaker = record5(speakers[0]), speakerRoot = Array.isArray(options.presentation?.speaker) ? "speaker.0" : "speaker";
4183
+ const primaryIndex = organizations.findIndex((item) => record6(item).role === "primary"), organizationIndex = primaryIndex >= 0 ? primaryIndex : organizations.findIndex(Boolean);
4184
+ const organization = record6(organizations[organizationIndex]), organizationRoot = Array.isArray(options.presentation?.organization) ? `organization.${organizationIndex}` : "organization", organizationPath = `${organizationRoot}.name`;
4185
+ const speakers = Array.isArray(options.presentation?.speaker) ? options.presentation.speaker : [options.presentation?.speaker], speaker = record6(speakers[0]), speakerRoot = Array.isArray(options.presentation?.speaker) ? "speaker.0" : "speaker";
4134
4186
  const fontFamily = options.fontFamilies?.body ?? "sans-serif";
4135
4187
  let headerBottom = 0, footerTop = height, configured = false;
4136
4188
  const error = (path, message, code = "text-overflow") => diagnostics.push({ code, path, message });
4137
4189
  for (const kind2 of ["header", "footer"]) {
4138
- const local = record5(slide.design)[kind2] !== void 0, value = local ? record5(slide.design)[kind2] : options.presentation?.design?.[kind2];
4190
+ const local = record6(slide.design)[kind2] !== void 0, value = local ? record6(slide.design)[kind2] : options.presentation?.design?.[kind2];
4139
4191
  if (value === void 0 || value === false) continue;
4140
4192
  const root = local ? `${sourceRoot}.design.${kind2}` : `design.${kind2}`;
4141
4193
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("Header/footer content must be an object or false.");
@@ -4143,7 +4195,7 @@ function layoutFurniture(input, options = {}) {
4143
4195
  const zones = [];
4144
4196
  for (const [index, zone] of ["left", "center", "right"].entries()) {
4145
4197
  const side = furnitureRtl && zone !== "center" ? zone === "left" ? "right" : "left" : zone;
4146
- const content = record5(record5(value)[zone]), path = `${root}.${zone}`, x = width * (0.07 + (furnitureRtl ? 2 - index : index) * 0.3), zoneWidth = width * 0.26, zoneParts = [];
4198
+ const content = record6(record6(value)[zone]), path = `${root}.${zone}`, x = width * (0.07 + (furnitureRtl ? 2 - index : index) * 0.3), zoneWidth = width * 0.26, zoneParts = [];
4147
4199
  let y = 0;
4148
4200
  const add = (field, text2, generated = false, sourcePath, extras = {}) => {
4149
4201
  if (text2 === void 0) return;
@@ -4180,7 +4232,7 @@ function layoutFurniture(input, options = {}) {
4180
4232
  y += box.height;
4181
4233
  };
4182
4234
  const imageBox = (source) => {
4183
- const imageHeight = Math.max(32 * scale, Math.min(height * 0.05, 72 * scale)), imageWidth = Math.min(zoneWidth, imageHeight * (intrinsicImageAspect(source, record5(options.presentation).assets) ?? 1));
4235
+ const imageHeight = Math.max(32 * scale, Math.min(height * 0.05, 72 * scale)), imageWidth = Math.min(zoneWidth, imageHeight * (intrinsicImageAspect(source, record6(options.presentation).assets) ?? 1));
4184
4236
  return { x: side === "left" ? x : side === "center" ? x + (zoneWidth - imageWidth) / 2 : x + zoneWidth - imageWidth, y, width: imageWidth, height: imageHeight };
4185
4237
  };
4186
4238
  if (content.logo === true) {
@@ -4206,7 +4258,7 @@ function layoutFurniture(input, options = {}) {
4206
4258
  else error(`${path}.speaker`, "Generated speaker needs a named speaker in the presentation.", "unresolved-content");
4207
4259
  }
4208
4260
  if (content.socials === true) {
4209
- const links = Object.entries(record5(organization.socials)).filter(([, value2]) => typeof value2 === "string" && value2.trim()).map(([platform, value2]) => ({ platform, sourcePath: `${organizationRoot}.socials.${platform}`, ...resolveSocialProfile(platform, value2, "organization") }));
4261
+ const links = Object.entries(record6(organization.socials)).filter(([, value2]) => typeof value2 === "string" && value2.trim()).map(([platform, value2]) => ({ platform, sourcePath: `${organizationRoot}.socials.${platform}`, ...resolveSocialProfile(platform, value2, "organization") }));
4210
4262
  if (links.length) add("socials", links.map((link) => link.text).join("\n"), true, `${organizationRoot}.socials`, { links });
4211
4263
  else error(`${path}.socials`, "Generated social profiles need a primary organization with socials.", "unresolved-content");
4212
4264
  }
@@ -4755,19 +4807,19 @@ function fitSourceText(text2, box, size, minimum, step, measure, prose = false,
4755
4807
  const layout = (fontSize) => {
4756
4808
  const tabWidth = measure(" ", fontSize) * 4;
4757
4809
  if (!Number.isFinite(tabWidth) || text2.includes(" ") && tabWidth <= 0) throw new RangeError("Text tabs require a positive finite measured space advance.");
4758
- const measureLine = (start2, end2, record6 = false) => {
4810
+ const measureLine = (start2, end2, record7 = false) => {
4759
4811
  let x = 0, offset = start2;
4760
4812
  const segments = [];
4761
4813
  for (const [index, segment2] of text2.slice(start2, end2).split(" ").entries()) {
4762
4814
  if (index) {
4763
4815
  const next = (Math.floor(x / tabWidth + 1e-9) + 1) * tabWidth;
4764
- if (record6) segments.push({ kind: "tab", start: offset, end: offset + 1, x, width: next - x });
4816
+ if (record7) segments.push({ kind: "tab", start: offset, end: offset + 1, x, width: next - x });
4765
4817
  x = next;
4766
4818
  offset++;
4767
4819
  }
4768
4820
  if (segment2) {
4769
4821
  const advance = measure(segment2, fontSize);
4770
- if (record6) segments.push({ kind: "text", start: offset, end: offset + segment2.length, x, width: advance });
4822
+ if (record7) segments.push({ kind: "text", start: offset, end: offset + segment2.length, x, width: advance });
4771
4823
  x += advance;
4772
4824
  offset += segment2.length;
4773
4825
  }
@@ -5124,12 +5176,12 @@ function fitList(input, box, requestedSize = 25, minFontSize = 16, options = { s
5124
5176
  function contentText(field, value) {
5125
5177
  if (field === "text" || headings.has(field)) return flatten(value);
5126
5178
  if (field === "items" || field === "bullets") return (Array.isArray(value) ? value : [value]).map((item) => {
5127
- const entry = record5(item);
5179
+ const entry = record6(item);
5128
5180
  return `\u2022 ${flatten(item)}${entry.description ? `
5129
5181
  ${flatten(entry.description)}` : ""}`;
5130
5182
  }).join("\n");
5131
- if (field === "code") return flatten(record5(value).source ?? value);
5132
- if (field === "quote") return flatten(record5(value).text ?? value);
5183
+ if (field === "code") return flatten(record6(value).source ?? value);
5184
+ if (field === "quote") return flatten(record6(value).text ?? value);
5133
5185
  return void 0;
5134
5186
  }
5135
5187
  function layoutTable(value, box, options = {}) {
@@ -5200,7 +5252,7 @@ function flatten(value) {
5200
5252
  if (value == null) return "";
5201
5253
  if (typeof value !== "object") return String(value);
5202
5254
  if (Array.isArray(value)) return value.map(flatten).join("");
5203
- const item = record5(value);
5255
+ const item = record6(value);
5204
5256
  return flatten(item.text ?? item.value ?? item.source ?? JSON.stringify(value));
5205
5257
  }
5206
5258
  function regionParts(key) {
@@ -5256,7 +5308,7 @@ function assertComposition(value) {
5256
5308
  if (value.overflow !== void 0 && !["warn", "error"].includes(value.overflow)) throw new RangeError("Invalid composition.overflow.");
5257
5309
  }
5258
5310
  function composeSlide(input, options = {}) {
5259
- const slide = record5(input), layout = record5(options.layout);
5311
+ const slide = record6(input), layout = record6(options.layout);
5260
5312
  const width = options.width ?? 1280, height = options.height ?? 720;
5261
5313
  if (![width, height].every((value) => Number.isFinite(value) && value > 0)) throw new RangeError("Canvas dimensions must be finite and positive.");
5262
5314
  const scale = Math.min(width, height) / 720;
@@ -5264,7 +5316,7 @@ function composeSlide(input, options = {}) {
5264
5316
  const rtl = deckDirection === "rtl", textDirection = rtl ? "rtl" : void 0;
5265
5317
  const hints = resolveDesignHints({ slide, layout, presentation: options.presentation, slideIndex: options.slideIndex, deck: { titleAlignment: options.titleAlignment, contentAlignment: options.contentAlignment, contentBox: options.contentBox } });
5266
5318
  const hasCards = hints.contentBox ?? false;
5267
- const composition = { ...record5(layout.composition), ...record5(slide.composition) };
5319
+ const composition = { ...record6(layout.composition), ...record6(slide.composition) };
5268
5320
  assertComposition(composition);
5269
5321
  const padding = (composition.padding ?? 0.08) * Math.min(width, height);
5270
5322
  const gap = (composition.gap ?? 1 / 30) * Math.min(width, height);
@@ -5345,16 +5397,16 @@ function composeSlide(input, options = {}) {
5345
5397
  const mirrorSide = (side) => !rtl ? side : side === "left" ? "right" : side === "right" ? "left" : side;
5346
5398
  const backgroundImage = resolveBackgroundImage(slide, options.presentation, options.themeBackground, width, height, path, mirrorSide, imageDiagnostics);
5347
5399
  const regions = Object.keys(slide).filter((key) => regionParts(key)).sort();
5348
- const assets = record5(options.presentation).assets;
5400
+ const assets = record6(options.presentation).assets;
5349
5401
  const imageContext = (placement) => ({ fit: hints.imageFit, scale, mirrorSide, diagnostics: imageDiagnostics, assets, ...placement ? { placement } : {} });
5350
5402
  const area = { left: 0, top: 0, right: width, bottom: height };
5351
- const imagePlaceholders = (Array.isArray(layout.placeholders) ? layout.placeholders : []).flatMap((placeholder, index) => record5(placeholder).type === "image" ? [{ placeholder: record5(placeholder), index }] : []);
5352
- const topImages = regions.length ? [] : Array.isArray(slide.blocks) ? slide.blocks.flatMap((block, index) => !Array.isArray(record5(block).blocks) && record5(block).image !== void 0 ? [{ host: record5(block), hostPath: `${path}.blocks.${index}`, block: index }] : []) : slide.image !== void 0 ? [{ host: { type: "image", image: slide.image }, hostPath: path }] : [];
5403
+ const imagePlaceholders = layoutLeaves(layout).flatMap((leaf) => leaf.type === "image" ? [{ placeholder: leaf.placeholder, path: leaf.path, depth: leaf.depth }] : []);
5404
+ const topImages = regions.length ? [] : Array.isArray(slide.blocks) ? slide.blocks.flatMap((block, index) => !Array.isArray(record6(block).blocks) && record6(block).image !== void 0 ? [{ host: record6(block), hostPath: `${path}.blocks.${index}`, block: index }] : []) : slide.image !== void 0 ? [{ host: { type: "image", image: slide.image }, hostPath: path }] : [];
5353
5405
  const placed = [], usedEdges = /* @__PURE__ */ new Set();
5354
5406
  topImages.forEach((node, order) => {
5355
5407
  const slot = imagePlaceholders[order];
5356
- const own2 = node.block !== void 0 && node.host.placement !== void 0 ? record5(node.host.placement) : void 0;
5357
- const value = own2 ?? (slot?.placeholder.placement !== void 0 ? record5(slot.placeholder.placement) : void 0);
5408
+ const own2 = node.block !== void 0 && node.host.placement !== void 0 ? record6(node.host.placement) : void 0;
5409
+ const value = own2 ?? (slot?.depth === 0 && slot.placeholder.placement !== void 0 ? record6(slot.placeholder.placement) : void 0);
5358
5410
  if (!value || !IMAGE_EDGES.includes(value.edge)) return;
5359
5411
  const edge = mirrorSide(value.edge);
5360
5412
  if (usedEdges.has(edge)) return;
@@ -5367,7 +5419,7 @@ function composeSlide(input, options = {}) {
5367
5419
  else if (edge === "right") area.right = region.x;
5368
5420
  else if (edge === "top") area.top += band;
5369
5421
  else area.bottom = region.y;
5370
- const placement = { edge, size, inset: value.inset === true, path: own2 ? `${node.hostPath}.placement` : `layout.placeholders.${slot.index}.placement` };
5422
+ const placement = { edge, size, inset: value.inset === true, path: own2 ? `${node.hostPath}.placement` : `${slot.path}.placement` };
5371
5423
  placed.push({ ...node, placement, region, slot: order });
5372
5424
  });
5373
5425
  const placedBlocks = new Set(placed.flatMap((entry) => entry.block !== void 0 ? [entry.block] : []));
@@ -5390,7 +5442,7 @@ function composeSlide(input, options = {}) {
5390
5442
  const emptyPayload = (value) => value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
5391
5443
  const emptyHost = (host) => emptyPayload(host.blocks) && fields.every((field) => emptyPayload(host[field]));
5392
5444
  const flowBlocks = Array.isArray(slide.blocks) ? slide.blocks.filter((_, index) => !placedBlocks.has(index)) : slide.blocks;
5393
- const hasBodyPayload = regions.some((key) => !emptyHost(record5(slide[key]))) || !emptyPayload(flowBlocks) || fields.some((field) => !(placedRoot && field === "image") && !emptyPayload(slide[field]));
5445
+ const hasBodyPayload = regions.some((key) => !emptyHost(record6(slide[key]))) || !emptyPayload(flowBlocks) || fields.some((field) => !(placedRoot && field === "image") && !emptyPayload(slide[field]));
5394
5446
  const isCover = !hasBodyPayload && (headingOnlyLayout || !hasLayoutRecord);
5395
5447
  coverGroup = isCover;
5396
5448
  let logo;
@@ -5457,7 +5509,7 @@ function composeSlide(input, options = {}) {
5457
5509
  const collect = (host, basePath, depth = 0, ancestors = []) => {
5458
5510
  if (Array.isArray(host.blocks)) {
5459
5511
  if (depth >= MAX_COMPOSITION_DEPTH || ancestors.includes(host)) throw new RangeError(`Content groups must be acyclic and nest at most ${MAX_COMPOSITION_DEPTH} levels.`);
5460
- const settings = record5(host.composition);
5512
+ const settings = record6(host.composition);
5461
5513
  assertComposition(settings);
5462
5514
  return [{
5463
5515
  field: "blocks",
@@ -5466,7 +5518,7 @@ function composeSlide(input, options = {}) {
5466
5518
  path: basePath,
5467
5519
  payload: host,
5468
5520
  composition: settings,
5469
- children: host.blocks.flatMap((block, index) => collect(record5(block), `${basePath}.blocks.${index}`, depth + 1, [...ancestors, host]))
5521
+ children: host.blocks.flatMap((block, index) => collect(record6(block), `${basePath}.blocks.${index}`, depth + 1, [...ancestors, host]))
5470
5522
  }];
5471
5523
  }
5472
5524
  const captioned = host.caption !== void 0 ? fields.filter((field) => host[field] !== void 0 && CAPTIONABLE_FIELDS.includes(field)) : [];
@@ -5482,7 +5534,7 @@ function composeSlide(input, options = {}) {
5482
5534
  }));
5483
5535
  };
5484
5536
  const regionsInReadingOrder = visualReadingOrder(regions.map((key) => ({ key, box: regionBox(regionParts(key), contentBox, gap) }))).map((entry) => entry.key);
5485
- const pending = regions.length ? regionsInReadingOrder.flatMap((key) => collect(record5(slide[key]), `${path}.${key}`).map((item) => ({ ...item, region: regionParts(key) }))) : Array.isArray(slide.blocks) ? slide.blocks.flatMap((block, index) => placedBlocks.has(index) ? [] : collect(record5(block), `${path}.blocks.${index}`)) : collect(placedRoot ? { ...slide, image: void 0 } : slide, path);
5537
+ const pending = regions.length ? regionsInReadingOrder.flatMap((key) => collect(record6(slide[key]), `${path}.${key}`).map((item) => ({ ...item, region: regionParts(key) }))) : Array.isArray(slide.blocks) ? slide.blocks.flatMap((block, index) => placedBlocks.has(index) ? [] : collect(record6(block), `${path}.blocks.${index}`)) : collect(placedRoot ? { ...slide, image: void 0 } : slide, path);
5486
5538
  const groups = [], flows = [];
5487
5539
  const decisions = options.explain ? [] : void 0;
5488
5540
  const inheritedSettings = (parent, own2 = {}) => ({
@@ -5580,12 +5632,23 @@ function composeSlide(input, options = {}) {
5580
5632
  };
5581
5633
  const scoreNode = (node, box, settings, penalties) => {
5582
5634
  if (!node.children) return leafScore(node, box, settings, penalties);
5583
- const own2 = inheritedSettings(settings, node.composition), area2 = inset(box, own2);
5584
- const cols = defaultColumns(node.children.length, area2, own2);
5585
- const boxes = gridBoxes(node.children.length, area2, cols, (own2.gap ?? 1 / 30) * Math.min(box.width, box.height), own2.weights ?? [], modeFor(own2) === "column");
5635
+ const own2 = inheritedSettings(settings, node.composition), area2 = inset(box, own2), slotCount = Math.max(node.children.length, node.reserved ?? 0);
5636
+ const cols = defaultColumns(slotCount, area2, own2);
5637
+ const boxes = gridBoxes(slotCount, area2, cols, (own2.gap ?? 1 / 30) * Math.min(box.width, box.height), own2.weights ?? [], modeFor(own2) === "column");
5586
5638
  return node.children.reduce((score, child, index) => score + scoreNode(child, boxes[index], own2, penalties), 0);
5587
5639
  };
5588
- const arrange = (nodes, area2, settings, reserved = 0, gapOverride, containerPath = path, recorded = true) => {
5640
+ const slots = [];
5641
+ const emptySlots = (entries, boxes, settings) => entries.forEach((entry, index) => {
5642
+ const box = boxes[index];
5643
+ if (!box) return;
5644
+ slots.push({ path: entry.path, type: entry.type, depth: entry.depth, box: acceptedBox(box) });
5645
+ if (!entry.children) return;
5646
+ const own2 = inheritedSettings(settings, record6(entry.placeholder.composition)), inner = inset(box, own2);
5647
+ const cols = defaultColumns(entry.children.length, inner, own2);
5648
+ const mirror = (cell) => rtl ? { ...cell, x: inner.x + inner.width - (cell.x - inner.x) - cell.width } : cell;
5649
+ emptySlots(entry.children, gridBoxes(entry.children.length, inner, cols, (own2.gap ?? 1 / 30) * Math.min(box.width, box.height), own2.weights ?? [], modeFor(own2) === "column").map(mirror), own2);
5650
+ });
5651
+ const arrange = (nodes, area2, settings, reserved = 0, gapOverride, containerPath = path, recorded = true, template) => {
5589
5652
  const count = Math.max(nodes.length, reserved);
5590
5653
  if (!count) return;
5591
5654
  const mode = modeFor(settings), localGap = (settings.gap ?? 1 / 30) * Math.min(area2.width, area2.height);
@@ -5597,9 +5660,9 @@ function composeSlide(input, options = {}) {
5597
5660
  let best = Infinity;
5598
5661
  for (let candidate = 1; candidate <= Math.min(count, settings.columns ?? 6); candidate++) {
5599
5662
  const boxes2 = gridBoxes(count, area2, candidate, actualGap, settings.weights ?? [], false);
5600
- const emptySlots = (Math.ceil(count / candidate) * candidate - count) * 2;
5601
- const penalties = candidates ? { cellProportions: 0, fontReduction: 0, textOverflow: 0, tableOverflow: 0, smallCells: 0, emptySlots } : void 0;
5602
- const score = nodes.reduce((sum, node, i) => sum + scoreNode(node, boxes2[i], settings, penalties), 0) + emptySlots;
5663
+ const emptySlots2 = (Math.ceil(count / candidate) * candidate - count) * 2;
5664
+ const penalties = candidates ? { cellProportions: 0, fontReduction: 0, textOverflow: 0, tableOverflow: 0, smallCells: 0, emptySlots: emptySlots2 } : void 0;
5665
+ const score = nodes.reduce((sum, node, i) => sum + scoreNode(node, boxes2[i], settings, penalties), 0) + emptySlots2;
5603
5666
  if (penalties && candidates) candidates.push({ columns: candidate, rows: Math.ceil(count / candidate), score, penalties });
5604
5667
  if (score < best) {
5605
5668
  best = score;
@@ -5617,13 +5680,15 @@ function composeSlide(input, options = {}) {
5617
5680
  const grid = gridGeometry(count, area2, cols, actualGap, settings.weights ?? [], mode === "column");
5618
5681
  const mirrored = (box) => rtl ? { ...box, x: area2.x + area2.width - (box.x - area2.x) - box.width } : box;
5619
5682
  const boxes = grid.boxes.map(mirrored);
5683
+ if (template) emptySlots(template.slice(nodes.length), boxes.slice(nodes.length), settings);
5620
5684
  if (recorded && !hasRegions) flows.push({ path: containerPath, box: { ...area2 }, composition: { ...settings }, columns: rtl ? grid.columns.map((track) => ({ offset: area2.width - track.offset - track.size, size: track.size })) : grid.columns, rows: grid.rows, gap: grid.gap, itemCount: nodes.length, slotCount: count });
5621
5685
  nodes.forEach((node, index) => {
5622
5686
  let box = node.region ? mirrored(regionBox(node.region, area2, actualGap)) : boxes[index];
5687
+ if (node.slot) slots.push({ path: node.slot.path, type: node.slot.type, depth: node.slot.depth, box: acceptedBox(box), ...node.slot.children ? {} : { content: node.path } });
5623
5688
  if (node.children) {
5624
5689
  const own2 = inheritedSettings(settings, node.composition), inner = inset(box, own2);
5625
5690
  if (!node.synthetic) groups.push({ path: node.path, box, contentBox: inner, composition: own2 });
5626
- arrange(node.children, inner, own2, 0, (own2.gap ?? 1 / 30) * Math.min(box.width, box.height), node.path, !node.synthetic);
5691
+ arrange(node.children, inner, own2, node.reserved ?? 0, (own2.gap ?? 1 / 30) * Math.min(box.width, box.height), node.path, !node.synthetic, node.slots);
5627
5692
  } else {
5628
5693
  const frameBox = hasCards ? acceptedBox(box) : void 0;
5629
5694
  box = payloadBox(box);
@@ -5669,18 +5734,59 @@ function composeSlide(input, options = {}) {
5669
5734
  if (placedSlots.size) {
5670
5735
  for (let index = placeholders.length - 1; index >= 0; index--) if (placedSlots.has(placeholders[index])) placeholders.splice(index, 1);
5671
5736
  }
5672
- const ownMode = record5(slide.composition).mode;
5737
+ const ownMode = record6(slide.composition).mode;
5673
5738
  const direction = hints.contentDirection;
5674
5739
  const directionMode = direction === "vertical" ? "column" : direction === "horizontal" ? "row" : "auto";
5675
5740
  const chartHint = hints.chartPrimary;
5676
5741
  const chartSide = chartHint === "left" || chartHint === "right" || chartHint === "top" || chartHint === "bottom" ? chartHint : void 0;
5677
5742
  const chartIndex = pending.findIndex((node) => !node.children && node.field === "chart");
5678
5743
  const chartPrimary = chartSide !== void 0 && !ownMode && !regions.length && chartIndex >= 0 && pending.some((node) => node.children || node.field !== "chart") ? chartSide : void 0;
5679
- const rootSettings = chartPrimary ? { ...composition, mode: chartPrimary === "left" || chartPrimary === "right" ? "row" : "column", columns: void 0, weights: chartPrimary === "left" || chartPrimary === "top" ? [3, 2] : [2, 3] } : { ...composition, mode: composition.mode ?? directionMode };
5680
- if (chartPrimary) {
5744
+ const kindOf = (node) => node.children ? node.children[0] ? kindOf(node.children[0]) : "text" : FIELD_KINDS[node.field] ?? "text";
5745
+ const sugar = chartPrimary ? chartPrimaryLayout(chartPrimary, pending.filter((_, index) => index !== chartIndex).map(kindOf)) : void 0;
5746
+ const rootSettings = sugar ? { ...composition, ...sugar.composition, columns: void 0 } : { ...composition, mode: composition.mode ?? directionMode };
5747
+ const recordSlots = sugar ? [] : layoutSlots(layout).filter((slot) => !headings.has(slot.type) && !placedSlots.has(slot.placeholder));
5748
+ const templated = !sugar && !regions.length && recordSlots.some((slot) => slot.children) && pending.every((node) => !node.children);
5749
+ const fillSlots = (entries, queue, cursor, recordSlot) => {
5750
+ const nodes = [];
5751
+ for (const entry of entries) {
5752
+ if (cursor.next >= queue.length) break;
5753
+ if (!entry.children) {
5754
+ nodes.push({ ...queue[cursor.next++], ...recordSlot ? { slot: entry } : {} });
5755
+ continue;
5756
+ }
5757
+ const own2 = record6(entry.placeholder.composition);
5758
+ assertComposition(own2);
5759
+ const children = fillSlots(entry.children, queue, cursor, recordSlot);
5760
+ nodes.push({
5761
+ field: "blocks",
5762
+ type: "group",
5763
+ value: children.map((child) => child.payload),
5764
+ path,
5765
+ payload: {},
5766
+ children,
5767
+ composition: own2,
5768
+ synthetic: true,
5769
+ reserved: own2.mode ? 0 : entry.children.length,
5770
+ ...recordSlot ? { slot: entry, slots: entry.children } : {}
5771
+ });
5772
+ }
5773
+ return nodes;
5774
+ };
5775
+ if (sugar) {
5681
5776
  const primary = pending[chartIndex], rest = pending.filter((_, index) => index !== chartIndex);
5682
- const container = { field: "blocks", type: "group", value: rest.map((node) => node.payload), path, payload: {}, children: rest, composition: {}, synthetic: true };
5683
- arrange(chartPrimary === "left" || chartPrimary === "top" ? [primary, container] : [container, primary], contentBox, rootSettings, 0);
5777
+ const queue = chartPrimary === "left" || chartPrimary === "top" ? [primary, ...rest] : [...rest, primary];
5778
+ arrange(fillSlots(layoutSlots(sugar, "design.chartPrimary"), queue, { next: 0 }, false), contentBox, rootSettings, 0);
5779
+ } else if (templated) {
5780
+ const cursor = { next: 0 };
5781
+ const nodes = fillSlots(recordSlots, pending, cursor, true);
5782
+ arrange([...nodes, ...pending.slice(cursor.next)], contentBox, rootSettings, composition.mode ? 0 : placeholders.length, void 0, path, true, recordSlots);
5783
+ const order = /* @__PURE__ */ new Map();
5784
+ const number = (entries) => entries.forEach((entry) => {
5785
+ order.set(entry.path, order.size);
5786
+ if (entry.children) number(entry.children);
5787
+ });
5788
+ number(recordSlots);
5789
+ slots.sort((a, b) => order.get(a.path) - order.get(b.path));
5684
5790
  } else arrange(pending, contentBox, rootSettings, composition.mode ? 0 : placeholders.length);
5685
5791
  if (listBullet?.value === "image" && !bulletImage && items.some((item) => (item.field === "items" || item.field === "bullets") && item.payload.numbering === void 0)) {
5686
5792
  diagnostics.push({ code: "unresolved-content", path: listBullet.path, message: "Picture bullets (listBullet: image) need design.logo or a primary organization logo; the marker glyph is drawn instead." });
@@ -5721,7 +5827,8 @@ function composeSlide(input, options = {}) {
5721
5827
  } : void 0;
5722
5828
  if (failures.length) throw new OPFCompositionError(failures, explanation);
5723
5829
  diagnostics.push(...imageDiagnostics, ...numberingDiagnostics);
5724
- return { width, height, contentBox, items, groups, flows, diagnostics, composition, design: hints, ...furniture ? { furniture } : {}, ...backgroundImage ? { backgroundImage } : {}, ...logo ? { logo } : {}, ...rtl ? { direction: "rtl" } : {}, ...footnotes ? { footnotes } : {}, ...explanation ? { explanation } : {} };
5830
+ for (const slot of slots) for (const key of ["x", "y", "width", "height"]) slot.box[key] = round2(slot.box[key]);
5831
+ return { width, height, contentBox, items, groups, flows, ...slots.length ? { slots } : {}, diagnostics, composition, design: hints, ...furniture ? { furniture } : {}, ...backgroundImage ? { backgroundImage } : {}, ...logo ? { logo } : {}, ...rtl ? { direction: "rtl" } : {}, ...footnotes ? { footnotes } : {}, ...explanation ? { explanation } : {} };
5725
5832
  }
5726
5833
  function resolveCanvasDimensions(input) {
5727
5834
  const presets = {
@@ -5736,7 +5843,7 @@ function resolveCanvasDimensions(input) {
5736
5843
  letter: [11, 8.5],
5737
5844
  a4: [11.69, 8.27]
5738
5845
  };
5739
- const value = record5(input);
5846
+ const value = record6(input);
5740
5847
  const preset = presets[typeof input === "string" ? input : value.preset] ?? presets.widescreen;
5741
5848
  const width = (value.widthInches ?? preset[0]) * 96;
5742
5849
  const height = (value.heightInches ?? preset[1]) * 96;
@@ -5744,4 +5851,4 @@ function resolveCanvasDimensions(input) {
5744
5851
  return { width, height };
5745
5852
  }
5746
5853
 
5747
- export { CAPTIONABLE_FIELDS, CAPTION_FONT_RATIO, CAPTION_MAX_RATIO, CHART_HIGHLIGHT_MUTED_MIN_CONTRAST, CHART_HIGHLIGHT_MUTED_MIX, CHART_SERIES_MIN_DIFFERENCE, CHART_SERIES_MIN_LIGHTNESS_STEP, CHART_TYPES, CITATION_MARKER_RAISE, CITATION_MARKER_SCALE, CODE_HIGHLIGHT_LANGUAGES, CODE_HIGHLIGHT_MAX_LENGTH, CODE_PANEL_BACKGROUND, CODE_PANEL_FOREGROUND, CODE_SYNTAX_MIN_CONTRAST, DARK_BACKGROUND_LUMINANCE, DEFAULT_CHART_LABEL_SEPARATOR, DEFAULT_FURNITURE_DATE_FORMAT, DEFAULT_SLIDE_NUMBER_FORMAT, DEFAULT_VARIABLE_DATE_FORMAT, DESIGN_HINT_KEYS, ENGINE_DEFAULT_CHART_TYPES, ENGINE_DEFAULT_COLOR_SCHEME, ENGINE_DEFAULT_FONT_SCHEME, ENGINE_DEFAULT_THEME, FONT_SIZE_GRID_PER_PX, FOOTNOTE_MAX_RATIO, LANGUAGES, LAYOUT_BODY_KINDS, MAX_COMPOSITION_DEPTH, MAX_NUMBERING_LEVELS, MAX_NUMBERING_VALUE, MAX_ROMAN_VALUE, METRIC_TREND_MIN_CONTRAST, METRIC_TREND_SHAPES, NUMBERING_STYLES, NUMBERING_SUFFIXES, OPFCompositionError, OPFVariableError, PATTERN_PRESETS, PATTERN_PRESET_ALIASES, PATTERN_TILE_SIZE, PICTURE_BULLET_SCALE, SOCIAL_PLATFORMS, SURFACE_ALT_MIN_CONTRAST, SURFACE_ALT_MIX, TIMELINE_OUTLINE_MIN_CONTRAST, TIMELINE_STATUSES, TIMELINE_TEXT_MIN_CONTRAST, VARIABLE_KINDS, annotationText, captionSettings, chartColorForFill, chartHighlightColors, chartHighlightMarks, chartLabelText, chartNumber, chartNumberFixesByCell, chartOptionSupport, chartOptionTarget, chartPaletteForFill, citationMarkerText, codeHighlightBands, codeHighlightColors, codeHighlightLines, codeHighlightSlice, codeLineNumbers, codeLineRuns, codeSyntaxPalette, codeSyntaxPaletteForScheme, coerceVariableValue, collectCitations, colorContrast, composeSlide, datasetDiagnostics, defaultSlideBackground, fitImage, fitList, fitRichText, fitText, formatChartLabelNumber, formatChartLabelPercent, formatDataNumber, formatFurnitureDate, formatListNumber, formatSlideNumber, formatVariableNumber, fromExcelNumberFormat, hasContentVariables, imageBackground, imageShape, inlineChartData, inlineDatasets, inlineTableData, instantiateForValidation, intrinsicImageAspect, intrinsicImageSize, isDarkColor, isDatasetRef, isRecord, isTemplate, isXYChartType, layoutCaption, layoutCode, layoutContent, layoutFootnotes, layoutFurniture, layoutMetric, layoutQuote, layoutTable, layoutTimeline, layoutWatermark, listBuiltinVariables, listNumbers, listVariables, measureText, measureTextOutline, metricTrendColor, metricTrendMark, metricTrendPoints, normalizeHexColor, normalizeLanguageFamily, numberFormatError, numberingAtLevel, numberingFindings, numberingStyleDraws, paragraphDirection, paragraphDirectionAt, pathFor, patternBitmap, patternRuns, physicalAlignment, placeTextLines, promotedRegionKeys, readingRows, referencesSlide, resolveCanvasDimensions, resolveChartData, resolveChartOptions, resolveCodeLanguage, resolveColorRef, resolveColorRoles, resolveDesignHints, resolveDesignRecords, resolveFontFamilies, resolveFontScheme, resolveLogo, resolveNumbering, resolvePatternPreset, resolveScriptFonts, resolveSlideDirection, resolveSocialProfile, resolveTableData, resolveTextStyle, resolveVariables, scriptFontRole, sliceNumberedItems, slideCitations, snapFontSizeDown, snapFontSizeUp, suggestChartNumberFix, surfaceAltColor, tableCellDisplayValue, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, timelineMarkerShapes, timelineTextColor, toExcelNumberFormat, tokenizeCode, unusedDatasets, variableDeclarations, visitContentPayloads, visualReadingOrder, walkCitationRuns, wrapText };
5854
+ export { CAPTIONABLE_FIELDS, CAPTION_FONT_RATIO, CAPTION_MAX_RATIO, CHART_HIGHLIGHT_MUTED_MIN_CONTRAST, CHART_HIGHLIGHT_MUTED_MIX, CHART_SERIES_MIN_DIFFERENCE, CHART_SERIES_MIN_LIGHTNESS_STEP, CHART_TYPES, CITATION_MARKER_RAISE, CITATION_MARKER_SCALE, CODE_HIGHLIGHT_LANGUAGES, CODE_HIGHLIGHT_MAX_LENGTH, CODE_PANEL_BACKGROUND, CODE_PANEL_FOREGROUND, CODE_SYNTAX_MIN_CONTRAST, DARK_BACKGROUND_LUMINANCE, DEFAULT_CHART_LABEL_SEPARATOR, DEFAULT_FURNITURE_DATE_FORMAT, DEFAULT_SLIDE_NUMBER_FORMAT, DEFAULT_VARIABLE_DATE_FORMAT, DESIGN_HINT_KEYS, ENGINE_DEFAULT_CHART_TYPES, ENGINE_DEFAULT_COLOR_SCHEME, ENGINE_DEFAULT_FONT_SCHEME, ENGINE_DEFAULT_THEME, FONT_SIZE_GRID_PER_PX, FOOTNOTE_MAX_RATIO, LANGUAGES, LAYOUT_BODY_KINDS, MAX_COMPOSITION_DEPTH, MAX_NUMBERING_LEVELS, MAX_NUMBERING_VALUE, MAX_PLACEHOLDER_GROUP_DEPTH, MAX_ROMAN_VALUE, METRIC_TREND_MIN_CONTRAST, METRIC_TREND_SHAPES, NUMBERING_STYLES, NUMBERING_SUFFIXES, OPFCompositionError, OPFVariableError, PATTERN_PRESETS, PATTERN_PRESET_ALIASES, PATTERN_TILE_SIZE, PICTURE_BULLET_SCALE, SOCIAL_PLATFORMS, SURFACE_ALT_MIN_CONTRAST, SURFACE_ALT_MIX, TIMELINE_OUTLINE_MIN_CONTRAST, TIMELINE_STATUSES, TIMELINE_TEXT_MIN_CONTRAST, VARIABLE_KINDS, annotationText, captionSettings, chartColorForFill, chartHighlightColors, chartHighlightMarks, chartLabelText, chartNumber, chartNumberFixesByCell, chartOptionSupport, chartOptionTarget, chartPaletteForFill, chartPrimaryLayout, citationMarkerText, codeHighlightBands, codeHighlightColors, codeHighlightLines, codeHighlightSlice, codeLineNumbers, codeLineRuns, codeSyntaxPalette, codeSyntaxPaletteForScheme, coerceVariableValue, collectCitations, colorContrast, composeSlide, datasetDiagnostics, defaultSlideBackground, fitImage, fitList, fitRichText, fitText, formatChartLabelNumber, formatChartLabelPercent, formatDataNumber, formatFurnitureDate, formatListNumber, formatSlideNumber, formatVariableNumber, fromExcelNumberFormat, hasContentVariables, hasPlaceholderGroups, imageBackground, imageShape, inlineChartData, inlineDatasets, inlineTableData, instantiateForValidation, intrinsicImageAspect, intrinsicImageSize, isDarkColor, isDatasetRef, isPlaceholderGroup, isRecord, isTemplate, isXYChartType, layoutCaption, layoutCode, layoutContent, layoutFootnotes, layoutFurniture, layoutLeaves, layoutMetric, layoutQuote, layoutSlots, layoutStructure, layoutTable, layoutTimeline, layoutWatermark, listBuiltinVariables, listNumbers, listVariables, measureText, measureTextOutline, metricTrendColor, metricTrendMark, metricTrendPoints, normalizeHexColor, normalizeLanguageFamily, numberFormatError, numberingAtLevel, numberingFindings, numberingStyleDraws, paragraphDirection, paragraphDirectionAt, pathFor, patternBitmap, patternRuns, physicalAlignment, placeTextLines, promotedRegionKeys, readingRows, referencesSlide, resolveCanvasDimensions, resolveChartData, resolveChartOptions, resolveCodeLanguage, resolveColorRef, resolveColorRoles, resolveDesignHints, resolveDesignRecords, resolveFontFamilies, resolveFontScheme, resolveLogo, resolveNumbering, resolvePatternPreset, resolveScriptFonts, resolveSlideDirection, resolveSocialProfile, resolveTableData, resolveTextStyle, resolveVariables, scriptFontRole, sliceNumberedItems, slideCitations, snapFontSizeDown, snapFontSizeUp, suggestChartNumberFix, surfaceAltColor, tableCellDisplayValue, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, timelineMarkerShapes, timelineTextColor, toExcelNumberFormat, tokenizeCode, unusedDatasets, variableDeclarations, visitContentPayloads, visualReadingOrder, walkCitationRuns, wrapText };