@openpresentation/opf 0.12.2 → 0.14.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 (416) hide show
  1. package/README.md +28 -23
  2. package/dist/{catalogs-BnI7JKcn.d.ts → catalogs-D-yyQWCQ.d.ts} +86 -208
  3. package/dist/catalogs.d.ts +2 -2
  4. package/dist/catalogs.js +1 -1
  5. package/dist/chart-data-B_3z9VTb.d.ts +188 -0
  6. package/dist/chunk-4LDGKLUC.js +192 -0
  7. package/dist/{chunk-V2537ENL.js → chunk-4VJDTF55.js} +2 -90
  8. package/dist/chunk-5ZU6JAPP.js +83 -0
  9. package/dist/{chunk-HOBSL7AJ.js → chunk-6OXY2CP3.js} +3604 -1644
  10. package/dist/{chunk-RUC7L5DJ.js → chunk-BFLVSVA7.js} +26 -26
  11. package/dist/chunk-CSQHPVGL.js +94 -0
  12. package/dist/{chunk-GJKJL4YY.js → chunk-D3TY4TT6.js} +34 -412
  13. package/dist/chunk-JDJBDYSR.js +4167 -0
  14. package/dist/{chunk-4LAM3N6S.js → chunk-LS2FMPI5.js} +1497 -1097
  15. package/dist/{chunk-SOSOBKQ2.js → chunk-Q2TCMN56.js} +6 -7
  16. package/dist/chunk-UPQQD7MA.js +1840 -0
  17. package/dist/{chunk-AUM5MXBR.js → chunk-XIZUK2XM.js} +36 -32
  18. package/dist/{chunk-E34DJ5LL.js → chunk-XOZ4KMSR.js} +2427 -10820
  19. package/dist/chunk-Z65B2B77.js +601 -0
  20. package/dist/{composition-DWbCiMwF.d.ts → composition-_WEwBe2a.d.ts} +744 -26
  21. package/dist/composition.d.ts +2 -1
  22. package/dist/composition.js +2 -2
  23. package/dist/convert.d.ts +9 -4
  24. package/dist/convert.js +57 -26
  25. package/dist/data.d.ts +5 -4
  26. package/dist/data.js +3 -1
  27. package/dist/diff.d.ts +5 -5
  28. package/dist/diff.js +6 -603
  29. package/dist/docs.js +70 -52
  30. package/dist/examples.d.ts +1 -1
  31. package/dist/examples.js +1466 -2963
  32. package/dist/finding-C_por275.d.ts +212 -0
  33. package/dist/format.d.ts +4 -4
  34. package/dist/format.js +2 -83
  35. package/dist/index.d.ts +318 -281
  36. package/dist/index.js +530 -943
  37. package/dist/markdown.d.ts +28 -25
  38. package/dist/markdown.js +8 -1940
  39. package/dist/pagination-BZpXLvFL.d.ts +119 -0
  40. package/dist/pagination.d.ts +5 -60
  41. package/dist/pagination.js +5 -5
  42. package/dist/patch.d.ts +12 -23
  43. package/dist/patch.js +5 -5
  44. package/dist/{presentation-DcF-MZcz.d.ts → presentation-C5eHkevA.d.ts} +516 -499
  45. package/dist/repo-readme.js +1 -1
  46. package/dist/{schemas-BSjlG8-R.d.ts → schemas-DDpqAgyS.d.ts} +49 -17
  47. package/dist/schemas.d.ts +1 -1
  48. package/dist/schemas.js +1 -1
  49. package/dist/spec/README.md +10 -5
  50. package/dist/spec/catalogs/audiences/index.json +2 -50
  51. package/dist/spec/catalogs/chart-types/100pct-stacked-area.json +21 -0
  52. package/dist/spec/catalogs/chart-types/100pct-stacked-bar.json +22 -0
  53. package/dist/spec/catalogs/chart-types/100pct-stacked-column.json +22 -0
  54. package/dist/spec/catalogs/chart-types/area.json +2 -60
  55. package/dist/spec/catalogs/chart-types/bar.json +2 -48
  56. package/dist/spec/catalogs/chart-types/box-and-whisker.json +2 -132
  57. package/dist/spec/catalogs/chart-types/column.json +2 -52
  58. package/dist/spec/catalogs/chart-types/combo.json +37 -0
  59. package/dist/spec/catalogs/chart-types/doughnut.json +2 -40
  60. package/dist/spec/catalogs/chart-types/filled-radar.json +2 -52
  61. package/dist/spec/catalogs/chart-types/funnel.json +2 -46
  62. package/dist/spec/catalogs/chart-types/histogram.json +2 -149
  63. package/dist/spec/catalogs/chart-types/index.json +50 -520
  64. package/dist/spec/catalogs/chart-types/line-with-markers.json +2 -60
  65. package/dist/spec/catalogs/chart-types/line.json +2 -60
  66. package/dist/spec/catalogs/chart-types/pareto.json +2 -50
  67. package/dist/spec/catalogs/chart-types/pie.json +2 -40
  68. package/dist/spec/catalogs/chart-types/radar-with-markers.json +2 -52
  69. package/dist/spec/catalogs/chart-types/radar.json +2 -52
  70. package/dist/spec/catalogs/chart-types/scatter.json +2 -73
  71. package/dist/spec/catalogs/chart-types/stacked-area.json +21 -0
  72. package/dist/spec/catalogs/chart-types/stacked-bar.json +22 -0
  73. package/dist/spec/catalogs/chart-types/stacked-column.json +22 -0
  74. package/dist/spec/catalogs/chart-types/stacked-line-with-markers.json +22 -0
  75. package/dist/spec/catalogs/chart-types/stacked-line.json +21 -0
  76. package/dist/spec/catalogs/chart-types/treemap.json +2 -48
  77. package/dist/spec/catalogs/chart-types/waterfall.json +2 -52
  78. package/dist/spec/catalogs/chart-types/world.json +2 -64
  79. package/dist/spec/catalogs/font-schemes/angsana-new.json +2 -2
  80. package/dist/spec/catalogs/font-schemes/aparajita.json +3 -3
  81. package/dist/spec/catalogs/font-schemes/aptos.json +1 -1
  82. package/dist/spec/catalogs/font-schemes/arabic-typesetting.json +2 -2
  83. package/dist/spec/catalogs/font-schemes/arial.json +1 -1
  84. package/dist/spec/catalogs/font-schemes/batang.json +2 -2
  85. package/dist/spec/catalogs/font-schemes/bookman.json +1 -1
  86. package/dist/spec/catalogs/font-schemes/calibri.json +1 -1
  87. package/dist/spec/catalogs/font-schemes/century-schoolbook.json +1 -1
  88. package/dist/spec/catalogs/font-schemes/consolas.json +2 -4
  89. package/dist/spec/catalogs/font-schemes/constantia.json +1 -1
  90. package/dist/spec/catalogs/font-schemes/courier-new.json +2 -4
  91. package/dist/spec/catalogs/font-schemes/daunpenh.json +2 -2
  92. package/dist/spec/catalogs/font-schemes/david.json +2 -2
  93. package/dist/spec/catalogs/font-schemes/dilleniaupc.json +2 -2
  94. package/dist/spec/catalogs/font-schemes/fangsong.json +2 -2
  95. package/dist/spec/catalogs/font-schemes/garamond.json +1 -1
  96. package/dist/spec/catalogs/font-schemes/gautami.json +2 -2
  97. package/dist/spec/catalogs/font-schemes/georgia.json +1 -1
  98. package/dist/spec/catalogs/font-schemes/gisha.json +3 -3
  99. package/dist/spec/catalogs/font-schemes/grandview.json +1 -1
  100. package/dist/spec/catalogs/font-schemes/gungsuh.json +2 -2
  101. package/dist/spec/catalogs/font-schemes/impact.json +1 -1
  102. package/dist/spec/catalogs/font-schemes/index.json +1 -1
  103. package/dist/spec/catalogs/font-schemes/kalinga.json +2 -2
  104. package/dist/spec/catalogs/font-schemes/kartika.json +2 -2
  105. package/dist/spec/catalogs/font-schemes/khmer-ui.json +2 -2
  106. package/dist/spec/catalogs/font-schemes/latha.json +2 -2
  107. package/dist/spec/catalogs/font-schemes/lucida-sans.json +1 -1
  108. package/dist/spec/catalogs/font-schemes/malgun-gothic.json +2 -2
  109. package/dist/spec/catalogs/font-schemes/mangal.json +4 -5
  110. package/dist/spec/catalogs/font-schemes/meiryo.json +2 -2
  111. package/dist/spec/catalogs/font-schemes/microsoft-jhenghei.json +2 -2
  112. package/dist/spec/catalogs/font-schemes/microsoft-yahei.json +5 -5
  113. package/dist/spec/catalogs/font-schemes/mingliu.json +2 -2
  114. package/dist/spec/catalogs/font-schemes/miriam.json +2 -2
  115. package/dist/spec/catalogs/font-schemes/montserrat.json +1 -1
  116. package/dist/spec/catalogs/font-schemes/ms-mincho.json +2 -2
  117. package/dist/spec/catalogs/font-schemes/nirmala-ui.json +2 -2
  118. package/dist/spec/catalogs/font-schemes/noto-naksh-arabic.json +2 -2
  119. package/dist/spec/catalogs/font-schemes/noto-nastaliq-urdu.json +3 -3
  120. package/dist/spec/catalogs/font-schemes/noto-sans-arabic.json +4 -4
  121. package/dist/spec/catalogs/font-schemes/noto-sans-armenian.json +2 -2
  122. package/dist/spec/catalogs/font-schemes/noto-sans-bengali.json +3 -3
  123. package/dist/spec/catalogs/font-schemes/noto-sans-devangari.json +3 -3
  124. package/dist/spec/catalogs/font-schemes/noto-sans-ethiopic.json +2 -2
  125. package/dist/spec/catalogs/font-schemes/noto-sans-georgian.json +2 -2
  126. package/dist/spec/catalogs/font-schemes/noto-sans-gujarati.json +2 -2
  127. package/dist/spec/catalogs/font-schemes/noto-sans-gurmukhi.json +2 -2
  128. package/dist/spec/catalogs/font-schemes/noto-sans-hebrew.json +2 -2
  129. package/dist/spec/catalogs/font-schemes/noto-sans-jp.json +2 -2
  130. package/dist/spec/catalogs/font-schemes/noto-sans-kannada.json +2 -2
  131. package/dist/spec/catalogs/font-schemes/noto-sans-khmer.json +2 -2
  132. package/dist/spec/catalogs/font-schemes/noto-sans-kr.json +2 -2
  133. package/dist/spec/catalogs/font-schemes/noto-sans-malayalam.json +2 -2
  134. package/dist/spec/catalogs/font-schemes/noto-sans-mongolian.json +2 -2
  135. package/dist/spec/catalogs/font-schemes/noto-sans-oriya.json +2 -2
  136. package/dist/spec/catalogs/font-schemes/noto-sans-sc.json +5 -5
  137. package/dist/spec/catalogs/font-schemes/noto-sans-tamil.json +2 -2
  138. package/dist/spec/catalogs/font-schemes/noto-sans-tc.json +2 -2
  139. package/dist/spec/catalogs/font-schemes/noto-sans-telugu.json +2 -2
  140. package/dist/spec/catalogs/font-schemes/noto-sans-thai.json +2 -2
  141. package/dist/spec/catalogs/font-schemes/noto-sans.json +5 -5
  142. package/dist/spec/catalogs/font-schemes/nyala.json +2 -2
  143. package/dist/spec/catalogs/font-schemes/open-sans.json +1 -1
  144. package/dist/spec/catalogs/font-schemes/pmingliu.json +2 -2
  145. package/dist/spec/catalogs/font-schemes/poppins.json +1 -1
  146. package/dist/spec/catalogs/font-schemes/pt-serif.json +1 -1
  147. package/dist/spec/catalogs/font-schemes/raavi.json +2 -2
  148. package/dist/spec/catalogs/font-schemes/raleway.json +1 -1
  149. package/dist/spec/catalogs/font-schemes/roboto.json +1 -1
  150. package/dist/spec/catalogs/font-schemes/rockwell.json +1 -1
  151. package/dist/spec/catalogs/font-schemes/sakkal-majalla.json +4 -4
  152. package/dist/spec/catalogs/font-schemes/seaford.json +1 -1
  153. package/dist/spec/catalogs/font-schemes/segoe-ui-light.json +1 -1
  154. package/dist/spec/catalogs/font-schemes/segoe-ui.json +1 -1
  155. package/dist/spec/catalogs/font-schemes/shonar-bangla.json +3 -3
  156. package/dist/spec/catalogs/font-schemes/shruti.json +2 -2
  157. package/dist/spec/catalogs/font-schemes/simsun.json +2 -2
  158. package/dist/spec/catalogs/font-schemes/skeena.json +1 -1
  159. package/dist/spec/catalogs/font-schemes/sylfaen.json +3 -3
  160. package/dist/spec/catalogs/font-schemes/tahoma.json +1 -1
  161. package/dist/spec/catalogs/font-schemes/tenorite.json +1 -1
  162. package/dist/spec/catalogs/font-schemes/times-new-roman.json +1 -1
  163. package/dist/spec/catalogs/font-schemes/traditional-arabic.json +4 -4
  164. package/dist/spec/catalogs/font-schemes/trebuchet-ms.json +1 -1
  165. package/dist/spec/catalogs/font-schemes/tunga.json +2 -2
  166. package/dist/spec/catalogs/font-schemes/verdana.json +1 -1
  167. package/dist/spec/catalogs/font-schemes/vrinda.json +2 -2
  168. package/dist/spec/catalogs/font-schemes/yu-gothic.json +2 -2
  169. package/dist/spec/catalogs/layouts/action-plan.json +9 -15
  170. package/dist/spec/catalogs/layouts/agenda.json +8 -15
  171. package/dist/spec/catalogs/layouts/appendix-index.json +7 -15
  172. package/dist/spec/catalogs/layouts/architecture-stack.json +9 -15
  173. package/dist/spec/catalogs/layouts/assumptions-dependencies.json +9 -15
  174. package/dist/spec/catalogs/layouts/before-after.json +9 -15
  175. package/dist/spec/catalogs/layouts/benchmark-target-table.json +9 -15
  176. package/dist/spec/catalogs/layouts/blank.json +11 -1
  177. package/dist/spec/catalogs/layouts/bullet-list.json +9 -15
  178. package/dist/spec/catalogs/layouts/business-model-canvas.json +9 -15
  179. package/dist/spec/catalogs/layouts/capability-map.json +14 -16
  180. package/dist/spec/catalogs/layouts/case-study.json +9 -15
  181. package/dist/spec/catalogs/layouts/chapter-recap.json +8 -15
  182. package/dist/spec/catalogs/layouts/chart-1x.json +15 -0
  183. package/dist/spec/catalogs/layouts/chart-2x.json +15 -0
  184. package/dist/spec/catalogs/layouts/chart-3x.json +15 -0
  185. package/dist/spec/catalogs/layouts/circular-lifecycle.json +9 -15
  186. package/dist/spec/catalogs/layouts/code-1x.json +10 -0
  187. package/dist/spec/catalogs/layouts/comparison.json +9 -15
  188. package/dist/spec/catalogs/layouts/competitive-landscape.json +14 -16
  189. package/dist/spec/catalogs/layouts/customer-journey.json +14 -16
  190. package/dist/spec/catalogs/layouts/customer-logos.json +9 -15
  191. package/dist/spec/catalogs/layouts/dashboard.json +9 -15
  192. package/dist/spec/catalogs/layouts/data-visualization.json +9 -15
  193. package/dist/spec/catalogs/layouts/decision-tree.json +14 -16
  194. package/dist/spec/catalogs/layouts/detailed-data-table.json +9 -15
  195. package/dist/spec/catalogs/layouts/ecosystem-map.json +14 -16
  196. package/dist/spec/catalogs/layouts/executive-decision.json +9 -15
  197. package/dist/spec/catalogs/layouts/executive-summary.json +9 -15
  198. package/dist/spec/catalogs/layouts/extract/canonical.json +8 -8
  199. package/dist/spec/catalogs/layouts/faq.json +9 -15
  200. package/dist/spec/catalogs/layouts/feature-comparison-matrix.json +9 -15
  201. package/dist/spec/catalogs/layouts/forecast-scenario.json +9 -15
  202. package/dist/spec/catalogs/layouts/funnel-pyramid.json +9 -15
  203. package/dist/spec/catalogs/layouts/gantt-project-plan.json +9 -15
  204. package/dist/spec/catalogs/layouts/geographic-map.json +13 -15
  205. package/dist/spec/catalogs/layouts/heatmap-cohort.json +9 -15
  206. package/dist/spec/catalogs/layouts/hub-and-spoke.json +9 -15
  207. package/dist/spec/catalogs/layouts/icon-grid.json +14 -16
  208. package/dist/spec/catalogs/layouts/image-1x.json +11 -1
  209. package/dist/spec/catalogs/layouts/image-2x.json +12 -2
  210. package/dist/spec/catalogs/layouts/image-3x.json +13 -3
  211. package/dist/spec/catalogs/layouts/image-bleed.json +12 -2
  212. package/dist/spec/catalogs/layouts/image-focus.json +14 -16
  213. package/dist/spec/catalogs/layouts/index.json +1 -1
  214. package/dist/spec/catalogs/layouts/key-takeaway.json +8 -15
  215. package/dist/spec/catalogs/layouts/kpi-scorecard.json +9 -15
  216. package/dist/spec/catalogs/layouts/list-1x.json +17 -1
  217. package/dist/spec/catalogs/layouts/list-2x.json +16 -0
  218. package/dist/spec/catalogs/layouts/list-3x.json +16 -0
  219. package/dist/spec/catalogs/layouts/list-4x.json +16 -0
  220. package/dist/spec/catalogs/layouts/list-5x.json +16 -0
  221. package/dist/spec/catalogs/layouts/list-6x.json +16 -0
  222. package/dist/spec/catalogs/layouts/logic-tree.json +9 -15
  223. package/dist/spec/catalogs/layouts/market-size.json +9 -15
  224. package/dist/spec/catalogs/layouts/matrix-quadrant.json +9 -15
  225. package/dist/spec/catalogs/layouts/media-1x.json +11 -1
  226. package/dist/spec/catalogs/layouts/network-dependency-map.json +13 -15
  227. package/dist/spec/catalogs/layouts/number-1x.json +16 -1
  228. package/dist/spec/catalogs/layouts/number-2x.json +15 -0
  229. package/dist/spec/catalogs/layouts/number-3x.json +15 -0
  230. package/dist/spec/catalogs/layouts/number-4x.json +15 -0
  231. package/dist/spec/catalogs/layouts/number-5x.json +15 -0
  232. package/dist/spec/catalogs/layouts/number-6x.json +15 -0
  233. package/dist/spec/catalogs/layouts/one-column-explainer.json +9 -15
  234. package/dist/spec/catalogs/layouts/org-chart.json +9 -15
  235. package/dist/spec/catalogs/layouts/persona-grid.json +14 -16
  236. package/dist/spec/catalogs/layouts/photo-grid.json +17 -19
  237. package/dist/spec/catalogs/layouts/pricing-table.json +9 -15
  238. package/dist/spec/catalogs/layouts/process-flow.json +9 -15
  239. package/dist/spec/catalogs/layouts/product-feature.json +14 -16
  240. package/dist/spec/catalogs/layouts/pros-cons.json +9 -15
  241. package/dist/spec/catalogs/layouts/qa-discussion.json +7 -15
  242. package/dist/spec/catalogs/layouts/quote-1x.json +11 -1
  243. package/dist/spec/catalogs/layouts/quote-highlight.json +9 -15
  244. package/dist/spec/catalogs/layouts/raci-matrix.json +9 -15
  245. package/dist/spec/catalogs/layouts/risk-heatmap.json +9 -15
  246. package/dist/spec/catalogs/layouts/roadmap.json +9 -15
  247. package/dist/spec/catalogs/layouts/screenshot-walkthrough.json +14 -16
  248. package/dist/spec/catalogs/layouts/section-divider.json +8 -15
  249. package/dist/spec/catalogs/layouts/single-message-statement.json +8 -15
  250. package/dist/spec/catalogs/layouts/stats-metrics.json +9 -15
  251. package/dist/spec/catalogs/layouts/status-rag.json +9 -15
  252. package/dist/spec/catalogs/layouts/swimlane-process.json +14 -16
  253. package/dist/spec/catalogs/layouts/swot-analysis.json +9 -15
  254. package/dist/spec/catalogs/layouts/table-1x.json +10 -0
  255. package/dist/spec/catalogs/layouts/team-bio.json +14 -16
  256. package/dist/spec/catalogs/layouts/testimonial-grid.json +9 -15
  257. package/dist/spec/catalogs/layouts/text-1x.json +11 -1
  258. package/dist/spec/catalogs/layouts/text-2x.json +10 -0
  259. package/dist/spec/catalogs/layouts/text-3x.json +10 -0
  260. package/dist/spec/catalogs/layouts/thank-you-cta.json +8 -15
  261. package/dist/spec/catalogs/layouts/three-column.json +9 -15
  262. package/dist/spec/catalogs/layouts/timeline-1x.json +11 -1
  263. package/dist/spec/catalogs/layouts/timeline.json +9 -15
  264. package/dist/spec/catalogs/layouts/title-slide.json +8 -15
  265. package/dist/spec/catalogs/layouts/title-subtitle.json +11 -1
  266. package/dist/spec/catalogs/layouts/title.json +11 -1
  267. package/dist/spec/catalogs/layouts/two-column.json +9 -15
  268. package/dist/spec/catalogs/layouts/venn-overlap.json +9 -15
  269. package/dist/spec/catalogs/layouts/waterfall-bridge.json +9 -15
  270. package/dist/spec/catalogs/layouts/wheel-radial.json +9 -15
  271. package/dist/spec/catalogs/manifest.json +11 -11
  272. package/dist/spec/catalogs/narratives/board-meeting.json +45 -45
  273. package/dist/spec/catalogs/narratives/business-narrative.json +19 -19
  274. package/dist/spec/catalogs/narratives/business-review.json +27 -27
  275. package/dist/spec/catalogs/narratives/capacity-planning.json +19 -19
  276. package/dist/spec/catalogs/narratives/challenge-resolution.json +17 -17
  277. package/dist/spec/catalogs/narratives/change-story.json +13 -13
  278. package/dist/spec/catalogs/narratives/classic-story.json +17 -17
  279. package/dist/spec/catalogs/narratives/company-intro.json +21 -21
  280. package/dist/spec/catalogs/narratives/conference-talk.json +4 -4
  281. package/dist/spec/catalogs/narratives/data-story.json +13 -14
  282. package/dist/spec/catalogs/narratives/early-startup-pitch.json +27 -27
  283. package/dist/spec/catalogs/narratives/educate.json +21 -21
  284. package/dist/spec/catalogs/narratives/employee-review.json +19 -19
  285. package/dist/spec/catalogs/narratives/failure-analysis.json +23 -23
  286. package/dist/spec/catalogs/narratives/focus.json +27 -27
  287. package/dist/spec/catalogs/narratives/golden-circle.json +4 -4
  288. package/dist/spec/catalogs/narratives/heros-journey.json +13 -13
  289. package/dist/spec/catalogs/narratives/index.json +145 -145
  290. package/dist/spec/catalogs/narratives/innovation.json +21 -21
  291. package/dist/spec/catalogs/narratives/justice.json +25 -25
  292. package/dist/spec/catalogs/narratives/marketing-strategy.json +25 -25
  293. package/dist/spec/catalogs/narratives/performance-improvement-plan.json +19 -19
  294. package/dist/spec/catalogs/narratives/performance-review.json +25 -25
  295. package/dist/spec/catalogs/narratives/persuade.json +17 -17
  296. package/dist/spec/catalogs/narratives/persuasive-sales.json +19 -19
  297. package/dist/spec/catalogs/narratives/pitch-deck.json +6 -6
  298. package/dist/spec/catalogs/narratives/problem-solution.json +5 -5
  299. package/dist/spec/catalogs/narratives/product-launch.json +23 -23
  300. package/dist/spec/catalogs/narratives/project-proposal.json +23 -23
  301. package/dist/spec/catalogs/narratives/pyramid-principle.json +9 -9
  302. package/dist/spec/catalogs/narratives/qbr.json +8 -8
  303. package/dist/spec/catalogs/narratives/rags-to-riches.json +17 -17
  304. package/dist/spec/catalogs/narratives/reveal.json +19 -19
  305. package/dist/spec/catalogs/narratives/scqa.json +4 -4
  306. package/dist/spec/catalogs/narratives/situation-complication-resolution.json +11 -11
  307. package/dist/spec/catalogs/narratives/sparkline.json +11 -11
  308. package/dist/spec/catalogs/narratives/star-method.json +11 -11
  309. package/dist/spec/catalogs/narratives/status-update.json +15 -15
  310. package/dist/spec/catalogs/narratives/strategic-advisory.json +17 -17
  311. package/dist/spec/catalogs/narratives/strategic-narrative.json +4 -4
  312. package/dist/spec/catalogs/narratives/survey-analysis.json +23 -23
  313. package/dist/spec/catalogs/narratives/survival-story.json +23 -23
  314. package/dist/spec/catalogs/narratives/transformation-arc.json +4 -4
  315. package/dist/spec/catalogs/narratives/trend-analysis.json +17 -17
  316. package/dist/spec/catalogs/narratives/underdog-victory.json +19 -19
  317. package/dist/spec/catalogs/narratives/venture-pitch.json +27 -27
  318. package/dist/spec/catalogs/narratives/vision-roadmap.json +13 -13
  319. package/dist/spec/catalogs/narratives/weekly-progress.json +23 -23
  320. package/dist/spec/catalogs/narratives/what-so-what-now-what.json +9 -9
  321. package/dist/spec/openapi.yaml +2 -5
  322. package/dist/spec/reference/engine-defaults.json +2 -2
  323. package/dist/spec/schemas/audience.schema.json +1 -1
  324. package/dist/spec/schemas/catalog-index.schema.json +2 -2
  325. package/dist/spec/schemas/chart-type.schema.json +19 -290
  326. package/dist/spec/schemas/color-scheme.schema.json +20 -12
  327. package/dist/spec/schemas/finding.schema.json +191 -0
  328. package/dist/spec/schemas/font-scheme.schema.json +19 -46
  329. package/dist/spec/schemas/layout.schema.json +100 -159
  330. package/dist/spec/schemas/narrative.schema.json +17 -25
  331. package/dist/spec/schemas/opf.schema.json +503 -339
  332. package/dist/spec/schemas/social-platform.schema.json +1 -26
  333. package/dist/spec/schemas/theme.schema.json +4 -1
  334. package/dist/spec-files.d.ts +1 -1
  335. package/dist/spec-files.js +1 -1
  336. package/dist/symbol-font-encodings.js +18448 -1
  337. package/dist/types.d.ts +4 -4
  338. package/dist/validation-types-zh03LeZy.d.ts +276 -0
  339. package/dist/validator.d.ts +60 -4
  340. package/dist/validator.js +4 -4
  341. package/dist/yaml.d.ts +87 -0
  342. package/dist/yaml.js +321 -0
  343. package/package.json +6 -10
  344. package/dist/audit.d.ts +0 -156
  345. package/dist/audit.js +0 -7
  346. package/dist/chunk-6LZK2IZB.js +0 -1690
  347. package/dist/chunk-FYLJVF7X.js +0 -18448
  348. package/dist/chunk-JKVKYVWL.js +0 -1332
  349. package/dist/chunk-MWPMJE6P.js +0 -622
  350. package/dist/lint.d.ts +0 -70
  351. package/dist/lint.js +0 -6
  352. package/dist/spec/catalogs/audiences/customers.json +0 -27
  353. package/dist/spec/catalogs/audiences/executives.json +0 -27
  354. package/dist/spec/catalogs/audiences/investors.json +0 -27
  355. package/dist/spec/catalogs/audiences/marketing-team.json +0 -27
  356. package/dist/spec/catalogs/audiences/regulators.json +0 -27
  357. package/dist/spec/catalogs/audiences/sales-team.json +0 -27
  358. package/dist/spec/catalogs/chart-types/100pct-bullet-bar-2x.json +0 -91
  359. package/dist/spec/catalogs/chart-types/100pct-bullet-bar-3x.json +0 -103
  360. package/dist/spec/catalogs/chart-types/100pct-bullet-bar.json +0 -79
  361. package/dist/spec/catalogs/chart-types/100pct-bullet-column-2x.json +0 -91
  362. package/dist/spec/catalogs/chart-types/100pct-bullet-column-3x.json +0 -103
  363. package/dist/spec/catalogs/chart-types/100pct-bullet-column.json +0 -79
  364. package/dist/spec/catalogs/chart-types/100pct-progress-bar.json +0 -105
  365. package/dist/spec/catalogs/chart-types/100pct-stacked-area-2x.json +0 -106
  366. package/dist/spec/catalogs/chart-types/100pct-stacked-area-3x.json +0 -123
  367. package/dist/spec/catalogs/chart-types/100pct-stacked-bar-2x.json +0 -89
  368. package/dist/spec/catalogs/chart-types/100pct-stacked-bar-3x.json +0 -100
  369. package/dist/spec/catalogs/chart-types/100pct-stacked-column-2x.json +0 -95
  370. package/dist/spec/catalogs/chart-types/100pct-stacked-column-3x.json +0 -108
  371. package/dist/spec/catalogs/chart-types/australia.json +0 -76
  372. package/dist/spec/catalogs/chart-types/box-and-whisker-2x.json +0 -136
  373. package/dist/spec/catalogs/chart-types/box-and-whisker-3x.json +0 -168
  374. package/dist/spec/catalogs/chart-types/bullet-bar-2x.json +0 -89
  375. package/dist/spec/catalogs/chart-types/bullet-bar-3x.json +0 -101
  376. package/dist/spec/catalogs/chart-types/bullet-bar.json +0 -77
  377. package/dist/spec/catalogs/chart-types/bullet-column-2x.json +0 -89
  378. package/dist/spec/catalogs/chart-types/bullet-column-3x.json +0 -101
  379. package/dist/spec/catalogs/chart-types/bullet-column.json +0 -77
  380. package/dist/spec/catalogs/chart-types/canada.json +0 -86
  381. package/dist/spec/catalogs/chart-types/clustered-bar-2x.json +0 -89
  382. package/dist/spec/catalogs/chart-types/clustered-column.json +0 -95
  383. package/dist/spec/catalogs/chart-types/dot-plot-2x.json +0 -99
  384. package/dist/spec/catalogs/chart-types/dot-plot-3x.json +0 -124
  385. package/dist/spec/catalogs/chart-types/dot-plot-4x.json +0 -149
  386. package/dist/spec/catalogs/chart-types/dot-plot-5x.json +0 -174
  387. package/dist/spec/catalogs/chart-types/dot-plot-6x.json +0 -199
  388. package/dist/spec/catalogs/chart-types/dot-plot.json +0 -74
  389. package/dist/spec/catalogs/chart-types/dumbbell.json +0 -120
  390. package/dist/spec/catalogs/chart-types/line-2x.json +0 -105
  391. package/dist/spec/catalogs/chart-types/line-3x.json +0 -127
  392. package/dist/spec/catalogs/chart-types/line-with-high-low-and-markers.json +0 -144
  393. package/dist/spec/catalogs/chart-types/line-with-high-low.json +0 -144
  394. package/dist/spec/catalogs/chart-types/line-with-markers-2x.json +0 -106
  395. package/dist/spec/catalogs/chart-types/line-with-markers-3x.json +0 -128
  396. package/dist/spec/catalogs/chart-types/sparkline-2x.json +0 -138
  397. package/dist/spec/catalogs/chart-types/sparkline-3x.json +0 -174
  398. package/dist/spec/catalogs/chart-types/sparkline-4x.json +0 -210
  399. package/dist/spec/catalogs/chart-types/sparkline-5x.json +0 -246
  400. package/dist/spec/catalogs/chart-types/sparkline-6x.json +0 -282
  401. package/dist/spec/catalogs/chart-types/sparkline.json +0 -102
  402. package/dist/spec/catalogs/chart-types/stacked-area-2x.json +0 -106
  403. package/dist/spec/catalogs/chart-types/stacked-area-3x.json +0 -123
  404. package/dist/spec/catalogs/chart-types/stacked-bar-2x.json +0 -89
  405. package/dist/spec/catalogs/chart-types/stacked-bar-3x.json +0 -100
  406. package/dist/spec/catalogs/chart-types/stacked-column-2x.json +0 -95
  407. package/dist/spec/catalogs/chart-types/stacked-column-3x.json +0 -108
  408. package/dist/spec/catalogs/chart-types/stacked-line-2x.json +0 -106
  409. package/dist/spec/catalogs/chart-types/stacked-line-3x.json +0 -123
  410. package/dist/spec/catalogs/chart-types/stacked-line-with-markers-2x.json +0 -107
  411. package/dist/spec/catalogs/chart-types/stacked-line-with-markers-3x.json +0 -124
  412. package/dist/spec/catalogs/chart-types/treemap-2x.json +0 -101
  413. package/dist/spec/catalogs/chart-types/treemap-3x.json +0 -122
  414. package/dist/spec/catalogs/chart-types/united-kingdom.json +0 -68
  415. package/dist/spec/catalogs/chart-types/united-states.json +0 -82
  416. package/dist/validator-DfG9yxba.d.ts +0 -172
