@openpresentation/opf 0.13.0 → 0.15.1

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 (611) hide show
  1. package/README.md +40 -49
  2. package/dist/catalog-refs-BSfGELpt.d.ts +128 -0
  3. package/dist/catalog-schemas-CinPh5WA.d.ts +12 -0
  4. package/dist/catalog.d.ts +91 -0
  5. package/dist/{chunk-E34DJ5LL.js → catalog.js} +16949 -17628
  6. package/dist/{chart-data-Cdr6rRVV.d.ts → chart-data-BnNqgnC-.d.ts} +17 -6
  7. package/dist/{chunk-7QBCWNWL.js → chunk-2CJPC4HI.js} +1807 -1904
  8. package/dist/{chunk-II5EYKZU.js → chunk-374R35TJ.js} +2 -92
  9. package/dist/chunk-3JG4YXWF.js +1842 -0
  10. package/dist/chunk-3RKB4RDJ.js +192 -0
  11. package/dist/{chunk-IS2CPNVJ.js → chunk-5FTUIDUH.js} +26 -26
  12. package/dist/{chunk-GJKJL4YY.js → chunk-HXHOZ7AH.js} +1379 -504
  13. package/dist/{chunk-MPM7NRG7.js → chunk-O744SXXA.js} +2014 -885
  14. package/dist/chunk-QDI75P2M.js +3515 -0
  15. package/dist/chunk-SPFV27O5.js +210 -0
  16. package/dist/chunk-TRDXRRQK.js +17 -0
  17. package/dist/chunk-TWQJLI6E.js +601 -0
  18. package/dist/{chunk-ATGDDJPX.js → chunk-U4J75TRG.js} +3 -3
  19. package/dist/{chunk-RAYB46AB.js → chunk-UBFJ2QHH.js} +34 -30
  20. package/dist/chunk-VGAOJEY7.js +94 -0
  21. package/dist/chunk-ZPU46TFA.js +83 -0
  22. package/dist/{composition-D7we69Tr.d.ts → composition-D2tghM82.d.ts} +947 -111
  23. package/dist/composition.d.ts +3 -2
  24. package/dist/composition.js +2 -2
  25. package/dist/convert.d.ts +18 -19
  26. package/dist/convert.js +61 -50
  27. package/dist/data.d.ts +4 -4
  28. package/dist/data.js +3 -3
  29. package/dist/diff.d.ts +4 -4
  30. package/dist/diff.js +7 -605
  31. package/dist/docs.js +56 -44
  32. package/dist/examples.d.ts +1 -1
  33. package/dist/examples.js +49451 -6259
  34. package/dist/finding-CT_f_vbD.d.ts +212 -0
  35. package/dist/format.d.ts +4 -4
  36. package/dist/format.js +2 -83
  37. package/dist/index.d.ts +492 -309
  38. package/dist/index.js +767 -1097
  39. package/dist/json-BFSdESOE.d.ts +19 -0
  40. package/dist/markdown.d.ts +40 -27
  41. package/dist/markdown.js +9 -1941
  42. package/dist/pagination-wedbe7eq.d.ts +119 -0
  43. package/dist/pagination.d.ts +4 -61
  44. package/dist/pagination.js +6 -6
  45. package/dist/patch.d.ts +12 -23
  46. package/dist/patch.js +6 -6
  47. package/dist/{presentation-mG-IYErQ.d.ts → presentation-CDKQVkyI.d.ts} +702 -1026
  48. package/dist/repo-readme.js +1 -1
  49. package/dist/schemas.d.ts +284 -1
  50. package/dist/schemas.js +1 -1
  51. package/dist/spec/README.md +11 -6
  52. package/dist/spec/catalogs/audiences/index.json +2 -50
  53. package/dist/spec/catalogs/chart-types/100pct-stacked-area.json +21 -0
  54. package/dist/spec/catalogs/chart-types/100pct-stacked-bar.json +22 -0
  55. package/dist/spec/catalogs/chart-types/100pct-stacked-column.json +22 -0
  56. package/dist/spec/catalogs/chart-types/area.json +2 -60
  57. package/dist/spec/catalogs/chart-types/bar.json +2 -48
  58. package/dist/spec/catalogs/chart-types/box-and-whisker.json +2 -132
  59. package/dist/spec/catalogs/chart-types/column.json +2 -52
  60. package/dist/spec/catalogs/chart-types/combo.json +37 -0
  61. package/dist/spec/catalogs/chart-types/doughnut.json +2 -40
  62. package/dist/spec/catalogs/chart-types/filled-radar.json +2 -52
  63. package/dist/spec/catalogs/chart-types/funnel.json +2 -46
  64. package/dist/spec/catalogs/chart-types/histogram.json +2 -149
  65. package/dist/spec/catalogs/chart-types/index.json +50 -520
  66. package/dist/spec/catalogs/chart-types/line-with-markers.json +2 -60
  67. package/dist/spec/catalogs/chart-types/line.json +2 -60
  68. package/dist/spec/catalogs/chart-types/pareto.json +2 -50
  69. package/dist/spec/catalogs/chart-types/pie.json +2 -40
  70. package/dist/spec/catalogs/chart-types/radar-with-markers.json +2 -52
  71. package/dist/spec/catalogs/chart-types/radar.json +2 -52
  72. package/dist/spec/catalogs/chart-types/scatter.json +2 -73
  73. package/dist/spec/catalogs/chart-types/stacked-area.json +21 -0
  74. package/dist/spec/catalogs/chart-types/stacked-bar.json +22 -0
  75. package/dist/spec/catalogs/chart-types/stacked-column.json +22 -0
  76. package/dist/spec/catalogs/chart-types/stacked-line-with-markers.json +22 -0
  77. package/dist/spec/catalogs/chart-types/stacked-line.json +21 -0
  78. package/dist/spec/catalogs/chart-types/treemap.json +2 -48
  79. package/dist/spec/catalogs/chart-types/waterfall.json +2 -52
  80. package/dist/spec/catalogs/chart-types/world.json +2 -64
  81. package/dist/spec/catalogs/font-schemes/angsana-new.json +2 -2
  82. package/dist/spec/catalogs/font-schemes/aparajita.json +3 -3
  83. package/dist/spec/catalogs/font-schemes/aptos.json +1 -1
  84. package/dist/spec/catalogs/font-schemes/arabic-typesetting.json +2 -2
  85. package/dist/spec/catalogs/font-schemes/arial.json +1 -1
  86. package/dist/spec/catalogs/font-schemes/batang.json +2 -2
  87. package/dist/spec/catalogs/font-schemes/bookman.json +1 -1
  88. package/dist/spec/catalogs/font-schemes/calibri.json +1 -1
  89. package/dist/spec/catalogs/font-schemes/century-schoolbook.json +1 -1
  90. package/dist/spec/catalogs/font-schemes/consolas.json +2 -4
  91. package/dist/spec/catalogs/font-schemes/constantia.json +1 -1
  92. package/dist/spec/catalogs/font-schemes/courier-new.json +2 -4
  93. package/dist/spec/catalogs/font-schemes/daunpenh.json +2 -2
  94. package/dist/spec/catalogs/font-schemes/david.json +2 -2
  95. package/dist/spec/catalogs/font-schemes/dilleniaupc.json +2 -2
  96. package/dist/spec/catalogs/font-schemes/fangsong.json +2 -2
  97. package/dist/spec/catalogs/font-schemes/garamond.json +1 -1
  98. package/dist/spec/catalogs/font-schemes/gautami.json +2 -2
  99. package/dist/spec/catalogs/font-schemes/georgia.json +1 -1
  100. package/dist/spec/catalogs/font-schemes/gisha.json +3 -3
  101. package/dist/spec/catalogs/font-schemes/grandview.json +1 -1
  102. package/dist/spec/catalogs/font-schemes/gungsuh.json +2 -2
  103. package/dist/spec/catalogs/font-schemes/impact.json +1 -1
  104. package/dist/spec/catalogs/font-schemes/index.json +1 -1
  105. package/dist/spec/catalogs/font-schemes/kalinga.json +2 -2
  106. package/dist/spec/catalogs/font-schemes/kartika.json +2 -2
  107. package/dist/spec/catalogs/font-schemes/khmer-ui.json +2 -2
  108. package/dist/spec/catalogs/font-schemes/latha.json +2 -2
  109. package/dist/spec/catalogs/font-schemes/lucida-sans.json +1 -1
  110. package/dist/spec/catalogs/font-schemes/malgun-gothic.json +2 -2
  111. package/dist/spec/catalogs/font-schemes/mangal.json +4 -5
  112. package/dist/spec/catalogs/font-schemes/meiryo.json +2 -2
  113. package/dist/spec/catalogs/font-schemes/microsoft-jhenghei.json +2 -2
  114. package/dist/spec/catalogs/font-schemes/microsoft-yahei.json +5 -5
  115. package/dist/spec/catalogs/font-schemes/mingliu.json +2 -2
  116. package/dist/spec/catalogs/font-schemes/miriam.json +2 -2
  117. package/dist/spec/catalogs/font-schemes/montserrat.json +1 -1
  118. package/dist/spec/catalogs/font-schemes/ms-mincho.json +2 -2
  119. package/dist/spec/catalogs/font-schemes/nirmala-ui.json +2 -2
  120. package/dist/spec/catalogs/font-schemes/noto-naksh-arabic.json +2 -2
  121. package/dist/spec/catalogs/font-schemes/noto-nastaliq-urdu.json +3 -3
  122. package/dist/spec/catalogs/font-schemes/noto-sans-arabic.json +4 -4
  123. package/dist/spec/catalogs/font-schemes/noto-sans-armenian.json +2 -2
  124. package/dist/spec/catalogs/font-schemes/noto-sans-bengali.json +3 -3
  125. package/dist/spec/catalogs/font-schemes/noto-sans-devangari.json +3 -3
  126. package/dist/spec/catalogs/font-schemes/noto-sans-ethiopic.json +2 -2
  127. package/dist/spec/catalogs/font-schemes/noto-sans-georgian.json +2 -2
  128. package/dist/spec/catalogs/font-schemes/noto-sans-gujarati.json +2 -2
  129. package/dist/spec/catalogs/font-schemes/noto-sans-gurmukhi.json +2 -2
  130. package/dist/spec/catalogs/font-schemes/noto-sans-hebrew.json +2 -2
  131. package/dist/spec/catalogs/font-schemes/noto-sans-jp.json +2 -2
  132. package/dist/spec/catalogs/font-schemes/noto-sans-kannada.json +2 -2
  133. package/dist/spec/catalogs/font-schemes/noto-sans-khmer.json +2 -2
  134. package/dist/spec/catalogs/font-schemes/noto-sans-kr.json +2 -2
  135. package/dist/spec/catalogs/font-schemes/noto-sans-malayalam.json +2 -2
  136. package/dist/spec/catalogs/font-schemes/noto-sans-mongolian.json +2 -2
  137. package/dist/spec/catalogs/font-schemes/noto-sans-oriya.json +2 -2
  138. package/dist/spec/catalogs/font-schemes/noto-sans-sc.json +5 -5
  139. package/dist/spec/catalogs/font-schemes/noto-sans-tamil.json +2 -2
  140. package/dist/spec/catalogs/font-schemes/noto-sans-tc.json +2 -2
  141. package/dist/spec/catalogs/font-schemes/noto-sans-telugu.json +2 -2
  142. package/dist/spec/catalogs/font-schemes/noto-sans-thai.json +2 -2
  143. package/dist/spec/catalogs/font-schemes/noto-sans.json +5 -5
  144. package/dist/spec/catalogs/font-schemes/nyala.json +2 -2
  145. package/dist/spec/catalogs/font-schemes/open-sans.json +1 -1
  146. package/dist/spec/catalogs/font-schemes/pmingliu.json +2 -2
  147. package/dist/spec/catalogs/font-schemes/poppins.json +1 -1
  148. package/dist/spec/catalogs/font-schemes/pt-serif.json +1 -1
  149. package/dist/spec/catalogs/font-schemes/raavi.json +2 -2
  150. package/dist/spec/catalogs/font-schemes/raleway.json +1 -1
  151. package/dist/spec/catalogs/font-schemes/roboto.json +1 -1
  152. package/dist/spec/catalogs/font-schemes/rockwell.json +1 -1
  153. package/dist/spec/catalogs/font-schemes/sakkal-majalla.json +4 -4
  154. package/dist/spec/catalogs/font-schemes/seaford.json +1 -1
  155. package/dist/spec/catalogs/font-schemes/segoe-ui-light.json +1 -1
  156. package/dist/spec/catalogs/font-schemes/segoe-ui.json +1 -1
  157. package/dist/spec/catalogs/font-schemes/shonar-bangla.json +3 -3
  158. package/dist/spec/catalogs/font-schemes/shruti.json +2 -2
  159. package/dist/spec/catalogs/font-schemes/simsun.json +2 -2
  160. package/dist/spec/catalogs/font-schemes/skeena.json +1 -1
  161. package/dist/spec/catalogs/font-schemes/sylfaen.json +3 -3
  162. package/dist/spec/catalogs/font-schemes/tahoma.json +1 -1
  163. package/dist/spec/catalogs/font-schemes/tenorite.json +1 -1
  164. package/dist/spec/catalogs/font-schemes/times-new-roman.json +1 -1
  165. package/dist/spec/catalogs/font-schemes/traditional-arabic.json +4 -4
  166. package/dist/spec/catalogs/font-schemes/trebuchet-ms.json +1 -1
  167. package/dist/spec/catalogs/font-schemes/tunga.json +2 -2
  168. package/dist/spec/catalogs/font-schemes/verdana.json +1 -1
  169. package/dist/spec/catalogs/font-schemes/vrinda.json +2 -2
  170. package/dist/spec/catalogs/font-schemes/yu-gothic.json +2 -2
  171. package/dist/spec/catalogs/layouts/action-plan.json +9 -15
  172. package/dist/spec/catalogs/layouts/agenda.json +8 -15
  173. package/dist/spec/catalogs/layouts/appendix-index.json +7 -15
  174. package/dist/spec/catalogs/layouts/architecture-stack.json +9 -15
  175. package/dist/spec/catalogs/layouts/assumptions-dependencies.json +9 -15
  176. package/dist/spec/catalogs/layouts/before-after.json +9 -15
  177. package/dist/spec/catalogs/layouts/benchmark-target-table.json +9 -15
  178. package/dist/spec/catalogs/layouts/blank.json +11 -1
  179. package/dist/spec/catalogs/layouts/bullet-list.json +9 -15
  180. package/dist/spec/catalogs/layouts/business-model-canvas.json +9 -15
  181. package/dist/spec/catalogs/layouts/capability-map.json +11 -16
  182. package/dist/spec/catalogs/layouts/case-study.json +9 -15
  183. package/dist/spec/catalogs/layouts/chapter-recap.json +8 -15
  184. package/dist/spec/catalogs/layouts/chart-1x-title-center.json +30 -0
  185. package/dist/spec/catalogs/layouts/chart-1x.json +15 -0
  186. package/dist/spec/catalogs/layouts/chart-2x-title-center.json +36 -0
  187. package/dist/spec/catalogs/layouts/chart-2x-vertical-title-left.json +37 -0
  188. package/dist/spec/catalogs/layouts/chart-2x.json +15 -0
  189. package/dist/spec/catalogs/layouts/chart-3x-bottom-vertical-title-left.json +46 -0
  190. package/dist/spec/catalogs/layouts/chart-3x-left-title-center.json +45 -0
  191. package/dist/spec/catalogs/layouts/chart-3x-left.json +40 -0
  192. package/dist/spec/catalogs/layouts/chart-3x-right-title-center.json +45 -0
  193. package/dist/spec/catalogs/layouts/chart-3x-right.json +40 -0
  194. package/dist/spec/catalogs/layouts/chart-3x-title-center.json +39 -0
  195. package/dist/spec/catalogs/layouts/chart-3x-top-vertical-title-left.json +46 -0
  196. package/dist/spec/catalogs/layouts/chart-3x.json +15 -0
  197. package/dist/spec/catalogs/layouts/circular-lifecycle.json +9 -15
  198. package/dist/spec/catalogs/layouts/code-1x.json +10 -0
  199. package/dist/spec/catalogs/layouts/comparison.json +9 -15
  200. package/dist/spec/catalogs/layouts/competitive-landscape.json +11 -16
  201. package/dist/spec/catalogs/layouts/customer-journey.json +11 -16
  202. package/dist/spec/catalogs/layouts/customer-logos.json +9 -15
  203. package/dist/spec/catalogs/layouts/dashboard.json +9 -15
  204. package/dist/spec/catalogs/layouts/data-visualization.json +9 -15
  205. package/dist/spec/catalogs/layouts/decision-tree.json +11 -16
  206. package/dist/spec/catalogs/layouts/detailed-data-table.json +9 -15
  207. package/dist/spec/catalogs/layouts/ecosystem-map.json +11 -16
  208. package/dist/spec/catalogs/layouts/executive-decision.json +9 -15
  209. package/dist/spec/catalogs/layouts/executive-summary.json +9 -15
  210. package/dist/spec/catalogs/layouts/extract/canonical.json +7 -16
  211. package/dist/spec/catalogs/layouts/faq.json +9 -15
  212. package/dist/spec/catalogs/layouts/feature-comparison-matrix.json +9 -15
  213. package/dist/spec/catalogs/layouts/forecast-scenario.json +9 -15
  214. package/dist/spec/catalogs/layouts/funnel-pyramid.json +9 -15
  215. package/dist/spec/catalogs/layouts/gantt-project-plan.json +9 -15
  216. package/dist/spec/catalogs/layouts/geographic-map.json +10 -15
  217. package/dist/spec/catalogs/layouts/heatmap-cohort.json +9 -15
  218. package/dist/spec/catalogs/layouts/hub-and-spoke.json +9 -15
  219. package/dist/spec/catalogs/layouts/icon-grid.json +11 -16
  220. package/dist/spec/catalogs/layouts/image-1x-crop-title-center.json +32 -0
  221. package/dist/spec/catalogs/layouts/image-1x-crop-title-left.json +32 -0
  222. package/dist/spec/catalogs/layouts/image-1x-crop.json +27 -0
  223. package/dist/spec/catalogs/layouts/image-1x-fit-title-center.json +32 -0
  224. package/dist/spec/catalogs/layouts/image-1x.json +14 -1
  225. package/dist/spec/catalogs/layouts/image-2x-crop-title-center.json +38 -0
  226. package/dist/spec/catalogs/layouts/image-2x-crop-vertical-title-left.json +39 -0
  227. package/dist/spec/catalogs/layouts/image-2x-crop-vertical.json +34 -0
  228. package/dist/spec/catalogs/layouts/image-2x-crop.json +33 -0
  229. package/dist/spec/catalogs/layouts/image-2x-fit-title-center.json +38 -0
  230. package/dist/spec/catalogs/layouts/image-2x-fit-vertical-title-left.json +39 -0
  231. package/dist/spec/catalogs/layouts/image-2x-fit-vertical.json +34 -0
  232. package/dist/spec/catalogs/layouts/image-2x.json +15 -2
  233. package/dist/spec/catalogs/layouts/image-3x-crop-title-center.json +41 -0
  234. package/dist/spec/catalogs/layouts/image-3x-crop-vertical-title-left.json +42 -0
  235. package/dist/spec/catalogs/layouts/image-3x-crop-vertical.json +37 -0
  236. package/dist/spec/catalogs/layouts/image-3x-crop.json +36 -0
  237. package/dist/spec/catalogs/layouts/image-3x-fit-title-center.json +41 -0
  238. package/dist/spec/catalogs/layouts/image-3x-fit-vertical-title-left.json +42 -0
  239. package/dist/spec/catalogs/layouts/image-3x-fit-vertical.json +37 -0
  240. package/dist/spec/catalogs/layouts/image-3x-fit.json +36 -0
  241. package/dist/spec/catalogs/layouts/image-3x.json +16 -3
  242. package/dist/spec/catalogs/layouts/image-focus.json +13 -18
  243. package/dist/spec/catalogs/layouts/image-only-1x-crop-title-center.json +31 -0
  244. package/dist/spec/catalogs/layouts/image-only-1x-fit-title-center.json +31 -0
  245. package/dist/spec/catalogs/layouts/image-only-2x-crop-title-center.json +37 -0
  246. package/dist/spec/catalogs/layouts/image-only-2x-crop-vertical-title-left.json +38 -0
  247. package/dist/spec/catalogs/layouts/image-only-2x-crop-vertical.json +33 -0
  248. package/dist/spec/catalogs/layouts/image-only-2x-fit-title-center.json +37 -0
  249. package/dist/spec/catalogs/layouts/image-only-2x-fit-vertical-title-left.json +38 -0
  250. package/dist/spec/catalogs/layouts/image-only-2x-fit-vertical.json +33 -0
  251. package/dist/spec/catalogs/layouts/image-only-3x-crop-title-center.json +40 -0
  252. package/dist/spec/catalogs/layouts/image-only-3x-crop-vertical-title-left.json +41 -0
  253. package/dist/spec/catalogs/layouts/image-only-3x-crop-vertical.json +36 -0
  254. package/dist/spec/catalogs/layouts/image-only-3x-crop.json +35 -0
  255. package/dist/spec/catalogs/layouts/image-only-3x-fit-title-center.json +40 -0
  256. package/dist/spec/catalogs/layouts/image-only-3x-fit-vertical-title-left.json +41 -0
  257. package/dist/spec/catalogs/layouts/image-only-3x-fit-vertical.json +36 -0
  258. package/dist/spec/catalogs/layouts/image-only-3x-fit.json +35 -0
  259. package/dist/spec/catalogs/layouts/index.json +1041 -151
  260. package/dist/spec/catalogs/layouts/key-takeaway.json +8 -15
  261. package/dist/spec/catalogs/layouts/kpi-scorecard.json +9 -15
  262. package/dist/spec/catalogs/layouts/list-1x-box-title-center.json +32 -0
  263. package/dist/spec/catalogs/layouts/list-1x-box-vertical-title-center.json +33 -0
  264. package/dist/spec/catalogs/layouts/list-1x-box-vertical-title-left.json +33 -0
  265. package/dist/spec/catalogs/layouts/list-1x-box-vertical.json +28 -0
  266. package/dist/spec/catalogs/layouts/list-1x-box.json +27 -0
  267. package/dist/spec/catalogs/layouts/list-1x-itemimage-title-center.json +32 -0
  268. package/dist/spec/catalogs/layouts/list-1x-itemimage-vertical-title-center.json +33 -0
  269. package/dist/spec/catalogs/layouts/list-1x-title-center.json +31 -0
  270. package/dist/spec/catalogs/layouts/list-1x-vertical-title-center.json +32 -0
  271. package/dist/spec/catalogs/layouts/list-1x.json +17 -1
  272. package/dist/spec/catalogs/layouts/list-2x-box-title-center.json +38 -0
  273. package/dist/spec/catalogs/layouts/list-2x-box-vertical-title-center.json +39 -0
  274. package/dist/spec/catalogs/layouts/list-2x-box-vertical-title-left.json +39 -0
  275. package/dist/spec/catalogs/layouts/list-2x-box-vertical.json +34 -0
  276. package/dist/spec/catalogs/layouts/list-2x-box.json +33 -0
  277. package/dist/spec/catalogs/layouts/list-2x-itemimage-title-center.json +38 -0
  278. package/dist/spec/catalogs/layouts/list-2x-itemimage-vertical-title-center.json +39 -0
  279. package/dist/spec/catalogs/layouts/list-2x-itemimage-vertical-title-left.json +39 -0
  280. package/dist/spec/catalogs/layouts/list-2x-itemimage-vertical.json +34 -0
  281. package/dist/spec/catalogs/layouts/list-2x-title-center.json +37 -0
  282. package/dist/spec/catalogs/layouts/list-2x-vertical-title-center.json +38 -0
  283. package/dist/spec/catalogs/layouts/list-2x-vertical-title-left.json +38 -0
  284. package/dist/spec/catalogs/layouts/list-2x-vertical.json +33 -0
  285. package/dist/spec/catalogs/layouts/list-2x.json +16 -0
  286. package/dist/spec/catalogs/layouts/list-3x-box-title-center.json +41 -0
  287. package/dist/spec/catalogs/layouts/list-3x-box-vertical-title-center.json +42 -0
  288. package/dist/spec/catalogs/layouts/list-3x-box-vertical-title-left.json +42 -0
  289. package/dist/spec/catalogs/layouts/list-3x-box-vertical.json +37 -0
  290. package/dist/spec/catalogs/layouts/list-3x-box.json +36 -0
  291. package/dist/spec/catalogs/layouts/list-3x-itemimage-title-center.json +41 -0
  292. package/dist/spec/catalogs/layouts/list-3x-itemimage-vertical-title-center.json +42 -0
  293. package/dist/spec/catalogs/layouts/list-3x-itemimage-vertical-title-left.json +42 -0
  294. package/dist/spec/catalogs/layouts/list-3x-itemimage-vertical.json +37 -0
  295. package/dist/spec/catalogs/layouts/list-3x-itemimage.json +36 -0
  296. package/dist/spec/catalogs/layouts/list-3x-title-center.json +40 -0
  297. package/dist/spec/catalogs/layouts/list-3x-vertical-title-center.json +41 -0
  298. package/dist/spec/catalogs/layouts/list-3x-vertical-title-left.json +41 -0
  299. package/dist/spec/catalogs/layouts/list-3x-vertical.json +36 -0
  300. package/dist/spec/catalogs/layouts/list-3x.json +16 -0
  301. package/dist/spec/catalogs/layouts/list-4x-box-title-center.json +45 -0
  302. package/dist/spec/catalogs/layouts/list-4x-box-vertical-title-center.json +45 -0
  303. package/dist/spec/catalogs/layouts/list-4x-box-vertical-title-left.json +45 -0
  304. package/dist/spec/catalogs/layouts/list-4x-box-vertical.json +40 -0
  305. package/dist/spec/catalogs/layouts/list-4x-box.json +40 -0
  306. package/dist/spec/catalogs/layouts/list-4x-itemimage-title-center.json +45 -0
  307. package/dist/spec/catalogs/layouts/list-4x-itemimage-vertical-title-center.json +45 -0
  308. package/dist/spec/catalogs/layouts/list-4x-itemimage-vertical-title-left.json +45 -0
  309. package/dist/spec/catalogs/layouts/list-4x-itemimage-vertical.json +40 -0
  310. package/dist/spec/catalogs/layouts/list-4x-title-center.json +44 -0
  311. package/dist/spec/catalogs/layouts/list-4x-vertical-title-center.json +44 -0
  312. package/dist/spec/catalogs/layouts/list-4x-vertical-title-left.json +44 -0
  313. package/dist/spec/catalogs/layouts/list-4x-vertical.json +39 -0
  314. package/dist/spec/catalogs/layouts/list-4x.json +16 -0
  315. package/dist/spec/catalogs/layouts/list-5x-box-title-center.json +48 -0
  316. package/dist/spec/catalogs/layouts/list-5x-box-vertical-title-center.json +50 -0
  317. package/dist/spec/catalogs/layouts/list-5x-box-vertical-title-left.json +50 -0
  318. package/dist/spec/catalogs/layouts/list-5x-box-vertical.json +45 -0
  319. package/dist/spec/catalogs/layouts/list-5x-box.json +43 -0
  320. package/dist/spec/catalogs/layouts/list-5x-itemimage-title-center.json +48 -0
  321. package/dist/spec/catalogs/layouts/list-5x-itemimage-vertical-title-center.json +48 -0
  322. package/dist/spec/catalogs/layouts/list-5x-itemimage-vertical-title-left.json +48 -0
  323. package/dist/spec/catalogs/layouts/list-5x-itemimage-vertical.json +43 -0
  324. package/dist/spec/catalogs/layouts/list-5x-title-center.json +47 -0
  325. package/dist/spec/catalogs/layouts/list-5x-vertical-title-center.json +47 -0
  326. package/dist/spec/catalogs/layouts/list-5x-vertical-title-left.json +47 -0
  327. package/dist/spec/catalogs/layouts/list-5x-vertical.json +42 -0
  328. package/dist/spec/catalogs/layouts/list-5x.json +16 -0
  329. package/dist/spec/catalogs/layouts/list-6x-box-title-center.json +51 -0
  330. package/dist/spec/catalogs/layouts/list-6x-box-vertical-title-center.json +53 -0
  331. package/dist/spec/catalogs/layouts/list-6x-box-vertical-title-left.json +53 -0
  332. package/dist/spec/catalogs/layouts/list-6x-box-vertical.json +48 -0
  333. package/dist/spec/catalogs/layouts/list-6x-box.json +46 -0
  334. package/dist/spec/catalogs/layouts/list-6x-itemimage-title-center.json +51 -0
  335. package/dist/spec/catalogs/layouts/list-6x-itemimage-vertical-title-center.json +51 -0
  336. package/dist/spec/catalogs/layouts/list-6x-itemimage-vertical-title-left.json +51 -0
  337. package/dist/spec/catalogs/layouts/list-6x-itemimage-vertical.json +46 -0
  338. package/dist/spec/catalogs/layouts/list-6x-title-center.json +50 -0
  339. package/dist/spec/catalogs/layouts/list-6x-vertical-title-center.json +50 -0
  340. package/dist/spec/catalogs/layouts/list-6x-vertical-title-left.json +50 -0
  341. package/dist/spec/catalogs/layouts/list-6x-vertical.json +45 -0
  342. package/dist/spec/catalogs/layouts/list-6x.json +16 -0
  343. package/dist/spec/catalogs/layouts/logic-tree.json +9 -15
  344. package/dist/spec/catalogs/layouts/market-size.json +9 -15
  345. package/dist/spec/catalogs/layouts/matrix-quadrant.json +9 -15
  346. package/dist/spec/catalogs/layouts/media-1x.json +11 -1
  347. package/dist/spec/catalogs/layouts/network-dependency-map.json +10 -15
  348. package/dist/spec/catalogs/layouts/number-1x-box-title-center.json +30 -0
  349. package/dist/spec/catalogs/layouts/number-1x-box.json +25 -0
  350. package/dist/spec/catalogs/layouts/number-1x-title-center.json +29 -0
  351. package/dist/spec/catalogs/layouts/number-1x.json +16 -1
  352. package/dist/spec/catalogs/layouts/number-2x-box-title-center.json +36 -0
  353. package/dist/spec/catalogs/layouts/number-2x-box.json +31 -0
  354. package/dist/spec/catalogs/layouts/number-2x-title-center.json +35 -0
  355. package/dist/spec/catalogs/layouts/number-2x.json +15 -0
  356. package/dist/spec/catalogs/layouts/number-3x-box-title-center.json +39 -0
  357. package/dist/spec/catalogs/layouts/number-3x-box.json +34 -0
  358. package/dist/spec/catalogs/layouts/number-3x-title-center.json +38 -0
  359. package/dist/spec/catalogs/layouts/number-3x.json +15 -0
  360. package/dist/spec/catalogs/layouts/number-4x-box-title-center.json +43 -0
  361. package/dist/spec/catalogs/layouts/number-4x-box.json +38 -0
  362. package/dist/spec/catalogs/layouts/number-4x-title-center.json +42 -0
  363. package/dist/spec/catalogs/layouts/number-4x.json +15 -0
  364. package/dist/spec/catalogs/layouts/number-5x-box-title-center.json +46 -0
  365. package/dist/spec/catalogs/layouts/number-5x-box.json +41 -0
  366. package/dist/spec/catalogs/layouts/number-5x-title-center.json +45 -0
  367. package/dist/spec/catalogs/layouts/number-5x.json +15 -0
  368. package/dist/spec/catalogs/layouts/number-6x-box-title-center.json +49 -0
  369. package/dist/spec/catalogs/layouts/number-6x-box.json +44 -0
  370. package/dist/spec/catalogs/layouts/number-6x-title-center.json +48 -0
  371. package/dist/spec/catalogs/layouts/number-6x.json +15 -0
  372. package/dist/spec/catalogs/layouts/one-column-explainer.json +9 -15
  373. package/dist/spec/catalogs/layouts/org-chart.json +9 -15
  374. package/dist/spec/catalogs/layouts/persona-grid.json +11 -16
  375. package/dist/spec/catalogs/layouts/photo-grid.json +14 -19
  376. package/dist/spec/catalogs/layouts/pricing-table.json +9 -15
  377. package/dist/spec/catalogs/layouts/process-flow.json +9 -15
  378. package/dist/spec/catalogs/layouts/product-feature.json +11 -16
  379. package/dist/spec/catalogs/layouts/pros-cons.json +9 -15
  380. package/dist/spec/catalogs/layouts/qa-discussion.json +7 -15
  381. package/dist/spec/catalogs/layouts/quote-1x.json +11 -1
  382. package/dist/spec/catalogs/layouts/quote-highlight.json +9 -15
  383. package/dist/spec/catalogs/layouts/raci-matrix.json +9 -15
  384. package/dist/spec/catalogs/layouts/risk-heatmap.json +9 -15
  385. package/dist/spec/catalogs/layouts/roadmap.json +9 -15
  386. package/dist/spec/catalogs/layouts/screenshot-walkthrough.json +11 -16
  387. package/dist/spec/catalogs/layouts/section-divider.json +8 -15
  388. package/dist/spec/catalogs/layouts/single-message-statement.json +8 -15
  389. package/dist/spec/catalogs/layouts/stats-metrics.json +9 -15
  390. package/dist/spec/catalogs/layouts/status-rag.json +9 -15
  391. package/dist/spec/catalogs/layouts/swimlane-process.json +11 -16
  392. package/dist/spec/catalogs/layouts/swot-analysis.json +9 -15
  393. package/dist/spec/catalogs/layouts/table-1x.json +10 -0
  394. package/dist/spec/catalogs/layouts/team-bio.json +11 -16
  395. package/dist/spec/catalogs/layouts/testimonial-grid.json +9 -15
  396. package/dist/spec/catalogs/layouts/text-1x-box-title-center.json +31 -0
  397. package/dist/spec/catalogs/layouts/text-1x-box-title-left.json +31 -0
  398. package/dist/spec/catalogs/layouts/text-1x-center-box-title-center.json +31 -0
  399. package/dist/spec/catalogs/layouts/text-1x-center-box-title-left.json +31 -0
  400. package/dist/spec/catalogs/layouts/text-1x-center-box.json +26 -0
  401. package/dist/spec/catalogs/layouts/text-1x-center-title-center.json +30 -0
  402. package/dist/spec/catalogs/layouts/text-1x-center-title-left.json +30 -0
  403. package/dist/spec/catalogs/layouts/text-1x-center.json +25 -0
  404. package/dist/spec/catalogs/layouts/text-1x-left-box.json +26 -0
  405. package/dist/spec/catalogs/layouts/text-1x-title-center.json +30 -0
  406. package/dist/spec/catalogs/layouts/text-1x.json +11 -1
  407. package/dist/spec/catalogs/layouts/text-2x-box-title-center.json +37 -0
  408. package/dist/spec/catalogs/layouts/text-2x-box-vertical-title-left.json +37 -0
  409. package/dist/spec/catalogs/layouts/text-2x-center-box-title-center.json +37 -0
  410. package/dist/spec/catalogs/layouts/text-2x-center-box-vertical-title-left.json +37 -0
  411. package/dist/spec/catalogs/layouts/text-2x-center-box.json +32 -0
  412. package/dist/spec/catalogs/layouts/text-2x-center-title-center.json +36 -0
  413. package/dist/spec/catalogs/layouts/text-2x-center-vertical-title-left.json +36 -0
  414. package/dist/spec/catalogs/layouts/text-2x-center.json +31 -0
  415. package/dist/spec/catalogs/layouts/text-2x-left-box.json +32 -0
  416. package/dist/spec/catalogs/layouts/text-2x-title-center.json +36 -0
  417. package/dist/spec/catalogs/layouts/text-2x-vertical-title-left.json +36 -0
  418. package/dist/spec/catalogs/layouts/text-2x.json +10 -0
  419. package/dist/spec/catalogs/layouts/text-3x-box-title-center.json +40 -0
  420. package/dist/spec/catalogs/layouts/text-3x-box-vertical-title-left.json +41 -0
  421. package/dist/spec/catalogs/layouts/text-3x-center-box-title-center.json +40 -0
  422. package/dist/spec/catalogs/layouts/text-3x-center-box-vertical-title-left.json +41 -0
  423. package/dist/spec/catalogs/layouts/text-3x-center-box.json +35 -0
  424. package/dist/spec/catalogs/layouts/text-3x-center-title-center.json +39 -0
  425. package/dist/spec/catalogs/layouts/text-3x-center-vertical-title-left.json +40 -0
  426. package/dist/spec/catalogs/layouts/text-3x-center.json +34 -0
  427. package/dist/spec/catalogs/layouts/text-3x-left-box.json +35 -0
  428. package/dist/spec/catalogs/layouts/text-3x-left.json +34 -0
  429. package/dist/spec/catalogs/layouts/text-3x-title-center.json +39 -0
  430. package/dist/spec/catalogs/layouts/text-3x-vertical-title-left.json +40 -0
  431. package/dist/spec/catalogs/layouts/text-3x.json +10 -0
  432. package/dist/spec/catalogs/layouts/thank-you-cta.json +8 -15
  433. package/dist/spec/catalogs/layouts/three-column.json +9 -15
  434. package/dist/spec/catalogs/layouts/timeline-1x.json +11 -1
  435. package/dist/spec/catalogs/layouts/timeline.json +9 -15
  436. package/dist/spec/catalogs/layouts/title-center-box.json +27 -0
  437. package/dist/spec/catalogs/layouts/title-center-image-bottom.json +34 -0
  438. package/dist/spec/catalogs/layouts/title-center-image-top.json +34 -0
  439. package/dist/spec/catalogs/layouts/title-center.json +26 -0
  440. package/dist/spec/catalogs/layouts/title-left-image-bottom.json +34 -0
  441. package/dist/spec/catalogs/layouts/title-left-image-left.json +34 -0
  442. package/dist/spec/catalogs/layouts/title-left-image-right.json +34 -0
  443. package/dist/spec/catalogs/layouts/title-left-image-top.json +34 -0
  444. package/dist/spec/catalogs/layouts/title-slide.json +8 -15
  445. package/dist/spec/catalogs/layouts/title-subtitle.json +11 -1
  446. package/dist/spec/catalogs/layouts/title.json +11 -1
  447. package/dist/spec/catalogs/layouts/two-column.json +9 -15
  448. package/dist/spec/catalogs/layouts/venn-overlap.json +9 -15
  449. package/dist/spec/catalogs/layouts/waterfall-bridge.json +9 -15
  450. package/dist/spec/catalogs/layouts/wheel-radial.json +9 -15
  451. package/dist/spec/catalogs/manifest.json +22 -22
  452. package/dist/spec/catalogs/narratives/board-meeting.json +45 -45
  453. package/dist/spec/catalogs/narratives/business-narrative.json +19 -19
  454. package/dist/spec/catalogs/narratives/business-review.json +27 -27
  455. package/dist/spec/catalogs/narratives/capacity-planning.json +19 -19
  456. package/dist/spec/catalogs/narratives/challenge-resolution.json +17 -17
  457. package/dist/spec/catalogs/narratives/change-story.json +13 -13
  458. package/dist/spec/catalogs/narratives/classic-story.json +17 -17
  459. package/dist/spec/catalogs/narratives/company-intro.json +21 -21
  460. package/dist/spec/catalogs/narratives/conference-talk.json +4 -4
  461. package/dist/spec/catalogs/narratives/data-story.json +13 -14
  462. package/dist/spec/catalogs/narratives/early-startup-pitch.json +27 -27
  463. package/dist/spec/catalogs/narratives/educate.json +21 -21
  464. package/dist/spec/catalogs/narratives/employee-review.json +19 -19
  465. package/dist/spec/catalogs/narratives/failure-analysis.json +23 -23
  466. package/dist/spec/catalogs/narratives/focus.json +27 -27
  467. package/dist/spec/catalogs/narratives/golden-circle.json +4 -4
  468. package/dist/spec/catalogs/narratives/heros-journey.json +13 -13
  469. package/dist/spec/catalogs/narratives/index.json +145 -145
  470. package/dist/spec/catalogs/narratives/innovation.json +21 -21
  471. package/dist/spec/catalogs/narratives/justice.json +25 -25
  472. package/dist/spec/catalogs/narratives/marketing-strategy.json +25 -25
  473. package/dist/spec/catalogs/narratives/performance-improvement-plan.json +19 -19
  474. package/dist/spec/catalogs/narratives/performance-review.json +25 -25
  475. package/dist/spec/catalogs/narratives/persuade.json +17 -17
  476. package/dist/spec/catalogs/narratives/persuasive-sales.json +19 -19
  477. package/dist/spec/catalogs/narratives/pitch-deck.json +6 -6
  478. package/dist/spec/catalogs/narratives/problem-solution.json +5 -5
  479. package/dist/spec/catalogs/narratives/product-launch.json +23 -23
  480. package/dist/spec/catalogs/narratives/project-proposal.json +23 -23
  481. package/dist/spec/catalogs/narratives/pyramid-principle.json +9 -9
  482. package/dist/spec/catalogs/narratives/qbr.json +8 -8
  483. package/dist/spec/catalogs/narratives/rags-to-riches.json +17 -17
  484. package/dist/spec/catalogs/narratives/reveal.json +19 -19
  485. package/dist/spec/catalogs/narratives/scqa.json +4 -4
  486. package/dist/spec/catalogs/narratives/situation-complication-resolution.json +11 -11
  487. package/dist/spec/catalogs/narratives/sparkline.json +11 -11
  488. package/dist/spec/catalogs/narratives/star-method.json +11 -11
  489. package/dist/spec/catalogs/narratives/status-update.json +15 -15
  490. package/dist/spec/catalogs/narratives/strategic-advisory.json +17 -17
  491. package/dist/spec/catalogs/narratives/strategic-narrative.json +4 -4
  492. package/dist/spec/catalogs/narratives/survey-analysis.json +23 -23
  493. package/dist/spec/catalogs/narratives/survival-story.json +23 -23
  494. package/dist/spec/catalogs/narratives/transformation-arc.json +4 -4
  495. package/dist/spec/catalogs/narratives/trend-analysis.json +17 -17
  496. package/dist/spec/catalogs/narratives/underdog-victory.json +19 -19
  497. package/dist/spec/catalogs/narratives/venture-pitch.json +27 -27
  498. package/dist/spec/catalogs/narratives/vision-roadmap.json +13 -13
  499. package/dist/spec/catalogs/narratives/weekly-progress.json +23 -23
  500. package/dist/spec/catalogs/narratives/what-so-what-now-what.json +9 -9
  501. package/dist/spec/openapi.yaml +4 -7
  502. package/dist/spec/reference/engine-defaults.json +22 -30
  503. package/dist/spec/reference/engine-vocabularies.json +1577 -0
  504. package/dist/spec/schemas/audience.schema.json +6 -27
  505. package/dist/spec/schemas/catalog-index.schema.json +2 -2
  506. package/dist/spec/schemas/catalog-manifest.schema.json +3 -3
  507. package/dist/spec/schemas/chart-type.schema.json +17 -321
  508. package/dist/spec/schemas/color-scheme.schema.json +23 -36
  509. package/dist/spec/schemas/finding.schema.json +191 -0
  510. package/dist/spec/schemas/font-scheme.schema.json +17 -75
  511. package/dist/spec/schemas/language.schema.json +7 -28
  512. package/dist/spec/schemas/layout.schema.json +130 -184
  513. package/dist/spec/schemas/narrative.schema.json +20 -48
  514. package/dist/spec/schemas/opf.schema.json +678 -701
  515. package/dist/spec/schemas/purpose.schema.json +5 -26
  516. package/dist/spec/schemas/social-platform.schema.json +2 -48
  517. package/dist/spec/schemas/theme.schema.json +13 -38
  518. package/dist/spec/schemas/tone.schema.json +4 -25
  519. package/dist/spec-files.d.ts +1 -1
  520. package/dist/spec-files.js +1 -1
  521. package/dist/symbol-font-encodings.js +18448 -1
  522. package/dist/types.d.ts +1125 -4
  523. package/dist/validation-types-BOcY9GTs.d.ts +276 -0
  524. package/dist/validator.d.ts +63 -4
  525. package/dist/validator.js +5 -5
  526. package/dist/yaml.d.ts +102 -0
  527. package/dist/yaml.js +322 -0
  528. package/package.json +9 -17
  529. package/dist/audit.d.ts +0 -35
  530. package/dist/audit.js +0 -7
  531. package/dist/catalogs-ywCamd8C.d.ts +0 -1465
  532. package/dist/catalogs.d.ts +0 -2
  533. package/dist/catalogs.js +0 -2
  534. package/dist/chunk-7N4VMAT4.js +0 -1694
  535. package/dist/chunk-FYLJVF7X.js +0 -18448
  536. package/dist/chunk-INU7ZNSB.js +0 -670
  537. package/dist/chunk-IYCJUKWD.js +0 -951
  538. package/dist/chunk-PIQXLBBJ.js +0 -445
  539. package/dist/chunk-S5W34SIJ.js +0 -1
  540. package/dist/lint-B7azdiuE.d.ts +0 -197
  541. package/dist/lint.d.ts +0 -7
  542. package/dist/lint.js +0 -6
  543. package/dist/previews.d.ts +0 -40
  544. package/dist/previews.js +0 -1
  545. package/dist/schemas-BbPonsTz.d.ts +0 -270
  546. package/dist/spec/catalogs/audiences/customers.json +0 -27
  547. package/dist/spec/catalogs/audiences/executives.json +0 -27
  548. package/dist/spec/catalogs/audiences/investors.json +0 -27
  549. package/dist/spec/catalogs/audiences/marketing-team.json +0 -27
  550. package/dist/spec/catalogs/audiences/regulators.json +0 -27
  551. package/dist/spec/catalogs/audiences/sales-team.json +0 -27
  552. package/dist/spec/catalogs/chart-types/100pct-bullet-bar-2x.json +0 -91
  553. package/dist/spec/catalogs/chart-types/100pct-bullet-bar-3x.json +0 -103
  554. package/dist/spec/catalogs/chart-types/100pct-bullet-bar.json +0 -79
  555. package/dist/spec/catalogs/chart-types/100pct-bullet-column-2x.json +0 -91
  556. package/dist/spec/catalogs/chart-types/100pct-bullet-column-3x.json +0 -103
  557. package/dist/spec/catalogs/chart-types/100pct-bullet-column.json +0 -79
  558. package/dist/spec/catalogs/chart-types/100pct-progress-bar.json +0 -105
  559. package/dist/spec/catalogs/chart-types/100pct-stacked-area-2x.json +0 -106
  560. package/dist/spec/catalogs/chart-types/100pct-stacked-area-3x.json +0 -123
  561. package/dist/spec/catalogs/chart-types/100pct-stacked-bar-2x.json +0 -89
  562. package/dist/spec/catalogs/chart-types/100pct-stacked-bar-3x.json +0 -100
  563. package/dist/spec/catalogs/chart-types/100pct-stacked-column-2x.json +0 -95
  564. package/dist/spec/catalogs/chart-types/100pct-stacked-column-3x.json +0 -108
  565. package/dist/spec/catalogs/chart-types/australia.json +0 -76
  566. package/dist/spec/catalogs/chart-types/box-and-whisker-2x.json +0 -136
  567. package/dist/spec/catalogs/chart-types/box-and-whisker-3x.json +0 -168
  568. package/dist/spec/catalogs/chart-types/bullet-bar-2x.json +0 -89
  569. package/dist/spec/catalogs/chart-types/bullet-bar-3x.json +0 -101
  570. package/dist/spec/catalogs/chart-types/bullet-bar.json +0 -77
  571. package/dist/spec/catalogs/chart-types/bullet-column-2x.json +0 -89
  572. package/dist/spec/catalogs/chart-types/bullet-column-3x.json +0 -101
  573. package/dist/spec/catalogs/chart-types/bullet-column.json +0 -77
  574. package/dist/spec/catalogs/chart-types/canada.json +0 -86
  575. package/dist/spec/catalogs/chart-types/clustered-bar-2x.json +0 -89
  576. package/dist/spec/catalogs/chart-types/clustered-column.json +0 -95
  577. package/dist/spec/catalogs/chart-types/dot-plot-2x.json +0 -99
  578. package/dist/spec/catalogs/chart-types/dot-plot-3x.json +0 -124
  579. package/dist/spec/catalogs/chart-types/dot-plot-4x.json +0 -149
  580. package/dist/spec/catalogs/chart-types/dot-plot-5x.json +0 -174
  581. package/dist/spec/catalogs/chart-types/dot-plot-6x.json +0 -199
  582. package/dist/spec/catalogs/chart-types/dot-plot.json +0 -74
  583. package/dist/spec/catalogs/chart-types/dumbbell.json +0 -120
  584. package/dist/spec/catalogs/chart-types/line-2x.json +0 -105
  585. package/dist/spec/catalogs/chart-types/line-3x.json +0 -127
  586. package/dist/spec/catalogs/chart-types/line-with-high-low-and-markers.json +0 -144
  587. package/dist/spec/catalogs/chart-types/line-with-high-low.json +0 -144
  588. package/dist/spec/catalogs/chart-types/line-with-markers-2x.json +0 -106
  589. package/dist/spec/catalogs/chart-types/line-with-markers-3x.json +0 -128
  590. package/dist/spec/catalogs/chart-types/sparkline-2x.json +0 -138
  591. package/dist/spec/catalogs/chart-types/sparkline-3x.json +0 -174
  592. package/dist/spec/catalogs/chart-types/sparkline-4x.json +0 -210
  593. package/dist/spec/catalogs/chart-types/sparkline-5x.json +0 -246
  594. package/dist/spec/catalogs/chart-types/sparkline-6x.json +0 -282
  595. package/dist/spec/catalogs/chart-types/sparkline.json +0 -102
  596. package/dist/spec/catalogs/chart-types/stacked-area-2x.json +0 -106
  597. package/dist/spec/catalogs/chart-types/stacked-area-3x.json +0 -123
  598. package/dist/spec/catalogs/chart-types/stacked-bar-2x.json +0 -89
  599. package/dist/spec/catalogs/chart-types/stacked-bar-3x.json +0 -100
  600. package/dist/spec/catalogs/chart-types/stacked-column-2x.json +0 -95
  601. package/dist/spec/catalogs/chart-types/stacked-column-3x.json +0 -108
  602. package/dist/spec/catalogs/chart-types/stacked-line-2x.json +0 -106
  603. package/dist/spec/catalogs/chart-types/stacked-line-3x.json +0 -123
  604. package/dist/spec/catalogs/chart-types/stacked-line-with-markers-2x.json +0 -107
  605. package/dist/spec/catalogs/chart-types/stacked-line-with-markers-3x.json +0 -124
  606. package/dist/spec/catalogs/chart-types/treemap-2x.json +0 -101
  607. package/dist/spec/catalogs/chart-types/treemap-3x.json +0 -122
  608. package/dist/spec/catalogs/chart-types/united-kingdom.json +0 -68
  609. package/dist/spec/catalogs/chart-types/united-states.json +0 -82
  610. package/dist/spec/catalogs/layouts/image-bleed.json +0 -10
  611. package/dist/validator-CIylxoIm.d.ts +0 -172
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://openpresentation.org/schema/opf/v1",
4
4
  "title": "Presentation",
