@openpresentation/opf 0.15.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +19 -1
  2. package/dist/{catalog-refs-BSfGELpt.d.ts → catalog-refs-C4K4y-vh.d.ts} +1 -1
  3. package/dist/{catalog-schemas-CinPh5WA.d.ts → catalog-schemas-g7RFQBdc.d.ts} +1 -1
  4. package/dist/catalog.d.ts +2 -2
  5. package/dist/catalog.js +3 -23224
  6. package/dist/{chart-data-BnNqgnC-.d.ts → chart-data-DTxdRRT1.d.ts} +1 -1
  7. package/dist/{chunk-3JG4YXWF.js → chunk-BIC3SWPK.js} +12 -5
  8. package/dist/{chunk-O744SXXA.js → chunk-CW7GEAV3.js} +350 -141
  9. package/dist/{chunk-374R35TJ.js → chunk-ESRYBYLY.js} +1 -1
  10. package/dist/chunk-IPW5BS3X.js +973 -0
  11. package/dist/{chunk-3RKB4RDJ.js → chunk-IS5H5JKH.js} +4 -2
  12. package/dist/{chunk-TWQJLI6E.js → chunk-M4UR3NPN.js} +2 -2
  13. package/dist/chunk-N4EVOFEA.js +58 -0
  14. package/dist/{chunk-QDI75P2M.js → chunk-NLNYNBTS.js} +217 -20
  15. package/dist/{chunk-U4J75TRG.js → chunk-O263APNI.js} +5 -2
  16. package/dist/{chunk-UBFJ2QHH.js → chunk-O2CX5NEH.js} +36 -14
  17. package/dist/{chunk-5FTUIDUH.js → chunk-O3CVNJTL.js} +1 -1
  18. package/dist/{chunk-VGAOJEY7.js → chunk-PPROJP2E.js} +1 -1
  19. package/dist/{chunk-2CJPC4HI.js → chunk-RA64UQKI.js} +203 -53
  20. package/dist/chunk-TCD2FB2K.js +319 -0
  21. package/dist/chunk-WCG3THTD.js +23712 -0
  22. package/dist/chunk-WWYWFVOI.js +793 -0
  23. package/dist/{chunk-ZPU46TFA.js → chunk-YT7FMQER.js} +1 -1
  24. package/dist/{composition-D2tghM82.d.ts → composition-BbZ_9C-c.d.ts} +238 -139
  25. package/dist/composition.d.ts +3 -3
  26. package/dist/composition.js +1 -1
  27. package/dist/conversion-SBpTOwdg.d.ts +444 -0
  28. package/dist/convert.d.ts +4 -4
  29. package/dist/convert.js +7 -7
  30. package/dist/data.d.ts +28 -11
  31. package/dist/data.js +2 -2
  32. package/dist/deck-report-Bx3naFlF.d.ts +40 -0
  33. package/dist/deck.d.ts +47 -0
  34. package/dist/deck.js +12 -0
  35. package/dist/diff.d.ts +1 -1
  36. package/dist/diff.js +5 -5
  37. package/dist/docs.js +33 -27
  38. package/dist/examples.d.ts +1 -1
  39. package/dist/examples.js +3229 -985
  40. package/dist/{finding-CT_f_vbD.d.ts → finding-CDQ_wH8j.d.ts} +1 -1
  41. package/dist/format.js +2 -2
  42. package/dist/index.d.ts +43 -12
  43. package/dist/index.js +15 -805
  44. package/dist/markdown.d.ts +7 -8
  45. package/dist/markdown.js +7 -7
  46. package/dist/node-engine.d.ts +94 -0
  47. package/dist/node-engine.js +21 -0
  48. package/dist/node.d.ts +60 -0
  49. package/dist/node.js +54 -0
  50. package/dist/{pagination-wedbe7eq.d.ts → pagination-BhX4M48Y.d.ts} +21 -7
  51. package/dist/pagination.d.ts +4 -4
  52. package/dist/pagination.js +4 -4
  53. package/dist/patch.d.ts +1 -1
  54. package/dist/patch.js +4 -4
  55. package/dist/{presentation-CDKQVkyI.d.ts → presentation-fb8CZmRq.d.ts} +28 -83
  56. package/dist/repo-readme.js +1 -1
  57. package/dist/schemas.d.ts +14 -0
  58. package/dist/schemas.js +1 -1
  59. package/dist/spec/catalogs/layouts/business-model-canvas.json +66 -1
  60. package/dist/spec/catalogs/layouts/capability-map.json +53 -3
  61. package/dist/spec/catalogs/layouts/chart-2x.json +32 -8
  62. package/dist/spec/catalogs/layouts/chart-3x-bottom-vertical-title-left.json +16 -10
  63. package/dist/spec/catalogs/layouts/chart-3x-left-title-center.json +16 -10
  64. package/dist/spec/catalogs/layouts/chart-3x-left.json +16 -10
  65. package/dist/spec/catalogs/layouts/chart-3x-right-title-center.json +16 -10
  66. package/dist/spec/catalogs/layouts/chart-3x-right.json +16 -10
  67. package/dist/spec/catalogs/layouts/chart-3x-top-vertical-title-left.json +16 -10
  68. package/dist/spec/catalogs/layouts/chart-3x.json +48 -12
  69. package/dist/spec/catalogs/layouts/dashboard.json +37 -5
  70. package/dist/spec/catalogs/layouts/image-2x.json +32 -8
  71. package/dist/spec/catalogs/layouts/image-3x.json +48 -12
  72. package/dist/spec/catalogs/layouts/index.json +1 -1
  73. package/dist/spec/catalogs/layouts/kpi-scorecard.json +48 -3
  74. package/dist/spec/catalogs/layouts/logic-tree.json +69 -0
  75. package/dist/spec/catalogs/layouts/market-size.json +26 -2
  76. package/dist/spec/catalogs/layouts/org-chart.json +53 -6
  77. package/dist/spec/catalogs/manifest.json +3 -3
  78. package/dist/spec/schemas/layout.schema.json +172 -5
  79. package/dist/spec/schemas/opf.schema.json +27 -43
  80. package/dist/types.d.ts +10 -32
  81. package/dist/{validation-types-BOcY9GTs.d.ts → validation-types-iZbXfES6.d.ts} +19 -8
  82. package/dist/validator.d.ts +8 -8
  83. package/dist/validator.js +3 -3
  84. package/dist/yaml.d.ts +6 -6
  85. package/dist/yaml.js +8 -321
  86. package/package.json +38 -1