@@ -14,17 +14,17 @@
14
14
  },
15
15
  "name": {
16
16
  "type": "string",
17
- "description": "Display name of the presentation for GUI/TUI lists, library/search indexing, OS-level metadata, and default export filenames. This is deck identity, not slide content. Use slides[].title and slides[].subtitle for text that should appear on presented slides.",
17
+ "description": "Display name of the presentation for GUI/TUI lists, library/search indexing, OS-level metadata, and default export filenames. This is deck identity, not slide content. Use slides[].title and slides[].subtitle for text that should appear on presented slides. Also the built-in variable 'deck.name'.",
18
18
  "examples": ["Q4 2026 Business Review", "Series B Pitch — Acme", "The Future of Agentic Workflows"]
19
19
  },
20
20
  "description": {
21
21
  "type": "string",
22
- "description": "Free-form prose describing what this presentation is about. Used by agents and humans as a deck-level summary; complements purpose (the goal) and narrative (the structured storyline). Round-trips to OOXML 'docProps/core.xml' as '<dc:description>' (PowerPoint surfaces this as the 'Comments' field in File → Properties).",
22
+ "description": "Free-form prose describing what this presentation is about. Used by agents and humans as a deck-level summary; complements purpose (the goal) and narrative (the structured storyline). Round-trips to OOXML 'docProps/core.xml' as '<dc:description>' (PowerPoint surfaces this as the 'Comments' field in File → Properties). Also the built-in variable 'deck.description'.",
23
23
  "examples": ["Quarterly business review covering revenue, product, and hiring milestones for the executive team."]
24
24
  },
25
25
  "filename": {
26
26
  "type": "string",
27
- "description": "Optional base filename for exports (without extension). Engine strips a trailing .pptx, .pdf, .png, or .svg (case-insensitive) and appends the target format's extension. When omitted, the engine slugifies name when present. See /docs/opf for full validation rules.",
27
+ "description": "Optional base filename for exports (without extension). The opf CLI (render, export) and the editor strip a trailing .pptx, .pdf, .png, or .svg (case-insensitive) and append the target format's extension. When omitted, they slugify name; with no usable name the CLI uses the input file name and the editor uses 'presentation'. See /docs/opf for full validation rules.",
28
28
  "examples": ["q4-2026-business-review", "acme-series-b-pitch"]
29
29
  },
30
30
  "organization": {
@@ -36,7 +36,7 @@
36
36
  "minItems": 1
37
37
  }
38
38
  ],
39
- "description": "Organization associated with the presentation, usually the presenting company. Array form supports hosts, partners, clients, and sponsors. The primary organization (declared via Organization.role or, if no role is set, the first item) drives default branding such as cover-slide logo."
39
+ "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."
40
40
  },
41
41
  "speaker": {
42
42
  "oneOf": [
@@ -47,7 +47,7 @@
47
47
  "minItems": 1
48
48
  }
49
49
  ],
50
- "description": "Person presenting the deck. Array form supports panels and multi-speaker decks. Used for cover slides, bio slides, footers, and panel attribution."
50
+ "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."
51
51
  },
52
52
  "author": {
53
53
  "oneOf": [
@@ -58,12 +58,13 @@
58
58
  "minItems": 1
59
59
  }
60
60
  ],
61
- "description": "Optional credit for the person who authored or contributed to the deck, distinct from speaker. Array form supports multiple contributors. Round-trips to OOXML 'docProps/core.xml' as '<dc:creator>' (semicolon-joined when multiple) and seeds '<cp:lastModifiedBy>' on first write; subsequent edits in PowerPoint overwrite '<cp:lastModifiedBy>' with the editing user.",
61
+ "description": "Optional credit for the person who authored or contributed to the deck, distinct from speaker. Array form supports multiple contributors. Round-trips to OOXML 'docProps/core.xml' as '<dc:creator>' (semicolon-joined when multiple) and seeds '<cp:lastModifiedBy>' on first write; subsequent edits in PowerPoint overwrite '<cp:lastModifiedBy>' with the editing user. Also the built-in variable 'deck.author' (an array joined with ', ').",
62
62
  "examples": ["Bob Lee", ["Bob Lee", "Carla Diaz"]]
63
63
  },