5
- "description": "Open-source JSON format for describing PowerPoint presentations. Agents describe narratives, content, audience, reusable assets, and design — the engine handles OOXML complexity.\n\nFields that reference catalog records (narrative, language, tone, audience, purpose, design.theme / design.theme.id, design.colorScheme / design.colorScheme.id, design.fontScheme / design.fontScheme.id, Slide.layout, Chart.type, Organization.socials / Speaker.socials) resolve through the catalog system: inline 'catalogs.<kind>.records[]' on the document → 'catalogs.<kind>.source' on the document → engine defaults → the default catalog at https://www.pptx.gallery/<kind>. The default catalog is published by pptx.gallery: the index at https://www.pptx.gallery/<kind>/index.json (also GET https://www.pptx.gallery/<kind> with Accept: application/json) and each record at https://www.pptx.gallery/<kind>/<id>.json. Engines resolve it from the pinned snapshot bundled in @openpresentation/opf (spec/catalogs, pinned by spec/catalogs/manifest.json) and never fetch it at run time; fetching a document's catalogs.<kind>.source is an explicit opt-in by the caller. Every reference resolves to the catalog record's 'id' field. When an OPF document omits a reference entirely, the engine falls back to engine defaults (see /spec/reference/engine-defaults.json for a reference example). The defaults file is engine configuration; it is not part of the OPF document contract and has no JSON Schema.\n\nMost catalog references accept the bare 'id' as a string for the common case (e.g. narrative = 'classic-story', design.colorScheme = 'cool-horizon'). Audience, purpose, tone, design, and language object forms use 'id' as a base catalog reference plus inline overrides. Asset references use 'asset:<id>' strings that point into the top-level assets registry, and content color fields (rich-text runs, styled table cells) accept hex values, color-scheme slot/role names, or 'var:<id>' references into the top-level variables map.",
5
+ "description": "Open-source JSON format for describing PowerPoint presentations. Agents describe narratives, content, audience, reusable assets, and design — the engine handles OOXML complexity.\n\nContent references (Slide.layout, design.theme, design.colorScheme / design.colorScheme.id, design.fontScheme / design.fontScheme.id, narrative, audience, purpose and tone, and the colorScheme / fontScheme a theme record names) point at catalog records. Each is a bare 'id' or a 'name:id' whose prefix names a group in 'catalogs'. A bare id resolves in catalogs.custom, then in the records embedded under catalogs.default, then in the catalog the host registered for catalogs.default.source (or the host's default catalog when 'default' is omitted); 'name:id' resolves in catalogs.<name>, then in the catalog the host registered for its source. A reference that resolves nowhere is the warning opf/unresolved-reference, and the slide uses automatic composition or the engine default. Engines never fetch a catalog: a saved document embeds every record it uses, and hosts register any other catalog by its source string. See docs/default-catalog.md.\n\nchart.type, the language tag and the keys of Organization.socials / Speaker.socials are engine vocabularies, validated directly with no catalog lookup. When a document names no theme, colour scheme or font scheme, every engine uses the same small engine defaults (spec/reference/engine-defaults.json). Asset references use 'asset:<id>' strings that point into the top-level assets registry, and content color fields (rich-text runs, styled table cells) accept hex values, color-scheme slot/role names, or 'var:<id>' references into the top-level variables map.",
6
6
  "type": "object",