@@ -52,7 +52,7 @@ var presentation = {
52
52
  "minItems": 1
53
53
  }
54
54
  ],
55
- "description": "Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (Organization.role 'primary', else the first item) supplies the default deck logo, the 'organization' and 'socials' header/footer fields and the 'organization.<field>' built-in variables; the others are available by id as 'organization.<id>.<field>' and are not drawn automatically."
55
+ "description": "Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (Organization.role 'primary', else the first item) supplies the default deck logo, the 'socials' header/footer field and the 'organization.<field>' built-in variables; the others are available by id as 'organization.<id>.<field>' and are not drawn automatically."
56
56
  },
57
57
  "speaker": {
58
58
  "oneOf": [
@@ -67,7 +67,7 @@ var presentation = {
67
67
  "minItems": 1
68
68
  }
69
69
  ],
70
- "description": "Person presenting the deck. Array form supports panels and multi-speaker decks. The first speaker is the primary speaker: the built-in variables 'speaker.<field>' and the 'speaker' header/footer field read it, 'speakers' lists every name, and 'speaker.<id>.<field>' addresses one by id. A speaker is drawn nowhere else."
70
+ "description": "Person presenting the deck. Array form supports panels and multi-speaker decks. The first speaker is the primary speaker: the built-in variables 'speaker.<field>' read it, 'speakers' lists every name, and 'speaker.<id>.<field>' addresses one by id. A speaker is drawn nowhere else."
71
71
  },