64
64
  "audience": {
65
65
  "oneOf": [
66
66
  { "type": "string" },
67
+ { "$ref": "#/$defs/Audience" },
67
68
  {
68
69
  "type": "array",
69
70
  "items": {
@@ -75,12 +76,13 @@
75
76
  "minItems": 1
76
77
  }
77
78
  ],
78
- "description": "Intended audiences for the presentation. Accepts either:\n- A single string shorthand: free-form description ('Series B investors'), an audiences catalog id ('executive'), an HTTPS URL, or a 'pkg:' reference.\n- An array of string shorthands and/or inline Audience objects for custom audience metadata or catalog-backed overrides. Object form mirrors https://openpresentation.org/schema/opf-audience/v1 without '$schema'.\n\nResolution order for catalog ids and references: inline catalogs.audiences.records[] → catalogs.audiences.source → default catalog at https://www.pptx.gallery/audiences.",
79
+ "description": "Intended audiences for the presentation. Accepts any of:\n- A single string shorthand: free-form description ('Series B investors'), an audiences catalog id ('executive'), an HTTPS URL, or a 'pkg:' reference.\n- A single inline Audience object, like purpose and tone, for custom audience metadata or catalog-backed overrides. Object form mirrors https://openpresentation.org/schema/opf-audience/v1 without '$schema'.\n- An array of string shorthands and/or inline Audience objects, for more than one audience.\n\nResolution order for catalog ids and references: inline catalogs.audiences.records[] → catalogs.audiences.source → default catalog at https://www.pptx.gallery/audiences.",
79
80
  "examples": [
80
81
  "executive",
81
82
  "Biology Students and Wildlife Enthusiasts",
82
83
  ["executive"],
83
84
  ["board", "c-suite"],
85
+ { "id": "executive", "attentionBudgetMinutes": 20 },
84
86
  ["Series B investors", "https://acme.com/decks/audiences/acme-board.json"],
85
87
  [
86
88
  { "id": "executive", "attentionBudgetMinutes": 20 },
@@ -164,7 +166,7 @@
164
166
  "duration": {
165
167
  "type": "integer",
166
168
  "minimum": 1,
167
- "description": "Target presentation duration, as an integer number of minutes. Used by AI to set pace and depth, and to compare against the resolved narrative's durationRange."
169
+ "description": "Target presentation duration, as an integer number of minutes. The opf-render presenter view counts the elapsed time against it, and core validate warns when it lies outside the resolved narrative's 'duration' range. Agents can also use it to set pace and depth. Nothing else reads it: the preview and the PPTX export draw no duration."
168
170
  },
169
171
  "tags": {
170
172
  "type": "array",
@@ -178,7 +180,7 @@
178
180
  },
179
181
  "variables": {
180
182
  "$ref": "#/$defs/Variables",
181
- "description": "Optional named variables: deck colors referenced as 'var:<id>' (the original use), and typed content variables (text, number, date, image, url, list) referenced inline as '{{<id>}}' or whole as 'var:<id>'. Variables are what makes a deck a fillable template when combined with 'template': true. Color variables complement the color scheme: scheme slots and roles stay the primary vocabulary; variables cover deck-specific named colors that have no scheme slot."
183
+ "description": "Optional named variables: deck colors referenced as 'var:<id>' (the original use), and typed content variables (text, number, date, image, url, list) referenced inline as '{{<id>}}' or whole as 'var:<id>'. Variables are what makes a deck a fillable template when combined with 'template': true. Color variables complement the color scheme: scheme slots and roles stay the primary vocabulary; variables cover deck-specific named colors that have no scheme slot. Built-in variables ('{{speaker.name}}', 'var:organization.logo', 'deck.name', ...) need no declaration; see Variables."
182
184
  },
183
185
  "template": {
184
186
  "type": "boolean",
@@ -186,17 +188,13 @@
186
188
  "default": false
187
189
  },
188
190
  "narrative": {
189
- "oneOf": [
190
- { "type": "string" },
191
- { "$ref": "#/$defs/Narrative" }
192
- ],
193
- "description": "Structured storyline describing the deck's arc and beats. Resolves to the 'id' of a 'narratives' catalog record.\n\nAccepts two forms:\n- String shorthand for the common case: 'narrative = \"classic-story\"'. Accepts a bare id (lowercase kebab-case), an HTTPS URL pointing at a record file, or a 'pkg:' reference to a locally-installed package.\n- Object form for inline overrides or fully custom narratives: 'narrative = { id: \"classic-story\", beats: [...] }'. The object shape mirrors https://openpresentation.org/schema/opf-narrative/v1 (sans '$schema'), so a library record and an inline narrative are interchangeable.\n\nResolution order: inline catalogs.narratives.records[] → catalogs.narratives.source → default catalog at https://www.pptx.gallery/narratives. Unknown ids produce a validation warning, not an error.\n\nDeck-level concerns that aren't part of the storyline (audience, tone, takeaway, duration) live as siblings on the presentation root rather than inside the narrative object.",
191
+ "type": "string",
192
+ "description": "The deck's narrative plan, by reference: the 'id' of a 'narratives' catalog record, an HTTPS URL pointing at a record file, or a 'pkg:' reference to a locally-installed package. The deck holds only this pointer; the plan (the arc, its beats and what each beat must do) lives in the catalog record, which validates against https://openpresentation.org/schema/opf-narrative/v1. A custom narrative is a record in catalogs.narratives.records, referenced here by its id.\n\nSlides link themselves to beats with slides[].beat. Core validate warns about a slides[].beat id the resolved narrative does not define and about a root 'duration' outside its 'duration' range, and reports a beat that no slide references. The preview and the PPTX export draw nothing from the narrative; opf-pptx stores the reference for round-trip.\n\nResolution order: inline catalogs.narratives.records[] → catalogs.narratives.source → default catalog at https://www.pptx.gallery/narratives. Unknown ids produce a validation warning, not an error.\n\nDeck-level concerns that are not part of the storyline (audience, tone, takeaway, duration) live as siblings on the presentation root.",
194
193
  "examples": [
195
194
  "classic-story",
196
195
  "problem-solution",
197
196
  "scqa",
198
197
  "pitch-deck",
199
- { "id": "custom-pitch", "name": "Custom Pitch", "beats": [{ "id": "hook", "name": "Hook" }, { "id": "ask", "name": "Ask" }] },
200
198
  "https://acme.com/decks/narratives/founder-pitch.json",
201
199
  "pkg:@acme/decks/narratives/founder-pitch"
202
200
  ]
@@ -210,9 +208,13 @@
210
208
  "references": {
211
209
  "type": "array",
212
210
  "items": { "$ref": "#/$defs/Reference" },
213
- "description": "Sources that text runs cite with 'cite'. Ids are unique. A cited reference is listed in the footnote area of every slide that cites it, with a marker number assigned per deck in order of first use; a reference no run cites is a lint warning (opf/unused-reference). referencesSlide() turns the cited references into an ordinary list slide.",
211
+ "description": "Sources that text runs cite with 'cite'. Ids are unique. A cited reference is listed in the footnote area of every slide that cites it, with a marker number assigned per deck in order of first use; a reference no run cites is the warning opf/unused-reference. referencesSlide() turns the cited references into an ordinary list slide.",
214
212
  "examples": [[{ "id": "gartner-2026", "text": "Gartner, Market Guide for Presentation Tooling, 2026", "url": "https://www.gartner.com" }]]
215
213
  },
214
+ "datasets": {
215
+ "$ref": "#/$defs/Datasets",
216
+ "description": "Optional shared data tables, keyed by id. A chart ('chart.data': { \"dataset\": \"<id>\" }) or a table ('table': { \"dataset\": \"<id>\" }) references one instead of holding its own copy; engines inline the reference before composing, rendering or exporting. A dataset nothing references is the warning opf/unused-dataset. See docs/chart-table-data.md."
217
+ },
216
218
  "assets": {
217
219
  "$ref": "#/$defs/Assets",
218
220
  "description": "Optional reusable asset registry for images, data files, videos, documents, fonts, and other resources referenced elsewhere in the deck via 'asset:<id>' strings."
@@ -238,12 +240,12 @@
238
240
  {
239
241
  "acme-logo": "./assets/acme-logo.svg",
240
242
  "watermark": { "src": "./assets/watermark.png", "alt": "Acme watermark" },
241
- "revenue": { "src": "./data/revenue.csv", "format": "csv" }
243
+ "revenue": { "src": "./data/revenue.csv", "mediaType": "text/csv" }
242
244
  }
243
245
  ]
244
246
  },
245
247
  "Asset": {
246
- "description": "Reusable or inline resource. A string is shorthand for { \"src\": value }. Source strings accept 'asset:<id>' references, HTTPS URLs, data URIs, relative paths resolved against the OPF file location, or local filesystem paths. Use object form when metadata such as alt text, title, mediaType, or format matters.",
248
+ "description": "Reusable or inline resource. A string is shorthand for { \"src\": value }. Source strings accept 'asset:<id>' references, HTTPS URLs, data URIs, relative paths resolved against the OPF file location, or local filesystem paths. Use object form when metadata such as alt text, title or mediaType matters.",
247
249
  "oneOf": [
248
250
  {
249
251
  "type": "string",
@@ -275,11 +277,6 @@
275
277
  "type": "string",
276
278
  "description": "Optional MIME media type when it cannot be inferred from src.",
277
279
  "examples": ["image/svg+xml", "image/png", "text/csv", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
278
- },
279
- "format": {
280
- "type": "string",
281
- "description": "Optional authoring/parsing format hint. Useful when format is more convenient than a full MIME media type.",
282
- "examples": ["csv", "tsv", "json", "xlsx", "svg", "png", "jpg", "webp"]
283
280
  }
284
281
  }
285
282
  }
@@ -522,28 +519,28 @@
522
519
  },
523
520
  "Organization": {
524
521
  "type": "object",
525
- "description": "An organization associated with the presentation — typically the presenting company, but also hosts, partners, clients, or sponsors. Surfaced on cover slides, footers, and brand bars; the primary organization's logo is the default deck logo unless overridden by design.logo.",
522
+ "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.",
526
523
  "required": ["id", "name"],
527
524
  "properties": {
528
525
  "id": {
529
526
  "type": "string",
530
527
  "pattern": "^[a-zA-Z0-9_-]+$",
531
- "description": "Stable identifier for the organization, used to reference it from Speaker.organizationId. Must be unique within the deck.",
528
+ "description": "Stable identifier for the organization, used to reference it from Speaker.organizationId and to address it in built-in variables as 'organization.<id>.<field>'. Must be unique among organizations (a duplicate is a validation error).",
532
529
  "examples": ["acme", "acme-corp", "devconf-2026"]
533
530
  },
534
531
  "name": {
535
532
  "type": "string",
536
- "description": "Display name shown on slides.",
533
+ "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.",
537
534
  "examples": ["Acme Corp", "DevConf 2026", "Beta Industries"]
538
535
  },
539
536
  "legalName": {
540
537
  "type": "string",
541
- "description": "Optional legal entity name when it differs from the display name.",
538
+ "description": "Optional legal entity name when it differs from the display name. Never drawn automatically; available as the built-in variable 'organization.legalName'.",
542
539
  "examples": ["Acme Corporation, Inc.", "Beta Industries, LLC"]
543
540
  },
544
541
  "logo": {
545
542
  "$ref": "#/$defs/Asset",
546
- "description": "Source for the organization's logo image. Accepts an HTTPS URL, data URI, relative path (resolved against the OPF file location), local path, or 'asset:<id>' reference. Common formats are SVG (preferred for vector logos), PNG (with transparency), or JPG. The primary organization's logo (role 'primary', else the first organization) is the fallback source wherever the deck logo is drawn: a slide's design.logo, then design.logo, then this value. It is therefore drawn on cover and section slides and by header/footer zones with logo: true, exactly as design.logo would be (see design.logo and docs/design-resolution.md).",
543
+ "description": "Source for the organization's logo image. Accepts an HTTPS URL, data URI, relative path (resolved against the OPF file location), local path, or 'asset:<id>' reference. Common formats are SVG (preferred for vector logos), PNG (with transparency), or JPG. The primary organization's logo (role 'primary', else the first organization) is the fallback source wherever the deck logo is drawn: a slide's design.logo, then design.logo, then this value. It is therefore drawn on cover and section slides and by header/footer zones with logo: true, exactly as design.logo would be (see design.logo and docs/design-resolution.md). It is also the built-in image variable 'organization.logo' (or 'organization.<id>.logo'), placed with 'var:organization.logo' in an image field.",
547
544
  "examples": [
548
545
  "https://cdn.acme.com/logo.svg",
549
546
  "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9V3iWggAAAAASUVORK5CYII=",
@@ -553,29 +550,29 @@
553
550
  },
554
551
  "domain": {
555
552
  "type": "string",
556
- "description": "Bare internet domain for the organization. Used for footers, contact slides, and engine-driven asset lookups (e.g., favicon-based brand defaults).",
553
+ "description": "Bare internet domain for the organization. Never drawn automatically and not used to look up assets; available as the built-in variable 'organization.domain'.",
557
554
  "examples": ["acme.com", "beta-industries.io", "devconf.org"]
558
555
  },
559
556
  "email": {
560
557
  "type": "string",
561
558
  "format": "email",
562
- "description": "General contact email for the organization. Used on contact slides and footer attribution.",
559
+ "description": "General contact email for the organization. Never drawn automatically; available as the built-in variable 'organization.email'.",
563
560
  "examples": ["hello@acme.com", "sales@acme.com", "info@beta-industries.io"]
564
561
  },
565
562
  "phone": {
566
563
  "type": "string",
567
- "description": "Main contact phone number for the organization. E.164 format is recommended.",
564
+ "description": "Main contact phone number for the organization. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'organization.phone'.",
568
565
  "examples": ["+14155551234", "+442071838750"]
569
566
  },
570
567
  "tagline": {
571
568
  "type": "string",
572
- "description": "Short tagline rendered alongside the organization name on cover slides.",
569
+ "description": "Short tagline. Never drawn automatically (cover slides do not show it); available as the built-in variable 'organization.tagline', for example in a footer 'text' of '{{organization.tagline}}'.",
573
570
  "examples": ["Build the future of work", "Infrastructure for AI agents", "Where teams ship faster"]
574
571
  },
575
572
  "role": {
576
573
  "type": "string",
577
574
  "enum": ["primary", "partner", "client", "sponsor", "host"],
578
- "description": "Role of the organization relative to the presentation. When omitted, the single organization or first organization in array form is treated as primary."
575
+ "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."
579
576
  },
580
577
  "socials": {
581
578
  "$ref": "#/$defs/Socials",
@@ -585,28 +582,28 @@
585
582
  },
586
583
  "Speaker": {
587
584
  "type": "object",
588
- "description": "A person presenting the deck. Used for cover slides, bio/intro slides, footer attribution, and panel formats with multiple presenters.",
585
+ "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.",
589
586
  "required": ["id", "name"],
590
587
  "properties": {
591
588
  "id": {
592
589
  "type": "string",
593
590
  "pattern": "^[a-zA-Z0-9_-]+$",
594
- "description": "Stable identifier for the speaker, used for cross-references within the deck. Must be unique within the deck.",
591
+ "description": "Stable identifier for the speaker. Must be unique among speakers (a duplicate is a validation error). Addresses this speaker in built-in variables as 'speaker.<id>.<field>', such as '{{speaker.alice.title}}'.",
595
592
  "examples": ["alice", "alice-chen", "speaker-1"]
596
593
  },
597
594
  "name": {
598
595
  "type": "string",
599
- "description": "Display name.",
596
+ "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'.",
600
597
  "examples": ["Alice Chen", "Bob Lee", "Dr. Carla Diaz"]
601
598
  },
602
599
  "title": {
603
600
  "type": "string",
604
- "description": "Role or title. Often paired with the speaker's organization on cover slides.",
601
+ "description": "Role or title. Built-in variable 'speaker.title'; the 'speaker' header/footer field draws it after the name ('Alice Chen, VP of Engineering').",
605
602
  "examples": ["VP of Engineering", "Founder & CEO", "Chief Product Officer", "Senior Staff Engineer"]
606
603
  },
607
604
  "photo": {
608
605
  "$ref": "#/$defs/Asset",
609
- "description": "Source for the speaker's headshot image. Accepts an HTTPS URL, data URI, relative path (resolved against the OPF file location), local path, or 'asset:<id>' reference. Common formats are JPG or PNG; SVG is not appropriate for photographic content. Authoring metadata for hosts and layouts (cover and bio slides built by a host): the reference renderer and exporter do not draw it, because no slide field or layout slot places a speaker; it round-trips through PPTX provenance.",
606
+ "description": "Source for the speaker's headshot image. Accepts an HTTPS URL, data URI, relative path (resolved against the OPF file location), local path, or 'asset:<id>' reference. Common formats are JPG or PNG; SVG is not appropriate for photographic content. Never drawn automatically: it is the built-in image variable 'speaker.photo' (or 'speaker.<id>.photo'), placed with 'var:speaker.photo' in a slide's image field. It also round-trips through PPTX provenance.",
610
607
  "examples": [
611
608
  "https://cdn.acme.com/headshots/alice.jpg",
612
609
  "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AJ//2Q==",
@@ -617,29 +614,29 @@
617
614
  "email": {
618
615
  "type": "string",
619
616
  "format": "email",
620
- "description": "Contact email, used on contact slides or footer attribution when appropriate.",
617
+ "description": "Contact email. Never drawn automatically; available as the built-in variable 'speaker.email'.",
621
618
  "examples": ["alice@acme.com", "alice.chen@acme.com"]
622
619
  },
623
620
  "phone": {
624
621
  "type": "string",
625
- "description": "Contact phone number for the speaker. E.164 format is recommended.",
622
+ "description": "Contact phone number for the speaker. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'speaker.phone'.",
626
623
  "examples": ["+14155551234", "+442071838750"]
627
624
  },
628
625
  "bio": {
629
626
  "type": "string",
630
- "description": "Short biographical paragraph for bio or 'about the speaker' slides.",
627
+ "description": "Short biographical paragraph. Never drawn automatically; available as the built-in variable 'speaker.bio', for a bio or 'about the speaker' slide you write.",
631
628
  "examples": [
632
629
  "Alice leads engineering at Acme, where she's spent the last six years scaling distributed systems from prototype to billions of requests per day. Previously, she was a staff engineer at Google."
633
630
  ]
634
631
  },
635
632
  "organizationId": {
636
633
  "type": "string",
637
- "description": "Reference to an Organization.id in organization. Lets a speaker be attributed to their org in panel or multi-org decks without repeating organization details.",
634
+ "description": "Reference to an Organization.id in organization; it must name an existing organization (a validation error otherwise). Attribution metadata for hosts: no engine draws it or changes which organization is primary because of it.",
638
635
  "examples": ["acme", "beta", "devconf-2026"]
639
636
  },
640
637
  "socials": {
641
638
  "$ref": "#/$defs/Socials",
642
- "description": "Optional social media handles or URLs for the speaker. Authoring metadata: no header/footer field renders speaker socials yet."
639
+ "description": "Optional social media handles or URLs for the speaker. Authoring metadata only: nothing draws speaker socials and there is no built-in variable for them (the 'socials' header/footer field draws the primary organization's socials)."
643
640
  }
644
641
  }
645
642
  },
@@ -667,137 +664,6 @@
667
664
  "description": "Platform id (lowercase kebab-case). Common ids include 'linkedin', 'x', 'github', 'youtube', 'instagram', 'facebook', 'tiktok', 'threads', 'mastodon', 'bluesky'."
668
665
  }
669
666
  },
670
- "Narrative": {
671
- "type": "object",
672
- "description": "Structured storyline used by AI to shape generated content. Mirrors the OPF Narrative Template record at https://openpresentation.org/schema/opf-narrative/v1 (sans '$schema'), so a library record and an inline narrative are interchangeable.\n\nNarrative declares the deck's intended story arc; slides may opt into beats via Slide.beat. The narrative does not constrain slide structure — validators warn on drift (orphan slides, unused beats) but never error. Slides are the source of truth; narrative is intent that travels with the deck.\n\nWhen 'id' matches a record in the resolved 'narratives' catalog, the inline fields override matching fields on the catalog record (beats merge by beat 'id'). When 'id' doesn't match, this object defines a fully custom inline narrative.\n\nDeck-level concerns that aren't part of the storyline (audience, tone, takeaway, duration) live as siblings on the presentation root rather than inside this object.",
673
- "properties": {
674
- "id": {
675
- "type": "string",
676
- "pattern": "^[a-z][a-z0-9-]*$",
677
- "description": "Stable slug identifying this narrative. When it matches a record in the resolved 'narratives' catalog, the catalog record's beats and metadata seed this narrative; inline fields override per-key. When it doesn't match, this is a fully custom inline narrative. Lowercase kebab-case.",
678
- "examples": ["classic-story", "problem-solution", "scqa", "pitch-deck", "qbr", "weekly-progress", "custom-pitch"]
679
- },
680
- "name": {
681
- "type": "string",
682
- "description": "Human-readable narrative name.",
683
- "examples": ["Classic Story", "Problem → Solution", "SCQA", "Founder Pitch"]
684
- },
685
- "summary": {
686
- "type": "string",
687
- "description": "One-sentence description of when and why to use this narrative."
688
- },
689
- "description": {
690
- "type": "string",
691
- "description": "Longer prose describing the narrative arc and ideal use cases. Used by AI-driven generation to seed deck-level direction.",
692
- "examples": [
693
- "Open with the cost of slow agentic workflows, contrast with what becomes possible at sub-second latency, then walk through our architecture and benchmark results, ending with a concrete adoption ask."
694
- ]
695
- },
696
- "audienceFit": {
697
- "type": "array",
698
- "items": { "type": "string" },
699
- "description": "Audiences this narrative works well for. Free-form strings or 'audiences' catalog ids.",
700
- "examples": [["executive", "investor", "customer"]]
701
- },
702
- "durationRange": {
703
- "type": "object",
704
- "description": "Typical talk-length window this narrative suits. Compared by validators against duration.",
705
- "properties": {
706
- "minMinutes": {
707
- "type": "number",
708
- "exclusiveMinimum": 0
709
- },
710
- "maxMinutes": {
711
- "type": "number",
712
- "exclusiveMinimum": 0
713
- }
714
- }
715
- },
716
- "tags": {
717
- "type": "array",
718
- "items": { "type": "string" },
719
- "description": "Free-form labels for filtering and search.",
720
- "examples": [["business", "pitch", "internal"]]
721
- },
722
- "preview": {
723
- "type": "object",
724
- "description": "Visual previews of the narrative, used by picker UIs and inline rendering. All sub-fields are optional.",
725
- "properties": {
726
- "src": {
727
- "type": "string",
728
- "format": "uri",
729
- "description": "Main preview image (PNG/JPG)."
730
- },
731
- "thumbnailSrc": {
732
- "type": "string",
733
- "format": "uri",
734
- "description": "Smaller thumbnail preview for dense grid views."
735
- },
736
- "vectorSrc": {
737
- "type": "string",
738
- "format": "uri",
739
- "description": "SVG / vector preview for crisp scaling at any size."
740
- }
741
- }
742
- },
743
- "beats": {
744
- "type": "array",
745
- "items": { "$ref": "#/$defs/NarrativeBeat" },
746
- "description": "Ordered list of beats that make up the narrative arc. When 'id' matches a catalog record, beats here override or extend matching catalog beats by their own 'id'. Beat IDs must be unique within the narrative."
747
- }
748
- }
749
- },
750
- "NarrativeBeat": {
751
- "type": "object",
752
- "description": "A single narrative beat — a labeled segment of the story arc with a specific dramatic purpose (e.g. 'hook', 'problem', 'evidence', 'ask'). Slides reference beats via Slide.beat. Beats may also carry slide-blueprint hints (slideType, layoutHint, thoughtCues, instructions) that guide the assigned slide.\n\nMirrors the Beat definition in narrative.schema.json (https://openpresentation.org/schema/opf-narrative/v1) so library entries and inline OPF beats are interchangeable.",
753
- "required": ["id", "name"],
754
- "properties": {
755
- "id": {
756
- "type": "string",
757
- "pattern": "^[a-z][a-z0-9-]*$",
758
- "description": "Stable slug used by Slide.beat to reference this beat. Lowercase kebab-case.",
759
- "examples": ["hook", "problem", "evidence", "ask", "next-steps"]
760
- },
761
- "name": {
762
- "type": "string",
763
- "description": "Human-readable beat name.",
764
- "examples": ["The Hook", "The Problem", "Why Now", "The Ask"]
765
- },
766
- "description": {
767
- "type": "string",
768
- "description": "Curator-written prose that explains what this beat should accomplish.",
769
- "examples": [
770
- "Quantify the pain customers feel today, with one striking stat the audience can repeat afterward."
771
- ]
772
- },
773
- "instructions": {
774
- "type": "string",
775
- "description": "Short author-facing instruction for the beat — typically one phrase. Complements 'description' with a concise directive.",
776
- "examples": ["Capture audience attention", "Introduce problem", "Detail implementation", "Inspire & conclude"]
777
- },
778
- "slideCount": {
779
- "type": "integer",
780
- "minimum": 1,
781
- "description": "Optional explicit slide count for this beat. Defaults to 1 when omitted; values >1 are reserved for beats that intentionally span multiple slides. Prefer decomposing a heavy beat into multiple beats over setting a high slideCount. The validator emits a warning if the deck's actual count differs significantly."
782
- },
783
- "slideType": {
784
- "type": "string",
785
- "enum": ["text", "list", "image", "chart", "table", "video", "code", "metric", "quote", "timeline"],
786
- "description": "Default content kind for the beat's slide. Mirrors ContentPayload.type and helps engines choose a sensible layout when only the beat is specified."
787
- },
788
- "layoutHint": {
789
- "type": "string",
790
- "description": "Suggested layout id for the beat's opening slide. Resolves the same way as Slide.layout — against catalogs.layouts and the default catalog at https://www.pptx.gallery/layouts.",
791
- "examples": ["section-divider", "title-slide", "title-left", "two-column", "text-left"]
792
- },
793
- "thoughtCues": {
794
- "type": "array",
795
- "items": { "type": "string" },
796
- "description": "Optional speaker or thinking cues attached to the beat. Surfaced in presenter notes.",
797
- "examples": [["What pain is the audience feeling right now?", "Why hasn't anyone solved this yet?"]]
798
- }
799
- }
800
- },
801
667
  "Design": {
802
668
  "type": "object",
803
669
  "description": "Visual design system applied to the presentation; individual slides may override fields via Slide.design.",
@@ -842,7 +708,7 @@
842
708
  "examples": [
843
709
  "aptos",
844
710
  "tenorite",
845
- { "id": "aptos", "heading": { "family": "Inter", "weight": 700 } }
711
+ { "id": "aptos", "heading": "Inter" }
846
712
  ]
847
713
  },
848
714
  "dimensions": {
@@ -878,8 +744,8 @@
878
744
  { "$ref": "#/$defs/Asset" },
879
745
  { "$ref": "#/$defs/Watermark" }
880
746
  ],
881
- "description": "Optional decorative watermark applied across slides. Use false to suppress an inherited watermark in slide-level design; a string is equivalent to { src: value }.",
882
- "examples": [false, "asset:watermark", { "src": "asset:watermark", "opacity": 0.08 }]
747
+ "description": "Optional decorative watermark applied across slides: an image or a text stamp, in the fixed frame and at the opacity described on Watermark. Use false to suppress an inherited watermark in slide-level design; a string or Asset is the image at opacity 0.08.",
748
+ "examples": [false, "asset:watermark", { "src": "asset:watermark", "opacity": 0.08 }, { "text": "DRAFT", "opacity": 0.1 }]
883
749
  },
884
750
  "header": {
885
751
  "oneOf": [
@@ -903,16 +769,16 @@
903
769
  "titleAlignment": {
904
770
  "type": "string",
905
771
  "enum": ["left", "center", "right"],
906
- "description": "Default horizontal alignment for title placeholders in resolved layouts."
772
+ "description": "Default horizontal alignment for title placeholders in resolved layouts. Effective value: the slide's design, then the deck's design, then the layout record's design.titleAlignment, then the engine default."
907
773
  },
908
774
  "contentAlignment": {
909
775
  "type": "string",
910
776
  "enum": ["left", "center", "right"],
911
- "description": "Default horizontal alignment for body/content regions in resolved layouts."
777
+ "description": "Default horizontal alignment for body/content regions in resolved layouts. Effective value: the slide's design, then the deck's design, then the layout record's design.contentAlignment, then the engine default. A cover's tag and subtitle follow titleAlignment unless the slide's own design sets contentAlignment."
912
778
  },
913
779
  "contentBox": {
914
780
  "type": "boolean",
915
- "description": "Whether body/content regions are rendered inside a visible card or surface."
781
+ "description": "Whether body/content regions are rendered inside a visible card or surface. Effective value: the slide's design, then the deck's design, then the layout record's design.contentBox, then the engine default."
916
782
  },
917
783
  "slideImage": {
918
784
  "oneOf": [
@@ -1014,27 +880,27 @@
1014
880
  }
1015
881
  }
1016
882
  ],
1017
- "description": "Optional slide-level image, separate from content images. It applies to a slide that sets its own design.slideImage, and to slides whose layout declares slideImage: true or whose root image is the same source as a deck-level value. A root image with the same source becomes the slide image instead of a content item. Blur, shadows, soft edges, background removal and device artwork are not part of the treatment vocabulary; see docs/image-treatments.md."
883
+ "description": "Optional slide-level image, separate from content images. It applies to a slide that sets its own design.slideImage, and to slides whose layout record sets design.slideImage or whose root image is the same source as a deck-level value. A root image with the same source becomes the slide image instead of a content item. Blur, shadows, soft edges, background removal and device artwork are not part of the treatment vocabulary; see docs/image-treatments.md."
1018
884
  },
1019
885
  "contentDirection": {
1020
886
  "type": "string",
1021
887
  "enum": ["horizontal", "vertical"],
1022
- "description": "Axis along which parallel body content is arranged. Sets the root arrangement mode of blocks and root payloads when no composition.mode is set on the slide or on its layout record: 'vertical' is column, 'horizontal' is row. Precedence: composition.mode (the slide's own, else the layout record's geometry contract), then this value (slide design, then deck design), then the layout record's slideLayoutDirection, then automatic selection. A layout that carries a composition.mode keeps its grid or direction whatever this value says. Promoted regions (left, top:left, ...) keep their explicit geometry and nested groups keep their own composition."
888
+ "description": "Axis along which parallel body content is arranged. Sets the root arrangement mode of blocks and root payloads when no composition.mode is set on the slide or on its layout record: 'vertical' is column, 'horizontal' is row. Precedence: composition.mode (the slide's own, else the layout record's geometry contract), then the effective value (slide design, then deck design, then the layout record's design.contentDirection), then automatic selection. A layout that carries a composition.mode keeps its grid or direction whatever this value says. Promoted regions (left, top:left, ...) keep their explicit geometry and nested groups keep their own composition."
1023
889
  },
1024
890
  "chartPrimary": {
1025
891
  "type": "string",
1026
892
  "enum": ["none", "top", "bottom", "left", "right"],
1027
- "description": "Where the primary chart sits relative to supporting content. Effective value: slide design, then deck design, then the layout record's contentTypeChartPrimary. 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."
893
+ "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."
1028
894
  },
1029
895
  "imageFill": {
1030
896
  "type": "string",
1031
897
  "enum": ["crop", "fit"],
1032
- "description": "How picture placeholders fill their allocated region."
898
+ "description": "How images fill their allocated region: crop covers the region, fit shows the whole image. Effective value: the slide's design, then the deck's design, then the layout record's design.imageFill, then the engine default."
1033
899
  },
1034
900
  "listBullet": {
1035
901
  "type": "string",
1036
902
  "enum": ["character", "image"],
1037
- "description": "Marker style for items and bullets lists. 'character' (the default) draws the glyph marker. 'image' draws the deck's icon logo (a slide's design.logo, then design.logo, then the primary organization's logo; light variants on dark backgrounds) as a picture bullet: a square of the marker's font size whose bottom sits on the marker baseline, so list geometry does not change. Without a logo the glyph is drawn and the engine reports unresolved-content at this field."
903
+ "description": "Marker style for items and bullets lists. Effective value: the slide's design, then the deck's design, then the layout record's design.listBullet, then 'character'. 'character' (the default) draws the glyph marker. 'image' draws the deck's icon logo (a slide's design.logo, then design.logo, then the primary organization's logo; light variants on dark backgrounds) as a picture bullet: a square of the marker's font size whose bottom sits on the marker baseline, so list geometry does not change. Without a logo the glyph is drawn and the engine reports unresolved-content at this field."
1038
904
  }
1039
905
  }
1040
906
  },
@@ -1074,7 +940,7 @@
1074
940
  { "$ref": "#/$defs/FontScheme" }
1075
941
  ],
1076
942
  "description": "Default font scheme for this theme. A string resolves against catalogs.fontSchemes; an object may provide an 'id' base reference plus overrides.",
1077
- "examples": ["aptos", { "id": "aptos", "heading": { "family": "Inter", "weight": 700 } }]
943
+ "examples": ["aptos", { "id": "aptos", "heading": "Inter" }]
1078
944
  },
1079
945
  "background": {
1080
946
  "oneOf": [
@@ -1101,7 +967,7 @@
1101
967
  },
1102
968
  "ColorScheme": {
1103
969
  "type": "object",
1104
- "description": "Color palette used by the design system. The slot fields (accent1-accent6, dark1, dark2, light1, light2, hyperlink, followedHyperlink) mirror color-scheme.schema.json (https://openpresentation.org/schema/opf-color-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields.\n\nTwo parallel models are supported and may be mixed:\n- OOXML slots - the 12-slot PowerPoint theme model that round-trips directly to OOXML. Use these for full control over the palette as PowerPoint sees it.\n- Abstract roles (primary, secondary, accent, background, surface, text, textSecondary, custom) - OPF-specific semantic aliases the engine maps onto the OOXML slots. Convenient for inline overrides without thinking in slot numbers; not part of the catalog record schema.\n\nIn design.colorScheme, 'id' resolves a colorSchemes catalog record as the base; slot and role overrides on the same object take precedence over the resolved scheme. The string shorthand on design.colorScheme is equivalent to setting only 'id'.",
970
+ "description": "Color palette used by the design system. Every slot and role is a hex color ('#RGB', '#RRGGBB' or '#RRGGBBAA'); a color-scheme name or a 'var:<id>' reference is not valid here, because those resolve through the scheme. The slot fields (accent1-accent6, dark1, dark2, light1, light2, hyperlink, followedHyperlink) mirror color-scheme.schema.json (https://openpresentation.org/schema/opf-color-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields.\n\nTwo parallel models are supported and may be mixed:\n- OOXML slots - the 12-slot PowerPoint theme model that round-trips directly to OOXML. Use these for full control over the palette as PowerPoint sees it.\n- Abstract roles (primary, secondary, accent, background, surface, text, textSecondary) - OPF-specific semantic aliases the engine maps onto the OOXML slots. Convenient for inline overrides without thinking in slot numbers; not part of the catalog record schema.\n\nIn design.colorScheme, 'id' resolves a colorSchemes catalog record as the base; slot and role overrides on the same object take precedence over the resolved scheme. The string shorthand on design.colorScheme is equivalent to setting only 'id'.",
1105
971
  "properties": {
1106
972
  "id": {
1107
973
  "type": "string",
@@ -1109,111 +975,105 @@
1109
975
  "examples": ["cool-horizon", "boost", "burnt-orange", "ocean-depth", "corporate-blue", "https://acme.com/decks/color-schemes/acme.json", "pkg:@acme/decks/color-schemes/acme"]
1110
976
  },
1111
977
  "accent1": {
1112
- "type": "string",
978
+ "$ref": "#/$defs/HexColor",
1113
979
  "description": "Accent 1 color (hex). Mirrors the OOXML accent1 slot.",
1114
980
  "examples": ["#2874A6", "#FD3223", "#F77F00"]
1115
981
  },
1116
982
  "accent2": {
1117
- "type": "string",
983
+ "$ref": "#/$defs/HexColor",
1118
984
  "description": "Accent 2 color (hex). Mirrors the OOXML accent2 slot.",
1119
985
  "examples": ["#1B4F72", "#0308DB", "#D62828"]
1120
986
  },
1121
987
  "accent3": {
1122
- "type": "string",
988
+ "$ref": "#/$defs/HexColor",
1123
989
  "description": "Accent 3 color (hex). Mirrors the OOXML accent3 slot.",
1124
990
  "examples": ["#5499C7", "#4F4955", "#003049"]
1125
991
  },
1126
992
  "accent4": {
1127
- "type": "string",
993
+ "$ref": "#/$defs/HexColor",
1128
994
  "description": "Accent 4 color (hex). Mirrors the OOXML accent4 slot.",
1129
995
  "examples": ["#7BDBB2", "#A1DB30", "#FCBF49"]
1130
996
  },
1131
997
  "accent5": {
1132
- "type": "string",
998
+ "$ref": "#/$defs/HexColor",
1133
999
  "description": "Accent 5 color (hex). Mirrors the OOXML accent5 slot.",
1134
1000
  "examples": ["#3AC67A", "#0682FE", "#EAE2B7"]
1135
1001
  },
1136
1002
  "accent6": {
1137
- "type": "string",
1003
+ "$ref": "#/$defs/HexColor",
1138
1004
  "description": "Accent 6 color (hex). Mirrors the OOXML accent6 slot.",
1139
1005
  "examples": ["#24A89E", "#FC03BE", "#BFBFBF"]
1140
1006
  },
1141
1007
  "dark1": {
1142
- "type": "string",
1008
+ "$ref": "#/$defs/HexColor",
1143
1009
  "description": "Dark 1 color (hex). Typically the deepest neutral; OOXML dark1.",
1144
1010
  "examples": ["#000000"]
1145
1011
  },
1146
1012
  "dark2": {
1147
- "type": "string",
1013
+ "$ref": "#/$defs/HexColor",
1148
1014
  "description": "Dark 2 color (hex). Secondary dark; OOXML dark2.",
1149
1015
  "examples": ["#011842", "#2C2C2C"]
1150
1016
  },
1151
1017
  "light1": {
1152
- "type": "string",
1018
+ "$ref": "#/$defs/HexColor",
1153
1019
  "description": "Light 1 color (hex). Typically the slide canvas; OOXML lt1.",
1154
1020
  "examples": ["#FFFFFF"]
1155
1021
  },
1156
1022
  "light2": {
1157
- "type": "string",
1023
+ "$ref": "#/$defs/HexColor",
1158
1024
  "description": "Light 2 color (hex). Secondary light surface; OOXML lt2.",
1159
1025
  "examples": ["#F0F0F0", "#F3F7FF"]
1160
1026
  },
1161
1027
  "hyperlink": {
1162
- "type": "string",
1163
- "description": "Hyperlink color (hex). OOXML hlink.",
1028
+ "$ref": "#/$defs/HexColor",
1029
+ "description": "Hyperlink color (hex). OOXML hlink. Link runs with no color of their own are drawn underlined in it, in the preview and in PowerPoint, unless it has under 4.5:1 contrast against the slide background, then in the slide text color.",
1164
1030
  "examples": ["#0563C1", "#0066CC"]
1165
1031
  },
1166
1032
  "followedHyperlink": {
1167
- "type": "string",
1033
+ "$ref": "#/$defs/HexColor",
1168
1034
  "description": "Followed-hyperlink color (hex). OOXML folHlink.",
1169
1035
  "examples": ["#954F72", "#551A8B"]
1170
1036
  },
1171
1037
  "primary": {
1172
- "type": "string",
1038
+ "$ref": "#/$defs/HexColor",
1173
1039
  "description": "Abstract role: primary brand color (hex). The engine maps this onto an OOXML accent slot when serializing.",
1174
1040
  "examples": ["#1E40AF", "#0F172A"]
1175
1041
  },
1176
1042
  "secondary": {
1177
- "type": "string",
1043
+ "$ref": "#/$defs/HexColor",
1178
1044
  "description": "Abstract role: secondary brand color (hex).",
1179
1045
  "examples": ["#3B82F6", "#64748B"]
1180
1046
  },
1181
1047
  "accent": {
1182
- "type": "string",
1048
+ "$ref": "#/$defs/HexColor",
1183
1049
  "description": "Abstract role: accent color used for highlights and emphasis (hex).",
1184
1050
  "examples": ["#F59E0B", "#22D3EE"]
1185
1051
  },
1186
1052
  "background": {
1187
- "type": "string",
1188
- "description": "Abstract role: default slide background color (hex). The engine maps this to one of light1 / light2 / dark1 / dark2 when serializing.",
1053
+ "$ref": "#/$defs/HexColor",
1054
+ "description": "Abstract role: default slide background color (hex), used when the design names no single-color background (and for gradient and picture backgrounds). Overrides light1 as that default.",
1189
1055
  "examples": ["#FFFFFF", "#0B1220"]
1190
1056
  },
1191
1057
  "surface": {
1192
- "type": "string",
1193
- "description": "Abstract role: color for elevated surfaces such as cards and panels (hex).",
1058
+ "$ref": "#/$defs/HexColor",
1059
+ "description": "Abstract role: color for elevated surfaces such as cards and panels (hex). Overrides the default of light2 (dark2 on a dark slide).",
1194
1060
  "examples": ["#F8FAFC", "#1E293B"]
1195
1061
  },
1196
1062
  "text": {
1197
- "type": "string",
1198
- "description": "Abstract role: primary body text color (hex).",
1063
+ "$ref": "#/$defs/HexColor",
1064
+ "description": "Abstract role: primary body text color (hex). Overrides dark1 on a light slide only; a dark slide always uses light1.",
1199
1065
  "examples": ["#0F172A", "#F8FAFC"]
1200
1066
  },
1201
1067
  "textSecondary": {
1202
- "type": "string",
1203
- "description": "Abstract role: secondary or muted text color used for captions and supporting copy (hex).",
1068
+ "$ref": "#/$defs/HexColor",
1069
+ "description": "Abstract role: secondary or muted text color used for captions and supporting copy (hex). Overrides the default of dark2 (light2 on a dark slide).",
1204
1070
  "examples": ["#475569", "#94A3B8"]
1205
- },
1206
- "custom": {
1207
- "type": "object",
1208
- "additionalProperties": { "type": "string" },
1209
- "description": "Map of custom named colors for advanced or theme-specific use.",
1210
- "examples": [{ "success": "#10B981", "warning": "#F59E0B", "danger": "#EF4444" }]
1211
1071
  }
1212
1072
  }
1213
1073
  },
1214
1074
  "FontScheme": {
1215
1075
  "type": "object",
1216
- "description": "Typography selections used by the design system. The pair fields (major, minor) and refinement fields (type, app, languageFamily) mirror font-scheme.schema.json (https://openpresentation.org/schema/opf-font-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields.\n\nTwo parallel models are supported and may be mixed:\n- OOXML pairs (major, minor) - heading and body family names that round-trip directly to PowerPoint majorFont/minorFont entries.\n- Abstract roles (heading, body, accent, code) - OPF-specific Font objects the engine maps onto the OOXML pair when serializing. Convenient for inline overrides and for adding accent/code roles that don't have a direct OOXML slot. Catalog records may also carry 'code' (for example the consolas and courier-new records); heading, body and accent are not part of the catalog record schema.\n\nIn design.fontScheme, 'id' resolves a fontSchemes catalog record as the base; pair and role overrides on the same object take precedence over the resolved scheme. The string shorthand on design.fontScheme is equivalent to setting only 'id'.",
1076
+ "description": "Typography selections used by the design system. The pair fields (major, minor) and refinement fields (type, app, languageFamily) mirror font-scheme.schema.json (https://openpresentation.org/schema/opf-font-scheme/v1) so library records and inline OPF overrides are interchangeable on those fields.\n\nTwo parallel models are supported and may be mixed:\n- OOXML pairs (major, minor) - heading and body family names that round-trip directly to PowerPoint majorFont/minorFont entries.\n- Abstract roles (heading, body, accent, code) - OPF-specific font family names the engine maps onto the OOXML pair when serializing. Convenient for inline overrides and for adding accent/code roles that don't have a direct OOXML slot. Catalog records may also carry 'code' (for example the consolas and courier-new records); heading, body and accent are not part of the catalog record schema. A role is a family name only: weight, style and tracking come from the text styles, and PowerPoint has no numeric weight.\n\nIn design.fontScheme, 'id' resolves a fontSchemes catalog record as the base; pair and role overrides on the same object take precedence over the resolved scheme. The string shorthand on design.fontScheme is equivalent to setting only 'id'.",
1217
1077
  "properties": {
1218
1078
  "id": {
1219
1079
  "type": "string",
@@ -1273,61 +1133,43 @@
1273
1133
  },
1274
1134
  "app": {
1275
1135
  "type": "string",
1276
- "enum": ["PowerPoint", "Google Slides"],
1277
- "description": "Target application this font pairing is intended for."
1136
+ "enum": ["powerpoint", "google-slides"],
1137
+ "description": "Target application this font pairing is intended for. Metadata for pickers and catalog filters: no engine changes its output by it."
1278
1138
  },
1279
1139
  "languageFamily": {
1280
1140
  "type": "string",
1281
- "enum": ["latin", "ea", "cs"],
1282
- "description": "OOXML font-language family this scheme is intended for: 'latin' for Latin-script content, 'ea' for East Asian scripts, 'cs' for Complex Scripts. As the design font scheme, an 'ea' or 'cs' scheme also fills that script slot with its own major/minor families when its languages list is empty or names the presentation language."
1141
+ "enum": ["latin", "ea", "cs", "eastAsian", "complexScript"],
1142
+ "description": "Font-language family this scheme is intended for: 'latin' for Latin-script content, 'ea' (or 'eastAsian', the same value) for East Asian scripts, 'cs' (or 'complexScript', the same value) for Complex Scripts. The long names match the 'eastAsian' and 'complexScript' slot keys; the short names match the OOXML a:ea and a:cs elements; core reads both spellings as one value. As the design font scheme, an 'ea'/'eastAsian' or 'cs'/'complexScript' scheme also fills that script slot with its own major/minor families when its languages list is empty or names the presentation language."
1283
1143
  },
1284
1144
  "heading": {
1285
- "$ref": "#/$defs/Font",
1286
- "description": "Abstract role: font used for slide titles and headings. Maps onto the OOXML major slot when serializing."
1145
+ "type": "string",
1146
+ "description": "Abstract role: font family name used for slide titles and headings. Maps onto the OOXML major slot when serializing."
1287
1147
  },
1288
1148
  "body": {
1289
- "$ref": "#/$defs/Font",
1290
- "description": "Abstract role: font used for body copy. Maps onto the OOXML minor slot when serializing."
1149
+ "type": "string",
1150
+ "description": "Abstract role: font family name used for body copy. Maps onto the OOXML minor slot when serializing."
1291
1151
  },
1292
1152
  "accent": {
1293
- "$ref": "#/$defs/Font",
1294
- "description": "Abstract role: font used for accent text. When set, the slide tag (eyebrow) and the quote body use this family instead of the body and heading families; nothing else changes. resolveFontFamilies() returns it as accent only when the scheme defines it. No direct OOXML slot: PPTX export writes it on those runs, and the theme fonts stay major/minor."
1295
- },
1296
- "code": {
1297
- "$ref": "#/$defs/Font",
1298
- "description": "Abstract role: monospaced font used for code blocks and inline code. No direct OOXML slot. Resolution: this override, then the resolved catalog record's 'code' (for example Consolas for the consolas scheme), then the engine fallback Roboto Mono. Heading and body families are never used as the code fallback."
1299
- }
1300
- }
1301
- },
1302
- "Font": {
1303
- "type": "object",
1304
- "description": "Specification for a single font role.",
1305
- "required": ["family"],
1306
- "properties": {
1307
- "family": {
1308
1153
  "type": "string",
1309
- "description": "Font family name.",
1310
- "examples": ["Inter", "Roboto", "Source Serif Pro", "JetBrains Mono"]
1154
+ "description": "Abstract role: font family name used for accent text. When set, the slide tag (eyebrow) and the quote body use this family instead of the body and heading families; nothing else changes. resolveFontFamilies() returns it as accent only when the scheme defines it. No direct OOXML slot: PPTX export writes it on those runs, and the theme fonts stay major/minor."
1311
1155
  },
1312
- "weight": {
1313
- "type": "number",
1314
- "description": "Numeric font weight (e.g., 400 for regular, 700 for bold)."
1315
- },
1316
- "style": {
1156
+ "code": {
1317
1157
  "type": "string",
1318
- "enum": ["normal", "italic"],
1319
- "description": "Font style."
1320
- },
1321
- "letterSpacing": {
1322
- "type": "number",
1323
- "description": "Letter spacing (tracking) in ems."
1158
+ "description": "Abstract role: monospaced font family name used for code blocks and for inline code runs (TextRun.code). No direct OOXML slot. Resolution: this override, then the resolved catalog record's 'code' (for example Consolas for the consolas scheme), then the engine fallback Roboto Mono. Heading and body families are never used as the code fallback."
1324
1159
  }
1325
1160
  }
1326
1161
  },
1162
+ "SlideDesign": {
1163
+ "description": "A slide's design: every Design field except dimensions. A PPTX has one slide size, so the size is set once, on the deck's design.dimensions (or its theme), and a slide's design cannot set it. A slide-level theme whose resolved dimensions differ from the deck's is a validate warning (opf/slide-theme-dimensions); exporting such a deck to PPTX fails with mixed-slide-dimensions.",
1164
+ "allOf": [
1165
+ { "$ref": "#/$defs/Design" },
1166
+ { "not": { "required": ["dimensions"] } }
1167
+ ]
1168
+ },
1327
1169
  "DimensionPreset": {
1328
1170
  "type": "string",
1329
- "enum": ["16:9", "4:3", "16:10", "letter", "a4", "widescreen", "standard"],
1330
- "description": "Named dimension preset; chooses both aspect ratio and physical size. 'widescreen' is an alias for 16:9 in PowerPoint widescreen size; 'standard' is an alias for 4:3 in PowerPoint standard size."
1171
+ "enum": ["16:9", "4:3", "16:10", "1:1", "4:5", "9:16", "letter", "a4", "widescreen", "standard"],
1172
+ "description": "Named dimension preset; chooses both aspect ratio and physical size. 'widescreen' is an alias for 16:9 in PowerPoint widescreen size; 'standard' is an alias for 4:3 in PowerPoint standard size. The social-feed ratios keep the widescreen short edge of 7.5 in: 1:1 is 7.5 x 7.5 in, 4:5 is 7.5 x 9.375 in and 9:16 is 7.5 x 13.333 in (portrait)."
1331
1173
  },
1332
1174
  "Dimensions": {
1333
1175
  "type": "object",
@@ -1367,7 +1209,7 @@
1367
1209
  "enum": [
1368
1210
  "accent1", "accent2", "accent3", "accent4", "accent5", "accent6",
1369
1211
  "dark1", "dark2", "light1", "light2", "hyperlink", "followedHyperlink",
1370
- "primary", "secondary", "accent", "background", "surface", "text", "textSecondary"
1212
+ "primary", "secondary", "accent", "background", "surface", "surfaceAlt", "text", "textSecondary"
1371
1213
  ]
1372
1214
  },
1373
1215
  {
@@ -1375,7 +1217,7 @@
1375
1217
  "pattern": "^var:[a-z][a-z0-9-]*$"
1376
1218
  }
1377
1219
  ],
1378
- "description": "A color value or reference, enforced on styled table cell fill and text colors and on cell border colors. Three forms:\n- Literal hex: '#RGB', '#RRGGBB', or '#RRGGBBAA'.\n- Color-scheme name, resolved through the effective color scheme after design resolution: an OOXML slot ('accent1'-'accent6', 'dark1', 'dark2', 'light1', 'light2', 'hyperlink', 'followedHyperlink') or an abstract role ('primary', 'secondary', 'accent', 'background', 'surface', 'text', 'textSecondary'). Roles resolve through the same role-to-slot mapping engines already apply to color schemes.\n- Variable reference: 'var:<id>' into the top-level variables map.\n\nNames keep content theme-portable: re-theming the deck updates every named reference, where a literal hex stays frozen. Unknown 'var:' ids produce a validation warning, never an error, and engines fall back to their default text color. TextRun.color documents the same three forms but remains an open string so imported decks with unrecognized run colors stay valid; validators warn there instead of erroring.",
1220
+ "description": "A color value or reference, enforced on styled table cell fill and text colors, cell border colors, solid background colors, gradient stop colors and pattern foreground and background colors. Three forms:\n- Literal hex: '#RGB', '#RRGGBB', or '#RRGGBBAA'.\n- Color-scheme name, resolved through the effective color scheme after design resolution: an OOXML slot ('accent1'-'accent6', 'dark1', 'dark2', 'light1', 'light2', 'hyperlink', 'followedHyperlink') or an abstract role ('primary', 'secondary', 'accent', 'background', 'surface', 'surfaceAlt', 'text', 'textSecondary'). Roles resolve through the same role-to-slot mapping engines already apply to color schemes. 'surfaceAlt' has no slot: it is the alternate surface of banded table rows, derived from 'surface' and 'text' (the surface mixed a tenth of the way towards the text color, further when needed, so it always differs visibly from 'surface' and keeps 4.5:1 text contrast; core surfaceAltColor).\n- Variable reference: 'var:<id>' into the top-level variables map.\n\nNames keep content theme-portable: re-theming the deck updates every named reference, where a literal hex stays frozen. Unknown 'var:' ids produce a validation warning, never an error, and engines fall back to their default text color. TextRun.color documents the same three forms but remains an open string so imported decks with unrecognized run colors stay valid; validators warn there instead of erroring.",
1379
1221
  "examples": ["#0F4C81", "accent2", "text", "var:risk"]
1380
1222
  },
1381
1223
  "Variables": {
@@ -1386,7 +1228,7 @@
1386
1228
  "additionalProperties": {
1387
1229
  "$ref": "#/$defs/Variable"
1388
1230
  },
1389
- "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. See docs/templates-and-variables.md.",
1231
+ "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.",
1390
1232
  "examples": [
1391
1233
  {
1392
1234
  "risk": "#B42318",
@@ -1588,19 +1430,25 @@
1588
1430
  "description": "Illustrative number shown in fill forms and used when a template is previewed with examples. Never written to output."
1589
1431
  },
1590
1432
  "format": {
1591
- "type": "string",
1592
- "description": "Display pattern used by '{{<id>}}'. A literal prefix, a numeric part of '#', '0', ',' and '.', and a literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits after '.' fix the decimals ('0' required, '#' optional), and a '%' in the suffix scales by 100. English separators only. Absent: the shortest decimal form.",
1593
- "examples": [
1594
- "#,##0",
1595
- "0.0",
1596
- "$#,##0.00",
1597
- "0.#%",
1598
- "#,##0.0M"
1599
- ]
1433
+ "$ref": "#/$defs/NumberFormat",
1434
+ "description": "Display pattern used by '{{<id>}}' (the shared NumberFormat syntax). A literal prefix, a numeric part of '#', '0', ',' and '.', and a literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits after '.' fix the decimals ('0' required, '#' optional), and a '%' in the suffix scales by 100. English separators only. Absent: the shortest decimal form."
1600
1435
  }
1601
1436
  },
1602
1437
  "description": "A number. '{{<id>}}' inserts it as text using 'format'; 'var:<id>' as a whole field supplies the number itself (chart values, font sizes)."
1603
1438
  },
1439
+ "NumberFormat": {
1440
+ "type": "string",
1441
+ "description": "Number display pattern, shared by NumberVariable.format, data column formats (DataColumn.format) and table cell formats (StyledTableCell.format). An optional literal prefix, a numeric part of '#', '0', ',' and '.', and an optional literal suffix. '0' pads digits, '#' is optional, ',' groups thousands, digits after '.' fix the decimals ('0' required, '#' optional), and a '%' in the prefix or suffix multiplies the value by 100. English separators only. A format applies only to number values; strings, booleans, null and rich runs display unchanged. A column or cell format that is not a valid pattern is a 'number-format-invalid' error. The PPTX export writes it as the equivalent Excel number format code.",
1442
+ "examples": [
1443
+ "#,##0",
1444
+ "0.0",
1445
+ "$#,##0.00",
1446
+ "0.#%",
1447
+ "0%",
1448
+ "#,##0.0M",
1449
+ "#,##0 units"
1450
+ ]
1451
+ },
1604
1452
  "DateVariable": {
1605
1453
  "type": "object",
1606
1454
  "required": [
@@ -1809,7 +1657,7 @@
1809
1657
  "description": "Fixed solid background fill."
1810
1658
  },
1811
1659
  "color": {
1812
- "type": "string",
1660
+ "$ref": "#/$defs/ColorRef",
1813
1661
  "description": "Fixed solid fill color: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef, resolved against the effective color scheme and the deck variables). Use { type: 'theme', slot: ... } for PowerPoint's four theme-controlled background choices.",
1814
1662
  "examples": ["#FFFFFF", "#0F172A", "#F8FAFC"]
1815
1663
  },
@@ -1841,14 +1689,15 @@
1841
1689
  },
1842
1690
  "stops": {
1843
1691
  "type": "array",
1844
- "description": "Ordered list of color stops along the gradient.",
1692
+ "minItems": 2,
1693
+ "description": "Ordered list of color stops along the gradient. A gradient needs at least two stops.",
1845
1694
  "items": {
1846
1695
  "type": "object",
1847
1696
  "description": "Color stop along the gradient.",
1848
1697
  "required": ["color", "position"],
1849
1698
  "properties": {
1850
1699
  "color": {
1851
- "type": "string",
1700
+ "$ref": "#/$defs/ColorRef",
1852
1701
  "description": "Stop color: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef).",
1853
1702
  "examples": ["#1E40AF", "#22D3EE"]
1854
1703
  },
@@ -1932,12 +1781,12 @@
1932
1781
  "examples": ["pct5", "ltHorz", "wdUpDiag"]
1933
1782
  },
1934
1783
  "foregroundColor": {
1935
- "type": "string",
1784
+ "$ref": "#/$defs/ColorRef",
1936
1785
  "description": "Foreground color for the pattern: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef).",
1937
1786
  "examples": ["#0F172A"]
1938
1787
  },
1939
1788
  "backgroundColor": {
1940
- "type": "string",
1789
+ "$ref": "#/$defs/ColorRef",
1941
1790
  "description": "Background color behind the pattern: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef).",
1942
1791
  "examples": ["#FFFFFF"]
1943
1792
  }
@@ -2008,13 +1857,23 @@
2008
1857
  },
2009
1858
  "Watermark": {
2010
1859
  "type": "object",
2011
- "description": "Decorative watermark image and rendering options. Use design.watermark = false to disable an inherited watermark.",
1860
+ "description": "Decorative watermark: an image ('src') or a text stamp ('text'), exactly one of the two, with its opacity. An image is drawn once per slide, contained and centered in a fixed frame (the middle 40% of the slide width and height, from 30% to 70% on each axis). A text watermark is one line of text in the heading font and the theme text color at the given opacity, centered on the slide and rotated 30 degrees counterclockwise (rising to the right), sized to span at most 70% of the slide width and at most 30% of the shorter slide edge in height; it is drawn behind the slide content and above the background and slide image. There is no option to move, scale, rotate or tile either form. PPTX export writes one native picture (image) or one native text box (text) per slide with the opacity as alpha, and import restores the watermark. A string or Asset (no opacity) is the image watermark at opacity 0.08. A watermark object needs opacity and exactly one of src and text; use a plain string or Asset for an image at the default opacity. Use design.watermark = false to disable an inherited watermark.",
2012
1861
  "additionalProperties": false,
2013
1862
  "required": ["opacity"],
1863
+ "oneOf": [
1864
+ { "required": ["src"] },
1865
+ { "required": ["text"] }
1866
+ ],
2014
1867
  "properties": {
2015
1868
  "src": {
2016
1869
  "type": "string",
2017
- "description": "Source for the watermark image."
1870
+ "description": "Source for the watermark image: an 'asset:<id>' reference, HTTPS URL, data URI, relative path or local path, as for Asset.src. Exactly one of src and text is set."
1871
+ },
1872
+ "text": {
1873
+ "type": "string",
1874
+ "minLength": 1,
1875
+ "description": "Text for a text watermark, such as DRAFT or CONFIDENTIAL; line breaks are drawn as spaces. Exactly one of src and text is set.",
1876
+ "examples": ["DRAFT", "CONFIDENTIAL"]
2018
1877
  },
2019
1878
  "opacity": {
2020
1879
  "type": "number",
@@ -2044,7 +1903,7 @@
2044
1903
  },
2045
1904
  "HeaderFooterItem": {
2046
1905
  "type": "object",
2047
- "description": "One header/footer zone. Every configured field renders; fields in one zone stack top to bottom in the order logo, image, text, organization, socials, section, slide number, date. Put a date and a slide number in different zones to keep each on the zone's single line.",
1906
+ "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.",
2048
1907
  "properties": {
2049
1908
  "logo": {
2050
1909
  "type": "boolean",
@@ -2086,7 +1945,11 @@
2086
1945
  },
2087
1946
  "organization": {
2088
1947
  "type": "boolean",
2089
- "description": "Whether to render the primary organization name from organization."
1948
+ "description": "Whether to render the primary organization's name from organization (role 'primary', else the first organization)."
1949
+ },
1950
+ "speaker": {
1951
+ "type": "boolean",
1952
+ "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."
2090
1953
  },
2091
1954
  "section": {
2092
1955
  "type": "boolean",
@@ -2121,26 +1984,44 @@
2121
1984
  "items": { "type": "string" }
2122
1985
  }
2123
1986
  ],
2124
- "description": "Optional reference to one or more narrative beats (each value matches an id from narrative.beats or the resolved template). A single string declares the slide's primary beat; an array declares that one slide covers multiple beats. Declares the slide's role in the story arc; does not constrain ordering or structure. Slides without a beat are valid; multiple slides may share a beat."
1987
+ "description": "Optional reference to one or more beats of the deck's narrative (each value is the id of a beat in the narrative record that the root 'narrative' resolves to). A single string declares the slide's primary beat; an array declares that one slide covers multiple beats. Declares the slide's role in the story arc; does not constrain ordering or structure. Core validate warns about a beat id the resolved narrative does not define, and reports a beat that no slide references. Slides without a beat are valid; multiple slides may share a beat."
2125
1988
  },
2126
1989
  "layout": {
2127
1990
  "type": "string",
2128
- "description": "Optional slide layout reference. Resolves to the 'id' of a 'layouts' catalog record. When omitted, engines infer a layout from the slide's root payload or promoted region keys. Accepts a bare id (lowercase kebab-case, e.g. 'title-subtitle'), an HTTPS URL pointing at a record file, or a 'pkg:' reference. Free-form custom layout names that don't resolve through any catalog fall through to engine-defined layouts.\n\nResolution order: inline catalogs.layouts.records[] → catalogs.layouts.source → default catalog at https://www.pptx.gallery/layouts.",
1991
+ "description": "Optional. Resolves to the 'id' of a 'layouts' catalog record. When omitted, or when it names an id that no inline record, host catalog or default catalog defines, the slide is composed with no layout record: engines arrange title, subtitle, tag and content automatically, a slide with no body payload is a cover (vertically centered), and design hints apply. A layout never changes what a slide contains. An unresolved id is an `unresolved-layout` diagnostic and a `catalog-reference` warning, never an error. Engines must not substitute a different layout for an omitted one. Accepts a bare id (lowercase kebab-case, e.g. 'title-subtitle'), an HTTPS URL pointing at a record file, or a 'pkg:' reference.\n\nResolution order: inline catalogs.layouts.records[] → catalogs.layouts.source → default catalog at https://www.pptx.gallery/layouts.",
2129
1992
  "examples": ["title", "title-subtitle", "text-2x", "chart-3x", "image-bleed", "blank", "https://acme.com/decks/layouts/cover.json", "pkg:@acme/decks/layouts/cover"]
2130
1993
  },
2131
1994
  "title": {
2132
- "type": "string",
2133
- "description": "Slide-level title content. When the resolved layout exposes a 'title' placeholder, the engine renders this value there.",
2134
- "examples": ["Quarterly Review", "Why Now", "Next Steps"]
1995
+ "oneOf": [
1996
+ { "type": "string" },
1997
+ {
1998
+ "type": "array",
1999
+ "items": { "$ref": "#/$defs/TextRun" }
2000
+ }
2001
+ ],
2002
+ "description": "Slide-level title content. When the resolved layout exposes a 'title' placeholder, the engine renders this value there. Use a string for plain text or TextRun[] for inline rich text (a colored word, bold, a link, a citation or footnote marker); the heading keeps its size and fitting rules either way, and run colors resolve like body text.",
2003
+ "examples": ["Quarterly Review", "Why Now", "Next Steps", ["Revenue grew ", { "text": "28%", "color": "accent1" }]]
2135
2004
  },
2136
2005
  "subtitle": {
2137
- "type": "string",
2138
- "description": "Slide-level subtitle or supporting line. When the resolved layout exposes a 'subtitle' placeholder, the engine renders this value there.",
2006
+ "oneOf": [
2007
+ { "type": "string" },
2008
+ {
2009
+ "type": "array",
2010
+ "items": { "$ref": "#/$defs/TextRun" }
2011
+ }
2012
+ ],
2013
+ "description": "Slide-level subtitle or supporting line. When the resolved layout exposes a 'subtitle' placeholder, the engine renders this value there. Use a string for plain text or TextRun[] for inline rich text (a colored word, bold, a link, a citation or footnote marker); the heading keeps its size and fitting rules either way, and run colors resolve like body text.",
2139
2014
  "examples": ["Prepared for the board", "Three signals changed this quarter"]
2140
2015
  },
2141
2016
  "tag": {
2142
- "type": "string",
2143
- "description": "Small slide-level label or badge. When the resolved layout exposes a 'tag' placeholder, the engine renders this value there.",
2017
+ "oneOf": [
2018
+ { "type": "string" },
2019
+ {
2020
+ "type": "array",
2021
+ "items": { "$ref": "#/$defs/TextRun" }
2022
+ }
2023
+ ],
2024
+ "description": "Small slide-level label or badge. When the resolved layout exposes a 'tag' placeholder, the engine renders this value there. Use a string for plain text or TextRun[] for inline rich text (a colored word, bold, a link, a citation or footnote marker); the heading keeps its size and fitting rules either way, and run colors resolve like body text.",
2144
2025
  "examples": ["Intro", "Confidential", "Appendix"]
2145
2026
  },
2146
2027
  "text": {
@@ -2174,7 +2055,7 @@
2174
2055
  },
2175
2056
  "video": {
2176
2057
  "$ref": "#/$defs/Asset",
2177
- "description": "Full-slide video source. Presence of this field infers type 'video'."
2058
+ "description": "Full-slide video source. Presence of this field infers type 'video'. Engines do not play video: the preview draws a placeholder (a play badge with the asset's title or source as its caption), and PPTX export draws the same placeholder as native shapes linked to an http(s) source, with no embedded media. Native playback is out of scope (docs/programs/release-readiness)."
2178
2059
  },
2179
2060
  "chart": {
2180
2061
  "$ref": "#/$defs/Chart",
@@ -2197,7 +2078,7 @@
2197
2078
  { "type": "number" },
2198
2079
  { "$ref": "#/$defs/Metric" }
2199
2080
  ],
2200
- "description": "Full-slide metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, and trend metadata. Numeric values remain numbers; renderers format them for display. Presence of this field infers type 'metric'."
2081
+ "description": "Full-slide metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, trend, and sentiment metadata. Numeric values remain numbers; renderers format them for display. Presence of this field infers type 'metric'."
2201
2082
  },
2202
2083
  "quote": {
2203
2084
  "oneOf": [
@@ -2220,7 +2101,7 @@
2220
2101
  "description": "Layout-agnostic content blocks rendered together as a composed payload when exact placement is unspecified. At slide root, multiple content payload kinds with no explicit type, blocks, or regions are accepted as shorthand for equivalent blocks."
2221
2102
  },
2222
2103
  "design": {
2223
- "$ref": "#/$defs/Design",
2104
+ "$ref": "#/$defs/SlideDesign",
2224
2105
  "description": "Slide-level design applied on top of the deck-wide design."
2225
2106
  },
2226
2107
  "left": {
@@ -2379,7 +2260,7 @@
2379
2260
  },
2380
2261
  "hidden": {
2381
2262
  "type": "boolean",
2382
- "description": "Whether the slide is hidden from the presented sequence."
2263
+ "description": "Whether the slide is hidden from the presented sequence. The player skips it, the PPTX export writes it as a hidden slide, and per-slide image and PDF output skips it unless the caller asks to include hidden slides (opf CLI: --include-hidden; the deck-level renderSvg: the opt-in skipHidden). A slide named explicitly (opf render --slides, renderSvg slideIndex) is still rendered."
2383
2264
  },
2384
2265
  "composition": {
2385
2266
  "$ref": "#/$defs/Composition"
@@ -2464,7 +2345,7 @@
2464
2345
  },
2465
2346
  "video": {
2466
2347
  "$ref": "#/$defs/Asset",
2467
- "description": "Source for a video item.",
2348
+ "description": "Source for a video item. Engines do not play video: the preview draws a placeholder (a play badge with the asset's title or source as its caption), and PPTX export draws the same placeholder as native shapes linked to an http(s) source, with no embedded media. Native playback is out of scope (docs/programs/release-readiness).",
2468
2349
  "examples": [
2469
2350
  "asset:demo-video",
2470
2351
  "./media/demo.mp4"
@@ -2499,7 +2380,7 @@
2499
2380
  { "type": "number" },
2500
2381
  { "$ref": "#/$defs/Metric" }
2501
2382
  ],
2502
- "description": "Metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, and trend metadata. Numeric values remain numbers; renderers format them for display.",
2383
+ "description": "Metric payload. A string or number is shorthand for { \"value\": value }; object form carries optional label, description, unit, delta, trend, and sentiment metadata. Numeric values remain numbers; renderers format them for display.",
2503
2384
  "examples": [
2504
2385
  "98%",
2505
2386
  42,
@@ -2517,7 +2398,7 @@
2517
2398
  { "type": "string" },
2518
2399
  { "$ref": "#/$defs/Quote" }
2519
2400
  ],
2520
- "description": "Quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution and source metadata.",
2401
+ "description": "Quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution, role, photo and source metadata.",
2521
2402
  "examples": [
2522
2403
  "This changed how our team handles reviews.",
2523
2404
  {
@@ -2750,30 +2631,46 @@
2750
2631
  },
2751
2632
  "Quote": {
2752
2633
  "type": "object",
2753
- "description": "Quote content with optional attribution metadata. Use 'text' for the quoted text, 'attribution' for the credited person or organization, and 'source' for a citation or URL. A string value in a quote field is shorthand for { \"text\": value }.",
2634
+ "description": "Quote content with optional attribution metadata. Use 'text' for the quoted text, 'attribution' for the credited person or organization, 'role' for that person's title and organization, 'photo' for their headshot and 'source' for a citation or URL. A string value in a quote field is shorthand for { \"text\": value }.",
2754
2635
  "additionalProperties": false,
2755
2636
  "required": ["text"],
2756
2637
  "properties": {
2757
2638
  "text": {
2758
- "type": "string",
2759
- "description": "Quoted text.",
2760
- "examples": ["This changed how our team handles reviews."]
2639
+ "oneOf": [
2640
+ { "type": "string" },
2641
+ {
2642
+ "type": "array",
2643
+ "items": { "$ref": "#/$defs/TextRun" }
2644
+ }
2645
+ ],
2646
+ "description": "Quoted text: a string, or TextRun[] for inline rich text (a colored phrase, bold, a link, or a citation or footnote marker). The engine draws the surrounding quotation marks.",
2647
+ "examples": ["This changed how our team handles reviews.", ["Cut review time by ", { "text": "40%", "bold": true }, { "text": " in a quarter", "cite": "acme-case-study" }]]
2761
2648
  },
2762
2649
  "attribution": {
2763
2650
  "type": "string",
2764
- "description": "Person or organization credited for the quote.",
2765
- "examples": ["VP Operations, Acme Corp"]
2651
+ "description": "Person or organization credited for the quote. Drawn after the quote text, in the muted text color.",
2652
+ "examples": ["Priya Raman", "Acme Corp"]
2653
+ },
2654
+ "role": {
2655
+ "type": "string",
2656
+ "description": "Title and organization of the attributed person. Drawn on its own line under the attribution; without an attribution it stands alone. It does not replace the attribution, so the person's name belongs in 'attribution'.",
2657
+ "examples": ["VP Operations, Acme", "Head of Platform"]
2658
+ },
2659
+ "photo": {
2660
+ "$ref": "#/$defs/Asset",
2661
+ "description": "Headshot of the attributed person, drawn as a circle beside the attribution and role lines (on the end side in a right-to-left deck). Give it alt text (validate's opf/missing-alt-text rule checks it). A raster photograph works best; an SVG exports as its PNG raster because the circular mask is not confirmed on SVG pictures in PowerPoint. An unresolved source draws the ordinary image placeholder.",
2662
+ "examples": ["./assets/priya-raman.jpg", {"src": "asset:priya-raman", "alt": "Priya Raman"}]
2766
2663
  },
2767
2664
  "source": {
2768
2665
  "type": "string",
2769
- "description": "Optional quote source, citation, or URL.",
2666
+ "description": "Optional quote source, citation, or URL. Follows the attribution and role after ' - '.",
2770
2667
  "examples": ["Customer interview, March 2026", "https://acme.com/case-study"]
2771
2668
  }
2772
2669
  }
2773
2670
  },
2774
2671
  "Code": {
2775
2672
  "type": "object",
2776
- "description": "Code content with optional rendering metadata. Use 'source' for the code text, 'language' for syntax highlighting, and 'filename' when the rendered block should show a file label. A string value in a code field is shorthand for { \"source\": value }.",
2673
+ "description": "Code content with optional rendering metadata. Use 'source' for the code text, 'language' for syntax highlighting, 'filename' when the rendered block should show a file label, and 'highlight' to emphasize lines. A string value in a code field is shorthand for { \"source\": value }.",
2777
2674
  "additionalProperties": false,
2778
2675
  "required": ["source"],
2779
2676
  "properties": {
@@ -2791,12 +2688,24 @@
2791
2688
  "type": "string",
2792
2689
  "description": "Optional file label shown with the code block.",
2793
2690
  "examples": ["app.py", "components/Button.tsx", "terraform/main.tf"]
2691
+ },
2692
+ "highlight": {
2693
+ "type": "array",
2694
+ "minItems": 1,
2695
+ "items": {
2696
+ "oneOf": [
2697
+ { "type": "integer", "minimum": 1 },
2698
+ { "type": "array", "minItems": 2, "maxItems": 2, "items": { "type": "integer", "minimum": 1 } }
2699
+ ]
2700
+ },
2701
+ "description": "Source lines to emphasize, 1-based: each entry is a line number or an inclusive [start, end] range, so [3, [5, 7]] marks lines 3 and 5 to 7. Lines are counted by line break in 'source', so a line that wraps stays one line. Engines draw a theme-derived band behind the marked lines and dim the other lines slightly, keeping every line at least 4.5:1 against its background; PPTX export draws one native rectangle behind the line text boxes and import restores the list. A line number past the last line, or a range written end before start, gets a validation warning (code-highlight-out-of-range, code-highlight-range-reversed) and is ignored. When a code block is paginated across slides each page keeps the lines that fall on it, renumbered from 1.",
2702
+ "examples": [[3], [3, [5, 7]], [[2, 4]]]
2794
2703
  }
2795
2704
  }
2796
2705
  },
2797
2706
  "Metric": {
2798
2707
  "type": "object",
2799
- "description": "Metric content with optional display metadata. Use 'value' for the primary value, 'label' for the metric name, 'description' for supporting context, 'unit' for a suffix/currency marker, 'delta' for change, and 'trend' for direction. A string or number value in a metric field is shorthand for { \"value\": value }; numeric values remain numbers and are formatted by renderers.",
2708
+ "description": "Metric content with optional display metadata. Use 'value' for the primary value, 'label' for the metric name, 'description' for supporting context, 'unit' for a suffix/currency marker, 'delta' for change, 'trend' for direction, and 'sentiment' for whether the change is good news. A string or number value in a metric field is shorthand for { \"value\": value }; numeric values remain numbers and are formatted by renderers.",
2800
2709
  "additionalProperties": false,
2801
2710
  "required": ["value"],
2802
2711
  "properties": {
@@ -2834,7 +2743,12 @@
2834
2743
  "trend": {
2835
2744
  "type": "string",
2836
2745
  "enum": ["up", "down", "flat"],
2837
- "description": "Metric trend direction."
2746
+ "description": "Metric trend direction. The preview and the PowerPoint export draw an arrow beside the trend word that always points the way the trend does (up, down or flat) and colour the arrow, the trend word and the delta text by `sentiment`."
2747
+ },
2748
+ "sentiment": {
2749
+ "type": "string",
2750
+ "enum": ["positive", "negative", "neutral"],
2751
+ "description": "Whether the change is good news. Positive draws the trend arrow and the trend and delta text in green, negative in red and neutral in the neutral text colour, each kept at 4.5:1 contrast or more against the slide background. When absent the colour follows the trend: up is positive, down is negative and flat is neutral. Set it when the direction is not the verdict, for example positive on a falling churn, cost or latency trend. The arrow always keeps the direction of `trend`. Only the trend arrow and the trend and delta text take the colour, so sentiment has no visible effect on a metric without a trend."
2838
2752
  }
2839
2753
  }
2840
2754
  },
@@ -2891,6 +2805,12 @@
2891
2805
  "type": "string",
2892
2806
  "description": "Optional event detail.",
2893
2807
  "examples": ["Launch with the first operations team."]
2808
+ },
2809
+ "status": {
2810
+ "type": "string",
2811
+ "enum": ["done", "current", "planned"],
2812
+ "description": "Progress of the event, drawn from the deck's own colors by both the SVG renderer and the PPTX exporter. Absent means no status and the event is drawn as a plain filled marker with normal text, which is also how 'done' looks. 'done' is a filled marker in the primary color with normal text. 'current' marks 'we are here': the filled marker is surrounded by a ring and the event label (`what`) is bold. 'planned' is an outlined, hollow marker (primary-color outline over the slide background) with muted secondary text. Text colors keep a 4.5:1 contrast against the slide background. In PPTX the markers are native ellipses (the ring is a second ellipse), and PPTX import restores the status the export recorded. Status describes progress only; schedule health such as at risk or blocked belongs in the event text.",
2813
+ "examples": ["done", "current", "planned"]
2894
2814
  }
2895
2815
  }
2896
2816
  },
@@ -3088,13 +3008,24 @@
3088
3008
  },
3089
3009
  "fontFamily": {
3090
3010
  "type": "string",
3091
- "description": "Run font family override.",
3011
+ "description": "Run font family override. It wins over the code font a run with 'code' takes.",
3092
3012
  "examples": ["Inter", "Source Serif Pro", "JetBrains Mono"]
3093
3013
  },
3014
+ "code": {
3015
+ "type": "boolean",
3016
+ "description": "Whether the run is an inline code span: it is drawn in the design's code font (design.fontScheme.code, else the resolved font scheme's code family, else Roboto Mono), unless the run names its own fontFamily. Only the font changes; there is no background, because a PPTX run cannot have one. Markdown backticks read and write code runs."
3017
+ },
3018
+ "lang": {
3019
+ "type": "string",
3020
+ "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$",
3021
+ "description": "BCP-47 language tag of the run, such as 'fr-FR' or 'ja-JP', when it differs from the deck language. It sets the proofing language (PPTX a:rPr lang) and the language the run's script fonts and glyph variants follow (for example the Japanese or the Simplified Chinese face for Han characters); without it the run follows the deck language.",
3022
+ "examples": ["fr-FR", "ja-JP", "de"]
3023
+ },
3094
3024
  "link": {
3095
3025
  "type": "string",
3096
- "description": "URL to link the run text to.",
3097
- "examples": ["https://acme.com", "https://acme.com/case-studies/beta"]
3026
+ "pattern": "^(https?://|mailto:|tel:)\\S+$",
3027
+ "description": "URL to link the run text to: an http(s) URL, a 'mailto:' address or a 'tel:' number, without whitespace. The preview draws it as a link and PPTX export writes a native hyperlink for all three; any other scheme is not valid.",
3028
+ "examples": ["https://acme.com", "https://acme.com/case-studies/beta", "mailto:hello@acme.com", "tel:+15551234567"]
3098
3029
  },
3099
3030
  "superscript": {
3100
3031
  "type": "boolean",
@@ -3109,7 +3040,7 @@
3109
3040
  { "type": "string", "minLength": 1 },
3110
3041
  { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }
3111
3042
  ],
3112
- "description": "One or more ids from the top-level references list that this run cites. Engines draw a superscript marker ('1', or '1,2' for several ids) directly after the run and list '<n> <reference text>' in the slide's footnote area; markers are numbered per deck in order of first use, and the same id keeps its number. An id missing from references is a validation error (cite-unknown-reference). Supported in text, bullets and list item runs.",
3043
+ "description": "One or more ids from the top-level references list that this run cites. Engines draw a superscript marker ('1', or '1,2' for several ids) directly after the run and list '<n> <reference text>' in the slide's footnote area; markers are numbered per deck in order of first use, and the same id keeps its number. An id missing from references is a validation error (cite-unknown-reference). Supported in text, bullets and list item runs, in the slide title, subtitle and tag, and in the quote text.",
3113
3044
  "examples": ["gartner-2026", ["gartner-2026", "annual-report"]]
3114
3045
  },
3115
3046
  "footnote": {
@@ -3117,7 +3048,7 @@
3117
3048
  { "type": "string", "minLength": 1 },
3118
3049
  { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/TextRun" } }
3119
3050
  ],
3120
- "description": "An inline note for this run, without a references entry. Engines draw a superscript marker after the run and list the note in the slide's footnote area; every footnote takes a new number in the deck's marker sequence. Supported in text, bullets and list item runs.",
3051
+ "description": "An inline note for this run, without a references entry. Engines draw a superscript marker after the run and list the note in the slide's footnote area; every footnote takes a new number in the deck's marker sequence. Supported in text, bullets and list item runs, in the slide title, subtitle and tag, and in the quote text.",
3121
3052
  "examples": ["Excludes the discontinued hardware line."]
3122
3053
  }
3123
3054
  }