7
7
  "required": ["slides"],
8
8
  "additionalProperties": false,
@@ -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,16 +76,22 @@
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: an audiences reference ('executive', or 'acme:board' for a record of the catalogs.acme group), or free-form text ('Series B investors'). A string that is a bare id or 'name:id' is a reference; any other string is free text and is never looked up.\n- A single inline Audience object, for custom audience metadata or overrides on a referenced record. Object form has the fields of https://openpresentation.org/schema/opf-audience/v1.\n- An array of strings and/or inline Audience objects, for more than one audience.\n\nA reference resolves like every content reference (see catalogs); one that resolves nowhere is the warning opf/unresolved-reference. Nothing is drawn from the audience.",
79
80
  "examples": [
80
81
  "executive",
81
82
  "Biology Students and Wildlife Enthusiasts",
82
83
  ["executive"],
83
84
  ["board", "c-suite"],
84
- ["Series B investors", "https://acme.com/decks/audiences/acme-board.json"],
85
+ { "id": "executive", "attentionBudgetMinutes": 20 },
86
+ ["Series B investors", "acme:board"],
85
87
  [
86
88
  { "id": "executive", "attentionBudgetMinutes": 20 },
87
- { "name": "Regional Sales Leaders", "seniority": "director", "technicalFluency": "medium", "decisionPower": "advisory" }
89
+ {
90
+ "name": "Regional Sales Leaders",
91
+ "seniority": "director",
92
+ "technicalFluency": "medium",
93
+ "decisionPower": "advisory"
94
+ }
88
95
  ]
89
96
  ]
90
97
  },
@@ -93,51 +100,39 @@
93
100
  { "type": "string" },
94
101
  { "$ref": "#/$defs/Purpose" }
95
102
  ],
96
- "description": "Primary goal of the presentation. Accepts either:\n- A string shorthand: free-form goal ('Raise a Series B round of $30M'), a purposes catalog id ('decide', 'align'), an HTTPS URL, or a 'pkg:' reference.\n- An inline Purpose object for custom purpose metadata or catalog-backed overrides. Object form mirrors https://openpresentation.org/schema/opf-purpose/v1 without '$schema'.\n\nResolution order for catalog ids and references: inline catalogs.purposes.records[] → catalogs.purposes.source → default catalog at https://www.pptx.gallery/purposes.",
103
+ "description": "Primary goal of the presentation. Accepts either:\n- A string: a purposes reference ('decide', or 'acme:fundraise' for a record of the catalogs.acme group), or a free-form goal ('Raise a Series B round of $30M'). A string that is a bare id or 'name:id' is a reference; any other string is free text and is never looked up.\n- An inline Purpose object for custom purpose metadata or overrides on a referenced record. Object form has the fields of https://openpresentation.org/schema/opf-purpose/v1.\n\nA reference resolves like every content reference (see catalogs); one that resolves nowhere is the warning opf/unresolved-reference. Nothing is drawn from the purpose.",
97
104
  "examples": [
98
105
  "Raise a Series B round of $30M",
99
106
  "align",
100
107
  "educate",
101
- "https://acme.com/decks/purposes/fundraise.json",
102
- "pkg:@acme/decks/purposes/fundraise",
108
+ "acme:fundraise",
103
109
  { "id": "decide", "outcome": "Approve the Q4 hiring plan" },
104
110
  { "name": "Secure renewal", "outcome": "Customer signs a 12-month renewal" }
105
111
  ]
106
112
  },
107
113
  "language": {
108
- "oneOf": [
109
- { "type": "string" },
110
- { "$ref": "#/$defs/Language" }
111
- ],
112
- "description": "Language for the presentation content. Accepts either:\n- A string shorthand: a BCP-47 language tag ('en-US', 'en-GB', 'ja-JP', 'fr'), a languages catalog id ('english', 'japanese'), an HTTPS URL, or a 'pkg:' reference.\n- An inline Language object for custom language metadata or catalog-backed overrides. Object form mirrors https://openpresentation.org/schema/opf-language/v1 without '$schema'.\n\nResolution order for catalog ids and references: inline catalogs.languages.records[] → catalogs.languages.source → default catalog at https://www.pptx.gallery/languages.",
114
+ "oneOf": [{ "$ref": "#/$defs/LanguageTag" }, { "$ref": "#/$defs/Language" }],
115
+ "description": "Language of the presentation content, an engine vocabulary: a BCP-47 language tag, validated directly with no catalog lookup. Accepts either:\n- A BCP-47 tag ('en-US', 'en-GB', 'ja', 'zh-Hans', 'ar-SA').\n- An inline Language object: a 'bcp47' tag plus overrides of what engines know about it (name, ooxmlLang, script, direction, the script font schemes).\n\nEngines know the script, base direction, OOXML language tag and default script fonts of the tags in spec/reference/engine-vocabularies.json; any other well-formed tag is accepted and its script is inferred from the tag. Language names and descriptions are catalog display metadata, not part of the document.",
113
116
  "examples": [
114
117
  "en-US",
115
118
  "en-GB",
116
119
  "es-MX",
117
- "ja-JP",
120
+ "ja",
118
121
  "fr",
119
- "english",
120
- "japanese",
121
- "https://acme.com/decks/languages/en-acme.json",
122
- "pkg:@acme/decks/languages/en-acme",
123
- { "id": "english", "bcp47": "en-US", "fontScheme": "aptos", "googleFontScheme": "roboto" },
122
+ { "bcp47": "en-US", "fontScheme": "aptos", "googleFontScheme": "roboto" },
124
123
  { "bcp47": "ar-SA", "name": "Arabic (Saudi Arabia)", "direction": "rtl", "script": "Arab" }
125
124
  ]
126
125
  },
127
126
  "tone": {
128
- "oneOf": [
129
- { "type": "string" },
130
- { "$ref": "#/$defs/Tone" }
131
- ],
132
- "description": "Desired tone for the presentation. Accepts either:\n- A string shorthand: a tones catalog id ('formal'), an HTTPS URL, or a 'pkg:' reference.\n- An inline Tone object for custom tone metadata or catalog-backed overrides. Object form mirrors https://openpresentation.org/schema/opf-tone/v1 without '$schema'.\n\nResolution order for catalog ids and references: inline catalogs.tones.records[] → catalogs.tones.source → default catalog at https://www.pptx.gallery/tones.",
127
+ "oneOf": [{ "$ref": "#/$defs/CatalogReference" }, { "$ref": "#/$defs/Tone" }],
128
+ "description": "Desired tone for the presentation. Accepts either:\n- A tones reference: a bare id ('formal') or 'name:id' for a record of a named catalogs group.\n- An inline Tone object for custom tone metadata or overrides on a referenced record. Object form has the fields of https://openpresentation.org/schema/opf-tone/v1.\n\nA reference resolves like every content reference (see catalogs); one that resolves nowhere is the warning opf/unresolved-reference. Nothing is drawn from the tone.",
133
129
  "examples": [
134
130
  "formal",
135
131
  "casual",
136
132
  "inspirational",
137
133
  "technical",
138
134
  "persuasive",
139
- "https://acme.com/decks/tones/acme-voice.json",
140
- "pkg:@acme/decks/tones/acme-voice",
135
+ "acme:house-voice",
141
136
  { "id": "formal", "voiceCues": ["Use precise, concise language."] },
142
137
  { "name": "Founder direct", "voiceCues": ["Use first person sparingly.", "Make the ask concrete."] }
143
138
  ]
@@ -164,7 +159,7 @@
164
159
  "duration": {
165
160
  "type": "integer",
166
161
  "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."
162
+ "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
163
  },
169
164
  "tags": {
170
165
  "type": "array",
@@ -178,7 +173,7 @@
178
173
  },
179
174
  "variables": {
180
175
  "$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."
176
+ "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
177
  },
183
178
  "template": {
184
179
  "type": "boolean",
@@ -186,20 +181,9 @@
186
181
  "default": false
187
182
  },
188
183
  "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.",
194
- "examples": [
195
- "classic-story",
196
- "problem-solution",
197
- "scqa",
198
- "pitch-deck",
199
- { "id": "custom-pitch", "name": "Custom Pitch", "beats": [{ "id": "hook", "name": "Hook" }, { "id": "ask", "name": "Ask" }] },
200
- "https://acme.com/decks/narratives/founder-pitch.json",
201
- "pkg:@acme/decks/narratives/founder-pitch"
202
- ]
184
+ "$ref": "#/$defs/CatalogReference",
185
+ "description": "The deck's narrative plan, by reference: a bare narratives id ('classic-story') or 'name:id' for a record of a named catalogs group ('acme:founder-pitch'). The deck holds only this pointer; the plan (the arc, its beats and what each beat must do) lives in the record, which has the fields of https://openpresentation.org/schema/opf-narrative/v1. A narrative the document defines itself is a record in catalogs.custom.narratives, referenced here by its key.\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\nThe reference resolves like every content reference (see catalogs); one that resolves nowhere is the warning opf/unresolved-reference, never an error.\n\nDeck-level concerns that are not part of the storyline (audience, tone, takeaway, duration) live as siblings on the presentation root.",
186
+ "examples": ["classic-story", "problem-solution", "scqa", "pitch-deck", "acme:founder-pitch"]
203
187
  },
204
188
  "slides": {
205
189
  "type": "array",
@@ -210,12 +194,12 @@
210
194
  "references": {
211
195
  "type": "array",
212
196
  "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.",
197
+ "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
198
  "examples": [[{ "id": "gartner-2026", "text": "Gartner, Market Guide for Presentation Tooling, 2026", "url": "https://www.gartner.com" }]]
215
199
  },
216
200
  "datasets": {
217
201
  "$ref": "#/$defs/Datasets",
218
- "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 a lint warning (opf/unused-dataset). See docs/chart-table-data.md."
202
+ "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."
219
203
  },
220
204
  "assets": {
221
205
  "$ref": "#/$defs/Assets",
@@ -223,7 +207,7 @@
223
207
  },
224
208
  "catalogs": {
225
209
  "$ref": "#/$defs/Catalogs",
226
- "description": "Optional per-kind catalog overrides. Each kind may declare a non-default 'source' and/or inline 'records' that override or supplement the default catalog at https://www.pptx.gallery/<kind>. References elsewhere in the document (e.g. design.theme, narrative) resolve through this section before falling back to the default catalog."
210
+ "description": "The catalog records this document embeds, grouped by the catalog they came from: 'default' (the catalog bare ids come from), 'custom' (records the document defines itself) and any number of named catalogs ('acme', referenced as 'acme:<id>'). A saved document embeds every record it references, so it renders the same with no catalog registered. See Catalogs."
227
211
  },
228
212
  "extensions": {
229
213
  "type": "object",
@@ -242,12 +226,12 @@
242
226
  {
243
227
  "acme-logo": "./assets/acme-logo.svg",
244
228
  "watermark": { "src": "./assets/watermark.png", "alt": "Acme watermark" },
245
- "revenue": { "src": "./data/revenue.csv", "format": "csv" }
229
+ "revenue": { "src": "./data/revenue.csv", "mediaType": "text/csv" }
246
230
  }
247
231
  ]
248
232
  },