72
72
  "author": {
73
73
  "oneOf": [
@@ -775,7 +775,7 @@ var presentation = {
775
775
  },
776
776
  "Organization": {
777
777
  "type": "object",
778
- "description": "An organization associated with the presentation, typically the presenting company, but also hosts, partners, clients, or sponsors. The primary organization (role 'primary', else the first one) supplies the default deck logo (unless design.logo overrides it), the 'organization' header/footer field (its name) and the 'socials' field (its socials). Every field is also a built-in variable ('{{organization.name}}', 'var:organization.logo', 'organization.<id>.<field>'). Nothing else about an organization is drawn automatically. See docs/templates-and-variables.md.",
778
+ "description": "An organization associated with the presentation, typically the presenting company, but also hosts, partners, clients, or sponsors. The primary organization (role 'primary', else the first one) supplies the default deck logo (unless design.logo overrides it) and the 'socials' header/footer field (its socials). Every field is also a built-in variable ('{{organization.name}}', 'var:organization.logo', 'organization.<id>.<field>'). Nothing else about an organization is drawn automatically. See docs/templates-and-variables.md.",
779
779
  "required": [
780
780
  "id",
781
781
  "name"
@@ -793,7 +793,7 @@ var presentation = {
793
793
  },
794
794
  "name": {
795
795
  "type": "string",
796
- "description": "Display name. Drawn by the 'organization' header/footer field for the primary organization; the built-in variable 'organization.name' (or 'organization.<id>.name') carries it into any string.",
796
+ "description": "Display name. The built-in variable 'organization.name' (primary organization) or 'organization.<id>.name' carries it into any string, such as a header or footer text of '{{organization.name}}'.",
797
797
  "examples": [
798
798
  "Acme Corp",
799
799
  "DevConf 2026",
@@ -863,7 +863,7 @@ var presentation = {
863
863
  "sponsor",
864
864
  "host"
865
865
  ],
866
- "description": "Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (deck logo, 'organization' and 'socials' header/footer fields, and the 'organization.<field>' built-ins). When no organization has role 'primary', the first one is primary. 'partner', 'client', 'sponsor' and 'host' are descriptive labels that change nothing."
866
+ "description": "Role of the organization relative to the presentation. Only 'primary' has behavior: it selects the primary organization (deck logo, the 'socials' header/footer field, and the 'organization.<field>' built-ins). When no organization has role 'primary', the first one is primary. 'partner', 'client', 'sponsor' and 'host' are descriptive labels that change nothing."
867
867
  },
868
868
  "socials": {
869
869
  "$ref": "#/$defs/Socials",
@@ -873,7 +873,7 @@ var presentation = {
873
873
  },
874
874
  "Speaker": {
875
875
  "type": "object",
876
- "description": "A person presenting the deck. A speaker is drawn only through built-in variables ('{{speaker.name}}' inside any string, 'var:speaker.photo' as a whole image field) and the 'speaker' header/footer field (the first speaker's name and title). No layout, cover or bio slide places a speaker on its own. See docs/templates-and-variables.md.",
876
+ "description": "A person presenting the deck. A speaker is drawn only through built-in variables ('{{speaker.name}}' inside any string, a header or footer text included, and 'var:speaker.photo' as a whole image field). No layout, cover or bio slide places a speaker on its own. See docs/templates-and-variables.md.",
877
877
  "required": [
878
878
  "id",
879
879
  "name"
@@ -891,7 +891,7 @@ var presentation = {
891
891
  },
892
892
  "name": {
893
893
  "type": "string",
894
- "description": "Display name. Built-in variable 'speaker.name' (first speaker) or 'speaker.<id>.name'; the first speaker's name is also drawn by the 'speaker' header/footer field and every name is listed by 'speakers'.",
894
+ "description": "Display name. Built-in variable 'speaker.name' (first speaker) or 'speaker.<id>.name'; every name is listed by 'speakers'.",
895
895
  "examples": [
896
896
  "Alice Chen",
897
897
  "Bob Lee",
@@ -900,7 +900,7 @@ var presentation = {
900
900
  },
901
901
  "title": {
902
902
  "type": "string",
903
- "description": "Role or title. Built-in variable 'speaker.title'; the 'speaker' header/footer field draws it after the name ('Alice Chen, VP of Engineering').",
903
+ "description": "Role or title. Built-in variable 'speaker.title' (or 'speaker.<id>.title'), for example in a footer text of '{{speaker.name}}, {{speaker.title}}'.",
904
904
  "examples": [
905
905
  "VP of Engineering",
906
906
  "Founder & CEO",
@@ -1144,7 +1144,7 @@ var presentation = {
1144
1144
  false,
1145
1145
  {
1146
1146
  "left": {
1147
- "section": true
1147
+ "text": "{{slide.section}}"
1148
1148
  },
1149
1149
  "right": {
1150
1150
  "image": "asset:acme-logo"
@@ -1173,7 +1173,7 @@ var presentation = {
1173
1173
  "text": "Confidential"
1174
1174
  },
1175
1175
  "right": {
1176
- "slideNumber": true
1176
+ "text": "{{slide.number}}"
1177
1177
  }
1178
1178
  }
1179
1179
  ]
@@ -1217,7 +1217,7 @@ var presentation = {
1217
1217
  "left",
1218
1218
  "right"
1219
1219
  ],
1220
- "description": "Where the primary chart sits relative to supporting content. Effective value: slide design, then deck design, then the layout record's design.chartPrimary. When the slide has no promoted regions and no composition.mode of its own, and its root nodes mix at least one chart with other content, the first chart becomes a primary track and the other nodes form one synthetic sub-grid: 'left'/'right' is a two-track row and 'top'/'bottom' a two-track column, weighted 3:2 in favor of the chart; explicit root columns and weights are ignored while it applies. 'none' (the default) keeps the ordinary automatic grid with equal weight."
1220
+ "description": "Where the primary chart sits relative to supporting content. Effective value: slide design, then deck design, then the layout record's design.chartPrimary. When the slide has no promoted regions and no composition.mode of its own, and its root nodes mix at least one chart with other content, the first chart becomes a primary track and the other nodes form one synthetic sub-grid: 'left'/'right' is a two-track row and 'top'/'bottom' a two-track column, weighted 3:2 in favor of the chart; explicit root columns and weights are ignored while it applies. 'none' (the default) keeps the ordinary automatic grid with equal weight. It is shorthand for a placeholder group (layout.schema.json PlaceholderGroup): the slide composes exactly as on a layout record whose root composition is that weighted row or column and whose placeholders are the chart and one automatic group of the other content, and while it applies it replaces the layout record's own placeholder groups as well."
1221
1221
  },
1222
1222
  "imageFit": {
1223
1223
  "type": "string",
@@ -1666,7 +1666,7 @@ var presentation = {
1666
1666
  "additionalProperties": {
1667
1667
  "$ref": "#/$defs/Variable"
1668
1668
  },
1669
- "description": "Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: expected in a template (root 'template': true), an error in a normal deck. Engines resolve variables with resolveVariables() before composing, rendering or exporting, so a template plus its values previews and exports exactly like the equivalent hand-written deck. Read-only built-in variables come from the deck's own metadata and use dotted names that a user id can never take: 'deck.name', 'deck.description', 'deck.author', 'speaker.name|title|email|phone|bio|photo' (first speaker), 'speakers' (every speaker's name, a list), 'organization.name|legalName|tagline|domain|email|phone|logo' (primary organization), and 'speaker.<id>.<field>' / 'organization.<id>.<field>' by id. They are written the same two ways ('{{speaker.name}}', 'var:speaker.photo'), are never declared, and an unknown built-in path is a validation error; a known one with no source value resolves to nothing with a warning. The id 'speakers' is reserved. See docs/templates-and-variables.md.",
1669
+ "description": "Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: expected in a template (root 'template': true), an error in a normal deck. Engines resolve variables with resolveVariables() before composing, rendering or exporting, so a template plus its values previews and exports exactly like the equivalent hand-written deck. Read-only built-in variables come from the deck's own metadata and use dotted names that a user id can never take: 'deck.name', 'deck.description', 'deck.author', 'speaker.name|title|email|phone|bio|photo' (first speaker), 'speakers' (every speaker's name, a list), 'organization.name|legalName|tagline|domain|email|phone|logo' (primary organization), and 'speaker.<id>.<field>' / 'organization.<id>.<field>' by id. They are written the same two ways ('{{speaker.name}}', 'var:speaker.photo'), are never declared, and an unknown built-in path is a validation error; a known one with no source value resolves to nothing with a warning. The id 'speakers' is reserved. Three built-ins are slide-scoped and written only as inline tokens: '{{slide.number}}' (the displayed slide number after pagination), '{{slide.section}}' (the slide's section) and '{{deck.slideCount}}' (the number of slides after pagination). resolveVariables() leaves them as written; they resolve per slide as it is composed (resolveSlideVariables(), and header/footer text in layoutFurniture()), so every 'slide.' name is reserved and 'var:slide.*' or 'var:deck.slideCount' is a validation error. See docs/templates-and-variables.md.",
1670
1670
  "examples": [
1671
1671
  {
1672
1672
  "risk": "#B42318",
@@ -2625,7 +2625,8 @@ var presentation = {
2625
2625
  },
2626
2626
  "HeaderFooterItem": {
2627
2627
  "type": "object",
2628
- "description": "One header/footer zone. Every configured field renders; fields in one zone stack top to bottom in the order logo, image, text, organization, speaker, socials, section, slide number, date. Put a date and a slide number in different zones to keep each on the zone's single line.",
2628
+ "description": "One header/footer zone. Every configured field renders; fields in one zone stack top to bottom in the order logo, image, text, socials, date. Generated values such as the slide number, the slide count, the section, the organization or the speaker are variables inside text ('{{slide.number}} / {{deck.slideCount}}', '{{organization.name}}'), so one text writes several values in the author's order; a line break is '\\n'. Put a date and a slide number in different zones to keep each on the zone's single line. See docs/templates-and-variables.md.",
2629
+ "additionalProperties": false,
2629
2630
  "properties": {
2630
2631
  "logo": {
2631
2632
  "type": "boolean",
@@ -2633,10 +2634,12 @@ var presentation = {
2633
2634
  },
2634
2635
  "text": {
2635
2636
  "type": "string",
2636
- "description": "Literal text rendered in this zone.",
2637
+ "description": "Text rendered in this zone. Variables resolve inside it like any string: deck-wide ones ('{{organization.name}}', '{{speaker.name}}, {{speaker.title}}', '{{customer}}') before composition, and the slide-scoped built-ins '{{slide.number}}', '{{slide.section}}' and '{{deck.slideCount}}' for each slide as it is laid out. Each '{{slide.number}}' is a live slide-number field: PPTX export writes it as a native field when its value fits within one accepted text line, and as static text with a diagnostic otherwise. '{{deck.slideCount}}' is fixed text, because PowerPoint has no slide-count field. Write '\\{{' for literal braces.",
2637
2638
  "examples": [
2638
2639
  "Confidential",
2639
- "Q4 Business Review"
2640
+ "Q4 Business Review",
2641
+ "{{slide.number}} / {{deck.slideCount}}",
2642
+ "{{organization.name}}\n{{slide.section}}"
2640
2643
  ]
2641
2644
  },
2642
2645
  "image": {
@@ -2647,21 +2650,6 @@ var presentation = {
2647
2650
  "./assets/footer-badge.svg"
2648
2651
  ]
2649
2652
  },
2650
- "slideNumber": {
2651
- "type": "boolean",
2652
- "description": "Whether to render the current slide number in this zone. PPTX export writes a native slide-number field when its value fits within one accepted text line; a value split across lines exports as static text with a diagnostic."
2653
- },
2654
- "slideNumberFormat": {
2655
- "type": "string",
2656
- "pattern": "[{]current[}]",
2657
- "description": 'Template for the slide number when slideNumber is true. {current} is the displayed slide number (a native PPTX field when its value fits within one accepted text line); {total} is the number of slides in the rendered or exported deck, written as fixed text because PowerPoint has no slide-count field. Other characters are literal. Defaults to "{current}".',
2658
- "examples": [
2659
- "{current}",
2660
- "{current} / {total}",
2661
- "A-{current}",
2662
- "Page {current} of {total}"
2663
- ]
2664
- },
2665
2653
  "date": {
2666
2654
  "oneOf": [
2667
2655
  {
@@ -2689,18 +2677,6 @@ var presentation = {
2689
2677
  "MMMM d, yyyy"
2690
2678
  ]
2691
2679
  },
2692
- "organization": {
2693
- "type": "boolean",
2694
- "description": "Whether to render the primary organization's name from organization (role 'primary', else the first organization)."
2695
- },
2696
- "speaker": {
2697
- "type": "boolean",
2698
- "description": "Whether to render the primary (first) speaker's name and title from speaker, joined as 'Ada Lovelace, CTO' (just the name when the speaker has no title). It is generated text: without a named speaker the engine reports unresolved-content at this field. PPTX export writes it as static text and import restores the flag from the file's furniture provenance."
2699
- },
2700
- "section": {
2701
- "type": "boolean",
2702
- "description": "Whether to render the current slide section label."
2703
- },
2704
2680
  "socials": {
2705
2681
  "type": "boolean",
2706
2682
  "description": "Whether to render the primary organization's social profiles from organization.socials, one line per platform in key order. A handle is formatted through the platform's socialPlatforms record (companyUrlPattern, else profileUrlPattern, else baseUrl, with handlePrefix stripped) and shown as that profile URL without its 'https://' scheme; a value that is already a URL is shown without an 'https://' scheme; an unknown platform key shows the raw value. Exporters link each line to its full URL when one exists. Speaker socials are not rendered by this field."
@@ -2846,14 +2822,14 @@ var presentation = {
2846
2822
  "items": {
2847
2823
  "$ref": "#/$defs/ListItem"
2848
2824
  },
2849
- "description": "Full-slide generic list payload. Presence of this field infers type 'list'. At slide root, multiple content payload kinds with no explicit type, blocks, or regions are accepted as shorthand for layout-agnostic blocks."
2825
+ "description": "Full-slide structured list payload. Presence of this field infers type 'list'. Entries are strings, TextRun[] or objects with `text`, optional `description` (supporting detail under the entry), `level` and `start`. Use `items` when entries share a shape (features, steps, options, risks) or any entry needs a `description`; use `bullets` for plain talking points. Both draw the same marker, nesting and numbering; they differ in the inferred kind and in `description`, which only `items` accepts. At slide root, multiple content payload kinds with no explicit type, blocks, or regions are accepted as shorthand for layout-agnostic blocks, so `items` next to `text` or `bullets` composes as separate blocks side by side."
2850
2826
  },
2851
2827
  "bullets": {
2852
2828
  "type": "array",
2853
2829
  "items": {
2854
2830
  "$ref": "#/$defs/BulletItem"
2855
2831
  },
2856
- "description": "Full-slide text-style bullet payload. Presence of this field infers type 'text'."
2832
+ "description": "Full-slide prose bullet payload: short talking points, each standing alone. Presence of this field infers type 'text', so it may sit beside `text` in one payload. Entries are strings, TextRun[] or objects with `text`, `level` and `start`; there is no `description` (use `items` for entries with supporting detail)."
2857
2833
  },
2858
2834
  "numbering": {
2859
2835
  "$ref": "#/$defs/NumberingSpec",
@@ -3100,7 +3076,7 @@ var presentation = {
3100
3076
  },
3101
3077
  "section": {
3102
3078
  "type": "string",
3103
- "description": "PowerPoint-style slide section label. Consecutive slides with the same value belong to the same section in presenter view, outlines, and PowerPoint section-aware exports.",
3079
+ "description": "PowerPoint-style slide section label. Consecutive slides with the same value belong to the same section in presenter view, outlines, and PowerPoint section-aware exports. Also the slide-scoped built-in variable '{{slide.section}}', for example in a header text.",
3104
3080
  "examples": [
3105
3081
  "Intro",
3106
3082
  "Problem",
@@ -3187,7 +3163,7 @@ var presentation = {
3187
3163
  "items": {
3188
3164
  "$ref": "#/$defs/ListItem"
3189
3165
  },
3190
- "description": "Generic list payload. Each item is either a plain string, a TextRun[] rich text sequence, or a ListItem object. List nesting uses item.level rather than nested content payloads.",
3166
+ "description": "Structured list payload (type 'list'). Each item is either a plain string, a TextRun[] rich text sequence, or a ListItem object with optional `description`. List nesting uses item.level rather than nested content payloads. Use `items` when entries share a shape or any entry needs a `description`; use `bullets` for plain talking points. Both draw the same marker, nesting and numbering. A block or region holds `items` or `bullets`, never both, and `items` cannot sit beside `text`.",
3191
3167
  "examples": [
3192
3168
  [
3193
3169
  "Sub-second agent latency",
@@ -3210,7 +3186,7 @@ var presentation = {
3210
3186
  "items": {
3211
3187
  "$ref": "#/$defs/BulletItem"
3212
3188
  },
3213
- "description": "Text-style bullet payload. Presence of this field infers type 'text'.",
3189
+ "description": "Prose bullet payload: short talking points, each standing alone. Presence of this field infers type 'text', so it may sit beside `text` in one payload. Entries have no `description`; use `items` for entries with supporting detail.",
3214
3190
  "examples": [
3215
3191
  [
3216
3192
  "Sub-second agent latency",
@@ -4822,6 +4798,13 @@ var presentation = {
4822
4798
  "fields": {
4823
4799
  "$ref": "#/$defs/DatasetFields",
4824
4800
  "description": "Dataset tables only: the dataset columns to show, by name and in order. Absent shows every column. An unknown name is a 'dataset-field-unknown' error."
4801
+ },
4802
+ "alt": {
4803
+ "type": "string",
4804
+ "description": "Text alternative for the table: what it shows (the point and the key numbers), not 'a table'. The preview exposes it as the table's accessible name (role group with aria-label; the cells stay readable) and the PowerPoint export writes it as the table frame's alternative text (descr); import reads it back. An empty string hides a purely decorative table, with the same meaning as an empty Asset alt. The alt is a summary read before the cells, which stay readable, so it is most useful for a large or dense table; validate does not require one and does not report a missing one.",
4805
+ "examples": [
4806
+ "North America grew 46% to $18.1M in Q4, ahead of EMEA at 40%."
4807
+ ]
4825
4808
  }
4826
4809
  }
4827
4810
  },
@@ -5708,7 +5691,7 @@ var presentation = {
5708
5691
  "Composition": {
5709
5692
  "type": "object",
5710
5693
  "additionalProperties": false,
5711
- "description": "Portable dynamic composition. Slide fields override the resolved layout. Nested groups arrange their children independently, inheriting only minFontSize and overflow. Explicit promoted regions retain their positions.",
5694
+ "description": "Portable dynamic composition. Slide fields override the resolved layout. Nested groups (content groups here, placeholder groups in a layout record) arrange their children independently, inheriting only minFontSize and overflow. Explicit promoted regions retain their positions.",
5712
5695
  "properties": {
5713
5696
  "mode": {
5714
5697
  "type": "string",
@@ -6453,9 +6436,9 @@ var layout = {
6453
6436
  "placeholders": {
6454
6437
  "type": "array",
6455
6438
  "items": {
6456
- "$ref": "#/$defs/Placeholder"
6439
+ "$ref": "#/$defs/PlaceholderEntry"
6457
6440
  },
6458
- "description": "Ordered regions the layout exposes. This is the single source of truth for what the layout holds: the content kind, the number of body regions and whether it has a title, subtitle or tag are derived from it (layoutContent in @openpresentation/opf), never stored separately. The engine fills 'title', 'subtitle' and 'tag' placeholders from Slide.title, Slide.subtitle and Slide.tag. The other placeholders are content-kind regions that composition uses to size the body and that pickers use to match layouts. Slide content itself lives either in root payload fields or promoted region keys on Slide. Chrome (slide number, footer, date) is not included here; it is owned by Design.header / Design.footer."
6441
+ "description": `Ordered regions the layout exposes. This is the single source of truth for what the layout holds: the content kind, the number of body regions and whether it has a title, subtitle or tag are derived from it (layoutContent in @openpresentation/opf, which counts the leaves of placeholder groups), never stored separately. The engine fills 'title', 'subtitle' and 'tag' placeholders from Slide.title, Slide.subtitle and Slide.tag. The other placeholders are content-kind regions that composition uses to size the body and that pickers use to match layouts. An entry may instead be a placeholder group ({ "type": "group", "composition", "placeholders" }) that arranges its own regions inside one cell of this record's composition, so a record can say 'two stacked panels beside a chart' once; groups nest at most three levels. Slide content stays flat: its body payloads fill the leaf regions in reading order (depth first, in array order). Slide content itself lives either in root payload fields, blocks or promoted region keys on Slide. Chrome (slide number, footer, date) is not included here; it is owned by Design.header / Design.footer.`
6459
6442
  },
6460
6443
  "tags": {
6461
6444
  "type": "array",
@@ -6513,6 +6496,173 @@ var layout = {
6513
6496
  }
6514
6497
  },
6515
6498
  "$defs": {
6499
+ "PlaceholderEntry": {
6500
+ "description": "One entry of a layout's placeholders: a region (Placeholder) or, when its type is 'group', a placeholder group (PlaceholderGroup).",
6501
+ "if": {
6502
+ "type": "object",
6503
+ "required": [
6504
+ "type"
6505
+ ],
6506
+ "properties": {
6507
+ "type": {
6508
+ "const": "group"
6509
+ }
6510
+ }
6511
+ },
6512
+ "then": {
6513
+ "$ref": "#/$defs/PlaceholderGroup"
6514
+ },
6515
+ "else": {
6516
+ "$ref": "#/$defs/Placeholder"
6517
+ }
6518
+ },
6519
+ "PlaceholderGroup": {
6520
+ "type": "object",
6521
+ "additionalProperties": false,
6522
+ "required": [
6523
+ "type",
6524
+ "placeholders"
6525
+ ],
6526
+ "description": "A placeholder group: regions arranged by their own composition inside one cell of the record's composition, the way a content group (a block with blocks) arranges its children. Slide content fills the group's leaf regions in reading order, as part of one flat sequence for the whole record. A group holds body regions and groups only (no title, subtitle or tag, and no placement), and groups nest at most three levels inside a record: this is the first. Groups draw nothing of their own and have no slide path; design.chartPrimary is shorthand for a record whose root holds the chart and one group of the other content.",
6527
+ "properties": {
6528
+ "type": {
6529
+ "const": "group",
6530
+ "description": "Marks the entry as a placeholder group."
6531
+ },
6532
+ "composition": {
6533
+ "$ref": "#/$defs/Composition",
6534
+ "description": "How the group arranges its regions, with the semantics of a content group's composition: only minFontSize and overflow inherit from the enclosing composition; padding defaults to 0 and gap is a fraction of the group's short edge. Omitted, the group arranges its regions automatically."
6535
+ },
6536
+ "placeholders": {
6537
+ "type": "array",
6538
+ "minItems": 1,
6539
+ "items": {
6540
+ "$ref": "#/$defs/PlaceholderGroupEntry2"
6541
+ },
6542
+ "description": "The group's regions and nested groups, in reading order."
6543
+ }
6544
+ }
6545
+ },
6546
+ "PlaceholderGroupEntry2": {
6547
+ "description": "An entry of a first-level group: a region (GroupedPlaceholder) or a second-level group (PlaceholderGroup2).",
6548
+ "if": {
6549
+ "type": "object",
6550
+ "required": [
6551
+ "type"
6552
+ ],
6553
+ "properties": {
6554
+ "type": {
6555
+ "const": "group"
6556
+ }
6557
+ }
6558
+ },
6559
+ "then": {
6560
+ "$ref": "#/$defs/PlaceholderGroup2"
6561
+ },
6562
+ "else": {
6563
+ "$ref": "#/$defs/GroupedPlaceholder"
6564
+ }
6565
+ },
6566
+ "PlaceholderGroup2": {
6567
+ "type": "object",
6568
+ "additionalProperties": false,
6569
+ "required": [
6570
+ "type",
6571
+ "placeholders"
6572
+ ],
6573
+ "description": "A placeholder group nested in another (the second level). Same fields as PlaceholderGroup.",
6574
+ "properties": {
6575
+ "type": {
6576
+ "const": "group",
6577
+ "description": "Marks the entry as a placeholder group."
6578
+ },
6579
+ "composition": {
6580
+ "$ref": "#/$defs/Composition",
6581
+ "description": "How the group arranges its regions; see PlaceholderGroup.composition."
6582
+ },
6583
+ "placeholders": {
6584
+ "type": "array",
6585
+ "minItems": 1,
6586
+ "items": {
6587
+ "$ref": "#/$defs/PlaceholderGroupEntry3"
6588
+ },
6589
+ "description": "The group's regions and nested groups, in reading order."
6590
+ }
6591
+ }
6592
+ },
6593
+ "PlaceholderGroupEntry3": {
6594
+ "description": "An entry of a second-level group: a region (GroupedPlaceholder) or a third-level group (PlaceholderGroup3).",
6595
+ "if": {
6596
+ "type": "object",
6597
+ "required": [
6598
+ "type"
6599
+ ],
6600
+ "properties": {
6601
+ "type": {
6602
+ "const": "group"
6603
+ }
6604
+ }
6605
+ },
6606
+ "then": {
6607
+ "$ref": "#/$defs/PlaceholderGroup3"
6608
+ },
6609
+ "else": {
6610
+ "$ref": "#/$defs/GroupedPlaceholder"
6611
+ }
6612
+ },
6613
+ "PlaceholderGroup3": {
6614
+ "type": "object",
6615
+ "additionalProperties": false,
6616
+ "required": [
6617
+ "type",
6618
+ "placeholders"
6619
+ ],
6620
+ "description": "The innermost placeholder group (the third level): it holds regions only, never another group.",
6621
+ "properties": {
6622
+ "type": {
6623
+ "const": "group",
6624
+ "description": "Marks the entry as a placeholder group."
6625
+ },
6626
+ "composition": {
6627
+ "$ref": "#/$defs/Composition",
6628
+ "description": "How the group arranges its regions; see PlaceholderGroup.composition."
6629
+ },
6630
+ "placeholders": {
6631
+ "type": "array",
6632
+ "minItems": 1,
6633
+ "items": {
6634
+ "$ref": "#/$defs/GroupedPlaceholder"
6635
+ },
6636
+ "description": "The group's regions, in reading order."
6637
+ }
6638
+ }
6639
+ },
6640
+ "GroupedPlaceholder": {
6641
+ "type": "object",
6642
+ "additionalProperties": false,
6643
+ "required": [
6644
+ "type"
6645
+ ],
6646
+ "description": "A region inside a placeholder group: a body content kind only. Headings ('title', 'subtitle', 'tag') stay at the record's top level, and only a top-level image placeholder may carry a placement.",
6647
+ "properties": {
6648
+ "type": {
6649
+ "type": "string",
6650
+ "enum": [
6651
+ "text",
6652
+ "list",
6653
+ "image",
6654
+ "video",
6655
+ "chart",
6656
+ "table",
6657
+ "code",
6658
+ "metric",
6659
+ "quote",
6660
+ "timeline"
6661
+ ],
6662
+ "description": "The content kind of the region, from the one OPF content-kind vocabulary (see Placeholder.type)."
6663
+ }
6664
+ }
6665
+ },
6516
6666
  "Placeholder": {
6517
6667
  "type": "object",
6518
6668
  "required": [
@@ -6553,7 +6703,7 @@ var layout = {
6553
6703
  },
6554
6704
  "placement": {
6555
6705
  "$ref": "#/$defs/ImagePlacement",
6556
- "description": "Image placeholders only. Bleeds the slide's image for this region to one slide edge, as an image block's placement does: the n-th image placeholder places the slide's n-th top-level image (Slide.image or an image block in blocks) unless that block sets its own placement. The headings and the other content compose in the rest of the slide."
6706
+ "description": "Image placeholders only. Bleeds the slide's image for this region to one slide edge, as an image block's placement does: the n-th image placeholder (counting the image regions of placeholder groups too, in reading order) places the slide's n-th top-level image (Slide.image or an image block in blocks) unless that block sets its own placement. Only a top-level image placeholder may carry a placement; a region inside a placeholder group cannot bleed to a slide edge. The headings and the other content compose in the rest of the slide."
6557
6707
  }
6558
6708
  }
6559
6709
  },
@@ -6631,7 +6781,7 @@ var layout = {
6631
6781
  "left",
6632
6782
  "right"
6633
6783
  ],
6634
- "description": "Where the primary chart sits relative to the other body content. Composition applies it below the slide's and the deck's design.chartPrimary, with the semantics described there."
6784
+ "description": "Where the primary chart sits relative to the other body content. Composition applies it below the slide's and the deck's design.chartPrimary, with the semantics described there. It is shorthand for a placeholder group: a record can state the same structure explicitly as a root row or column of the chart and one PlaceholderGroup of the other regions, weighted 3:2."
6635
6785
  },
6636
6786
  "imageFit": {
6637
6787
  "type": "string",
@@ -6655,7 +6805,7 @@ var layout = {
6655
6805
  "Composition": {
6656
6806
  "type": "object",
6657
6807
  "additionalProperties": false,
6658
- "description": "Portable dynamic composition. Slide fields override the resolved layout. Nested groups arrange their children independently, inheriting only minFontSize and overflow. Explicit promoted regions retain their positions.",
6808
+ "description": "Portable dynamic composition. Slide fields override the resolved layout. Nested groups (placeholder groups here, content groups in a slide) arrange their children independently, inheriting only minFontSize and overflow. Explicit promoted regions retain their positions.",
6659
6809
  "properties": {
6660
6810
  "mode": {
6661
6811
  "type": "string",