@@ -3193,22 +3124,42 @@
3193
3124
  "properties": {
3194
3125
  "type": {
3195
3126
  "type": "string",
3196
- "description": "Chart type id. Resolves to the id of a chartTypes catalog record; renderers map that record through mappings.openxml and any renderer-specific mapping they understand. The bundled catalog covers the chart types Aspose.Slides officially supports; deprecated ids still resolve but validate with a warning naming their replacement.",
3127
+ "description": "Chart type id. Resolves to the id of a chartTypes catalog record; renderers map that record through mappings.openxml and any renderer-specific mapping they understand. The bundled catalog covers the chart types Aspose.Slides officially supports, plus 'combo' (clustered columns with line series, see 'line' and 'secondaryAxis').",
3197
3128
  "examples": [
3198
3129
  "column", "bar", "line", "pie", "doughnut", "area", "scatter", "radar",
3199
- "waterfall", "funnel", "treemap", "stacked-column-3x"
3130
+ "waterfall", "funnel", "treemap", "stacked-column", "combo"
3200
3131
  ]
3201
3132
  },
3202
3133
  "data": {
3203
3134
  "oneOf": [
3204
3135
  { "$ref": "#/$defs/ChartData" },
3205
- { "$ref": "#/$defs/ChartDataSource" }
3136
+ { "$ref": "#/$defs/DatasetRef" }
3206
3137
  ],
3207
- "description": "Chart data. Inline data uses a tabular columns/rows shape; renderers convert rows to chart series internally."
3138
+ "description": "Chart data. Inline data uses a tabular columns/rows shape; renderers convert rows to chart series internally. A DatasetRef ({ \"dataset\": \"<id>\" }) plots a top-level dataset instead. Data from a spreadsheet or file is imported into inline columns and rows, and its origin is recorded in data.source."
3139
+ },
3140
+ "mapping": {
3141
+ "$ref": "#/$defs/ChartMapping",
3142
+ "description": "Optional series mapping by column name: which column is the category, which is the X column of a scatter chart, and which columns are plotted, in order. Absent keeps the positional rule (first column the category, then the X column for scatter, every other column a series)."
3143
+ },
3144
+ "line": {
3145
+ "type": "array",
3146
+ "items": { "type": "string" },
3147
+ "minItems": 1,
3148
+ "uniqueItems": true,
3149
+ "description": "Combo charts only (type 'combo'): the plotted series, by column name, that are drawn as lines with markers; every other plotted series is drawn as clustered columns. Absent: the last plotted series is the line. A combo chart keeps at least one column series, so a list that names every series draws the first one as columns. Column series are drawn, exported and listed in the legend first, then the line series on the primary axis, then those on the secondary axis, each in plotted order. A name that is not a column is a 'chart-mapping-unknown-column' error; a column that is not plotted is a 'chart-mapping-adapted' warning. On any other chart type it is ignored with a 'chart-option-adapted' warning.",
3150
+ "examples": [["Margin"]]
3151
+ },
3152
+ "secondaryAxis": {
3153
+ "type": "array",
3154
+ "items": { "type": "string" },
3155
+ "minItems": 1,
3156
+ "uniqueItems": true,
3157
+ "description": "Combo charts only: line series, by column name, plotted against a secondary value axis at the right of the plot. That axis has its own scale, and its tick labels use the number format of the first secondary series' column (DataColumn.format), for example '0%' for a margin. Absent: every series shares the primary value axis at the left. A name that is not a line series (see 'line') stays on the primary axis with a 'chart-mapping-adapted' warning; an unknown column is a 'chart-mapping-unknown-column' error. On any other chart type it is ignored with a 'chart-option-adapted' warning.",
3158
+ "examples": [["Margin"]]
3208
3159
  },
3209
3160
  "axisTitles": {
3210
3161
  "$ref": "#/$defs/ChartAxisTitles",
3211
- "description": "Optional axis titles. Absent keeps today's untitled axes. Supported on the chart types that have a category/value (or X/Y) axis pair (column, bar, line, area, scatter, and the histogram, pareto, waterfall and box-and-whisker constructs; the funnel construct takes a category title only); a title on a type without that axis (pie, doughnut, radar, treemap, map) is dropped with a 'chart-option-adapted' diagnostic by renderers and exporters."
3162
+ "description": "Optional axis titles. Absent keeps today's untitled axes. Supported on the chart types that have a category/value (or X/Y) axis pair (column, bar, line, area, scatter, and the histogram, pareto, waterfall and box-and-whisker constructs; the funnel construct takes a category title only; a combo chart also takes 'secondary' when it plots a series on the secondary axis); a title on a type without that axis (pie, doughnut, radar, treemap, map) is dropped with a 'chart-option-adapted' diagnostic by renderers and exporters."
3212
3163
  },
3213
3164
  "legend": {
3214
3165
  "type": "string",
@@ -3221,13 +3172,22 @@
3221
3172
  { "$ref": "#/$defs/ChartDataLabels" }
3222
3173
  ],
3223
3174
  "description": "Optional data labels. true shows value labels at each type's default position, false (or absent) shows none, which is today's behaviour. Use the object form for the label content, position and separator."
3175
+ },
3176
+ "alt": {
3177
+ "type": "string",
3178
+ "description": "Text alternative for the chart: what the data shows (the point and the key numbers), not 'a chart' or 'bar chart'. The preview exposes it as the chart's accessible name (role img with aria-label) and the PowerPoint export writes it as the chart frame's alternative text (descr); import reads it back. An empty string marks the chart decorative, with the same meaning as an empty Asset alt, but it is a reviewed choice that opf/chart-text-alternative still reports as info (a chart almost always carries a message). When absent, validate accepts text beside the chart instead.",
3179
+ "examples": ["Revenue grew from $12M in Q1 to $18M in Q2, with the steepest rise in the West region."]
3180
+ },
3181
+ "highlight": {
3182
+ "$ref": "#/$defs/ChartHighlight",
3183
+ "description": "Optional emphasis: the series and/or categories that carry the message. The engines draw the highlighted marks in the deck's primary (accent) color and every other mark in a muted neutral derived from the theme (a light grey on a light chart panel, a dim grey on a dark one); there is no color field. A mark is highlighted when its series OR its category is named. Absent keeps today's colors exactly. Supported on column, bar and line charts (series and categories; on a line a category highlight marks that category's points), pie and doughnut charts (categories, one per slice), and area, scatter and radar charts (series); the histogram, pareto, waterfall, funnel, treemap, box-and-whisker, map and combo constructs drop the option with a 'chart-option-adapted' diagnostic, as does a part the type cannot highlight. A name that matches no column or row label is a 'chart-highlight-unknown-name' error."
3224
3184
  }