249
233
  "Asset": {
250
- "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.",
234
+ "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.",
251
235
  "oneOf": [
252
236
  {
253
237
  "type": "string",
@@ -279,11 +263,6 @@
279
263
  "type": "string",
280
264
  "description": "Optional MIME media type when it cannot be inferred from src.",
281
265
  "examples": ["image/svg+xml", "image/png", "text/csv", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
282
- },
283
- "format": {
284
- "type": "string",
285
- "description": "Optional authoring/parsing format hint. Useful when format is more convenient than a full MIME media type.",
286
- "examples": ["csv", "tsv", "json", "xlsx", "svg", "png", "jpg", "webp"]
287
266
  }
288
267
  }
289
268
  }
@@ -291,16 +270,15 @@
291
270
  },
292
271
  "Audience": {
293
272
  "type": "object",
294
- "description": "Inline audience metadata for the presentation. Use 'id' to reference an audiences catalog record and override selected fields, or use 'name' for a custom inline audience.",
273
+ "description": "Inline audience metadata for the presentation. Use 'id' to reference an audiences record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline audience.",
295
274
  "anyOf": [
296
275
  { "required": ["id"] },
297
276
  { "required": ["name"] }
298
277
  ],
299
278
  "properties": {
300
279
  "id": {
301
- "type": "string",
302
- "pattern": "^[a-z][a-z0-9-]*$",
303
- "description": "Optional audiences catalog id to resolve before applying inline overrides.",
280
+ "$ref": "#/$defs/CatalogReference",
281
+ "description": "Optional audiences reference (a bare id or 'name:id') resolved before applying the inline overrides.",
304
282
  "examples": ["executive", "board", "engineering-team"]
305
283
  },
306
284
  "name": {
@@ -338,12 +316,12 @@
338
316
  "recommendedNarratives": {
339
317
  "type": "array",
340
318
  "items": { "type": "string" },
341
- "description": "Soft cross-link: narrative-catalog ids that work well for this audience."
319
+ "description": "Soft cross-link: narrative ids that work well for this audience, for picker UIs. Never resolved, embedded or validated as references."
342
320
  },
343
321
  "recommendedTones": {
344
322
  "type": "array",
345
323
  "items": { "type": "string" },
346
- "description": "Soft cross-link: tone-catalog ids that work well for this audience."
324
+ "description": "Soft cross-link: tone ids that work well for this audience, for picker UIs. Never resolved, embedded or validated as references."
347
325
  },
348
326
  "tags": {
349
327
  "type": "array",
@@ -354,16 +332,15 @@
354
332
  },
355
333
  "Purpose": {
356
334
  "type": "object",
357
- "description": "Inline purpose metadata for the presentation. Use 'id' to reference a purposes catalog record and override selected fields, or use 'name' for a custom inline purpose.",
335
+ "description": "Inline purpose metadata for the presentation. Use 'id' to reference a purposes record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline purpose.",
358
336
  "anyOf": [
359
337
  { "required": ["id"] },
360
338
  { "required": ["name"] }
361
339
  ],
362
340
  "properties": {
363
341
  "id": {
364
- "type": "string",
365
- "pattern": "^[a-z][a-z0-9-]*$",
366
- "description": "Optional purposes catalog id to resolve before applying inline overrides.",
342
+ "$ref": "#/$defs/CatalogReference",
343
+ "description": "Optional purposes reference (a bare id or 'name:id') resolved before applying the inline overrides.",
367
344
  "examples": ["inform", "decide", "align", "persuade", "educate"]
368
345
  },
369
346
  "name": {
@@ -390,12 +367,12 @@
390
367
  "recommendedNarratives": {
391
368
  "type": "array",
392
369
  "items": { "type": "string" },
393
- "description": "Soft cross-link: narrative-catalog ids that work well for this purpose."
370
+ "description": "Soft cross-link: narrative ids that work well for this purpose, for picker UIs. Never resolved, embedded or validated as references."
394
371
  },
395
372
  "recommendedTones": {
396
373
  "type": "array",
397
374
  "items": { "type": "string" },
398
- "description": "Soft cross-link: tone-catalog ids that work well for this purpose."
375
+ "description": "Soft cross-link: tone ids that work well for this purpose, for picker UIs. Never resolved, embedded or validated as references."
399
376
  },
400
377
  "tags": {
401
378
  "type": "array",
@@ -406,31 +383,23 @@
406
383
  },
407
384
  "Language": {
408
385
  "type": "object",
409
- "description": "Inline language metadata for the presentation. Use 'id' to reference a languages catalog record and override selected fields, or use 'bcp47' for a custom language tag without a catalog record.",
410
- "anyOf": [
411
- { "required": ["id"] },
412
- { "required": ["bcp47"] }
413
- ],
386
+ "description": "Inline language metadata for the presentation: a BCP-47 'bcp47' tag plus overrides of what engines know about it. Languages are an engine vocabulary, not catalog records, so there is no 'id'.",
387
+ "required": ["bcp47"],
388
+ "not": { "required": ["id"] },
414
389
  "properties": {
415
- "id": {
416
- "type": "string",
417
- "pattern": "^[a-z][a-z0-9-]*$",
418
- "description": "Optional languages catalog id to resolve before applying inline overrides.",
419
- "examples": ["english", "japanese", "chinese-simplified", "english-uk"]
420
- },
421
390
  "name": {
422
391
  "type": "string",
423
392
  "description": "Human-readable language name.",
424
393
  "examples": ["English", "English (United Kingdom)", "Arabic (Saudi Arabia)"]
425
394
  },
426
395
  "bcp47": {
427
- "type": "string",
396
+ "$ref": "#/$defs/LanguageTag",
428
397
  "description": "BCP-47 language tag used for locale-aware rendering, proofing, and accessibility metadata. Use 'en-GB' for UK English; 'en-UK' is not a valid BCP-47 region form.",
429
398
  "examples": ["en", "en-US", "en-GB", "ar-SA", "zh-Hans"]
430
399
  },
431
400
  "ooxmlLang": {
432
401
  "type": "string",
433
- "description": "Curated culture tag for OOXML text-run language attributes (a:rPr/@lang, a:endParaRPr/@lang), in the language-[Script-]REGION form Office recognizes (e.g. 'ja-JP', 'ar-SA', 'ms-MY', 'nb-NO', 'fil-PH', 'zh-CN'). Engines emit it instead of 'bcp47' when the document names the language by catalog id or by a tag without a region; a region-bearing tag written by the author is emitted as written. Curated rather than inferred so output does not depend on the runtime's locale data.",
402
+ "description": "Culture tag for OOXML text-run language attributes (a:rPr/@lang, a:endParaRPr/@lang), in the language-[Script-]REGION form Office recognizes (e.g. 'ja-JP', 'ar-SA', 'ms-MY', 'nb-NO', 'fil-PH', 'zh-CN'). Without it, engines emit the curated tag of their language vocabulary (spec/reference/engine-vocabularies.json) for a tag without a region, and a region-bearing tag as written. Curated rather than inferred so output does not depend on the runtime's locale data.",
434
403
  "examples": ["en-US", "ja-JP", "ar-SA", "ms-MY", "nb-NO", "zh-CN"]
435
404
  },
436
405
  "code": {
@@ -441,20 +410,20 @@
441
410
  "direction": {
442
411
  "type": "string",
443
412
  "enum": ["ltr", "rtl"],
444
- "description": "Base text direction for the language. When omitted, engines derive it from the script: Arabic (Arab), Hebrew (Hebr), Syriac (Syrc), Thaana (Thaa), N'Ko (Nkoo), Adlam (Adlm), Samaritan (Samr), Mandaic (Mand) and Hanifi Rohingya (Rohg) are right-to-left; every other script is left-to-right. Bundled language records state it explicitly."
413
+ "description": "Base text direction for the language. When omitted, engines derive it from the script: Arabic (Arab), Hebrew (Hebr), Syriac (Syrc), Thaana (Thaa), N'Ko (Nkoo), Adlam (Adlm), Samaritan (Samr), Mandaic (Mand) and Hanifi Rohingya (Rohg) are right-to-left; every other script is left-to-right. The engine language vocabulary (spec/reference/engine-vocabularies.json) states it for the tags it knows."
445
414
  },
446
415
  "script": {
447
416
  "type": "string",
448
- "description": "ISO 15924 script code of the language's writing system. The script selects the OOXML font slot the language's text uses: East Asian scripts (Hans, Hant, Hani, Jpan, Kore, Hang, Hira, Kana, Bopo, Yiii) use the eastAsian slot; complex scripts (for example Arab, Hebr, Syrc, Thaa, Deva, Beng, Guru, Gujr, Orya, Taml, Telu, Knda, Mlym, Sinh, Thai, Laoo, Tibt, Mymr, Khmr, Mong) use the complexScript slot; every other script (Latn, Cyrl, Grek, Armn, Geor, Ethi, ...) uses the latin slot. When omitted, engines infer it from the BCP-47 tag. Bundled language records state it explicitly. See docs/programs/font-fidelity-everywhere/script-font-model.md.",
417
+ "description": "ISO 15924 script code of the language's writing system. The script selects the OOXML font slot the language's text uses: East Asian scripts (Hans, Hant, Hani, Jpan, Kore, Hang, Hira, Kana, Bopo, Yiii) use the eastAsian slot; complex scripts (for example Arab, Hebr, Syrc, Thaa, Deva, Beng, Guru, Gujr, Orya, Taml, Telu, Knda, Mlym, Sinh, Thai, Laoo, Tibt, Mymr, Khmr, Mong) use the complexScript slot; every other script (Latn, Cyrl, Grek, Armn, Geor, Ethi, ...) uses the latin slot. When omitted, engines infer it from the BCP-47 tag. The engine language vocabulary (spec/reference/engine-vocabularies.json) states it for the tags it knows. See docs/programs/font-fidelity-everywhere/script-font-model.md.",
449
418
  "examples": ["Latn", "Cyrl", "Grek", "Arab", "Hebr", "Jpan", "Kore", "Hans", "Hant", "Deva", "Thai"]
450
419
  },
451
420
  "fontScheme": {
452
- "type": "string",
453
- "description": "Default font-scheme id for this language when targeting PowerPoint output. Resolves against catalogs.fontSchemes the same way design.fontScheme or design.fontScheme.id does. Its major/minor families fill the language's script slot (eastAsian or complexScript, chosen by 'script') unless the effective design font scheme sets that slot itself; the latin slot always follows the design font scheme."
421
+ "$ref": "#/$defs/CatalogReference",
422
+ "description": "Font-scheme reference (a bare id or 'name:id') for this language's script slot when targeting PowerPoint output. Its major/minor families fill the language's script slot (eastAsian or complexScript, chosen by 'script') unless the effective design font scheme sets that slot itself; the latin slot always follows the design font scheme. Without it, engines use the default script fonts of their language vocabulary."
454
423
  },
455
424
  "googleFontScheme": {
456
- "type": "string",
457
- "description": "Default font-scheme id for this language when targeting Google Slides output. Resolves against catalogs.fontSchemes the same way design.fontScheme or design.fontScheme.id does. Used in place of 'fontScheme' when resolving script fonts for Google Slides output."
425
+ "$ref": "#/$defs/CatalogReference",
426
+ "description": "Font-scheme reference (a bare id or 'name:id') used in place of 'fontScheme' when resolving script fonts for Google Slides output."
458
427
  },
459
428
  "summary": {
460
429
  "type": "string",
@@ -473,16 +442,15 @@
473
442
  },
474
443
  "Tone": {
475
444
  "type": "object",
476
- "description": "Inline tone metadata for the presentation. Use 'id' to reference a tones catalog record and override selected fields, or use 'name' for a custom inline tone.",
445
+ "description": "Inline tone metadata for the presentation. Use 'id' to reference a tones record (a bare id or 'name:id') and override selected fields, or use 'name' for a custom inline tone.",
477
446
  "anyOf": [
478
447
  { "required": ["id"] },
479
448
  { "required": ["name"] }
480
449
  ],
481
450
  "properties": {
482
451
  "id": {
483
- "type": "string",
484
- "pattern": "^[a-z][a-z0-9-]*$",
485
- "description": "Optional tones catalog id to resolve before applying inline overrides.",
452
+ "$ref": "#/$defs/CatalogReference",
453
+ "description": "Optional tones reference (a bare id or 'name:id') resolved before applying the inline overrides.",
486
454
  "examples": ["formal", "casual", "technical", "persuasive"]
487
455
  },
488
456
  "name": {
@@ -515,7 +483,7 @@
515
483
  "recommendedNarratives": {
516
484
  "type": "array",
517
485
  "items": { "type": "string" },
518
- "description": "Soft cross-link: narrative-catalog ids this tone pairs well with."
486
+ "description": "Soft cross-link: narrative ids this tone pairs well with, for picker UIs. Never resolved, embedded or validated as references."
519
487
  },
520
488
  "tags": {
521
489
  "type": "array",
@@ -526,28 +494,28 @@
526
494
  },
527
495
  "Organization": {
528
496
  "type": "object",
529
- "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.",
497
+ "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.",
530
498
  "required": ["id", "name"],
531
499
  "properties": {
532
500
  "id": {
533
501
  "type": "string",
534
502
  "pattern": "^[a-zA-Z0-9_-]+$",
535
- "description": "Stable identifier for the organization, used to reference it from Speaker.organizationId. Must be unique within the deck.",
503
+ "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).",
536
504
  "examples": ["acme", "acme-corp", "devconf-2026"]
537
505
  },
538
506
  "name": {
539
507
  "type": "string",
540
- "description": "Display name shown on slides.",
508
+ "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.",
541
509
  "examples": ["Acme Corp", "DevConf 2026", "Beta Industries"]
542
510
  },
543
511
  "legalName": {
544
512
  "type": "string",
545
- "description": "Optional legal entity name when it differs from the display name.",
513
+ "description": "Optional legal entity name when it differs from the display name. Never drawn automatically; available as the built-in variable 'organization.legalName'.",
546
514
  "examples": ["Acme Corporation, Inc.", "Beta Industries, LLC"]
547
515
  },
548
516
  "logo": {
549
517
  "$ref": "#/$defs/Asset",
550
- "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).",
518
+ "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.",
551
519
  "examples": [
552
520
  "https://cdn.acme.com/logo.svg",
553
521
  "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9V3iWggAAAAASUVORK5CYII=",
@@ -557,29 +525,29 @@
557
525
  },
558
526
  "domain": {
559
527
  "type": "string",
560
- "description": "Bare internet domain for the organization. Used for footers, contact slides, and engine-driven asset lookups (e.g., favicon-based brand defaults).",
528
+ "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'.",
561
529
  "examples": ["acme.com", "beta-industries.io", "devconf.org"]
562
530
  },
563
531
  "email": {
564
532
  "type": "string",
565
533
  "format": "email",
566
- "description": "General contact email for the organization. Used on contact slides and footer attribution.",
534
+ "description": "General contact email for the organization. Never drawn automatically; available as the built-in variable 'organization.email'.",
567
535
  "examples": ["hello@acme.com", "sales@acme.com", "info@beta-industries.io"]
568
536
  },
569
537
  "phone": {
570
538
  "type": "string",
571
- "description": "Main contact phone number for the organization. E.164 format is recommended.",
539
+ "description": "Main contact phone number for the organization. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'organization.phone'.",
572
540
  "examples": ["+14155551234", "+442071838750"]
573
541
  },
574
542
  "tagline": {
575
543
  "type": "string",
576
- "description": "Short tagline rendered alongside the organization name on cover slides.",
544
+ "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}}'.",
577
545
  "examples": ["Build the future of work", "Infrastructure for AI agents", "Where teams ship faster"]
578
546
  },
579
547
  "role": {
580
548
  "type": "string",
581
549
  "enum": ["primary", "partner", "client", "sponsor", "host"],
582
- "description": "Role of the organization relative to the presentation. When omitted, the single organization or first organization in array form is treated as primary."
550
+ "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."
583
551
  },
584
552
  "socials": {
585
553
  "$ref": "#/$defs/Socials",
@@ -589,28 +557,28 @@
589
557
  },
590
558
  "Speaker": {
591
559
  "type": "object",
592
- "description": "A person presenting the deck. Used for cover slides, bio/intro slides, footer attribution, and panel formats with multiple presenters.",
560
+ "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.",
593
561
  "required": ["id", "name"],
594
562
  "properties": {
595
563
  "id": {
596
564
  "type": "string",
597
565
  "pattern": "^[a-zA-Z0-9_-]+$",
598
- "description": "Stable identifier for the speaker, used for cross-references within the deck. Must be unique within the deck.",
566
+ "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}}'.",
599
567
  "examples": ["alice", "alice-chen", "speaker-1"]
600
568
  },
601
569
  "name": {
602
570
  "type": "string",
603
- "description": "Display name.",
571
+ "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'.",
604
572
  "examples": ["Alice Chen", "Bob Lee", "Dr. Carla Diaz"]
605
573
  },
606
574
  "title": {
607
575
  "type": "string",
608
- "description": "Role or title. Often paired with the speaker's organization on cover slides.",
576
+ "description": "Role or title. Built-in variable 'speaker.title'; the 'speaker' header/footer field draws it after the name ('Alice Chen, VP of Engineering').",
609
577
  "examples": ["VP of Engineering", "Founder & CEO", "Chief Product Officer", "Senior Staff Engineer"]
610
578
  },
611
579
  "photo": {
612
580
  "$ref": "#/$defs/Asset",
613
- "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.",
581
+ "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.",
614
582
  "examples": [
615
583
  "https://cdn.acme.com/headshots/alice.jpg",
616
584
  "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAr/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AJ//2Q==",
@@ -621,35 +589,35 @@
621
589
  "email": {
622
590
  "type": "string",
623
591
  "format": "email",
624
- "description": "Contact email, used on contact slides or footer attribution when appropriate.",
592
+ "description": "Contact email. Never drawn automatically; available as the built-in variable 'speaker.email'.",
625
593
  "examples": ["alice@acme.com", "alice.chen@acme.com"]
626
594
  },
627
595
  "phone": {
628
596
  "type": "string",
629
- "description": "Contact phone number for the speaker. E.164 format is recommended.",
597
+ "description": "Contact phone number for the speaker. E.164 format is recommended. Never drawn automatically; available as the built-in variable 'speaker.phone'.",
630
598
  "examples": ["+14155551234", "+442071838750"]
631
599
  },
632
600
  "bio": {
633
601
  "type": "string",
634
- "description": "Short biographical paragraph for bio or 'about the speaker' slides.",
602
+ "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.",
635
603
  "examples": [
636
604
  "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."
637
605
  ]
638
606
  },
639
607
  "organizationId": {
640
608
  "type": "string",
641
- "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.",
609
+ "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.",
642
610
  "examples": ["acme", "beta", "devconf-2026"]
643
611
  },
644
612
  "socials": {
645
613
  "$ref": "#/$defs/Socials",
646
- "description": "Optional social media handles or URLs for the speaker. Authoring metadata: no header/footer field renders speaker socials yet."
614
+ "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)."
647
615
  }
648
616
  }
649
617
  },
650
618
  "Socials": {
651
619
  "type": "object",
652
- "description": "Social media handles or URLs, keyed by platform id from the 'socialPlatforms' catalog. Each value is a string — either a full URL or a platform handle (e.g., '@acme'). The catalog record for each platform carries the URL pattern and handle prefix that engines use to render and link the profile URL, plus brand color and themed icons as catalog metadata for authoring UIs (engines render the profile URL, not icons or brand colors).\n\nKeys resolve to the 'id' of a 'socialPlatforms' catalog record. Resolution order: inline catalogs.socialPlatforms.records[] → catalogs.socialPlatforms.source → default catalog at https://www.pptx.gallery/social-platforms. Unknown platform keys render with engine fallbacks (the raw value as text). Used both for organization and speaker records.",
620
+ "description": "Social media handles or URLs, keyed by platform. The keys are an engine vocabulary validated directly (no catalog lookup): engines link a handle with the platform's URL pattern and handle prefix (spec/reference/engine-vocabularies.json). Each value is a string, either a full URL or a platform handle (e.g., '@acme'). Brand colours and icons are catalog display metadata for authoring UIs; engines render the profile URL, not icons or brand colours. Used both for organization and speaker records.",
653
621
  "additionalProperties": {
654
622
  "type": "string",
655
623
  "description": "Handle or URL for the platform identified by the property key."
@@ -667,139 +635,8 @@
667
635
  }
668
636
  ],
669
637
  "propertyNames": {
670
- "pattern": "^[a-z][a-z0-9-]*$",
671
- "description": "Platform id (lowercase kebab-case). Common ids include 'linkedin', 'x', 'github', 'youtube', 'instagram', 'facebook', 'tiktok', 'threads', 'mastodon', 'bluesky'."
672
- }
673
- },
674
- "Narrative": {
675
- "type": "object",
676
- "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.",
677
- "properties": {
678
- "id": {
679
- "type": "string",
680
- "pattern": "^[a-z][a-z0-9-]*$",
681
- "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.",
682
- "examples": ["classic-story", "problem-solution", "scqa", "pitch-deck", "qbr", "weekly-progress", "custom-pitch"]
683
- },
684
- "name": {
685
- "type": "string",
686
- "description": "Human-readable narrative name.",
687
- "examples": ["Classic Story", "Problem → Solution", "SCQA", "Founder Pitch"]
688
- },
689
- "summary": {
690
- "type": "string",
691
- "description": "One-sentence description of when and why to use this narrative."
692
- },
693
- "description": {
694
- "type": "string",
695
- "description": "Longer prose describing the narrative arc and ideal use cases. Used by AI-driven generation to seed deck-level direction.",
696
- "examples": [
697
- "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."
698
- ]
699
- },
700
- "audienceFit": {
701
- "type": "array",
702
- "items": { "type": "string" },
703
- "description": "Audiences this narrative works well for. Free-form strings or 'audiences' catalog ids.",
704
- "examples": [["executive", "investor", "customer"]]
705
- },
706
- "durationRange": {
707
- "type": "object",
708
- "description": "Typical talk-length window this narrative suits. Compared by validators against duration.",
709
- "properties": {
710
- "minMinutes": {
711
- "type": "number",
712
- "exclusiveMinimum": 0
713
- },
714
- "maxMinutes": {
715
- "type": "number",
716
- "exclusiveMinimum": 0
717
- }
718
- }
719
- },
720
- "tags": {
721
- "type": "array",
722
- "items": { "type": "string" },
723
- "description": "Free-form labels for filtering and search.",
724
- "examples": [["business", "pitch", "internal"]]
725
- },
726
- "preview": {
727
- "type": "object",
728
- "description": "Visual previews of the narrative, used by picker UIs and inline rendering. All sub-fields are optional.",
729
- "properties": {
730
- "src": {
731
- "type": "string",
732
- "format": "uri",
733
- "description": "Main preview image (PNG/JPG)."
734
- },
735
- "thumbnailSrc": {
736
- "type": "string",
737
- "format": "uri",
738
- "description": "Smaller thumbnail preview for dense grid views."
739
- },
740
- "vectorSrc": {
741
- "type": "string",
742
- "format": "uri",
743
- "description": "SVG / vector preview for crisp scaling at any size."
744
- }
745
- }
746
- },
747
- "beats": {
748
- "type": "array",
749
- "items": { "$ref": "#/$defs/NarrativeBeat" },
750
- "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."
751
- }
752
- }
753
- },
754
- "NarrativeBeat": {
755
- "type": "object",
756
- "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.",
757
- "required": ["id", "name"],
758
- "properties": {
759
- "id": {
760
- "type": "string",
761
- "pattern": "^[a-z][a-z0-9-]*$",
762
- "description": "Stable slug used by Slide.beat to reference this beat. Lowercase kebab-case.",
763
- "examples": ["hook", "problem", "evidence", "ask", "next-steps"]
764
- },
765
- "name": {
766
- "type": "string",
767
- "description": "Human-readable beat name.",
768
- "examples": ["The Hook", "The Problem", "Why Now", "The Ask"]
769
- },
770
- "description": {
771
- "type": "string",
772
- "description": "Curator-written prose that explains what this beat should accomplish.",
773
- "examples": [
774
- "Quantify the pain customers feel today, with one striking stat the audience can repeat afterward."
775
- ]
776
- },
777
- "instructions": {
778
- "type": "string",
779
- "description": "Short author-facing instruction for the beat — typically one phrase. Complements 'description' with a concise directive.",
780
- "examples": ["Capture audience attention", "Introduce problem", "Detail implementation", "Inspire & conclude"]
781
- },
782
- "slideCount": {
783
- "type": "integer",
784
- "minimum": 1,
785
- "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."
786
- },
787
- "slideType": {
788
- "type": "string",
789
- "enum": ["text", "list", "image", "chart", "table", "video", "code", "metric", "quote", "timeline"],
790
- "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."
791
- },
792
- "layoutHint": {
793
- "type": "string",
794
- "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.",
795
- "examples": ["section-divider", "title-slide", "title-left", "two-column", "text-left"]
796
- },
797
- "thoughtCues": {
798
- "type": "array",
799
- "items": { "type": "string" },
800
- "description": "Optional speaker or thinking cues attached to the beat. Surfaced in presenter notes.",
801
- "examples": [["What pain is the audience feeling right now?", "Why hasn't anyone solved this yet?"]]
802
- }
638
+ "enum": ["linkedin", "x", "github", "youtube", "instagram", "facebook", "tiktok", "threads", "mastodon", "bluesky"],
639
+ "description": "Platform key, one of the engine's social platforms."
803
640
  }
804
641
  },
805
642
  "Design": {
@@ -807,29 +644,16 @@
807
644
  "description": "Visual design system applied to the presentation; individual slides may override fields via Slide.design.",
808
645
  "properties": {
809
646
  "theme": {
810
- "oneOf": [
811
- { "type": "string" },
812
- { "$ref": "#/$defs/Theme" }
813
- ],
814
- "description": "Theme for the deck. Accepts two forms:\n- String shorthand: 'design.theme = \"minimal\"'. Bare id, HTTPS URL, or 'pkg:' reference resolved as the 'id' of a 'themes' catalog record.\n- Object form: a Theme with an optional 'id' base reference plus theme-level overrides.\n\nInline overrides on design.colorScheme / design.fontScheme / design.background / design.dimensions take precedence over the resolved theme.\n\nResolution order: inline catalogs.themes.records[] -> catalogs.themes.source -> default catalog at https://www.pptx.gallery/themes.",
815
- "examples": [
816
- "minimal",
817
- "classic",
818
- "dark",
819
- "bold",
820
- "corporate-minimal",
821
- "ocean-depth",
822
- "https://acme.com/decks/themes/acme-brand.json",
823
- "pkg:@acme/decks/themes/acme-brand",
824
- { "id": "minimal", "background": "dark1", "dimensions": "widescreen" }
825
- ]
647
+ "$ref": "#/$defs/CatalogReference",
648
+ "description": "Theme for the deck: a themes reference, a bare id ('minimal') or 'name:id' ('acme:brand'). The theme supplies the default colour scheme, font scheme, background and dimensions; design.colorScheme, design.fontScheme, design.background and design.dimensions override it field by field. A reference that resolves nowhere is the warning opf/unresolved-reference and engines use the engine default theme.",
649
+ "examples": ["minimal", "classic", "dark", "bold", "acme:brand"]
826
650
  },
827
651
  "colorScheme": {
828
652
  "oneOf": [
829
653
  { "type": "string" },
830
654
  { "$ref": "#/$defs/ColorScheme" }
831
655
  ],
832
- "description": "Color scheme for the presentation. Accepts two forms:\n- String shorthand: 'design.colorScheme = \"cool-horizon\"'. Bare id, HTTPS URL, or 'pkg:' reference resolved as the 'id' of a 'colorSchemes' catalog record.\n- Object form: a ColorScheme with an optional 'id' base reference plus slot/role overrides.\n\nResolution order: inline catalogs.colorSchemes.records[] -> catalogs.colorSchemes.source -> default catalog at https://www.pptx.gallery/color-schemes.",
656
+ "description": "Color scheme for the presentation. Accepts two forms:\n- A colorSchemes reference: a bare id ('cool-horizon') or 'name:id' ('acme:ocean').\n- Object form: a ColorScheme with an optional 'id' reference as the base plus slot/role overrides. Without 'id', the overrides apply on top of the engine default colour scheme.\n\nWithout either, the theme's colorScheme applies, then the engine default colour scheme.",
833
657
  "examples": [
834
658
  "cool-horizon",
835
659
  "boost",
@@ -842,11 +666,11 @@
842
666
  { "type": "string" },
843
667
  { "$ref": "#/$defs/FontScheme" }
844
668
  ],
845
- "description": "Font scheme for heading, body, accent, and code text. Accepts two forms:\n- String shorthand: 'design.fontScheme = \"aptos\"'. Bare id, HTTPS URL, or 'pkg:' reference resolved as the 'id' of a 'fontSchemes' catalog record.\n- Object form: a FontScheme with an optional 'id' base reference plus pair/role overrides.\n\nResolution order: inline catalogs.fontSchemes.records[] -> catalogs.fontSchemes.source -> default catalog at https://www.pptx.gallery/font-schemes.",
669
+ "description": "Font scheme for heading, body, accent, and code text. Accepts two forms:\n- A fontSchemes reference: a bare id ('aptos') or 'name:id' ('acme:inter').\n- Object form: a FontScheme with an optional 'id' reference as the base plus pair/role overrides.\n\nWithout either, the theme's fontScheme applies, then the engine default font scheme.",
846
670
  "examples": [
847
671
  "aptos",
848
672
  "tenorite",
849
- { "id": "aptos", "heading": { "family": "Inter", "weight": 700 } }
673
+ { "id": "aptos", "heading": "Inter" }
850
674
  ]
851
675
  },
852
676
  "dimensions": {
@@ -862,7 +686,7 @@
862
686
  { "$ref": "#/$defs/BackgroundShortcut" },
863
687
  { "$ref": "#/$defs/Background" }
864
688
  ],
865
- "description": "Default slide background applied across the deck unless overridden on a slide. String shorthand accepts theme slots ('light1', 'light2', 'dark1', 'dark2') or hex colors; object forms support theme, solid, gradient, image, or pattern fills.",
689
+ "description": "Default slide background applied across the deck unless overridden on a slide (slides.N.design.background); the resolved theme's background is the fallback. A background is the only canvas fill and never moves content. String shorthand accepts theme slots ('light1', 'light2', 'dark1', 'dark2'), hex colors, or an image source starting with 'asset:', 'https://', 'data:', './' or '../' (a cover image); object forms support theme, solid, gradient, image, or pattern fills.",
866
690
  "examples": ["dark1", "#ffff00", { "type": "theme", "slot": "light1" }]
867
691
  },
868
692
  "logo": {
@@ -882,8 +706,8 @@
882
706
  { "$ref": "#/$defs/Asset" },
883
707
  { "$ref": "#/$defs/Watermark" }
884
708
  ],
885
- "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 }.",
886
- "examples": [false, "asset:watermark", { "src": "asset:watermark", "opacity": 0.08 }]
709
+ "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.",
710
+ "examples": [false, "asset:watermark", { "src": "asset:watermark", "opacity": 0.08 }, { "text": "DRAFT", "opacity": 0.1 }]
887
711
  },
888
712
  "header": {
889
713
  "oneOf": [
@@ -907,322 +731,153 @@
907
731
  "titleAlignment": {
908
732
  "type": "string",
909
733
  "enum": ["left", "center", "right"],
910
- "description": "Default horizontal alignment for title placeholders in resolved layouts."
734
+ "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."
911
735
  },
912
736
  "contentAlignment": {
913
737
  "type": "string",
914
738
  "enum": ["left", "center", "right"],
915
- "description": "Default horizontal alignment for body/content regions in resolved layouts."
739
+ "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."
916
740
  },
917
741
  "contentBox": {
918
742
  "type": "boolean",
919
- "description": "Whether body/content regions are rendered inside a visible card or surface."
920
- },
921
- "slideImage": {
922
- "oneOf": [
923
- { "$ref": "#/$defs/Asset" },
924
- {
925
- "type": "object",
926
- "additionalProperties": false,
927
- "required": ["position"],
928
- "properties": {
929
- "src": {
930
- "type": "string",
931
- "description": "Source for the slide-level image. When omitted in a design override, the object only configures how a slide-level image should be placed when one is supplied by a content payload, asset, or renderer convention."
932
- },
933
- "position": {
934
- "type": "string",
935
- "enum": ["background", "top", "bottom", "left", "right"],
936
- "description": "Where the slide-level image sits relative to the content. 'background' is a full-bleed image behind the content. The other positions give the image a band along that edge (see size) and compose headings and content in the rest of the slide."
937
- },
938
- "alt": {
939
- "type": "string",
940
- "description": "Alternative text for the slide-level image. Overrides the alt text of a referenced asset."
941
- },
942
- "fill": {
943
- "type": "string",
944
- "enum": ["crop", "fit"],
945
- "description": "How the image fills its frame: 'crop' covers the frame from the center; 'fit' shows the whole image centered inside it. Overrides design.imageFill for this image. Default: design.imageFill, else 'crop'."
946
- },
947
- "size": {
948
- "type": "number",
949
- "minimum": 0.1,
950
- "maximum": 0.9,
951
- "description": "Share of the slide width (left/right) or height (top/bottom) given to the image band. Ignored for 'background'. Default 0.5."
952
- },
953
- "inset": {
954
- "type": "boolean",
955
- "description": "Place the frame inside the slide padding instead of edge to edge, like a card. Default false."
956
- },
957
- "aspectRatio": {
958
- "type": "number",
959
- "exclusiveMinimum": 0,
960
- "maximum": 10,
961
- "description": "Width-to-height ratio of the frame. The frame becomes the largest centered box with this ratio inside the band or slide, for example 2.39 for a cinematic letterbox or 0.5 for a phone-shaped frame. 'circle' always uses 1."
962
- },
963
- "shape": {
964
- "type": "string",
965
- "enum": ["rectangle", "rounded", "circle", "hexagon"],
966
- "description": "Mask applied to the frame, exported as the picture's native preset geometry (rect, roundRect, ellipse, hexagon). 'circle' makes the frame square. Default 'rectangle'."
967
- },
968
- "cornerRadius": {
969
- "type": "number",
970
- "minimum": 0,
971
- "maximum": 0.5,
972
- "description": "Corner radius for shape 'rounded' as a fraction of the frame's shorter side. Default 0.16667, PowerPoint's roundRect default."
973
- },
974
- "border": {
975
- "type": "object",
976
- "additionalProperties": false,
977
- "required": ["color", "width"],
978
- "properties": {
979
- "color": { "$ref": "#/$defs/ColorRef", "description": "Line color: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference." },
980
- "width": { "type": "number", "minimum": 0, "maximum": 64, "description": "Line width in reference pixels at a 720-pixel short edge, centered on the frame outline like a native picture line. 0 removes the line." }
981
- },
982
- "description": "Solid line along the frame's shape. A thick dark border on a rounded portrait frame gives a device bezel."
983
- },
984
- "opacity": {
985
- "type": "number",
986
- "minimum": 0,
987
- "maximum": 1,
988
- "description": "Image opacity from 0 to 1, exported as a native alphaModFix. The border and overlay keep their own opacity. Default 1."
989
- },
990
- "recolor": {
991
- "oneOf": [
992
- { "type": "string", "const": "grayscale", "description": "Replace each pixel with its luminance, exported as native a:grayscl." },
993
- {
994
- "type": "object",
995
- "additionalProperties": false,
996
- "required": ["dark", "light"],
997
- "properties": {
998
- "dark": { "$ref": "#/$defs/ColorRef", "description": "Color at luminance 0." },
999
- "light": { "$ref": "#/$defs/ColorRef", "description": "Color at luminance 1." }
1000
- },
1001
- "description": "Duotone: map each pixel's luminance linearly from dark to light, exported as native a:duotone."
1002
- }
1003
- ],
1004
- "description": "Color treatment for the image pixels. Luminance uses Rec. 601 weights (0.299, 0.587, 0.114) on sRGB values."
1005
- },
1006
- "overlay": {
1007
- "type": "object",
1008
- "additionalProperties": false,
1009
- "required": ["color", "opacity"],
1010
- "properties": {
1011
- "color": { "$ref": "#/$defs/ColorRef", "description": "Overlay fill color." },
1012
- "opacity": { "type": "number", "minimum": 0, "maximum": 1, "description": "Overlay fill opacity." },
1013
- "edge": { "type": "string", "enum": ["top", "bottom", "left", "right"], "description": "Cover only a band along this edge of the frame, for example a caption strip. Rectangle frames only; other shapes report unsupported-image-treatment and draw no overlay. Omit to cover the whole frame." },
1014
- "size": { "type": "number", "minimum": 0.05, "maximum": 1, "description": "Band share of the frame for an edge overlay. Default 0.3." }
1015
- },
1016
- "description": "Solid scrim drawn over the image in the frame's shape, beneath headings and content, for example to keep overlaid text readable. Exported as a native shape above the picture."
1017
- }
1018
- }
1019
- }
1020
- ],
1021
- "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."
743
+ "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."
1022
744
  },
1023
745
  "contentDirection": {
1024
746
  "type": "string",
1025
747
  "enum": ["horizontal", "vertical"],
1026
- "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."
748
+ "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."
1027
749
  },
1028
750
  "chartPrimary": {
1029
751
  "type": "string",
1030
752
  "enum": ["none", "top", "bottom", "left", "right"],
1031
- "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."
753
+ "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."
1032
754
  },
1033
- "imageFill": {
755
+ "imageFit": {
1034
756
  "type": "string",
1035
- "enum": ["crop", "fit"],
1036
- "description": "How picture placeholders fill their allocated region."
757
+ "enum": ["cover", "contain", "stretch"],
758
+ "description": "Default fit of image blocks (and Slide.image) that set no fit of their own: 'cover' fills the frame and crops what overflows around the block's focus, 'contain' shows the whole picture centered in the frame, 'stretch' scales it to the frame exactly. Effective value: the slide's design, then the deck's design, then the layout record's design.imageFit, then 'cover'. Backgrounds set their own fit."
1037
759
  },
1038
760
  "listBullet": {
1039
761
  "type": "string",
1040
762
  "enum": ["character", "image"],
1041
- "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."
1042
- }
1043
- }
1044
- },
1045
- "Theme": {
1046
- "type": "object",
1047
- "description": "Theme bundle used by the design system. In design.theme, 'id' resolves a themes catalog record as the base; any sibling fields override the resolved theme. The string shorthand on design.theme is equivalent to setting only 'id'.",
1048
- "properties": {
1049
- "id": {
1050
- "type": "string",
1051
- "description": "Theme reference. Resolves to the 'id' of a 'themes' catalog record. Accepts a bare id (lowercase kebab-case, e.g. 'minimal'), an HTTPS URL pointing at a record file, or a 'pkg:' reference. Field overrides on the surrounding Theme object take precedence over the resolved theme.\n\nResolution order: inline catalogs.themes.records[] -> catalogs.themes.source -> default catalog at https://www.pptx.gallery/themes.",
1052
- "examples": ["minimal", "classic", "dark", "bold", "https://acme.com/decks/themes/acme-brand.json", "pkg:@acme/decks/themes/acme-brand"]
1053
- },
1054
- "name": {
1055
- "type": "string",
1056
- "description": "Human-readable theme name shown in pickers.",
1057
- "examples": ["Minimal", "Classic", "Dark", "Bold"]
1058
- },
1059
- "summary": {
1060
- "type": "string",
1061
- "description": "One-sentence positioning of the theme - when to reach for it."
1062
- },
1063
- "description": {
1064
- "type": "string",
1065
- "description": "Longer prose describing what the theme looks and feels like and the kinds of decks it suits."
1066
- },
1067
- "colorScheme": {
1068
- "oneOf": [
1069
- { "type": "string" },
1070
- { "$ref": "#/$defs/ColorScheme" }
1071
- ],
1072
- "description": "Default color scheme for this theme. A string resolves against catalogs.colorSchemes; an object may provide an 'id' base reference plus overrides.",
1073
- "examples": ["cool-horizon", { "id": "cool-horizon", "accent1": "#FF6F61" }]
1074
- },
1075
- "fontScheme": {
1076
- "oneOf": [
1077
- { "type": "string" },
1078
- { "$ref": "#/$defs/FontScheme" }
1079
- ],
1080
- "description": "Default font scheme for this theme. A string resolves against catalogs.fontSchemes; an object may provide an 'id' base reference plus overrides.",
1081
- "examples": ["aptos", { "id": "aptos", "heading": { "family": "Inter", "weight": 700 } }]
1082
- },
1083
- "background": {
1084
- "oneOf": [
1085
- { "$ref": "#/$defs/BackgroundShortcut" },
1086
- { "$ref": "#/$defs/Background" }
1087
- ],
1088
- "description": "Default background for this theme. String shorthand accepts theme slots ('light1', 'light2', 'dark1', 'dark2') or hex colors.",
1089
- "examples": ["dark1", "#ffff00", { "type": "theme", "slot": "dark2" }]
1090
- },
1091
- "dimensions": {
1092
- "oneOf": [
1093
- { "$ref": "#/$defs/DimensionPreset" },
1094
- { "$ref": "#/$defs/Dimensions" }
1095
- ],
1096
- "description": "Default slide size for this theme. A string preset is equivalent to { preset: value }.",
1097
- "examples": ["widescreen", { "preset": "16:9" }]
1098
- },
1099
- "tags": {
1100
- "type": "array",
1101
- "items": { "type": "string" },
1102
- "description": "Free-form labels for filtering and search."
763
+ "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."
1103
764
  }
1104
765
  }
1105
766
  },
1106
767
  "ColorScheme": {
1107
768
  "type": "object",
1108
- "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'.",
769
+ "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 record as the base; slot and role overrides on the same object take precedence over the resolved scheme. The string form of design.colorScheme is equivalent to setting only 'id'.",
1109
770
  "properties": {
1110
771
  "id": {
1111
- "type": "string",
1112
- "description": "Color scheme reference. Resolves to the 'id' of a 'colorSchemes' catalog record. Accepts a bare id (lowercase kebab-case, e.g. 'cool-horizon'), an HTTPS URL pointing at a record file, or a 'pkg:' reference. Slot and role overrides on the surrounding ColorScheme object take precedence over the resolved scheme.\n\nResolution order: inline catalogs.colorSchemes.records[] -> catalogs.colorSchemes.source -> default catalog at https://www.pptx.gallery/color-schemes.",
1113
- "examples": ["cool-horizon", "boost", "burnt-orange", "ocean-depth", "corporate-blue", "https://acme.com/decks/color-schemes/acme.json", "pkg:@acme/decks/color-schemes/acme"]
772
+ "$ref": "#/$defs/CatalogReference",
773
+ "description": "Colour-scheme reference (a bare id or 'name:id') resolved as the base; slot and role overrides on the surrounding ColorScheme object take precedence over the resolved scheme.",
774
+ "examples": ["cool-horizon", "boost", "burnt-orange", "ocean-depth", "corporate-blue"]
1114
775
  },
1115
776
  "accent1": {
1116
- "type": "string",
777
+ "$ref": "#/$defs/HexColor",
1117
778
  "description": "Accent 1 color (hex). Mirrors the OOXML accent1 slot.",
1118
779
  "examples": ["#2874A6", "#FD3223", "#F77F00"]
1119
780
  },
1120
781
  "accent2": {
1121
- "type": "string",
782
+ "$ref": "#/$defs/HexColor",
1122
783
  "description": "Accent 2 color (hex). Mirrors the OOXML accent2 slot.",
1123
784
  "examples": ["#1B4F72", "#0308DB", "#D62828"]
1124
785
  },
1125
786
  "accent3": {
1126
- "type": "string",
787
+ "$ref": "#/$defs/HexColor",
1127
788
  "description": "Accent 3 color (hex). Mirrors the OOXML accent3 slot.",
1128
789
  "examples": ["#5499C7", "#4F4955", "#003049"]
1129
790
  },
1130
791
  "accent4": {
1131
- "type": "string",
792
+ "$ref": "#/$defs/HexColor",
1132
793
  "description": "Accent 4 color (hex). Mirrors the OOXML accent4 slot.",
1133
794
  "examples": ["#7BDBB2", "#A1DB30", "#FCBF49"]
1134
795
  },
1135
796
  "accent5": {
1136
- "type": "string",
797
+ "$ref": "#/$defs/HexColor",
1137
798
  "description": "Accent 5 color (hex). Mirrors the OOXML accent5 slot.",
1138
799
  "examples": ["#3AC67A", "#0682FE", "#EAE2B7"]
1139
800
  },
1140
801
  "accent6": {
1141
- "type": "string",
802
+ "$ref": "#/$defs/HexColor",
1142
803
  "description": "Accent 6 color (hex). Mirrors the OOXML accent6 slot.",
1143
804
  "examples": ["#24A89E", "#FC03BE", "#BFBFBF"]
1144
805
  },
1145
806
  "dark1": {
1146
- "type": "string",
807
+ "$ref": "#/$defs/HexColor",
1147
808
  "description": "Dark 1 color (hex). Typically the deepest neutral; OOXML dark1.",
1148
809
  "examples": ["#000000"]
1149
810
  },
1150
811
  "dark2": {
1151
- "type": "string",
812
+ "$ref": "#/$defs/HexColor",
1152
813
  "description": "Dark 2 color (hex). Secondary dark; OOXML dark2.",
1153
814
  "examples": ["#011842", "#2C2C2C"]
1154
815
  },
1155
816
  "light1": {
1156
- "type": "string",
817
+ "$ref": "#/$defs/HexColor",
1157
818
  "description": "Light 1 color (hex). Typically the slide canvas; OOXML lt1.",
1158
819
  "examples": ["#FFFFFF"]
1159
820
  },
1160
821
  "light2": {
1161
- "type": "string",
822
+ "$ref": "#/$defs/HexColor",
1162
823
  "description": "Light 2 color (hex). Secondary light surface; OOXML lt2.",
1163
824
  "examples": ["#F0F0F0", "#F3F7FF"]
1164
825
  },
1165
826
  "hyperlink": {
1166
- "type": "string",
1167
- "description": "Hyperlink color (hex). OOXML hlink.",
827
+ "$ref": "#/$defs/HexColor",
828
+ "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.",
1168
829
  "examples": ["#0563C1", "#0066CC"]
1169
830
  },
1170
831
  "followedHyperlink": {
1171
- "type": "string",
832
+ "$ref": "#/$defs/HexColor",
1172
833
  "description": "Followed-hyperlink color (hex). OOXML folHlink.",
1173
834
  "examples": ["#954F72", "#551A8B"]
1174
835
  },
1175
836
  "primary": {
1176
- "type": "string",
837
+ "$ref": "#/$defs/HexColor",
1177
838
  "description": "Abstract role: primary brand color (hex). The engine maps this onto an OOXML accent slot when serializing.",
1178
839
  "examples": ["#1E40AF", "#0F172A"]
1179
840
  },
1180
841
  "secondary": {
1181
- "type": "string",
842
+ "$ref": "#/$defs/HexColor",
1182
843
  "description": "Abstract role: secondary brand color (hex).",
1183
844
  "examples": ["#3B82F6", "#64748B"]
1184
845
  },
1185
846
  "accent": {
1186
- "type": "string",
847
+ "$ref": "#/$defs/HexColor",
1187
848
  "description": "Abstract role: accent color used for highlights and emphasis (hex).",
1188
849
  "examples": ["#F59E0B", "#22D3EE"]
1189
850
  },
1190
851
  "background": {
1191
- "type": "string",
1192
- "description": "Abstract role: default slide background color (hex). The engine maps this to one of light1 / light2 / dark1 / dark2 when serializing.",
852
+ "$ref": "#/$defs/HexColor",
853
+ "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.",
1193
854
  "examples": ["#FFFFFF", "#0B1220"]
1194
855
  },
1195
856
  "surface": {
1196
- "type": "string",
1197
- "description": "Abstract role: color for elevated surfaces such as cards and panels (hex).",
857
+ "$ref": "#/$defs/HexColor",
858
+ "description": "Abstract role: color for elevated surfaces such as cards and panels (hex). Overrides the default of light2 (dark2 on a dark slide).",
1198
859
  "examples": ["#F8FAFC", "#1E293B"]
1199
860
  },
1200
861
  "text": {
1201
- "type": "string",
1202
- "description": "Abstract role: primary body text color (hex).",
862
+ "$ref": "#/$defs/HexColor",
863
+ "description": "Abstract role: primary body text color (hex). Overrides dark1 on a light slide only; a dark slide always uses light1.",
1203
864
  "examples": ["#0F172A", "#F8FAFC"]
1204
865
  },
1205
866
  "textSecondary": {
1206
- "type": "string",
1207
- "description": "Abstract role: secondary or muted text color used for captions and supporting copy (hex).",
867
+ "$ref": "#/$defs/HexColor",
868
+ "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).",
1208
869
  "examples": ["#475569", "#94A3B8"]
1209
- },
1210
- "custom": {
1211
- "type": "object",
1212
- "additionalProperties": { "type": "string" },
1213
- "description": "Map of custom named colors for advanced or theme-specific use.",
1214
- "examples": [{ "success": "#10B981", "warning": "#F59E0B", "danger": "#EF4444" }]
1215
870
  }
1216
871
  }
1217
872
  },
1218
873
  "FontScheme": {
1219
874
  "type": "object",
1220
- "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'.",
875
+ "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 record as the base; pair and role overrides on the same object take precedence over the resolved scheme. The string form of design.fontScheme is equivalent to setting only 'id'.",
1221
876
  "properties": {
1222
877
  "id": {
1223
- "type": "string",
1224
- "description": "Font scheme reference. Resolves to the 'id' of a 'fontSchemes' catalog record. Accepts a bare id (lowercase kebab-case, e.g. 'aptos'), an HTTPS URL pointing at a record file, or a 'pkg:' reference. Field overrides on the surrounding FontScheme object take precedence over the resolved scheme.\n\nResolution order: inline catalogs.fontSchemes.records[] -> catalogs.fontSchemes.source -> default catalog at https://www.pptx.gallery/font-schemes.",
1225
- "examples": ["aptos", "tenorite", "seaford", "noto-sans", "modern-sans", "classic-serif", "https://acme.com/decks/font-schemes/acme.json", "pkg:@acme/decks/font-schemes/acme"]
878
+ "$ref": "#/$defs/CatalogReference",
879
+ "description": "Font-scheme reference (a bare id or 'name:id') resolved as the base; field overrides on the surrounding FontScheme object take precedence over the resolved scheme.",
880
+ "examples": ["aptos", "tenorite", "seaford", "noto-sans", "modern-sans", "classic-serif"]
1226
881
  },
1227
882
  "major": {
1228
883
  "type": "string",
@@ -1277,61 +932,43 @@
1277
932
  },
1278
933
  "app": {
1279
934
  "type": "string",
1280
- "enum": ["PowerPoint", "Google Slides"],
1281
- "description": "Target application this font pairing is intended for."
935
+ "enum": ["powerpoint", "google-slides"],
936
+ "description": "Target application this font pairing is intended for. Metadata for pickers and catalog filters: no engine changes its output by it."
1282
937
  },
1283
938
  "languageFamily": {
1284
939
  "type": "string",
1285
- "enum": ["latin", "ea", "cs"],
1286
- "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."
940
+ "enum": ["latin", "ea", "cs", "eastAsian", "complexScript"],
941
+ "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."
1287
942
  },
1288
943
  "heading": {
1289
- "$ref": "#/$defs/Font",
1290
- "description": "Abstract role: font used for slide titles and headings. Maps onto the OOXML major slot when serializing."
944
+ "type": "string",
945
+ "description": "Abstract role: font family name used for slide titles and headings. Maps onto the OOXML major slot when serializing."
1291
946
  },
1292
947
  "body": {
1293
- "$ref": "#/$defs/Font",
1294
- "description": "Abstract role: font used for body copy. Maps onto the OOXML minor slot when serializing."
948
+ "type": "string",
949
+ "description": "Abstract role: font family name used for body copy. Maps onto the OOXML minor slot when serializing."
1295
950
  },
1296
951
  "accent": {
1297
- "$ref": "#/$defs/Font",
1298
- "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."
1299
- },
1300
- "code": {
1301
- "$ref": "#/$defs/Font",
1302
- "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."
1303
- }
1304
- }
1305
- },
1306
- "Font": {
1307
- "type": "object",
1308
- "description": "Specification for a single font role.",
1309
- "required": ["family"],
1310
- "properties": {
1311
- "family": {
1312
952
  "type": "string",
1313
- "description": "Font family name.",
1314
- "examples": ["Inter", "Roboto", "Source Serif Pro", "JetBrains Mono"]
1315
- },
1316
- "weight": {
1317
- "type": "number",
1318
- "description": "Numeric font weight (e.g., 400 for regular, 700 for bold)."
953
+ "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."
1319
954
  },
1320
- "style": {
955
+ "code": {
1321
956
  "type": "string",
1322
- "enum": ["normal", "italic"],
1323
- "description": "Font style."
1324
- },
1325
- "letterSpacing": {
1326
- "type": "number",
1327
- "description": "Letter spacing (tracking) in ems."
957
+ "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 font-scheme 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."
1328
958
  }
1329
959
  }
1330
960
  },
961
+ "SlideDesign": {
962
+ "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.",
963
+ "allOf": [
964
+ { "$ref": "#/$defs/Design" },
965
+ { "not": { "required": ["dimensions"] } }
966
+ ]
967
+ },
1331
968
  "DimensionPreset": {
1332
969
  "type": "string",
1333
- "enum": ["16:9", "4:3", "16:10", "letter", "a4", "widescreen", "standard"],
1334
- "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."
970
+ "enum": ["16:9", "4:3", "16:10", "1:1", "4:5", "9:16", "letter", "a4", "widescreen", "standard"],
971
+ "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)."
1335
972
  },
1336
973
  "Dimensions": {
1337
974
  "type": "object",
@@ -1371,7 +1008,7 @@
1371
1008
  "enum": [
1372
1009
  "accent1", "accent2", "accent3", "accent4", "accent5", "accent6",
1373
1010
  "dark1", "dark2", "light1", "light2", "hyperlink", "followedHyperlink",
1374
- "primary", "secondary", "accent", "background", "surface", "text", "textSecondary"
1011
+ "primary", "secondary", "accent", "background", "surface", "surfaceAlt", "text", "textSecondary"
1375
1012
  ]
1376
1013
  },
1377
1014
  {
@@ -1379,7 +1016,7 @@
1379
1016
  "pattern": "^var:[a-z][a-z0-9-]*$"
1380
1017
  }
1381
1018
  ],
1382
- "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.",
1019
+ "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.",
1383
1020
  "examples": ["#0F4C81", "accent2", "text", "var:risk"]
1384
1021
  },
1385
1022
  "Variables": {
@@ -1390,7 +1027,7 @@
1390
1027
  "additionalProperties": {
1391
1028
  "$ref": "#/$defs/Variable"
1392
1029
  },
1393
- "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.",
1030
+ "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.",
1394
1031
  "examples": [
1395
1032
  {
1396
1033
  "risk": "#B42318",
@@ -1778,10 +1415,17 @@
1778
1415
  "BackgroundShortcut": {
1779
1416
  "oneOf": [
1780
1417
  { "$ref": "#/$defs/ThemeBackgroundSlot" },
1781
- { "$ref": "#/$defs/HexColor" }
1418
+ { "$ref": "#/$defs/HexColor" },
1419
+ { "$ref": "#/$defs/ImageSource" }
1782
1420
  ],
1783
- "description": "String shorthand for a background. Theme slots ('light1', 'light2', 'dark1', 'dark2') are equivalent to { type: 'theme', slot: value }; hex colors are equivalent to { type: 'solid', color: value }.",
1784
- "examples": ["dark1", "light2", "#ffff00"]
1421
+ "description": "String shorthand for a background. Theme slots ('light1', 'light2', 'dark1', 'dark2') are equivalent to { type: 'theme', slot: value }; hex colors are equivalent to { type: 'solid', color: value }; an image source (starting with 'asset:', 'https://', 'data:', './' or '../') is equivalent to { type: 'image', src: value }, a cover image. Any other string is invalid.",
1422
+ "examples": ["dark1", "light2", "#ffff00", "asset:cover-photo", "./images/harbour.jpg"]
1423
+ },
1424
+ "ImageSource": {
1425
+ "type": "string",
1426
+ "pattern": "^(asset:|https://|data:|\\./|\\.\\./)",
1427
+ "description": "An image source in a string shorthand: an 'asset:<id>' reference, an HTTPS URL, a data URI, or a relative path starting with './' or '../' (resolved against the OPF file location).",
1428
+ "examples": ["asset:cover-photo", "https://cdn.acme.com/backgrounds/cover.jpg", "./images/harbour.jpg"]
1785
1429
  },
1786
1430
  "Background": {
1787
1431
  "description": "Background fill applied to slides. Theme backgrounds preserve PowerPoint's color-scheme background choice; other variants represent fixed background fills.",
@@ -1819,7 +1463,7 @@
1819
1463
  "description": "Fixed solid background fill."
1820
1464
  },
1821
1465
  "color": {
1822
- "type": "string",
1466
+ "$ref": "#/$defs/ColorRef",
1823
1467
  "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.",
1824
1468
  "examples": ["#FFFFFF", "#0F172A", "#F8FAFC"]
1825
1469
  },
@@ -1851,14 +1495,15 @@
1851
1495
  },
1852
1496
  "stops": {
1853
1497
  "type": "array",
1854
- "description": "Ordered list of color stops along the gradient.",
1498
+ "minItems": 2,
1499
+ "description": "Ordered list of color stops along the gradient. A gradient needs at least two stops.",
1855
1500
  "items": {
1856
1501
  "type": "object",
1857
1502
  "description": "Color stop along the gradient.",
1858
1503
  "required": ["color", "position"],
1859
1504
  "properties": {
1860
1505
  "color": {
1861
- "type": "string",
1506
+ "$ref": "#/$defs/ColorRef",
1862
1507
  "description": "Stop color: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef).",
1863
1508
  "examples": ["#1E40AF", "#22D3EE"]
1864
1509
  },
@@ -1873,54 +1518,180 @@
1873
1518
  }
1874
1519
  }
1875
1520
  },
1876
- "opacity": {
1521
+ "opacity": {
1522
+ "type": "number",
1523
+ "minimum": 0,
1524
+ "maximum": 1,
1525
+ "description": "Background opacity from 0 (fully transparent) to 1 (fully opaque)."
1526
+ }
1527
+ }
1528
+ },
1529
+ "ImageBackground": {
1530
+ "type": "object",
1531
+ "additionalProperties": false,
1532
+ "description": "Picture background: the image fills the whole canvas behind everything and never moves content (to push content aside, use an image block with placement). The string shorthand 'asset:x' (or any image source) is { type: 'image', src: 'asset:x' }, a cover image. Paint order: the colour scheme's default slide background, the picture, then the overlay, then furniture and content.",
1533
+ "required": ["type", "src"],
1534
+ "properties": {
1535
+ "type": {
1536
+ "type": "string",
1537
+ "const": "image",
1538
+ "description": "Picture background."
1539
+ },
1540
+ "src": {
1541
+ "type": "string",
1542
+ "description": "Source for the background image: an 'asset:<id>' reference, HTTPS URL, data URI, or a relative path resolved against the OPF file location.",
1543
+ "examples": [
1544
+ "https://cdn.acme.com/backgrounds/cover.jpg",
1545
+ "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAA...",
1546
+ "./backgrounds/cover.jpg",
1547
+ "asset:bg-cover"
1548
+ ]
1549
+ },
1550
+ "alt": {
1551
+ "type": "string",
1552
+ "description": "Alternative text. A background with alt is meaningful: PowerPoint export draws it as a full-slide picture at the back that carries the text, so screen readers see it. Without alt it is decorative and exports as the native slide background; the alt text of a referenced asset does not count, so a background is decorative unless it says otherwise."
1553
+ },
1554
+ "fit": {
1555
+ "type": "string",
1556
+ "enum": ["cover", "contain", "stretch", "tile"],
1557
+ "description": "How the picture fills the canvas: 'cover' fills it and crops what overflows around focus; 'contain' shows the whole picture centered over the colour scheme's default background; 'stretch' scales it to the canvas exactly; 'tile' repeats it at its own size from the top-left corner. Default 'cover'."
1558
+ },
1559
+ "focus": {
1560
+ "$ref": "#/$defs/ImageFocus",
1561
+ "description": "Point of the picture to keep in view when 'cover' crops it. Ignored by the other fits."
1562
+ },
1563
+ "opacity": {
1564
+ "type": "number",
1565
+ "minimum": 0,
1566
+ "maximum": 1,
1567
+ "description": "Picture opacity from 0 (fully transparent) to 1 (fully opaque); the overlay keeps its own opacity. Default 1."
1568
+ },
1569
+ "recolor": {
1570
+ "$ref": "#/$defs/ImageRecolor",
1571
+ "description": "Grayscale or duotone treatment of the picture's pixels, as on an image block: recolor and opacity apply to the pixels, then the overlay is drawn above."
1572
+ },
1573
+ "overlay": {
1574
+ "$ref": "#/$defs/Overlay",
1575
+ "description": "Scrim over the whole picture or a band along one edge (edge, size), beneath furniture and content, for example to keep a title readable over a busy photo."
1576
+ }
1577
+ },
1578
+ "examples": [
1579
+ { "type": "image", "src": "asset:cover-photo" },
1580
+ { "type": "image", "src": "./images/harbour.jpg", "alt": "Container ships at dawn", "focus": { "x": 0.5, "y": 0.7 }, "overlay": { "color": "dark1", "opacity": 0.4 } }
1581
+ ]
1582
+ },
1583
+ "ImageFocus": {
1584
+ "type": "object",
1585
+ "additionalProperties": false,
1586
+ "required": ["x", "y"],
1587
+ "description": "A point of a picture as fractions of its width and height, from the top-left corner. A cover fit centers this point in the frame as far as the picture still covers the frame, so it always stays in view: { x: 0.5, y: 0.5 } (the default) is a center crop, { x: 0, y: 0 } keeps the top-left corner.",
1588
+ "properties": {
1589
+ "x": { "type": "number", "minimum": 0, "maximum": 1, "description": "Horizontal position, 0 (left edge) to 1 (right edge)." },
1590
+ "y": { "type": "number", "minimum": 0, "maximum": 1, "description": "Vertical position, 0 (top edge) to 1 (bottom edge)." }
1591
+ },
1592
+ "examples": [{ "x": 0.5, "y": 0.7 }]
1593
+ },
1594
+ "Overlay": {
1595
+ "type": "object",
1596
+ "additionalProperties": false,
1597
+ "required": ["color", "opacity"],
1598
+ "description": "Solid scrim over a picture: the whole frame in the frame's shape, or a band along one edge. Shared by image backgrounds and image blocks. Drawn directly above its picture and beneath headings and content; PowerPoint export writes one native shape above the picture.",
1599
+ "properties": {
1600
+ "color": {
1601
+ "$ref": "#/$defs/ColorRef",
1602
+ "description": "Overlay fill color: a hex color (eight-digit hex keeps its alpha), a color-scheme slot or role name, or a 'var:<id>' variable reference."
1603
+ },
1604
+ "opacity": {
1605
+ "type": "number",
1606
+ "minimum": 0,
1607
+ "maximum": 1,
1608
+ "description": "Overlay fill opacity."
1609
+ },
1610
+ "edge": {
1611
+ "type": "string",
1612
+ "enum": ["top", "bottom", "left", "right"],
1613
+ "description": "Cover only a band along this edge of the frame, for example a caption strip. Backgrounds and rectangle image frames only; on another shape the overlay is not drawn and composition reports unsupported-image-treatment. Omit to cover the whole frame. In a right-to-left deck 'left' and 'right' are the start and end sides."
1614
+ },
1615
+ "size": {
1877
1616
  "type": "number",
1878
- "minimum": 0,
1617
+ "minimum": 0.05,
1879
1618
  "maximum": 1,
1880
- "description": "Background opacity from 0 (fully transparent) to 1 (fully opaque)."
1619
+ "description": "Band share of the frame for an edge overlay. Default 0.3. Ignored without edge."
1881
1620
  }
1882
- }
1621
+ },
1622
+ "examples": [
1623
+ { "color": "dark1", "opacity": 0.4 },
1624
+ { "color": "#000000", "opacity": 0.75, "edge": "bottom", "size": 0.25 }
1625
+ ]
1883
1626
  },
1884
- "ImageBackground": {
1627
+ "ImagePlacement": {
1885
1628
  "type": "object",
1886
- "description": "Fixed image slide background fill.",
1887
- "required": ["type", "image"],
1629
+ "additionalProperties": false,
1630
+ "required": ["edge"],
1631
+ "description": "Bleeds an image block to one edge of the slide: the image takes a band along that edge, edge to edge, and the headings and other content compose in the rest of the slide at the normal padding. Only top-level blocks (slides.N.blocks.I) can be placed, at most one per edge; a layout's image placeholder may carry the same placement for the slide's image. Bands are taken in block order, each spanning the free area left by earlier ones. Placement is the only mechanism that moves content aside; a background never does. A slide whose only body is placed images is composed like a cover: its headings center in the free area. In a right-to-left deck 'left' is the start side, drawn at the right.",
1888
1632
  "properties": {
1889
- "type": {
1633
+ "edge": {
1890
1634
  "type": "string",
1891
- "const": "image",
1892
- "description": "Fixed image background fill."
1635
+ "enum": ["left", "right", "top", "bottom"],
1636
+ "description": "The slide edge the image bleeds to."
1893
1637
  },
1894
- "image": {
1895
- "type": "object",
1896
- "description": "Image fill definition.",
1897
- "required": ["src"],
1898
- "properties": {
1899
- "src": {
1900
- "type": "string",
1901
- "description": "Source for the background image.",
1902
- "examples": [
1903
- "https://cdn.acme.com/backgrounds/cover.jpg",
1904
- "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAA...",
1905
- "./backgrounds/cover.jpg",
1906
- "asset:bg-cover"
1907
- ]
1908
- },
1909
- "fit": {
1910
- "type": "string",
1911
- "enum": ["cover", "contain", "tile"],
1912
- "description": "How the image fills the slide background."
1913
- }
1914
- }
1638
+ "size": {
1639
+ "type": "number",
1640
+ "minimum": 0.1,
1641
+ "maximum": 0.9,
1642
+ "description": "Share of the slide width (left, right) or height (top, bottom) given to the band. Default 0.5."
1915
1643
  },
1916
- "opacity": {
1644
+ "inset": {
1645
+ "type": "boolean",
1646
+ "description": "Draw the frame inside the slide padding on every side of the band, like a card, instead of edge to edge. Default false."
1647
+ }
1648
+ },
1649
+ "examples": [{ "edge": "left", "size": 0.45 }, { "edge": "right", "size": 0.4, "inset": true }]
1650
+ },
1651
+ "ImageBorder": {
1652
+ "type": "object",
1653
+ "additionalProperties": false,
1654
+ "required": ["color", "width"],
1655
+ "description": "Solid line along the image frame's shape, centered on the outline with a miter join, like a native picture line. A thick dark border on a rounded portrait frame gives a device bezel.",
1656
+ "properties": {
1657
+ "color": {
1658
+ "$ref": "#/$defs/ColorRef",
1659
+ "description": "Line color: a hex color, a color-scheme slot or role name, or a 'var:<id>' variable reference."
1660
+ },
1661
+ "width": {
1917
1662
  "type": "number",
1918
1663
  "minimum": 0,
1919
- "maximum": 1,
1920
- "description": "Background opacity from 0 (fully transparent) to 1 (fully opaque)."
1664
+ "maximum": 64,
1665
+ "description": "Line width in reference pixels at a 720-pixel short edge. 0 removes the line."
1921
1666
  }
1922
1667
  }
1923
1668
  },
1669
+ "ImageRecolor": {
1670
+ "oneOf": [
1671
+ {
1672
+ "type": "string",
1673
+ "const": "grayscale",
1674
+ "description": "Replace each pixel with its luminance, exported as native a:grayscl."
1675
+ },
1676
+ {
1677
+ "type": "object",
1678
+ "additionalProperties": false,
1679
+ "required": ["dark", "light"],
1680
+ "properties": {
1681
+ "dark": {
1682
+ "$ref": "#/$defs/ColorRef",
1683
+ "description": "Color at luminance 0."
1684
+ },
1685
+ "light": {
1686
+ "$ref": "#/$defs/ColorRef",
1687
+ "description": "Color at luminance 1."
1688
+ }
1689
+ },
1690
+ "description": "Duotone: map each pixel's luminance linearly from dark to light, exported as native a:duotone."
1691
+ }
1692
+ ],
1693
+ "description": "Color treatment for the image pixels. Luminance uses Rec. 601 weights (0.299, 0.587, 0.114) on sRGB values."
1694
+ },
1924
1695
  "PatternBackground": {
1925
1696
  "type": "object",
1926
1697
  "description": "Fixed pattern slide background fill.",
@@ -1942,12 +1713,12 @@
1942
1713
  "examples": ["pct5", "ltHorz", "wdUpDiag"]
1943
1714
  },
1944
1715
  "foregroundColor": {
1945
- "type": "string",
1716
+ "$ref": "#/$defs/ColorRef",
1946
1717
  "description": "Foreground color for the pattern: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef).",
1947
1718
  "examples": ["#0F172A"]
1948
1719
  },
1949
1720
  "backgroundColor": {
1950
- "type": "string",
1721
+ "$ref": "#/$defs/ColorRef",
1951
1722
  "description": "Background color behind the pattern: a hex string, a color-scheme slot or role name, or a var:<id> variable reference (a ColorRef).",
1952
1723
  "examples": ["#FFFFFF"]
1953
1724
  }
@@ -2018,13 +1789,23 @@
2018
1789
  },
2019
1790
  "Watermark": {
2020
1791
  "type": "object",
2021
- "description": "Decorative watermark image and rendering options. Use design.watermark = false to disable an inherited watermark.",
1792
+ "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. 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.",
2022
1793
  "additionalProperties": false,
2023
1794
  "required": ["opacity"],
1795
+ "oneOf": [
1796
+ { "required": ["src"] },
1797
+ { "required": ["text"] }
1798
+ ],
2024
1799
  "properties": {
2025
1800
  "src": {
2026
1801
  "type": "string",
2027
- "description": "Source for the watermark image."
1802
+ "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."
1803
+ },
1804
+ "text": {
1805
+ "type": "string",
1806
+ "minLength": 1,
1807
+ "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.",
1808
+ "examples": ["DRAFT", "CONFIDENTIAL"]
2028
1809
  },
2029
1810
  "opacity": {
2030
1811
  "type": "number",
@@ -2054,7 +1835,7 @@
2054
1835
  },
2055
1836
  "HeaderFooterItem": {
2056
1837
  "type": "object",
2057
- "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.",
1838
+ "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.",
2058
1839
  "properties": {
2059
1840
  "logo": {
2060
1841
  "type": "boolean",
@@ -2096,7 +1877,11 @@
2096
1877
  },
2097
1878
  "organization": {
2098
1879
  "type": "boolean",
2099
- "description": "Whether to render the primary organization name from organization."
1880
+ "description": "Whether to render the primary organization's name from organization (role 'primary', else the first organization)."
1881
+ },
1882
+ "speaker": {
1883
+ "type": "boolean",
1884
+ "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."
2100
1885
  },
2101
1886
  "section": {
2102
1887
  "type": "boolean",
@@ -2131,26 +1916,44 @@
2131
1916
  "items": { "type": "string" }
2132
1917
  }
2133
1918
  ],
2134
- "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."
1919
+ "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."
2135
1920
  },
2136
1921
  "layout": {
2137
- "type": "string",
2138
- "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.",
2139
- "examples": ["title", "title-subtitle", "text-2x", "chart-3x", "image-bleed", "blank", "https://acme.com/decks/layouts/cover.json", "pkg:@acme/decks/layouts/cover"]
1922
+ "$ref": "#/$defs/CatalogReference",
1923
+ "description": "Optional layout: a layouts reference, a bare id ('title-subtitle') or 'name:id' ('acme:hero'). When omitted, 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; there is no finding. A reference that resolves nowhere is the warning opf/unresolved-reference (and the 'unresolved-reference' diagnostic of resolveSlideContext), the slide composes automatically, and a strict export fails. A layout never changes what a slide contains, and engines never substitute a different layout.",
1924
+ "examples": ["title", "title-subtitle", "text-2x", "chart-3x", "blank", "acme:hero"]
2140
1925
  },
2141
1926
  "title": {
2142
- "type": "string",
2143
- "description": "Slide-level title content. When the resolved layout exposes a 'title' placeholder, the engine renders this value there.",
2144
- "examples": ["Quarterly Review", "Why Now", "Next Steps"]
1927
+ "oneOf": [
1928
+ { "type": "string" },
1929
+ {
1930
+ "type": "array",
1931
+ "items": { "$ref": "#/$defs/TextRun" }
1932
+ }
1933
+ ],
1934
+ "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.",
1935
+ "examples": ["Quarterly Review", "Why Now", "Next Steps", ["Revenue grew ", { "text": "28%", "color": "accent1" }]]
2145
1936
  },
2146
1937
  "subtitle": {
2147
- "type": "string",
2148
- "description": "Slide-level subtitle or supporting line. When the resolved layout exposes a 'subtitle' placeholder, the engine renders this value there.",
1938
+ "oneOf": [
1939
+ { "type": "string" },
1940
+ {
1941
+ "type": "array",
1942
+ "items": { "$ref": "#/$defs/TextRun" }
1943
+ }
1944
+ ],
1945
+ "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.",
2149
1946
  "examples": ["Prepared for the board", "Three signals changed this quarter"]
2150
1947
  },
2151
1948
  "tag": {
2152
- "type": "string",
2153
- "description": "Small slide-level label or badge. When the resolved layout exposes a 'tag' placeholder, the engine renders this value there.",
1949
+ "oneOf": [
1950
+ { "type": "string" },
1951
+ {
1952
+ "type": "array",
1953
+ "items": { "$ref": "#/$defs/TextRun" }
1954
+ }
1955
+ ],
1956
+ "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.",
2154
1957
  "examples": ["Intro", "Confidential", "Appendix"]
2155
1958
  },
2156
1959
  "text": {
@@ -2180,11 +1983,11 @@
2180
1983
  },
2181
1984
  "image": {
2182
1985
  "$ref": "#/$defs/Asset",
2183
- "description": "Full-slide image source. Presence of this field infers type 'image'."
1986
+ "description": "Shorthand for one image block: the picture composes as body content, like { type: 'image', image: value }, with the effective design.imageFit. A layout's image placeholder may place it along an edge (placement). For fit, focus, treatments or an explicit placement use an image block in blocks; for a full-slide picture behind the content use design.background. Presence of this field infers type 'image'."
2184
1987
  },
2185
1988
  "video": {
2186
1989
  "$ref": "#/$defs/Asset",
2187
- "description": "Full-slide video source. Presence of this field infers type 'video'."
1990
+ "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)."
2188
1991
  },
2189
1992
  "chart": {
2190
1993
  "$ref": "#/$defs/Chart",
@@ -2207,7 +2010,7 @@
2207
2010
  { "type": "number" },
2208
2011
  { "$ref": "#/$defs/Metric" }
2209
2012
  ],
2210
- "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'."
2013
+ "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'."
2211
2014
  },
2212
2015
  "quote": {
2213
2016
  "oneOf": [
@@ -2230,7 +2033,7 @@
2230
2033
  "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."
2231
2034
  },
2232
2035
  "design": {
2233
- "$ref": "#/$defs/Design",
2036
+ "$ref": "#/$defs/SlideDesign",
2234
2037
  "description": "Slide-level design applied on top of the deck-wide design."
2235
2038
  },
2236
2039
  "left": {
@@ -2389,7 +2192,7 @@
2389
2192
  },
2390
2193
  "hidden": {
2391
2194
  "type": "boolean",
2392
- "description": "Whether the slide is hidden from the presented sequence."
2195
+ "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."
2393
2196
  },
2394
2197
  "composition": {
2395
2198
  "$ref": "#/$defs/Composition"
@@ -2472,9 +2275,57 @@
2472
2275
  "asset:img-diagram"
2473
2276
  ]
2474
2277
  },
2278
+ "fit": {
2279
+ "type": "string",
2280
+ "enum": ["cover", "contain", "stretch"],
2281
+ "description": "Image payloads only. How the picture fills its frame: 'cover' fills it and crops what overflows around focus; 'contain' shows the whole picture centered; 'stretch' scales it to the frame exactly. Default: the effective design.imageFit, else 'cover'."
2282
+ },
2283
+ "focus": {
2284
+ "$ref": "#/$defs/ImageFocus",
2285
+ "description": "Image payloads only. Point of the picture to keep in view when 'cover' crops it. Ignored by the other fits."
2286
+ },
2287
+ "aspectRatio": {
2288
+ "type": "number",
2289
+ "exclusiveMinimum": 0,
2290
+ "maximum": 10,
2291
+ "description": "Image payloads only. Width-to-height ratio of the frame: the frame becomes the largest centered box with this ratio inside the block's region, for example 2.39 for a cinematic letterbox or 0.5 for a phone-shaped frame. shape 'circle' always uses 1."
2292
+ },
2293
+ "shape": {
2294
+ "type": "string",
2295
+ "enum": ["rectangle", "rounded", "circle", "hexagon"],
2296
+ "description": "Image payloads only. Mask applied to the frame, exported as the picture's native preset geometry (rect, roundRect, ellipse, hexagon). 'circle' makes the frame square. Default 'rectangle'."
2297
+ },
2298
+ "cornerRadius": {
2299
+ "type": "number",
2300
+ "minimum": 0,
2301
+ "maximum": 0.5,
2302
+ "description": "Image payloads only. Corner radius for shape 'rounded' as a fraction of the frame's shorter side. Default 0.16667, PowerPoint's roundRect default."
2303
+ },
2304
+ "border": {
2305
+ "$ref": "#/$defs/ImageBorder",
2306
+ "description": "Image payloads only. Line along the frame's shape."
2307
+ },
2308
+ "opacity": {
2309
+ "type": "number",
2310
+ "minimum": 0,
2311
+ "maximum": 1,
2312
+ "description": "Image payloads only. Picture opacity from 0 to 1, exported as a native alphaModFix. The border and overlay keep their own opacity. Default 1."
2313
+ },
2314
+ "recolor": {
2315
+ "$ref": "#/$defs/ImageRecolor",
2316
+ "description": "Image payloads only. Grayscale or duotone treatment of the picture's pixels."
2317
+ },
2318
+ "overlay": {
2319
+ "$ref": "#/$defs/Overlay",
2320
+ "description": "Image payloads only. Scrim over the picture in the frame's shape, or a band along one edge of a rectangle frame."
2321
+ },
2322
+ "placement": {
2323
+ "$ref": "#/$defs/ImagePlacement",
2324
+ "description": "Image payloads only, and only on a top-level block (slides.N.blocks.I), at most one per edge. Bleeds the image to one slide edge; the headings and the other blocks compose in the rest of the slide."
2325
+ },
2475
2326
  "video": {
2476
2327
  "$ref": "#/$defs/Asset",
2477
- "description": "Source for a video item.",
2328
+ "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).",
2478
2329
  "examples": [
2479
2330
  "asset:demo-video",
2480
2331
  "./media/demo.mp4"
@@ -2509,7 +2360,7 @@
2509
2360
  { "type": "number" },
2510
2361
  { "$ref": "#/$defs/Metric" }
2511
2362
  ],
2512
- "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.",
2363
+ "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.",
2513
2364
  "examples": [
2514
2365
  "98%",
2515
2366
  42,
@@ -2527,7 +2378,7 @@
2527
2378
  { "type": "string" },
2528
2379
  { "$ref": "#/$defs/Quote" }
2529
2380
  ],
2530
- "description": "Quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution and source metadata.",
2381
+ "description": "Quote payload. A string is shorthand for { \"text\": value }; object form carries optional attribution, role, photo and source metadata.",
2531
2382
  "examples": [
2532
2383
  "This changed how our team handles reviews.",
2533
2384
  {
@@ -2760,30 +2611,46 @@
2760
2611
  },
2761
2612
  "Quote": {
2762
2613
  "type": "object",
2763
- "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 }.",
2614
+ "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 }.",
2764
2615
  "additionalProperties": false,
2765
2616
  "required": ["text"],
2766
2617
  "properties": {
2767
2618
  "text": {
2768
- "type": "string",
2769
- "description": "Quoted text.",
2770
- "examples": ["This changed how our team handles reviews."]
2619
+ "oneOf": [
2620
+ { "type": "string" },
2621
+ {
2622
+ "type": "array",
2623
+ "items": { "$ref": "#/$defs/TextRun" }
2624
+ }
2625
+ ],
2626
+ "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.",
2627
+ "examples": ["This changed how our team handles reviews.", ["Cut review time by ", { "text": "40%", "bold": true }, { "text": " in a quarter", "cite": "acme-case-study" }]]
2771
2628
  },
2772
2629
  "attribution": {
2773
2630
  "type": "string",
2774
- "description": "Person or organization credited for the quote.",
2775
- "examples": ["VP Operations, Acme Corp"]
2631
+ "description": "Person or organization credited for the quote. Drawn after the quote text, in the muted text color.",
2632
+ "examples": ["Priya Raman", "Acme Corp"]
2633
+ },
2634
+ "role": {
2635
+ "type": "string",
2636
+ "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'.",
2637
+ "examples": ["VP Operations, Acme", "Head of Platform"]
2638
+ },
2639
+ "photo": {
2640
+ "$ref": "#/$defs/Asset",
2641
+ "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.",
2642
+ "examples": ["./assets/priya-raman.jpg", {"src": "asset:priya-raman", "alt": "Priya Raman"}]
2776
2643
  },
2777
2644
  "source": {
2778
2645
  "type": "string",
2779
- "description": "Optional quote source, citation, or URL.",
2646
+ "description": "Optional quote source, citation, or URL. Follows the attribution and role after ' - '.",
2780
2647
  "examples": ["Customer interview, March 2026", "https://acme.com/case-study"]
2781
2648
  }
2782
2649
  }
2783
2650
  },
2784
2651
  "Code": {
2785
2652
  "type": "object",
2786
- "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 }.",
2653
+ "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 }.",
2787
2654
  "additionalProperties": false,
2788
2655
  "required": ["source"],
2789
2656
  "properties": {
@@ -2801,12 +2668,24 @@
2801
2668
  "type": "string",
2802
2669
  "description": "Optional file label shown with the code block.",
2803
2670
  "examples": ["app.py", "components/Button.tsx", "terraform/main.tf"]
2671
+ },
2672
+ "highlight": {
2673
+ "type": "array",
2674
+ "minItems": 1,
2675
+ "items": {
2676
+ "oneOf": [
2677
+ { "type": "integer", "minimum": 1 },
2678
+ { "type": "array", "minItems": 2, "maxItems": 2, "items": { "type": "integer", "minimum": 1 } }
2679
+ ]
2680
+ },
2681
+ "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.",
2682
+ "examples": [[3], [3, [5, 7]], [[2, 4]]]
2804
2683
  }
2805
2684
  }
2806
2685
  },
2807
2686
  "Metric": {
2808
2687
  "type": "object",
2809
- "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.",
2688
+ "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.",
2810
2689
  "additionalProperties": false,
2811
2690
  "required": ["value"],
2812
2691
  "properties": {
@@ -2844,7 +2723,12 @@
2844
2723
  "trend": {
2845
2724
  "type": "string",
2846
2725
  "enum": ["up", "down", "flat"],
2847
- "description": "Metric trend direction."
2726
+ "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`."
2727
+ },
2728
+ "sentiment": {
2729
+ "type": "string",
2730
+ "enum": ["positive", "negative", "neutral"],
2731
+ "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."
2848
2732
  }
2849
2733
  }
2850
2734
  },
@@ -2901,6 +2785,12 @@
2901
2785
  "type": "string",
2902
2786
  "description": "Optional event detail.",
2903
2787
  "examples": ["Launch with the first operations team."]
2788
+ },
2789
+ "status": {
2790
+ "type": "string",
2791
+ "enum": ["done", "current", "planned"],
2792
+ "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.",
2793
+ "examples": ["done", "current", "planned"]
2904
2794
  }
2905
2795
  }
2906
2796
  },
@@ -3098,13 +2988,24 @@
3098
2988
  },
3099
2989
  "fontFamily": {
3100
2990
  "type": "string",
3101
- "description": "Run font family override.",
2991
+ "description": "Run font family override. It wins over the code font a run with 'code' takes.",
3102
2992
  "examples": ["Inter", "Source Serif Pro", "JetBrains Mono"]
3103
2993
  },
2994
+ "code": {
2995
+ "type": "boolean",
2996
+ "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."
2997
+ },
2998
+ "lang": {
2999
+ "type": "string",
3000
+ "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$",
3001
+ "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.",
3002
+ "examples": ["fr-FR", "ja-JP", "de"]
3003
+ },
3104
3004
  "link": {
3105
3005
  "type": "string",
3106
- "description": "URL to link the run text to.",
3107
- "examples": ["https://acme.com", "https://acme.com/case-studies/beta"]
3006
+ "pattern": "^(https?://|mailto:|tel:)\\S+$",
3007
+ "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.",
3008
+ "examples": ["https://acme.com", "https://acme.com/case-studies/beta", "mailto:hello@acme.com", "tel:+15551234567"]
3108
3009
  },
3109
3010
  "superscript": {
3110
3011
  "type": "boolean",
@@ -3119,7 +3020,7 @@
3119
3020
  { "type": "string", "minLength": 1 },
3120
3021
  { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }
3121
3022
  ],
3122
- "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.",
3023
+ "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.",
3123
3024
  "examples": ["gartner-2026", ["gartner-2026", "annual-report"]]
3124
3025
  },
3125
3026
  "footnote": {
@@ -3127,7 +3028,7 @@
3127
3028
  { "type": "string", "minLength": 1 },
3128
3029
  { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/TextRun" } }
3129
3030
  ],
3130
- "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.",
3031
+ "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.",
3131
3032
  "examples": ["Excludes the discontinued hardware line."]
3132
3033
  }
3133
3034
  }
@@ -3203,27 +3104,40 @@
3203
3104
  "properties": {
3204
3105
  "type": {
3205
3106
  "type": "string",
3206
- "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.",
3207
- "examples": [
3208
- "column", "bar", "line", "pie", "doughnut", "area", "scatter", "radar",
3209
- "waterfall", "funnel", "treemap", "stacked-column-3x"
3210
- ]
3107
+ "enum": ["100pct-stacked-area", "100pct-stacked-bar", "100pct-stacked-column", "area", "bar", "box-and-whisker", "column", "combo", "doughnut", "filled-radar", "funnel", "histogram", "line", "line-with-markers", "pareto", "pie", "radar", "radar-with-markers", "scatter", "stacked-area", "stacked-bar", "stacked-column", "stacked-line", "stacked-line-with-markers", "treemap", "waterfall", "world"],
3108
+ "description": "Chart type, an engine vocabulary validated directly (no catalog lookup): the chart types Aspose.Slides officially supports, plus 'combo' (clustered columns with line series, see 'line' and 'secondaryAxis'). Each engine maps a type to its own chart model; labels and picker groups are catalog display metadata.",
3109
+ "examples": ["column", "bar", "line", "pie", "doughnut", "area", "scatter", "radar", "waterfall", "funnel", "treemap", "stacked-column", "combo"]
3211
3110
  },
3212
3111
  "data": {
3213
3112
  "oneOf": [
3214
3113
  { "$ref": "#/$defs/ChartData" },
3215
- { "$ref": "#/$defs/ChartDataSource" },
3216
3114
  { "$ref": "#/$defs/DatasetRef" }
3217
3115
  ],
3218
- "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. A ChartDataSource is not resolved by any engine (a 'chart-data-source-unresolved' warning; engines draw a placeholder); prefer inline data with a 'source'."
3116
+ "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."
3219
3117
  },
3220
3118
  "mapping": {
3221
3119
  "$ref": "#/$defs/ChartMapping",
3222
3120
  "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)."
3223
3121
  },
3122
+ "line": {
3123
+ "type": "array",
3124
+ "items": { "type": "string" },
3125
+ "minItems": 1,
3126
+ "uniqueItems": true,
3127
+ "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.",
3128
+ "examples": [["Margin"]]
3129
+ },
3130
+ "secondaryAxis": {
3131
+ "type": "array",
3132
+ "items": { "type": "string" },
3133
+ "minItems": 1,
3134
+ "uniqueItems": true,
3135
+ "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.",
3136
+ "examples": [["Margin"]]
3137
+ },
3224
3138
  "axisTitles": {
3225
3139
  "$ref": "#/$defs/ChartAxisTitles",
3226
- "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."
3140
+ "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."
3227
3141
  },
3228
3142
  "legend": {
3229
3143
  "type": "string",
@@ -3236,13 +3150,22 @@
3236
3150
  { "$ref": "#/$defs/ChartDataLabels" }
3237
3151
  ],
3238
3152
  "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."
3153
+ },
3154
+ "alt": {
3155
+ "type": "string",
3156
+ "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.",
3157
+ "examples": ["Revenue grew from $12M in Q1 to $18M in Q2, with the steepest rise in the West region."]
3158
+ },
3159
+ "highlight": {
3160
+ "$ref": "#/$defs/ChartHighlight",
3161
+ "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."
3239
3162
  }
3240
3163
  }
3241
3164
  },
3242
3165
  "ChartAxisTitles": {
3243
3166
  "type": "object",
3244
3167
  "additionalProperties": false,
3245
- "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.",
3168
+ "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.",
3246
3169
  "properties": {
3247
3170
  "category": {
3248
3171
  "type": "string",
@@ -3251,11 +3174,41 @@
3251
3174
  },
3252
3175
  "value": {
3253
3176
  "type": "string",
3254
- "description": "Title of the value (Y) axis.",
3177
+ "description": "Title of the value (Y) axis. On a combo chart, the primary (left) value axis.",
3255
3178
  "examples": ["Revenue ($M)", "Customers"]
3179
+ },
3180
+ "secondary": {
3181
+ "type": "string",
3182
+ "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'.",
3183
+ "examples": ["Margin (%)"]
3256
3184
  }
3257
3185
  }
3258
3186
  },
3187
+ "ChartHighlight": {
3188
+ "type": "object",
3189
+ "additionalProperties": false,
3190
+ "minProperties": 1,
3191
+ "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.",
3192
+ "properties": {
3193
+ "series": {
3194
+ "type": "array",
3195
+ "items": { "type": "string" },
3196
+ "minItems": 1,
3197
+ "uniqueItems": true,
3198
+ "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.",
3199
+ "examples": [["Revenue"]]
3200
+ },
3201
+ "categories": {
3202
+ "type": "array",
3203
+ "items": { "type": "string" },
3204
+ "minItems": 1,
3205
+ "uniqueItems": true,
3206
+ "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.",
3207
+ "examples": [["Q3"]]
3208
+ }
3209
+ },
3210
+ "examples": [{ "series": ["Revenue"] }, { "categories": ["Q3"] }]
3211
+ },
3259
3212
  "ChartDataLabels": {
3260
3213
  "type": "object",
3261
3214
  "additionalProperties": false,
@@ -3271,7 +3224,7 @@
3271
3224
  "position": {
3272
3225
  "type": "string",
3273
3226
  "enum": ["auto", "center", "inside-end", "inside-base", "outside-end", "above", "below", "left", "right"],
3274
- "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."
3227
+ "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."
3275
3228
  },
3276
3229
  "separator": {
3277
3230
  "type": "string",
@@ -3571,35 +3524,6 @@
3571
3524
  },
3572
3525
  "examples": [{ "category": "Quarter", "series": ["Costs", "Revenue"] }, { "category": "Region", "x": "Spend", "series": ["Revenue"] }]
3573
3526
  },
3574
- "ChartDataSource": {
3575
- "type": "object",
3576
- "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.",
3577
- "additionalProperties": false,
3578
- "required": ["src"],
3579
- "properties": {
3580
- "src": {
3581
- "type": "string",
3582
- "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.",
3583
- "examples": ["asset:revenue", "asset:pipeline-csv", "./data/revenue.csv", "https://cdn.acme.com/data/revenue.csv"]
3584
- },
3585
- "sheet": {
3586
- "type": "string",
3587
- "description": "Optional sheet name or table name for spreadsheet-like assets.",
3588
- "examples": ["Sheet1", "Revenue"]
3589
- },
3590
- "range": {
3591
- "type": "string",
3592
- "description": "Optional A1-style range or engine-defined range selector for spreadsheet-like assets.",
3593
- "examples": ["A1:D8", "Revenue!A1:D8"]
3594
- },
3595
- "columns": {
3596
- "type": "array",
3597
- "items": { "type": "string" },
3598
- "description": "Optional ordered columns or fields to read from the source. When omitted, renderers may use the source's own header row or schema.",
3599
- "examples": [["Quarter", "Revenue"], ["Month", "Revenue", "Costs"]]
3600
- }
3601
- }
3602
- },
3603
3527
  "ChartDataCell": {
3604
3528
  "oneOf": [
3605
3529
  { "type": "string" },
@@ -3788,86 +3712,139 @@
3788
3712
  },
3789
3713
  "Catalogs": {
3790
3714
  "type": "object",
3791
- "description": "Catalog overrides for the in-document references. Every property is optional. The default catalog for a kind lives at https://www.pptx.gallery/<kind> (e.g. https://www.pptx.gallery/narratives, https://www.pptx.gallery/themes). pptx.gallery is its canonical publisher: GET https://www.pptx.gallery/<kind>/index.json (or https://www.pptx.gallery/<kind> with Accept: application/json) returns a catalog index (https://openpresentation.org/schema/opf-catalog-index/v1) and https://www.pptx.gallery/<kind>/<id>.json returns one record that validates against the kind's companion schema; publisher metadata sits in top-level 'x-*' members that consumers ignore. Engines resolve the default catalog from the pinned snapshot bundled in @openpresentation/opf (spec/catalogs, pinned to a publisher commit and content hash by spec/catalogs/manifest.json), so resolution is deterministic offline and in the cloud; they do not fetch pptx.gallery at run time. For each kind, declaring a 'source' replaces the default registry and/or 'records' adds inline records that take precedence over anything fetched from a source.\n\nResolution order for any reference (e.g. narrative, design.theme): inline catalogs.<kind>.records[] → catalogs.<kind>.source → default catalog at https://www.pptx.gallery/<kind>. Every reference resolves to the catalog record's 'id' field. References that are themselves URLs or 'pkg:' references skip the catalog lookup and resolve directly.",
3715
+ "description": "The catalog records a document embeds, grouped by the catalog they came from. Each property is a group; inside a group, records are keyed by kind and then by id.\n- 'default': the catalog bare ids come from. It needs a 'source' when it holds records. Omitted: bare ids fall back to the host's default catalog. false: no catalog fallback, so every bare id must be embedded (under 'custom' or 'default').\n- 'custom': the records the document defines itself. It has no source.\n- any other name ('acme'): an extra catalog, identified by its 'source'. The name is the prefix references use ('acme:hero'). Names are lowercase kebab-case; 'default' and 'custom' are reserved.\n\nResolution, for every kind: a bare id resolves in 'custom', then in the records embedded under 'default', then in the catalog the host registered for default.source (the host's default catalog when 'default' is omitted). 'name:id' resolves in that group's records, then in the catalog the host registered for its source. A reference written inside an embedded record (a theme's colorScheme) resolves in that record's own group first. A prefix that names no group is a validation error; a reference that resolves nowhere is the warning opf/unresolved-reference. Engines never fetch a source: hosts register catalogs by their source string, and authoring tools embed every record a document uses when they save it.",
3792
3716
  "properties": {
3793
- "narratives": {
3794
- "$ref": "#/$defs/CatalogEntry",
3795
- "description": "Catalog of narrative templates. Records validate against https://openpresentation.org/schema/opf-narrative/v1. Default source: https://www.pptx.gallery/narratives."
3717
+ "default": {
3718
+ "oneOf": [
3719
+ {
3720
+ "allOf": [
3721
+ { "$ref": "#/$defs/CatalogGroup" },
3722
+ {
3723
+ "if": {
3724
+ "anyOf": [
3725
+ { "required": ["layouts"] },
3726
+ { "required": ["themes"] },
3727
+ { "required": ["colorSchemes"] },
3728
+ { "required": ["fontSchemes"] },
3729
+ { "required": ["narratives"] },
3730
+ { "required": ["audiences"] },
3731
+ { "required": ["purposes"] },
3732
+ { "required": ["tones"] }
3733
+ ]
3734
+ },
3735
+ "then": { "required": ["source"] }
3736
+ }
3737
+ ]
3738
+ },
3739
+ { "const": false }
3740
+ ],
3741
+ "description": "The catalog bare ids come from: its source and the records of it this document embeds. false turns off the catalog fallback for bare ids.",
3742
+ "examples": [
3743
+ {
3744
+ "source": "https://www.pptx.gallery",
3745
+ "layouts": {
3746
+ "two-column": { "name": "Two column", "placeholders": [{ "type": "title" }, { "type": "text" }, { "type": "text" }] }
3747
+ }
3748
+ },
3749
+ false
3750
+ ]
3751
+ },
3752
+ "custom": {
3753
+ "allOf": [{ "$ref": "#/$defs/CatalogGroup" }, { "not": { "required": ["source"] } }],
3754
+ "description": "Records the document defines itself, referenced by bare id. Checked first for every bare id. Forking a catalog record copies it here under a new id, so the change of ownership is visible."
3755
+ }
3756
+ },
3757
+ "propertyNames": {
3758
+ "pattern": "^[a-z][a-z0-9-]*$",
3759
+ "description": "Group name: lowercase kebab-case. 'default' and 'custom' are reserved."
3760
+ },
3761
+ "additionalProperties": {
3762
+ "allOf": [{ "$ref": "#/$defs/CatalogGroup" }, { "required": ["source"] }],
3763
+ "description": "A named catalog: its source and the records of it this document embeds. References name it as a prefix ('acme:hero')."
3764
+ },
3765
+ "examples": [
3766
+ {
3767
+ "default": {
3768
+ "source": "https://www.pptx.gallery",
3769
+ "themes": { "minimal": { "name": "Minimal", "colorScheme": "cool-horizon", "fontScheme": "aptos" } }
3770
+ },
3771
+ "acme": {
3772
+ "source": "pkg:@acme/opf-catalog",
3773
+ "layouts": { "hero": { "name": "Hero", "placeholders": [{ "type": "title" }] } }
3774
+ },
3775
+ "custom": {
3776
+ "layouts": { "q4-special": { "name": "Q4 special", "placeholders": [{ "type": "title" }, { "type": "chart" }] } }
3777
+ }
3778
+ }
3779
+ ]
3780
+ },
3781
+ "CatalogGroup": {
3782
+ "type": "object",
3783
+ "additionalProperties": false,
3784
+ "description": "One catalog group: the catalog's source and the records of it the document embeds, by kind and then by id.",
3785
+ "properties": {
3786
+ "source": { "$ref": "#/$defs/CatalogSource" },
3787
+ "layouts": {
3788
+ "$ref": "#/$defs/CatalogRecords",
3789
+ "description": "Layout records keyed by id, each with the fields of https://openpresentation.org/schema/opf-layout/v1. Referenced from Slide.layout."
3796
3790
  },
3797
3791
  "themes": {
3798
- "$ref": "#/$defs/CatalogEntry",
3799
- "description": "Catalog of themes. Records validate against https://openpresentation.org/schema/opf-theme/v1. Default source: https://www.pptx.gallery/themes."
3792
+ "$ref": "#/$defs/CatalogRecords",
3793
+ "description": "Theme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-theme/v1. Referenced from design.theme."
3800
3794
  },
3801
3795
  "colorSchemes": {
3802
- "$ref": "#/$defs/CatalogEntry",
3803
- "description": "Catalog of color schemes. Records validate against https://openpresentation.org/schema/opf-color-scheme/v1. Default source: https://www.pptx.gallery/color-schemes."
3796
+ "$ref": "#/$defs/CatalogRecords",
3797
+ "description": "Colour-scheme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-color-scheme/v1. Referenced from design.colorScheme and a theme's colorScheme."
3804
3798
  },
3805
3799
  "fontSchemes": {
3806
- "$ref": "#/$defs/CatalogEntry",
3807
- "description": "Catalog of font schemes. Records validate against https://openpresentation.org/schema/opf-font-scheme/v1. Default source: https://www.pptx.gallery/font-schemes."
3808
- },
3809
- "languages": {
3810
- "$ref": "#/$defs/CatalogEntry",
3811
- "description": "Catalog of languages. Records validate against https://openpresentation.org/schema/opf-language/v1. Default source: https://www.pptx.gallery/languages."
3800
+ "$ref": "#/$defs/CatalogRecords",
3801
+ "description": "Font-scheme records keyed by id, each with the fields of https://openpresentation.org/schema/opf-font-scheme/v1. Referenced from design.fontScheme, a theme's fontScheme and a language's font schemes."
3812
3802
  },
3813
- "layouts": {
3814
- "$ref": "#/$defs/CatalogEntry",
3815
- "description": "Catalog of slide layouts. Records validate against https://openpresentation.org/schema/opf-layout/v1. Default source: https://www.pptx.gallery/layouts."
3816
- },
3817
- "chartTypes": {
3818
- "$ref": "#/$defs/CatalogEntry",
3819
- "description": "Catalog of chart types. Records validate against https://openpresentation.org/schema/opf-chart-type/v1. Default source: https://www.pptx.gallery/chart-types."
3803
+ "narratives": {
3804
+ "$ref": "#/$defs/CatalogRecords",
3805
+ "description": "Narrative records keyed by id, each with the fields of https://openpresentation.org/schema/opf-narrative/v1. Referenced from narrative."
3820
3806
  },
3821
- "tones": {
3822
- "$ref": "#/$defs/CatalogEntry",
3823
- "description": "Catalog of presentation tones. Records validate against https://openpresentation.org/schema/opf-tone/v1. Default source: https://www.pptx.gallery/tones. Referenced from tone."
3807
+ "audiences": {
3808
+ "$ref": "#/$defs/CatalogRecords",
3809
+ "description": "Audience records keyed by id, each with the fields of https://openpresentation.org/schema/opf-audience/v1. Referenced from audience."
3824
3810
  },
3825
3811
  "purposes": {
3826
- "$ref": "#/$defs/CatalogEntry",
3827
- "description": "Catalog of presentation purposes. Records validate against https://openpresentation.org/schema/opf-purpose/v1. Default source: https://www.pptx.gallery/purposes. Referenced from purpose."
3828
- },
3829
- "audiences": {
3830
- "$ref": "#/$defs/CatalogEntry",
3831
- "description": "Catalog of presentation audiences. Records validate against https://openpresentation.org/schema/opf-audience/v1. Default source: https://www.pptx.gallery/audiences. Referenced from audience."
3812
+ "$ref": "#/$defs/CatalogRecords",
3813
+ "description": "Purpose records keyed by id, each with the fields of https://openpresentation.org/schema/opf-purpose/v1. Referenced from purpose."
3832
3814
  },
3833
- "socialPlatforms": {
3834
- "$ref": "#/$defs/CatalogEntry",
3835
- "description": "Catalog of social-media platforms. Records validate against https://openpresentation.org/schema/opf-social-platform/v1. Default source: https://www.pptx.gallery/social-platforms. Referenced via the property keys of any Socials object (Organization.socials, Speaker.socials)."
3815
+ "tones": {
3816
+ "$ref": "#/$defs/CatalogRecords",
3817
+ "description": "Tone records keyed by id, each with the fields of https://openpresentation.org/schema/opf-tone/v1. Referenced from tone."
3836
3818
  }
3837
3819
  }
3838
3820
  },
3839
- "CatalogEntry": {
3821
+ "CatalogSource": {
3822
+ "type": "string",
3823
+ "pattern": "^(?:https://[^\\s]+|pkg:[^\\s]+)$",
3824
+ "description": "Identity of a catalog: an HTTPS URL ('https://www.pptx.gallery') or a package reference ('pkg:@acme/opf-catalog'). It names the catalog; engines never fetch or install it. A host registers the catalog's records under this exact string, and copying slides between documents matches groups by it.",
3825
+ "examples": ["https://www.pptx.gallery", "pkg:@acme/opf-catalog"]
3826
+ },
3827
+ "CatalogRecords": {
3840
3828
  "type": "object",
3841
- "description": "A catalog override for one record kind. 'source' replaces the default registry; 'records' adds inline records that take precedence over anything fetched from a source. Either or both may be provided; both omitted means the kind uses its default catalog.",
3842
- "properties": {
3843
- "source": {
3844
- "oneOf": [
3845
- { "$ref": "#/$defs/CatalogSource" },
3846
- {
3847
- "type": "array",
3848
- "items": { "$ref": "#/$defs/CatalogSource" },
3849
- "minItems": 1,
3850
- "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."
3851
- }
3852
- ],
3853
- "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."
3854
- },
3855
- "records": {
3856
- "type": "array",
3857
- "items": { "type": "object" },
3858
- "description": "Inline catalog records embedded in this OPF document. Each record validates against the kind's companion schema (e.g. https://openpresentation.org/schema/opf-narrative/v1 for narratives). Inline records win over anything resolved from 'source' and over the default catalog."
3859
- }
3829
+ "description": "Embedded records of one kind, keyed by id. The key is the record's id; an embedded record carries no '$schema', 'id', origin or digest. Catalog display metadata ('x-*' members) is stripped when a record is embedded.",
3830
+ "propertyNames": { "pattern": "^[a-z][a-z0-9-]*$" },
3831
+ "additionalProperties": {
3832
+ "type": "object",
3833
+ "not": { "anyOf": [{ "required": ["$schema"] }, { "required": ["id"] }] },
3834
+ "description": "One embedded record: the fields of the kind's companion schema without '$schema' and 'id'."
3860
3835
  }
3861
3836
  },
3862
- "CatalogSource": {
3837
+ "CatalogReference": {
3863
3838
  "type": "string",
3864
- "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.",
3865
- "examples": [
3866
- "https://www.pptx.gallery/narratives",
3867
- "https://acme.com/decks/narratives/index.json",
3868
- "pkg:@acme/decks/narratives",
3869
- "pkg:pptx-gallery"
3870
- ]
3839
+ "pattern": "^(?:[a-z][a-z0-9-]*:)?[a-z][a-z0-9-]*$",
3840
+ "description": "A content reference: a bare id ('two-column') or 'name:id' ('acme:hero'), where the prefix names a group of 'catalogs'. URLs and 'pkg:' strings are not references; a named catalog group replaces them. See Catalogs for the resolution order.",
3841
+ "examples": ["two-column", "acme:hero", "default:minimal", "custom:q4-special"]
3842
+ },
3843
+ "LanguageTag": {
3844
+ "type": "string",
3845
+ "pattern": "^(?:[A-Za-z]{2,3}|[iIxX])(?:-[A-Za-z0-9]{1,8})*$",
3846
+ "description": "A BCP-47 language tag ('en-US', 'ja', 'zh-Hans', 'sr-Latn-ME'), an engine vocabulary. Use 'en-GB' for UK English; 'en-UK' is not a valid region.",
3847
+ "examples": ["en-US", "en-GB", "ja", "zh-Hans", "ar-SA"]
3871
3848
  },
3872
3849
  "Composition": {
3873
3850
  "type": "object",