3225
3185
  }
3226
3186
  },
3227
3187
  "ChartAxisTitles": {
3228
3188
  "type": "object",
3229
3189
  "additionalProperties": false,
3230
- "description": "Titles for the two axes of a chart. 'category' is the axis that carries the row labels (the horizontal axis of a column or line chart, the vertical axis of a bar chart, the X axis of a scatter chart); 'value' is the other axis.",
3190
+ "description": "Titles for the axes of a chart. 'category' is the axis that carries the row labels (the horizontal axis of a column or line chart, the vertical axis of a bar chart, the X axis of a scatter chart); 'value' is the other axis; 'secondary' is the secondary value axis of a combo chart.",
3231
3191
  "properties": {
3232
3192
  "category": {
3233
3193
  "type": "string",
@@ -3236,11 +3196,41 @@
3236
3196
  },
3237
3197
  "value": {
3238
3198
  "type": "string",
3239
- "description": "Title of the value (Y) axis.",
3199
+ "description": "Title of the value (Y) axis. On a combo chart, the primary (left) value axis.",
3240
3200
  "examples": ["Revenue ($M)", "Customers"]
3201
+ },
3202
+ "secondary": {
3203
+ "type": "string",
3204
+ "description": "Combo charts only: title of the secondary value axis at the right of the plot, drawn rotated like the primary value axis title. Dropped with a 'chart-option-adapted' diagnostic on any other chart type and on a combo chart without 'secondaryAxis'.",
3205
+ "examples": ["Margin (%)"]
3241
3206
  }
3242
3207
  }
3243
3208
  },
3209
+ "ChartHighlight": {
3210
+ "type": "object",
3211
+ "additionalProperties": false,
3212
+ "minProperties": 1,
3213
+ "description": "Which series and categories a chart emphasizes. Names are matched exactly: series by data column name (after any 'fields' selection; a series is a plotted value column), categories by row label (the first column, or mapping.category), as text. A category label shared by several rows highlights each of them.",
3214
+ "properties": {
3215
+ "series": {
3216
+ "type": "array",
3217
+ "items": { "type": "string" },
3218
+ "minItems": 1,
3219
+ "uniqueItems": true,
3220
+ "description": "Series (value columns) to highlight: all marks of each named series. A name that is not a column is a 'chart-highlight-unknown-name' error; a column that is not plotted as a series (the category or X column, or one mapping.series leaves out) highlights nothing and is a 'chart-highlight-adapted' warning.",
3221
+ "examples": [["Revenue"]]
3222
+ },
3223
+ "categories": {
3224
+ "type": "array",
3225
+ "items": { "type": "string" },
3226
+ "minItems": 1,
3227
+ "uniqueItems": true,
3228
+ "description": "Categories (row labels) to highlight: the marks of each named category (a column or bar of every series, a line's points, a pie or doughnut slice). A label no row has is a 'chart-highlight-unknown-name' error.",
3229
+ "examples": [["Q3"]]
3230
+ }
3231
+ },
3232
+ "examples": [{ "series": ["Revenue"] }, { "categories": ["Q3"] }]
3233
+ },
3244
3234
  "ChartDataLabels": {
3245
3235
  "type": "object",
3246
3236
  "additionalProperties": false,
@@ -3256,7 +3246,7 @@
3256
3246
  "position": {
3257
3247
  "type": "string",
3258
3248
  "enum": ["auto", "center", "inside-end", "inside-base", "outside-end", "above", "below", "left", "right"],
3259
- "description": "Where a label sits relative to its mark. 'auto' (the default) is the type's default: outside-end for clustered columns and bars, pie slices and the histogram, pareto and waterfall constructs; center for stacked columns and bars; above for line and scatter points. Clustered columns and bars and the histogram, pareto and waterfall constructs take center, inside-end, inside-base and outside-end; stacked columns and bars take center, inside-end and inside-base; pie takes center, inside-end and outside-end; line and scatter points take above, below, left, right and center. Area, doughnut and radar charts and the funnel and treemap constructs take only 'auto' (the box-and-whisker and map constructs have no data labels). An unsupported position falls back to 'auto' with a 'chart-option-adapted' diagnostic."
3249
+ "description": "Where a label sits relative to its mark. 'auto' (the default) is the type's default: outside-end for clustered columns and bars, pie slices and the histogram, pareto and waterfall constructs; center for stacked columns and bars; above for line and scatter points. Clustered columns and bars and the histogram, pareto and waterfall constructs take center, inside-end, inside-base and outside-end; stacked columns and bars take center, inside-end and inside-base; pie takes center, inside-end and outside-end; line and scatter points take above, below, left, right and center. Area, doughnut and radar charts and the funnel and treemap constructs take only 'auto' (the box-and-whisker and map constructs have no data labels). A combo chart places the labels of its column series like clustered columns and those of its line series like line points; a position only one part takes applies to that part, and the other part keeps its default (outside-end, above). An unsupported position falls back to 'auto' with a 'chart-option-adapted' diagnostic."
3260
3250
  },
3261
3251
  "separator": {
3262
3252
  "type": "string",
@@ -3267,11 +3257,16 @@
3267
3257
  },
3268
3258
  "Table": {
3269
3259
  "type": "object",
3270
- "description": "Table content. Columns are optional; rows are the only required field.",
3260
+ "description": "Table content, inline or dataset-backed. An inline table has 'rows' (required) and optional 'columns'. A dataset-backed table has 'dataset' (required) and optional 'fields', and no 'rows' or 'columns': it takes its headers, rows and column formats from the dataset (per-cell styles need an inline table).",
3271
3261
  "additionalProperties": false,
3272
- "required": [
3273
- "rows"
3274
- ],
3262
+ "if": { "required": ["dataset"] },
3263
+ "then": {
3264
+ "properties": { "rows": false, "columns": false }
3265
+ },
3266
+ "else": {
3267
+ "required": ["rows"],
3268
+ "properties": { "fields": false }
3269
+ },
3275
3270
  "properties": {
3276
3271
  "columns": {
3277
3272
  "type": "array",
@@ -3289,12 +3284,15 @@
3289
3284
  {
3290
3285
  "$ref": "#/$defs/StyledTableCell"
3291
3286
  },
3287
+ {
3288
+ "$ref": "#/$defs/DataColumn"
3289
+ },
3292
3290
  {
3293
3291
  "type": "null"
3294
3292
  }
3295
3293
  ]
3296
3294
  },
3297
- "description": "Optional column labels. Labels may be strings, rich runs or styled cell objects. Null is an empty label or a placeholder covered by a preceding column span.",
3295
+ "description": "Optional column labels. Labels may be strings, rich runs, styled cell objects or DataColumn objects ({ \"name\", \"format\" }). A header's 'format' (DataColumn or StyledTableCell) is the column's number format; a body cell's own format wins. Null is an empty label or a placeholder covered by a preceding column span.",
3298
3296
  "examples": [
3299
3297
  [
3300
3298
  "Region",
@@ -3329,21 +3327,34 @@
3329
3327
  ]
3330
3328
  ]
3331
3329
  ]
3330
+ },
3331
+ "dataset": {
3332
+ "$ref": "#/$defs/DatasetId",
3333
+ "description": "Id of a top-level dataset that supplies this table's headers, rows and column formats. Excludes 'rows' and 'columns'. An unknown id is a 'dataset-unknown' error."
3334
+ },
3335
+ "fields": {
3336
+ "$ref": "#/$defs/DatasetFields",
3337
+ "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."
3332
3338
  }
3333
3339
  }
3334
3340
  },
3335
3341
  "ChartData": {
3336
3342
  "type": "object",
3337
- "description": "Inline tabular data driving a chart. The first column usually supplies category/x-axis labels; subsequent columns are plotted measures unless a chart type or renderer maps them differently.",
3343
+ "description": "Inline tabular data driving a chart. The first column usually supplies category/x-axis labels; subsequent columns are plotted measures unless 'chart.mapping' or a chart type maps them differently. Value cells are numbers; a string is read only in strict decimal syntax ('12', '-3.5', '1e6'), and anything else ('12%', '$5', '(5)', '1,234') is a gap and a 'chart-value-not-numeric' warning.",
3338
3344
  "additionalProperties": false,
3339
3345
  "required": ["columns", "rows"],
3340
3346
  "properties": {
3341
3347
  "columns": {
3342
3348
  "type": "array",
3343
- "items": { "type": "string" },
3349
+ "items": {
3350
+ "oneOf": [
3351
+ { "type": "string" },
3352
+ { "$ref": "#/$defs/DataColumn" }
3353
+ ]
3354
+ },
3344
3355
  "minItems": 1,
3345
- "description": "Ordered column labels for the chart data table.",
3346
- "examples": [["Quarter", "Revenue"], ["Quarter", "Revenue", "Costs"]]
3356
+ "description": "Ordered column labels for the chart data table. A label is a string or a DataColumn ({ \"name\", \"format\" }) whose number format the data labels, value axis and exported workbook use.",
3357
+ "examples": [["Quarter", "Revenue"], ["Quarter", "Revenue", "Costs"], ["Quarter", { "name": "Revenue", "format": "$#,##0.0" }, { "name": "Margin", "format": "0%" }]]
3347
3358
  },
3348
3359
  "rows": {
3349
3360
  "type": "array",
@@ -3354,38 +3365,187 @@
3354
3365
  "minItems": 1,
3355
3366
  "description": "Tabular chart rows. Each row aligns by index with columns.",
3356
3367
  "examples": [[["Q1", 12], ["Q2", 18], ["Q3", 24], ["Q4", 31]]]
3368
+ },
3369
+ "source": {
3370
+ "$ref": "#/$defs/DataSourceRef",
3371
+ "description": "Optional provenance: where this inline data came from. Engines never read, fetch or refresh it; they keep it through editing, export and re-import."
3357
3372
  }
3358
3373
  }
3359
3374
  },
3360
- "ChartDataSource": {
3375
+ "DataColumn": {
3376
+ "type": "object",
3377
+ "additionalProperties": false,
3378
+ "required": ["name"],
3379
+ "description": "A named data column with an optional number format. Anywhere a chart or dataset column is a string it may be a DataColumn; the string form is { \"name\": value } with no format. A table column header may also be a DataColumn.",
3380
+ "properties": {
3381
+ "name": {
3382
+ "type": "string",
3383
+ "description": "Column label, used as the series name, the table header and the name 'fields' and 'chart.mapping' address.",
3384
+ "examples": ["Revenue", "Margin"]
3385
+ },
3386
+ "format": {
3387
+ "$ref": "#/$defs/NumberFormat",
3388
+ "description": "Number format of the column's number values (data labels, value axis, table cells, exported workbook). Absent: the General form."
3389
+ }
3390
+ },
3391
+ "examples": [{ "name": "Revenue", "format": "$#,##0.0" }, { "name": "Margin", "format": "0%" }]
3392
+ },
3393
+ "DataSourceRef": {
3361
3394
  "type": "object",
3362
- "description": "Chart data sourced from an asset reference, URL, data URI, relative path, or local path such as CSV, TSV, JSON, or XLSX. The source is interpreted as a table; optional columns select or order fields from that table.",
3363
3395
  "additionalProperties": false,
3364
3396
  "required": ["src"],
3397
+ "description": "Provenance of inline data or a dataset: the file, sheet and range it came from and when. Engines never read, fetch or refresh it; they keep it through editing, export and re-import. Re-import the data to update it.",
3365
3398
  "properties": {
3366
3399
  "src": {
3367
3400
  "type": "string",
3368
- "description": "Data source. Use 'asset:<id>' to reference the top-level assets registry, or provide an HTTPS URL, data URI, relative path, or local filesystem path.",
3369
- "examples": ["asset:revenue", "asset:pipeline-csv", "./data/revenue.csv", "https://cdn.acme.com/data/revenue.csv"]
3401
+ "description": "Where the data came from: an 'asset:<id>' reference, a URL, or a relative or local path.",
3402
+ "examples": ["./data/revenue.csv", "asset:revenue", "https://cdn.acme.com/data/revenue.csv"]
3370
3403
  },
3371
3404
  "sheet": {
3372
3405
  "type": "string",
3373
- "description": "Optional sheet name or table name for spreadsheet-like assets.",
3374
- "examples": ["Sheet1", "Revenue"]
3406
+ "description": "Sheet or table name inside a spreadsheet source.",
3407
+ "examples": ["Sheet1"]
3375
3408
  },
3376
3409
  "range": {
3377
3410
  "type": "string",
3378
- "description": "Optional A1-style range or engine-defined range selector for spreadsheet-like assets.",
3379
- "examples": ["A1:D8", "Revenue!A1:D8"]
3411
+ "description": "A1-style range inside a spreadsheet source.",
3412
+ "examples": ["A1:D8"]
3380
3413
  },
3381
- "columns": {
3414
+ "fields": {
3382
3415
  "type": "array",
3383
3416
  "items": { "type": "string" },
3384
- "description": "Optional ordered columns or fields to read from the source. When omitted, renderers may use the source's own header row or schema.",
3385
- "examples": [["Quarter", "Revenue"], ["Month", "Revenue", "Costs"]]
3417
+ "description": "The source fields the data was taken from, in order.",
3418
+ "examples": [["Quarter", "Revenue"]]
3419
+ },
3420
+ "retrieved": {
3421
+ "type": "string",
3422
+ "anyOf": [{ "format": "date" }, { "format": "date-time" }],
3423
+ "description": "When the data was taken from the source: an ISO 8601 date or date-time.",
3424
+ "examples": ["2026-10-05", "2026-10-05T09:30:00Z"]
3425
+ },
3426
+ "description": {
3427
+ "type": "string",
3428
+ "description": "Free-form note about the source.",
3429
+ "examples": ["Finance export, unaudited"]
3430
+ }
3431
+ },
3432
+ "examples": [{ "src": "./data/revenue.csv", "retrieved": "2026-10-05" }]
3433
+ },
3434
+ "DatasetId": {
3435
+ "type": "string",
3436
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._-]*$",
3437
+ "description": "Id of an entry in the top-level datasets map (the assets id pattern).",
3438
+ "examples": ["revenue", "pipeline-2026"]
3439
+ },
3440
+ "DatasetFields": {
3441
+ "type": "array",
3442
+ "items": { "type": "string" },
3443
+ "minItems": 1,
3444
+ "uniqueItems": true,
3445
+ "description": "Dataset column names to use, in order, each at most once. Selects and orders the dataset's columns; an unknown name is a 'dataset-field-unknown' error.",
3446
+ "examples": [["Quarter", "Revenue"]]
3447
+ },
3448
+ "Datasets": {
3449
+ "type": "object",
3450
+ "description": "Shared data tables keyed by id (the assets id pattern). Charts reference one with 'chart.data': { \"dataset\": \"<id>\" } and tables with 'table': { \"dataset\": \"<id>\" }.",
3451
+ "propertyNames": { "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._-]*$" },
3452
+ "additionalProperties": { "$ref": "#/$defs/Dataset" },
3453
+ "examples": [
3454
+ {
3455
+ "revenue": {
3456
+ "title": "Revenue by quarter",
3457
+ "columns": ["Quarter", { "name": "Revenue", "format": "$#,##0.0" }, "Costs"],
3458
+ "rows": [["Q1", 12, 8], ["Q2", 18, 11], ["Q3", 24, 15]],
3459
+ "source": { "src": "./data/revenue.csv", "retrieved": "2026-10-05" }
3460
+ }
3461
+ }
3462
+ ]
3463
+ },
3464
+ "Dataset": {
3465
+ "type": "object",
3466
+ "additionalProperties": false,
3467
+ "required": ["columns", "rows"],
3468
+ "description": "One shared data table: named columns (strings or DataColumn objects with a number format) and rows of scalar cells. Column names are unique ('data-column-duplicate' error). Rich text and styling belong in an inline table.",
3469
+ "properties": {
3470
+ "title": {
3471
+ "type": "string",
3472
+ "description": "Human name of the dataset, shown by editors.",
3473
+ "examples": ["Revenue by quarter"]
3474
+ },
3475
+ "description": {
3476
+ "type": "string",
3477
+ "description": "What the dataset holds.",
3478
+ "examples": ["Quarterly revenue and costs, in $M."]
3479
+ },
3480
+ "columns": {
3481
+ "type": "array",
3482
+ "items": {
3483
+ "oneOf": [
3484
+ { "type": "string" },
3485
+ { "$ref": "#/$defs/DataColumn" }
3486
+ ]
3487
+ },
3488
+ "minItems": 1,
3489
+ "description": "Ordered, uniquely named columns.",
3490
+ "examples": [["Quarter", { "name": "Revenue", "format": "$#,##0.0" }, "Costs"]]
3491
+ },
3492
+ "rows": {
3493
+ "type": "array",
3494
+ "items": {
3495
+ "type": "array",
3496
+ "items": { "$ref": "#/$defs/ChartDataCell" }
3497
+ },
3498
+ "description": "Rows of scalar cells (string, number, boolean, null); each row aligns by index with columns.",
3499
+ "examples": [[["Q1", 12, 8], ["Q2", 18, 11]]]
3500
+ },
3501
+ "source": {
3502
+ "$ref": "#/$defs/DataSourceRef",
3503
+ "description": "Optional provenance. Engines never read, fetch or refresh it."
3386
3504
  }
3387
3505
  }
3388
3506
  },
3507
+ "DatasetRef": {
3508
+ "type": "object",
3509
+ "additionalProperties": false,
3510
+ "required": ["dataset"],
3511
+ "description": "Chart data taken from a top-level dataset. Engines inline it before plotting; 'chart.mapping' names columns after the 'fields' selection.",
3512
+ "properties": {
3513
+ "dataset": {
3514
+ "$ref": "#/$defs/DatasetId",
3515
+ "description": "Id of the dataset. An unknown id is a 'dataset-unknown' error."
3516
+ },
3517
+ "fields": {
3518
+ "$ref": "#/$defs/DatasetFields",
3519
+ "description": "The dataset columns to use, by name and in order. Absent uses every column."
3520
+ }
3521
+ },
3522
+ "examples": [{ "dataset": "revenue" }, { "dataset": "revenue", "fields": ["Quarter", "Revenue"] }]
3523
+ },
3524
+ "ChartMapping": {
3525
+ "type": "object",
3526
+ "additionalProperties": false,
3527
+ "description": "Series mapping by column name (after any 'fields' selection). Mapping only selects and orders columns; pie, doughnut and other single-series constructs still plot one series. An unknown name is a 'chart-mapping-unknown-column' error; a series that repeats the category or X column, and an X column on a chart type without an X axis, are dropped with a 'chart-mapping-adapted' warning.",
3528
+ "properties": {
3529
+ "category": {
3530
+ "type": "string",
3531
+ "description": "The label (category) column. Default: the first column.",
3532
+ "examples": ["Quarter"]
3533
+ },
3534
+ "x": {
3535
+ "type": "string",
3536
+ "description": "The X column of an XY (scatter) chart. Default: the second column (the first, when the category is the second column), and only when there are three or more columns: with two, the second column is the one series, plotted against row numbers. An X that leaves no series is plotted as the series with a 'chart-mapping-adapted' warning.",
3537
+ "examples": ["Spend"]
3538
+ },
3539
+ "series": {
3540
+ "type": "array",
3541
+ "items": { "type": "string" },
3542
+ "minItems": 1,
3543
+ "description": "The plotted columns, in order. Default: every column that is not the category or the X column.",
3544
+ "examples": [["Revenue", "Costs"]]
3545
+ }
3546
+ },
3547
+ "examples": [{ "category": "Quarter", "series": ["Costs", "Revenue"] }, { "category": "Region", "x": "Spend", "series": ["Revenue"] }]
3548
+ },
3389
3549
  "ChartDataCell": {
3390
3550
  "oneOf": [
3391
3551
  { "type": "string" },
@@ -3452,6 +3612,10 @@
3452
3612
  "type": "integer",
3453
3613
  "minimum": 1,
3454
3614
  "description": "Number of grid rows covered, starting at this cell. Covered positions must contain null. Header cells cannot span into body rows. Default 1."
3615
+ },
3616
+ "format": {
3617
+ "$ref": "#/$defs/NumberFormat",
3618
+ "description": "Number format. On a body cell it formats that cell's number value (and wins over the column's format); on a header cell (in 'columns') it is the column's format, the same as a DataColumn header. Strings, booleans, null and rich runs display unchanged."
3455
3619
  }
3456
3620
  },
3457
3621
  "description": "A cell with explicit visual style or merged geometry. Its position remains its array column index; use null placeholders for every covered grid position."
@@ -3574,7 +3738,7 @@
3574
3738
  "properties": {
3575
3739
  "narratives": {
3576
3740
  "$ref": "#/$defs/CatalogEntry",
3577
- "description": "Catalog of narrative templates. Records validate against https://openpresentation.org/schema/opf-narrative/v1. Default source: https://www.pptx.gallery/narratives."
3741
+ "description": "Catalog of narrative templates. Records validate against https://openpresentation.org/schema/opf-narrative/v1. A custom narrative goes in 'catalogs.narratives.records' and is referenced by its id from the root 'narrative'. Default source: https://www.pptx.gallery/narratives."
3578
3742
  },
3579
3743
  "themes": {
3580
3744
  "$ref": "#/$defs/CatalogEntry",
@@ -3632,7 +3796,7 @@
3632
3796
  "description": "Ordered search path of sources. The engine tries each in order; first match wins. The default catalog for the kind is appended implicitly at the end."
3633
3797
  }
3634
3798
  ],
3635
- "description": "Single source or an ordered search path of sources. When omitted, the engine falls back to the default catalog at https://www.pptx.gallery/<kind>, resolved from its bundled snapshot. Fetching a declared source is an explicit opt-in: the engine or its caller performs that fetch."
3799
+ "description": "Single source or an ordered search path of sources. When omitted, the engine falls back to the default catalog at https://www.pptx.gallery/<kind>, resolved from its bundled snapshot. Engines never fetch a source. The host loads the records itself and passes them to the renderer and exporter as a 'catalogSources' map from the source string to its records; a source with no entry in that map contributes no records, except the default catalog's own URL and the '@openpresentation/opf' package, which resolve from the bundled snapshot."
3636
3800
  },
3637
3801
  "records": {
3638
3802
  "type": "array",
@@ -3643,7 +3807,7 @@
3643
3807
  },
3644
3808
  "CatalogSource": {
3645
3809
  "type": "string",
3646
- "description": "Catalog source location. Accepts:\n- A bare URL pointing at a catalog directory (e.g. 'https://acme.com/decks/narratives'); record ids resolve to '<base>/<id>.json'.\n- A URL pointing at an index file (e.g. 'https://acme.com/decks/narratives/index.json'); records are resolved relative to the index file's directory and the index entries describe what's available. Index files follow https://openpresentation.org/schema/opf-catalog-index/v1; the default catalog's index is https://www.pptx.gallery/<kind>/index.json.\n- A package reference of the form 'pkg:<package>[/<subpath>]'; resolved through a locally-installed package on the engine's package path.",
3810
+ "description": "Catalog source location. Accepts:\n- A bare URL pointing at a catalog directory (e.g. 'https://acme.com/decks/narratives'); record ids resolve to '<base>/<id>.json'.\n- A URL pointing at an index file (e.g. 'https://acme.com/decks/narratives/index.json'); records are resolved relative to the index file's directory and the index entries describe what's available. Index files follow https://openpresentation.org/schema/opf-catalog-index/v1; the default catalog's index is https://www.pptx.gallery/<kind>/index.json.\n- A package reference of the form 'pkg:<package>[/<subpath>]'. 'pkg:@openpresentation/opf/...' resolves from the bundled snapshot; any other package reference needs the host to supply its records in 'catalogSources'.\n\nEngines never fetch or install anything for a source: the host supplies the records of every source other than the bundled default, as a 'catalogSources' map from the source string to its records.",
3647
3811
  "examples": [
3648
3812
  "https://www.pptx.gallery/narratives",
3649
3813
  "https://acme.com/decks/narratives/index.json",