@openpresentation/opf 0.11.2 → 0.11.4

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 (236) hide show
  1. package/dist/{catalogs-DJ-B5ZyD.d.ts → catalogs-CUClB3nl.d.ts} +202 -5
  2. package/dist/catalogs.d.ts +1 -1
  3. package/dist/catalogs.js +1 -1
  4. package/dist/{chunk-BVLWFMDX.js → chunk-3H2U7FDR.js} +224 -26
  5. package/dist/{chunk-K7IWYODS.js → chunk-3OJN7QAW.js} +17 -4
  6. package/dist/{chunk-7LF37SG3.js → chunk-AEUSVEUY.js} +4915 -1640
  7. package/dist/{chunk-DJRZMGKY.js → chunk-FDCWQHWO.js} +143 -68
  8. package/dist/{chunk-SDETQV7Y.js → chunk-HJL64ETN.js} +505 -0
  9. package/dist/{chunk-LTSDDBNW.js → chunk-KJKASEPE.js} +262 -23
  10. package/dist/{chunk-SMYEM3HV.js → chunk-NSVVQ52O.js} +7 -6
  11. package/dist/{chunk-KZ6BT5UG.js → chunk-RXNFDGPC.js} +5 -5
  12. package/dist/{composition-DzLwY-Mi.d.ts → composition-Dl-d9FRj.d.ts} +93 -6
  13. package/dist/composition.d.ts +1 -1
  14. package/dist/composition.js +1 -1
  15. package/dist/docs.js +21 -15
  16. package/dist/examples.d.ts +1 -1
  17. package/dist/font-policy.js +1 -1
  18. package/dist/index.d.ts +4 -4
  19. package/dist/index.js +11 -11
  20. package/dist/lint.d.ts +2 -2
  21. package/dist/lint.js +5 -5
  22. package/dist/pagination.d.ts +1 -1
  23. package/dist/pagination.js +5 -5
  24. package/dist/{presentation-bceTClm8.d.ts → presentation-j5PVrqfV.d.ts} +41 -29
  25. package/dist/repo-readme.js +1 -1
  26. package/dist/schemas.js +1 -1
  27. package/dist/spec/README.md +3 -2
  28. package/dist/spec/catalogs/audiences/index.json +2 -0
  29. package/dist/spec/catalogs/chart-types/box-and-whisker.json +101 -49
  30. package/dist/spec/catalogs/chart-types/histogram.json +130 -108
  31. package/dist/spec/catalogs/chart-types/index.json +2 -0
  32. package/dist/spec/catalogs/chart-types/pareto.json +22 -106
  33. package/dist/spec/catalogs/color-schemes/black-and-white.json +10 -1
  34. package/dist/spec/catalogs/color-schemes/bold-red.json +10 -1
  35. package/dist/spec/catalogs/color-schemes/boost.json +10 -1
  36. package/dist/spec/catalogs/color-schemes/burnt-orange.json +10 -1
  37. package/dist/spec/catalogs/color-schemes/cool-horizon.json +10 -1
  38. package/dist/spec/catalogs/color-schemes/corporate-blue.json +10 -1
  39. package/dist/spec/catalogs/color-schemes/deep-purple.json +10 -1
  40. package/dist/spec/catalogs/color-schemes/forest-green.json +10 -1
  41. package/dist/spec/catalogs/color-schemes/golden-yellow.json +10 -1
  42. package/dist/spec/catalogs/color-schemes/index.json +2 -0
  43. package/dist/spec/catalogs/color-schemes/luxury.json +10 -1
  44. package/dist/spec/catalogs/color-schemes/pastel-red.json +10 -1
  45. package/dist/spec/catalogs/color-schemes/slate-gray.json +10 -1
  46. package/dist/spec/catalogs/color-schemes/steel-blue.json +10 -1
  47. package/dist/spec/catalogs/color-schemes/vibes.json +10 -1
  48. package/dist/spec/catalogs/font-schemes/angsana-new.json +2 -1
  49. package/dist/spec/catalogs/font-schemes/aparajita.json +2 -1
  50. package/dist/spec/catalogs/font-schemes/aptos.json +2 -1
  51. package/dist/spec/catalogs/font-schemes/arabic-typesetting.json +2 -1
  52. package/dist/spec/catalogs/font-schemes/arial.json +2 -1
  53. package/dist/spec/catalogs/font-schemes/batang.json +2 -1
  54. package/dist/spec/catalogs/font-schemes/bookman.json +2 -1
  55. package/dist/spec/catalogs/font-schemes/calibri.json +2 -1
  56. package/dist/spec/catalogs/font-schemes/century-schoolbook.json +2 -1
  57. package/dist/spec/catalogs/font-schemes/consolas.json +2 -1
  58. package/dist/spec/catalogs/font-schemes/constantia.json +2 -1
  59. package/dist/spec/catalogs/font-schemes/courier-new.json +2 -1
  60. package/dist/spec/catalogs/font-schemes/daunpenh.json +2 -1
  61. package/dist/spec/catalogs/font-schemes/david.json +2 -1
  62. package/dist/spec/catalogs/font-schemes/dilleniaupc.json +2 -1
  63. package/dist/spec/catalogs/font-schemes/fangsong.json +2 -1
  64. package/dist/spec/catalogs/font-schemes/garamond.json +2 -1
  65. package/dist/spec/catalogs/font-schemes/gautami.json +2 -1
  66. package/dist/spec/catalogs/font-schemes/georgia.json +2 -1
  67. package/dist/spec/catalogs/font-schemes/gisha.json +2 -1
  68. package/dist/spec/catalogs/font-schemes/grandview.json +2 -1
  69. package/dist/spec/catalogs/font-schemes/gungsuh.json +2 -1
  70. package/dist/spec/catalogs/font-schemes/impact.json +2 -1
  71. package/dist/spec/catalogs/font-schemes/index.json +236 -234
  72. package/dist/spec/catalogs/font-schemes/kalinga.json +2 -1
  73. package/dist/spec/catalogs/font-schemes/kartika.json +2 -1
  74. package/dist/spec/catalogs/font-schemes/khmer-ui.json +2 -1
  75. package/dist/spec/catalogs/font-schemes/latha.json +2 -1
  76. package/dist/spec/catalogs/font-schemes/lucida-sans.json +2 -1
  77. package/dist/spec/catalogs/font-schemes/malgun-gothic.json +2 -1
  78. package/dist/spec/catalogs/font-schemes/mangal.json +2 -1
  79. package/dist/spec/catalogs/font-schemes/meiryo.json +2 -1
  80. package/dist/spec/catalogs/font-schemes/microsoft-jhenghei.json +2 -1
  81. package/dist/spec/catalogs/font-schemes/microsoft-yahei.json +2 -1
  82. package/dist/spec/catalogs/font-schemes/mingliu.json +2 -1
  83. package/dist/spec/catalogs/font-schemes/miriam.json +2 -1
  84. package/dist/spec/catalogs/font-schemes/montserrat.json +2 -1
  85. package/dist/spec/catalogs/font-schemes/ms-mincho.json +2 -1
  86. package/dist/spec/catalogs/font-schemes/nirmala-ui.json +2 -1
  87. package/dist/spec/catalogs/font-schemes/noto-naksh-arabic.json +2 -1
  88. package/dist/spec/catalogs/font-schemes/noto-nastaliq-urdu.json +2 -1
  89. package/dist/spec/catalogs/font-schemes/noto-sans-arabic.json +2 -1
  90. package/dist/spec/catalogs/font-schemes/noto-sans-armenian.json +2 -1
  91. package/dist/spec/catalogs/font-schemes/noto-sans-bengali.json +2 -1
  92. package/dist/spec/catalogs/font-schemes/noto-sans-devangari.json +2 -1
  93. package/dist/spec/catalogs/font-schemes/noto-sans-ethiopic.json +2 -1
  94. package/dist/spec/catalogs/font-schemes/noto-sans-georgian.json +2 -1
  95. package/dist/spec/catalogs/font-schemes/noto-sans-gujarati.json +2 -1
  96. package/dist/spec/catalogs/font-schemes/noto-sans-gurmukhi.json +2 -1
  97. package/dist/spec/catalogs/font-schemes/noto-sans-hebrew.json +2 -1
  98. package/dist/spec/catalogs/font-schemes/noto-sans-jp.json +2 -1
  99. package/dist/spec/catalogs/font-schemes/noto-sans-kannada.json +2 -1
  100. package/dist/spec/catalogs/font-schemes/noto-sans-khmer.json +2 -1
  101. package/dist/spec/catalogs/font-schemes/noto-sans-kr.json +2 -1
  102. package/dist/spec/catalogs/font-schemes/noto-sans-malayalam.json +2 -1
  103. package/dist/spec/catalogs/font-schemes/noto-sans-mongolian.json +2 -1
  104. package/dist/spec/catalogs/font-schemes/noto-sans-oriya.json +2 -1
  105. package/dist/spec/catalogs/font-schemes/noto-sans-sc.json +2 -1
  106. package/dist/spec/catalogs/font-schemes/noto-sans-tamil.json +2 -1
  107. package/dist/spec/catalogs/font-schemes/noto-sans-tc.json +2 -1
  108. package/dist/spec/catalogs/font-schemes/noto-sans-telugu.json +2 -1
  109. package/dist/spec/catalogs/font-schemes/noto-sans-thai.json +2 -1
  110. package/dist/spec/catalogs/font-schemes/noto-sans.json +2 -1
  111. package/dist/spec/catalogs/font-schemes/nyala.json +2 -1
  112. package/dist/spec/catalogs/font-schemes/open-sans.json +2 -1
  113. package/dist/spec/catalogs/font-schemes/pmingliu.json +2 -1
  114. package/dist/spec/catalogs/font-schemes/poppins.json +2 -1
  115. package/dist/spec/catalogs/font-schemes/pt-serif.json +2 -1
  116. package/dist/spec/catalogs/font-schemes/raavi.json +2 -1
  117. package/dist/spec/catalogs/font-schemes/raleway.json +2 -1
  118. package/dist/spec/catalogs/font-schemes/roboto.json +2 -1
  119. package/dist/spec/catalogs/font-schemes/rockwell.json +2 -1
  120. package/dist/spec/catalogs/font-schemes/sakkal-majalla.json +2 -1
  121. package/dist/spec/catalogs/font-schemes/seaford.json +2 -1
  122. package/dist/spec/catalogs/font-schemes/segoe-ui-light.json +2 -1
  123. package/dist/spec/catalogs/font-schemes/segoe-ui.json +2 -1
  124. package/dist/spec/catalogs/font-schemes/shonar-bangla.json +2 -1
  125. package/dist/spec/catalogs/font-schemes/shruti.json +2 -1
  126. package/dist/spec/catalogs/font-schemes/simsun.json +2 -1
  127. package/dist/spec/catalogs/font-schemes/skeena.json +2 -1
  128. package/dist/spec/catalogs/font-schemes/sylfaen.json +2 -1
  129. package/dist/spec/catalogs/font-schemes/tahoma.json +2 -1
  130. package/dist/spec/catalogs/font-schemes/tenorite.json +2 -1
  131. package/dist/spec/catalogs/font-schemes/times-new-roman.json +2 -1
  132. package/dist/spec/catalogs/font-schemes/traditional-arabic.json +2 -1
  133. package/dist/spec/catalogs/font-schemes/trebuchet-ms.json +2 -1
  134. package/dist/spec/catalogs/font-schemes/tunga.json +2 -1
  135. package/dist/spec/catalogs/font-schemes/verdana.json +2 -1
  136. package/dist/spec/catalogs/font-schemes/vrinda.json +2 -1
  137. package/dist/spec/catalogs/font-schemes/yu-gothic.json +2 -1
  138. package/dist/spec/catalogs/languages/index.json +2 -0
  139. package/dist/spec/catalogs/layouts/action-plan.json +35 -0
  140. package/dist/spec/catalogs/layouts/agenda.json +35 -0
  141. package/dist/spec/catalogs/layouts/appendix-index.json +38 -0
  142. package/dist/spec/catalogs/layouts/architecture-stack.json +35 -0
  143. package/dist/spec/catalogs/layouts/assumptions-dependencies.json +35 -0
  144. package/dist/spec/catalogs/layouts/before-after.json +41 -0
  145. package/dist/spec/catalogs/layouts/benchmark-target-table.json +35 -0
  146. package/dist/spec/catalogs/layouts/bullet-list.json +38 -0
  147. package/dist/spec/catalogs/layouts/business-model-canvas.json +35 -0
  148. package/dist/spec/catalogs/layouts/capability-map.json +35 -0
  149. package/dist/spec/catalogs/layouts/case-study.json +35 -0
  150. package/dist/spec/catalogs/layouts/chapter-recap.json +35 -0
  151. package/dist/spec/catalogs/layouts/circular-lifecycle.json +35 -0
  152. package/dist/spec/catalogs/layouts/comparison.json +41 -0
  153. package/dist/spec/catalogs/layouts/competitive-landscape.json +35 -0
  154. package/dist/spec/catalogs/layouts/customer-journey.json +35 -0
  155. package/dist/spec/catalogs/layouts/customer-logos.json +38 -0
  156. package/dist/spec/catalogs/layouts/dashboard.json +41 -0
  157. package/dist/spec/catalogs/layouts/data-visualization.json +45 -0
  158. package/dist/spec/catalogs/layouts/decision-tree.json +35 -0
  159. package/dist/spec/catalogs/layouts/detailed-data-table.json +41 -0
  160. package/dist/spec/catalogs/layouts/ecosystem-map.json +35 -0
  161. package/dist/spec/catalogs/layouts/executive-decision.json +38 -0
  162. package/dist/spec/catalogs/layouts/executive-summary.json +38 -0
  163. package/dist/spec/catalogs/layouts/faq.json +35 -0
  164. package/dist/spec/catalogs/layouts/feature-comparison-matrix.json +35 -0
  165. package/dist/spec/catalogs/layouts/forecast-scenario.json +45 -0
  166. package/dist/spec/catalogs/layouts/funnel-pyramid.json +38 -0
  167. package/dist/spec/catalogs/layouts/gantt-project-plan.json +38 -0
  168. package/dist/spec/catalogs/layouts/geographic-map.json +35 -0
  169. package/dist/spec/catalogs/layouts/heatmap-cohort.json +38 -0
  170. package/dist/spec/catalogs/layouts/hub-and-spoke.json +35 -0
  171. package/dist/spec/catalogs/layouts/icon-grid.json +35 -0
  172. package/dist/spec/catalogs/layouts/image-focus.json +35 -0
  173. package/dist/spec/catalogs/layouts/index.json +352 -0
  174. package/dist/spec/catalogs/layouts/key-takeaway.json +32 -0
  175. package/dist/spec/catalogs/layouts/kpi-scorecard.json +44 -0
  176. package/dist/spec/catalogs/layouts/logic-tree.json +35 -0
  177. package/dist/spec/catalogs/layouts/market-size.json +35 -0
  178. package/dist/spec/catalogs/layouts/matrix-quadrant.json +48 -0
  179. package/dist/spec/catalogs/layouts/network-dependency-map.json +35 -0
  180. package/dist/spec/catalogs/layouts/one-column-explainer.json +35 -0
  181. package/dist/spec/catalogs/layouts/org-chart.json +45 -0
  182. package/dist/spec/catalogs/layouts/persona-grid.json +35 -0
  183. package/dist/spec/catalogs/layouts/photo-grid.json +48 -0
  184. package/dist/spec/catalogs/layouts/pricing-table.json +35 -0
  185. package/dist/spec/catalogs/layouts/process-flow.json +35 -0
  186. package/dist/spec/catalogs/layouts/product-feature.json +35 -0
  187. package/dist/spec/catalogs/layouts/pros-cons.json +41 -0
  188. package/dist/spec/catalogs/layouts/qa-discussion.json +38 -0
  189. package/dist/spec/catalogs/layouts/quote-highlight.json +35 -0
  190. package/dist/spec/catalogs/layouts/raci-matrix.json +35 -0
  191. package/dist/spec/catalogs/layouts/risk-heatmap.json +35 -0
  192. package/dist/spec/catalogs/layouts/roadmap.json +35 -0
  193. package/dist/spec/catalogs/layouts/screenshot-walkthrough.json +35 -0
  194. package/dist/spec/catalogs/layouts/section-divider.json +35 -0
  195. package/dist/spec/catalogs/layouts/single-message-statement.json +32 -0
  196. package/dist/spec/catalogs/layouts/stats-metrics.json +45 -0
  197. package/dist/spec/catalogs/layouts/status-rag.json +35 -0
  198. package/dist/spec/catalogs/layouts/swimlane-process.json +35 -0
  199. package/dist/spec/catalogs/layouts/swot-analysis.json +48 -0
  200. package/dist/spec/catalogs/layouts/team-bio.json +35 -0
  201. package/dist/spec/catalogs/layouts/testimonial-grid.json +44 -0
  202. package/dist/spec/catalogs/layouts/thank-you-cta.json +35 -0
  203. package/dist/spec/catalogs/layouts/three-column.json +45 -0
  204. package/dist/spec/catalogs/layouts/timeline.json +38 -0
  205. package/dist/spec/catalogs/layouts/title-slide.json +35 -0
  206. package/dist/spec/catalogs/layouts/two-column.json +42 -0
  207. package/dist/spec/catalogs/layouts/venn-overlap.json +35 -0
  208. package/dist/spec/catalogs/layouts/waterfall-bridge.json +45 -0
  209. package/dist/spec/catalogs/layouts/wheel-radial.json +35 -0
  210. package/dist/spec/catalogs/manifest.json +111 -0
  211. package/dist/spec/catalogs/narratives/index.json +746 -144
  212. package/dist/spec/catalogs/purposes/index.json +2 -0
  213. package/dist/spec/catalogs/social-platforms/index.json +2 -1
  214. package/dist/spec/catalogs/themes/index.json +10 -8
  215. package/dist/spec/catalogs/tones/index.json +2 -0
  216. package/dist/spec/reference/font-policy.json +5 -5
  217. package/dist/spec/schemas/audience.schema.json +21 -0
  218. package/dist/spec/schemas/catalog-index.schema.json +40 -3
  219. package/dist/spec/schemas/catalog-manifest.schema.json +83 -0
  220. package/dist/spec/schemas/chart-type.schema.json +5 -0
  221. package/dist/spec/schemas/color-scheme.schema.json +21 -0
  222. package/dist/spec/schemas/font-scheme.schema.json +21 -0
  223. package/dist/spec/schemas/language.schema.json +21 -0
  224. package/dist/spec/schemas/layout.schema.json +21 -0
  225. package/dist/spec/schemas/narrative.schema.json +21 -0
  226. package/dist/spec/schemas/opf.schema.json +22 -18
  227. package/dist/spec/schemas/purpose.schema.json +21 -0
  228. package/dist/spec/schemas/social-platform.schema.json +26 -5
  229. package/dist/spec/schemas/theme.schema.json +21 -0
  230. package/dist/spec/schemas/tone.schema.json +21 -0
  231. package/dist/spec-files.d.ts +2 -2
  232. package/dist/spec-files.js +1 -1
  233. package/dist/types.d.ts +2 -2
  234. package/dist/validator.d.ts +2 -2
  235. package/dist/validator.js +4 -4
  236. package/package.json +1 -1
@@ -35,6 +35,114 @@ function tableRowBoundaries(value) {
35
35
  return [...allowed];
36
36
  }
37
37
 
38
+ // src/image-aspect.ts
39
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
40
+ function sourceOf(value) {
41
+ if (typeof value === "string") return value;
42
+ return isRecord(value) && typeof value.src === "string" ? value.src : void 0;
43
+ }
44
+ var PREFIX_BYTES = 64 * 1024;
45
+ var JPEG_PREFIX_BYTES = 1024 * 1024;
46
+ function dataBytes(uri, limit) {
47
+ const match = /^data:([^;,]*)((?:;[^;,]*)*),/i.exec(uri);
48
+ if (!match) return void 0;
49
+ const type = (match[1] ?? "").toLowerCase(), start = match[0].length;
50
+ try {
51
+ if (/;base64/i.test(match[2] ?? "")) {
52
+ const chars = Math.ceil(limit / 3) * 4, text = uri.slice(start, start + chars + chars / 8).replace(/\s+/g, "").slice(0, chars);
53
+ return { type, bytes: Uint8Array.from(atob(text.slice(0, text.length - text.length % 4)), (char) => char.charCodeAt(0)) };
54
+ }
55
+ const prefix = uri.slice(start, start + limit).replace(/%[0-9a-f]?$/i, "");
56
+ return { type, bytes: new TextEncoder().encode(decodeURIComponent(prefix)) };
57
+ } catch {
58
+ return void 0;
59
+ }
60
+ }
61
+ var at = (b, i) => b[i] ?? 0;
62
+ var u16be = (b, i) => at(b, i) << 8 | at(b, i + 1);
63
+ var u32be = (b, i) => (at(b, i) << 24 | at(b, i + 1) << 16 | at(b, i + 2) << 8 | at(b, i + 3)) >>> 0;
64
+ var u16le = (b, i) => at(b, i) | at(b, i + 1) << 8;
65
+ var u24le = (b, i) => at(b, i) | at(b, i + 1) << 8 | at(b, i + 2) << 16;
66
+ var ascii = (b, i, n) => String.fromCharCode(...b.subarray(i, i + n));
67
+ function rasterDimensions(b) {
68
+ if (b.length >= 24 && u32be(b, 0) === 2303741511 && ascii(b, 12, 4) === "IHDR") return { width: u32be(b, 16), height: u32be(b, 20) };
69
+ if (b.length >= 10 && (ascii(b, 0, 6) === "GIF87a" || ascii(b, 0, 6) === "GIF89a")) return { width: u16le(b, 6), height: u16le(b, 8) };
70
+ if (b.length >= 4 && b[0] === 255 && b[1] === 216) {
71
+ let i = 2;
72
+ while (i + 9 < b.length) {
73
+ if (at(b, i) !== 255) {
74
+ i++;
75
+ continue;
76
+ }
77
+ const marker = at(b, i + 1);
78
+ if (marker === 255) {
79
+ i++;
80
+ continue;
81
+ }
82
+ if (marker === 216 || marker === 1 || marker >= 208 && marker <= 215) {
83
+ i += 2;
84
+ continue;
85
+ }
86
+ if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) return { height: u16be(b, i + 5), width: u16be(b, i + 7) };
87
+ i += 2 + u16be(b, i + 2);
88
+ }
89
+ return void 0;
90
+ }
91
+ if (b.length >= 30 && ascii(b, 0, 4) === "RIFF" && ascii(b, 8, 4) === "WEBP") {
92
+ const chunk = ascii(b, 12, 4);
93
+ if (chunk === "VP8X") return { width: u24le(b, 24) + 1, height: u24le(b, 27) + 1 };
94
+ if (chunk === "VP8L" && at(b, 20) === 47) {
95
+ const bits = u16le(b, 21) | u16le(b, 23) << 16;
96
+ return { width: (bits & 16383) + 1, height: (bits >>> 14 & 16383) + 1 };
97
+ }
98
+ if (chunk === "VP8 ") return { width: u16le(b, 26) & 16383, height: u16le(b, 28) & 16383 };
99
+ }
100
+ return void 0;
101
+ }
102
+ function svgDimensions(text) {
103
+ const root = /<svg\b([^>]*)>/i.exec(text)?.[1];
104
+ if (root === void 0) return void 0;
105
+ const attribute = (name) => new RegExp(`\\s${name}\\s*=\\s*["']([^"']*)["']`, "i").exec(root)?.[1];
106
+ const length = (value) => {
107
+ const match = value && /^\s*([0-9]*\.?[0-9]+)\s*(px|pt|in|cm|mm|em)?\s*$/i.exec(value);
108
+ return match ? Number(match[1]) : void 0;
109
+ };
110
+ const width = length(attribute("width")), height = length(attribute("height"));
111
+ if (width && height) return { width, height };
112
+ const box = attribute("viewBox")?.trim().split(/[\s,]+/).map(Number);
113
+ if (box && box.length === 4 && box.every(Number.isFinite)) {
114
+ const [, , width2 = 0, height2 = 0] = box;
115
+ if (width2 > 0 && height2 > 0) return { width: width2, height: height2 };
116
+ }
117
+ return void 0;
118
+ }
119
+ var cache = /* @__PURE__ */ new Map();
120
+ var CACHE_ENTRIES = 64;
121
+ function readAspect(source) {
122
+ const first = dataBytes(source, PREFIX_BYTES);
123
+ if (!first) return void 0;
124
+ let dimensions;
125
+ if (first.type.startsWith("image/svg")) dimensions = svgDimensions(new TextDecoder().decode(first.bytes));
126
+ else {
127
+ dimensions = rasterDimensions(first.bytes);
128
+ if (!dimensions && first.bytes.length >= 2 && first.bytes[0] === 255 && first.bytes[1] === 216) {
129
+ const more = dataBytes(source, JPEG_PREFIX_BYTES);
130
+ if (more) dimensions = rasterDimensions(more.bytes);
131
+ }
132
+ }
133
+ return dimensions && dimensions.width > 0 && dimensions.height > 0 ? dimensions.width / dimensions.height : void 0;
134
+ }
135
+ function intrinsicImageAspect(value, assets) {
136
+ let source = sourceOf(value);
137
+ for (let hop = 0; source?.startsWith("asset:") && hop < 8; hop++) source = sourceOf(isRecord(assets) ? assets[source.slice(6)] : void 0);
138
+ if (!source?.startsWith("data:")) return void 0;
139
+ if (cache.has(source)) return cache.get(source);
140
+ const aspect = readAspect(source);
141
+ if (cache.size >= CACHE_ENTRIES) cache.delete(cache.keys().next().value);
142
+ cache.set(source, aspect);
143
+ return aspect;
144
+ }
145
+
38
146
  // src/furniture-fields.ts
39
147
  var DEFAULT_FURNITURE_DATE_FORMAT = "M/d/yyyy";
40
148
  var DEFAULT_SLIDE_NUMBER_FORMAT = "{current}";
@@ -268,10 +376,12 @@ function resolveFontSchemeReference(reference, lookup, path = "design.fontScheme
268
376
  function resolveFontFamilies(input) {
269
377
  const scheme = record2(input);
270
378
  const family = (value) => typeof value === "string" ? value : record2(value).family;
379
+ const accent = family(scheme.accent);
271
380
  return {
272
381
  heading: family(scheme.heading) ?? scheme.major ?? scheme.minor ?? DEFAULT_FONT_FAMILIES.heading,
273
382
  body: family(scheme.body) ?? scheme.minor ?? scheme.major ?? DEFAULT_FONT_FAMILIES.body,
274
- code: family(scheme.code) ?? FALLBACK_CODE_FAMILY
383
+ code: family(scheme.code) ?? FALLBACK_CODE_FAMILY,
384
+ ...typeof accent === "string" && accent ? { accent } : {}
275
385
  };
276
386
  }
277
387
  function resolveTextStyle(style, measurement) {
@@ -438,6 +548,45 @@ function resolveSlideImage(slide, layout, presentation, width, height, path, pad
438
548
  }
439
549
  return result;
440
550
  }
551
+ var LOGO_LOCKUP_CHAINS = {
552
+ dark: ["light", "default", "stackedLight", "stacked", "wordmarkLight", "wordmark", "iconLight", "icon", "dark", "stackedDark", "wordmarkDark", "iconDark"],
553
+ light: ["dark", "default", "stackedDark", "stacked", "wordmarkDark", "wordmark", "iconDark", "icon", "light", "stackedLight", "wordmarkLight", "iconLight"]
554
+ };
555
+ var LOGO_SET_KEYS = new Set(LOGO_LOCKUP_CHAINS.dark);
556
+ function resolveLogo(presentation, slide, options = {}) {
557
+ const slot = options.slot === "icon" || options.slot === "stacked" ? options.slot : "lockup";
558
+ if (options.slot !== void 0 && slot !== options.slot) throw new RangeError("Logo slot must be lockup, icon or stacked.");
559
+ const tone = options.onDark === true ? "dark" : "light";
560
+ const lockup = LOGO_LOCKUP_CHAINS[tone];
561
+ const chain = slot === "icon" ? [tone === "dark" ? "iconLight" : "iconDark", "icon", ...lockup] : slot === "stacked" ? [tone === "dark" ? "stackedLight" : "stackedDark", "stacked", ...lockup] : lockup;
562
+ const usable = (value) => {
563
+ const source = assetSource(value);
564
+ return typeof source === "string" && source.length > 0;
565
+ };
566
+ const pick = (value, path) => {
567
+ if (value === void 0 || value === null || value === false) return null;
568
+ if (typeof value === "string" || typeof value === "object" && !Array.isArray(value) && "src" in record2(value)) {
569
+ return usable(value) ? { source: value, path, variant: "default", slot } : null;
570
+ }
571
+ const set = record2(value);
572
+ if (!Object.keys(set).some((key) => LOGO_SET_KEYS.has(key))) return null;
573
+ for (const key of chain) {
574
+ const variant = set[key];
575
+ if (variant !== void 0 && usable(variant)) return { source: variant, path: `${path}.${key}`, variant: key, slot };
576
+ }
577
+ return null;
578
+ };
579
+ const deck = record2(presentation), slideIndex = Number.isSafeInteger(options.slideIndex) && options.slideIndex >= 0 ? options.slideIndex : 0;
580
+ const own = pick(record2(record2(slide).design).logo, `slides.${slideIndex}.design.logo`);
581
+ if (own) return own;
582
+ const shared = pick(record2(deck.design).logo, "design.logo");
583
+ if (shared) return shared;
584
+ const organizations = Array.isArray(deck.organization) ? deck.organization : [deck.organization];
585
+ const primaryIndex = organizations.findIndex((item) => record2(item).role === "primary");
586
+ const index = primaryIndex >= 0 ? primaryIndex : organizations.findIndex(Boolean);
587
+ if (index < 0) return null;
588
+ return pick(record2(organizations[index]).logo, Array.isArray(deck.organization) ? `organization.${index}.logo` : "organization.logo");
589
+ }
441
590
  var webUrl = /^https?:\/\/\S+$/i;
442
591
  function resolveSocialProfile(platform, value, records = [], owner = "organization") {
443
592
  const raw = String(value).trim().replace(/\s+/gu, " ");
@@ -516,8 +665,20 @@ function layoutFurniture(input, options = {}) {
516
665
  if (accepted.overflow) error(partPath, "Repeated text exceeds its zone at the selected readability floor; change the furniture or slide design.");
517
666
  y += box.height;
518
667
  };
668
+ const imageBox = (source) => {
669
+ const imageHeight = Math.max(32 * scale, Math.min(height * 0.05, 72 * scale)), imageWidth = Math.min(zoneWidth, imageHeight * (intrinsicImageAspect(source, record2(options.presentation).assets) ?? 1));
670
+ return { x: zone === "left" ? x : zone === "center" ? x + (zoneWidth - imageWidth) / 2 : x + zoneWidth - imageWidth, y, width: imageWidth, height: imageHeight };
671
+ };
672
+ if (content.logo === true) {
673
+ const resolved = resolveLogo(options.presentation, slide, { slot: "icon", onDark: options.darkBackground, slideIndex: options.slideIndex });
674
+ if (resolved) {
675
+ const box = imageBox(resolved.source);
676
+ zoneParts.push({ type: "image", kind: kind2, zone, field: "logo", path: `${path}.logo`, sourcePath: resolved.path, generated: true, image: resolved.source, box, alignment: zone });
677
+ y += box.height;
678
+ } else error(`${path}.logo`, "Generated logo needs design.logo or a primary organization logo.", "unresolved-content");
679
+ }
519
680
  if (content.image !== void 0) {
520
- const box = { x, y, width: zoneWidth, height: Math.max(32 * scale, Math.min(height * 0.05, 72 * scale)) };
681
+ const box = imageBox(content.image);
521
682
  zoneParts.push({ type: "image", kind: kind2, zone, field: "image", path: `${path}.image`, sourcePath: `${path}.image`, generated: false, image: content.image, box, alignment: zone });
522
683
  y += box.height;
523
684
  }
@@ -644,7 +805,7 @@ function layoutQuote(value, box, options = {}) {
644
805
  [source(bodyPath, quote.text, 1)],
645
806
  { x: box.x + 18, y: box.y + 18, width: box.width - 36, height: box.height - (footer ? 94 : 36) },
646
807
  28,
647
- options.fonts?.heading ?? "sans-serif",
808
+ options.fonts?.accent ?? options.fonts?.heading ?? "sans-serif",
648
809
  600,
649
810
  bodyPath
650
811
  );
@@ -780,15 +941,15 @@ function layoutMetric(value, box, options = {}) {
780
941
  const width = Math.max(...lineInk(part, fit).map((line) => Math.max(line.width, line.outline ? line.outline.x + line.outline.width : 0) - Math.min(0, line.outline?.x ?? 0)));
781
942
  return width > 0 ? width + 2 * rasterPadding : 0;
782
943
  };
783
- const cache = /* @__PURE__ */ new Map();
944
+ const cache2 = /* @__PURE__ */ new Map();
784
945
  const measure = (part, size, width) => {
785
946
  const key = JSON.stringify([part.role, size, width]);
786
- let fit = cache.get(key);
947
+ let fit = cache2.get(key);
787
948
  if (!fit) {
788
949
  fit = fitCodeText(part.text, { ...box, width: hasOutlines ? Math.max(Number.MIN_VALUE, width - 2 * rasterPadding) : width }, size, size, scale, textWidthMeasurer(part.style, options.textMeasurement));
789
950
  fit = place(part, fit, width);
790
951
  if (!Number.isFinite(occupied(fit))) throw new RangeError("Metric text layout exceeds finite coordinates.");
791
- cache.set(key, fit);
952
+ cache2.set(key, fit);
792
953
  }
793
954
  return fit;
794
955
  };
@@ -904,10 +1065,10 @@ function layoutTimeline(value, box, options = {}) {
904
1065
  const metadata = [], events = timeline.events.map(() => []);
905
1066
  for (const part of source) if (part.eventIndex === void 0) metadata.push(part);
906
1067
  else events[part.eventIndex].push(part);
907
- const cache = /* @__PURE__ */ new Map();
1068
+ const cache2 = /* @__PURE__ */ new Map();
908
1069
  const measure = (part, size, width, alignment) => {
909
1070
  const key = JSON.stringify([part.path, size, width, alignment]);
910
- let measured = cache.get(key);
1071
+ let measured = cache2.get(key);
911
1072
  if (measured) return measured;
912
1073
  const fit = fitText(part.text, { width: Math.max(Number.MIN_VALUE, width - (outlines ? 2 * padding : 0)), height: Number.MAX_VALUE }, size, size, textWidthMeasurer(part.style, options.textMeasurement));
913
1074
  const ink = fit.sourceLines.map((line, index) => {
@@ -930,7 +1091,7 @@ function layoutTimeline(value, box, options = {}) {
930
1091
  const height = placement?.height ?? fit.sourceLines.length * fit.lineHeight;
931
1092
  if (!Number.isFinite(height)) throw new RangeError("Timeline text exceeds finite layout coordinates.");
932
1093
  measured = { fit, ink, height };
933
- cache.set(key, measured);
1094
+ cache2.set(key, measured);
934
1095
  return measured;
935
1096
  };
936
1097
  let selected, attempts = 0;
@@ -1267,6 +1428,7 @@ function richTextLayouter(input, box, requestedSize, options) {
1267
1428
  };
1268
1429
  return layout;
1269
1430
  }
1431
+ var PICTURE_BULLET_SCALE = 0.65;
1270
1432
  function fitList(input, box, requestedSize = 25, minFontSize = 16, options = { style: { fontFamily: "sans-serif", fontWeight: 400 } }) {
1271
1433
  if (!Array.isArray(input) || ![box.width, box.height, requestedSize, minFontSize].every((value) => Number.isFinite(value) && value > 0)) throw new RangeError("List content, dimensions and font sizes must be valid.");
1272
1434
  const source = input.map((value, index) => {
@@ -1300,6 +1462,7 @@ function fitList(input, box, requestedSize = 25, minFontSize = 16, options = { s
1300
1462
  y += description.height;
1301
1463
  }
1302
1464
  overflow ||= text.overflow || !!description?.overflow;
1465
+ const marker = { text: ["\u2022", "\u25E6", "\u25AA"][item.level % 3], x: box.x + offset, y: textBox.y + (text.richLines[0]?.baseline ?? fontSize), fontSize, style: resolveTextStyle(style, options.textMeasurement), indent }, side = fontSize * PICTURE_BULLET_SCALE;
1303
1466
  entries.push({
1304
1467
  index: item.index,
1305
1468
  level: item.level,
@@ -1311,7 +1474,8 @@ function fitList(input, box, requestedSize = 25, minFontSize = 16, options = { s
1311
1474
  textBox,
1312
1475
  description,
1313
1476
  descriptionBox,
1314
- marker: { text: ["\u2022", "\u25E6", "\u25AA"][item.level % 3], x: box.x + offset, y: textBox.y + (text.richLines[0]?.baseline ?? fontSize), fontSize, style: resolveTextStyle(style, options.textMeasurement), indent }
1477
+ marker,
1478
+ ...options.bulletImage ? { bulletImage: options.bulletImage, bulletBox: { x: marker.x, y: marker.y - side, width: side, height: side } } : {}
1315
1479
  });
1316
1480
  if (item.index < source.length - 1) y += fontSize * 0.28;
1317
1481
  }
@@ -1465,12 +1629,20 @@ function composeSlide(input, options = {}) {
1465
1629
  const minSize = (composition.minFontSize ?? 16) * scale;
1466
1630
  const rasterPadding = (options.textRasterPadding ?? 1) * scale;
1467
1631
  if (!Number.isFinite(rasterPadding) || rasterPadding < 0) throw new RangeError("Text raster padding must be finite and nonnegative.");
1468
- const alignmentFor = (field) => (field === "title" ? record2(slide.design).titleAlignment ?? options.titleAlignment : record2(slide.design).contentAlignment ?? options.contentAlignment) ?? "left";
1632
+ let coverGroup = false;
1633
+ const alignmentFor = (field) => (field === "title" || coverGroup && (field === "tag" || field === "subtitle") && record2(slide.design).contentAlignment === void 0 ? record2(slide.design).titleAlignment ?? options.titleAlignment : record2(slide.design).contentAlignment ?? options.contentAlignment) ?? "left";
1469
1634
  const styleFor = (field, path2) => resolveTextStyle({
1470
- fontFamily: (field === "title" ? options.fonts?.heading : field === "code" ? options.fonts?.code : options.fonts?.body) ?? (field === "code" ? "monospace" : "sans-serif"),
1635
+ fontFamily: (field === "title" ? options.fonts?.heading : field === "code" ? options.fonts?.code : field === "tag" ? options.fonts?.accent ?? options.fonts?.body : options.fonts?.body) ?? (field === "code" ? "monospace" : "sans-serif"),
1471
1636
  fontWeight: field === "title" ? 700 : 400,
1472
1637
  path: path2
1473
1638
  }, options.textMeasurement);
1639
+ const slideDesign = record2(slide.design), deckDesign = record2(record2(options.presentation).design);
1640
+ const designHint = (field) => slideDesign[field] !== void 0 ? { value: slideDesign[field], path: `slides.${options.slideIndex ?? 0}.design.${field}` } : deckDesign[field] !== void 0 ? { value: deckDesign[field], path: `design.${field}` } : void 0;
1641
+ const listBullet = designHint("listBullet");
1642
+ const bulletImage = listBullet?.value === "image" ? (() => {
1643
+ const resolved = resolveLogo(options.presentation, slide, { slot: "icon", onDark: options.darkBackground, slideIndex: options.slideIndex });
1644
+ return resolved ? { source: resolved.source, path: resolved.path } : void 0;
1645
+ })() : void 0;
1474
1646
  const widthFor = (field, path2) => textWidthMeasurer(styleFor(field, path2), options.textMeasurement);
1475
1647
  const fitPlacedText = (field, value, text, box, size, minimum, path2) => {
1476
1648
  const style = styleFor(field, path2), rich = field === "text" && Array.isArray(value);
@@ -1522,7 +1694,7 @@ function composeSlide(input, options = {}) {
1522
1694
  }
1523
1695
  throw new Error("Text placement did not evaluate its bounded floor trial.");
1524
1696
  };
1525
- const fitContent = (field, value, text, box, size, minimum, path2) => field === "text" ? fitPlacedText(field, value, text, box, size, minimum, path2) : field === "items" || field === "bullets" ? fitList(value, box, size, minimum, { style: styleFor(field, path2), textMeasurement: options.textMeasurement }) : fitText(text, box, size, minimum, widthFor(field, path2));
1697
+ const fitContent = (field, value, text, box, size, minimum, path2) => field === "text" ? fitPlacedText(field, value, text, box, size, minimum, path2) : field === "items" || field === "bullets" ? fitList(value, box, size, minimum, { style: styleFor(field, path2), textMeasurement: options.textMeasurement, ...bulletImage ? { bulletImage } : {} }) : fitText(text, box, size, minimum, widthFor(field, path2));
1526
1698
  const path = `slides.${options.slideIndex ?? 0}`;
1527
1699
  const slideImageDiagnostics = [];
1528
1700
  const slideImage = resolveSlideImage(slide, layout, options.presentation, width, height, path, padding, scale, slideImageDiagnostics);
@@ -1544,7 +1716,17 @@ function composeSlide(input, options = {}) {
1544
1716
  const emptyHost = (host) => emptyPayload(host.blocks) && fields.every((field) => emptyPayload(host[field]));
1545
1717
  const hasBodyPayload = regions.some((key) => !emptyHost(record2(slide[key]))) || !emptyPayload(slide.blocks) || fields.some((field) => !emptyPayload(slide[field]));
1546
1718
  const isCover = !hasBodyPayload && (headingOnlyLayout || !layout.id && !layoutPlaceholders.some((placeholder) => !headings.has(placeholder.type ?? "")));
1547
- let y = headingTop;
1719
+ coverGroup = isCover;
1720
+ let logo;
1721
+ if (isCover) {
1722
+ const resolved = resolveLogo(options.presentation, slide, { slot: "lockup", onDark: options.darkBackground, slideIndex: options.slideIndex });
1723
+ if (resolved) {
1724
+ const logoHeight = 56 * scale;
1725
+ const box = { x: round(area.left + padding), y: round(headingTop), width: round(Math.max(scale, Math.min(4 * logoHeight, area.right - area.left - 2 * padding))), height: round(logoHeight) };
1726
+ logo = { box, slot: "lockup", path: resolved.path, source: resolved.source, variant: resolved.variant, anchor: "left" };
1727
+ }
1728
+ }
1729
+ let y = logo ? logo.box.y + logo.box.height + gap : headingTop;
1548
1730
  const headingItems = [];
1549
1731
  for (const field of ["tag", "title", "subtitle"]) {
1550
1732
  if (!slide[field]) continue;
@@ -1681,14 +1863,14 @@ function composeSlide(input, options = {}) {
1681
1863
  const boxes = gridBoxes(node.children.length, area2, cols, (own.gap ?? 1 / 30) * Math.min(box.width, box.height), own.weights ?? [], modeFor(own) === "column");
1682
1864
  return node.children.reduce((score, child, index) => score + scoreNode(child, boxes[index], own, penalties), 0);
1683
1865
  };
1684
- const arrange = (nodes, area2, settings, reserved = 0, gapOverride, containerPath = path) => {
1866
+ const arrange = (nodes, area2, settings, reserved = 0, gapOverride, containerPath = path, recorded = true) => {
1685
1867
  const count = Math.max(nodes.length, reserved);
1686
1868
  if (!count) return;
1687
1869
  const mode = modeFor(settings), localGap = (settings.gap ?? 1 / 30) * Math.min(area2.width, area2.height);
1688
1870
  const actualGap = gapOverride ?? (settings === rootSettings ? gap : localGap);
1689
1871
  let cols = defaultColumns(count, area2, settings);
1690
1872
  const hasRegions = nodes.some((node) => node.region);
1691
- const candidates = decisions ? [] : void 0;
1873
+ const candidates = decisions && recorded ? [] : void 0;
1692
1874
  if (mode === "auto" && !hasRegions) {
1693
1875
  let best = Infinity;
1694
1876
  for (let candidate = 1; candidate <= Math.min(count, settings.columns ?? 6); candidate++) {
@@ -1703,22 +1885,22 @@ function composeSlide(input, options = {}) {
1703
1885
  }
1704
1886
  }
1705
1887
  }
1706
- decisions?.push({
1888
+ if (recorded) decisions?.push({
1707
1889
  path: containerPath,
1708
1890
  mode: hasRegions ? "regions" : mode,
1709
- reason: hasRegions ? "promoted-regions" : mode === "auto" ? "lowest-score" : "configured-mode",
1891
+ reason: hasRegions ? "promoted-regions" : settings === rootSettings && chartPrimary ? "chart-primary" : mode === "auto" ? "lowest-score" : "configured-mode",
1710
1892
  ...hasRegions ? {} : { selectedColumns: cols },
1711
1893
  candidates: candidates ?? []
1712
1894
  });
1713
1895
  const grid = gridGeometry(count, area2, cols, actualGap, settings.weights ?? [], mode === "column");
1714
1896
  const boxes = grid.boxes;
1715
- if (!nodes.some((node) => node.region)) flows.push({ path: containerPath, box: { ...area2 }, composition: { ...settings }, columns: grid.columns, rows: grid.rows, gap: grid.gap, itemCount: nodes.length, slotCount: count });
1897
+ if (recorded && !hasRegions) flows.push({ path: containerPath, box: { ...area2 }, composition: { ...settings }, columns: grid.columns, rows: grid.rows, gap: grid.gap, itemCount: nodes.length, slotCount: count });
1716
1898
  nodes.forEach((node, index) => {
1717
1899
  let box = node.region ? regionBox(node.region, area2, actualGap) : boxes[index];
1718
1900
  if (node.children) {
1719
1901
  const own = inheritedSettings(settings, node.composition), inner = inset(box, own);
1720
- groups.push({ path: node.path, box, contentBox: inner, composition: own });
1721
- arrange(node.children, inner, own, 0, (own.gap ?? 1 / 30) * Math.min(box.width, box.height), node.path);
1902
+ if (!node.synthetic) groups.push({ path: node.path, box, contentBox: inner, composition: own });
1903
+ arrange(node.children, inner, own, 0, (own.gap ?? 1 / 30) * Math.min(box.width, box.height), node.path, !node.synthetic);
1722
1904
  } else {
1723
1905
  const frameBox = hasCards ? acceptedBox(box) : void 0;
1724
1906
  box = payloadBox(box);
@@ -1744,7 +1926,8 @@ function composeSlide(input, options = {}) {
1744
1926
  ...quoteLayout ? { quoteLayout } : {},
1745
1927
  ...codeLayout ? { codeLayout } : {},
1746
1928
  ...metricLayout ? { metricLayout } : {},
1747
- ...timelineLayout ? { timelineLayout } : {}
1929
+ ...timelineLayout ? { timelineLayout } : {},
1930
+ ...bulletImage && (node.field === "items" || node.field === "bullets") ? { bulletImage } : {}
1748
1931
  });
1749
1932
  if (box.width < 100 * scale || box.height < 60 * scale) diagnostics.push({ code: "small-cell", path: node.path, message: "Content cell is too small for comfortable reading; use fewer blocks or a different composition." });
1750
1933
  }
@@ -1755,8 +1938,23 @@ function composeSlide(input, options = {}) {
1755
1938
  const picture = placeholders.findIndex((p) => p.type === "picture");
1756
1939
  if (picture >= 0) placeholders.splice(picture, 1);
1757
1940
  }
1758
- const rootSettings = { ...composition, mode: composition.mode ?? (layout.slideLayoutDirection === "Vertical" ? "column" : layout.slideLayoutDirection === "Horizontal" ? "row" : "auto") };
1759
- arrange(pending, contentBox, rootSettings, composition.mode ? 0 : placeholders.length);
1941
+ const ownMode = record2(slide.composition).mode;
1942
+ const direction = designHint("contentDirection")?.value;
1943
+ const directionMode = direction === "vertical" ? "column" : direction === "horizontal" ? "row" : void 0;
1944
+ const layoutDirectionMode = layout.slideLayoutDirection === "Vertical" ? "column" : layout.slideLayoutDirection === "Horizontal" ? "row" : "auto";
1945
+ const chartHint = designHint("chartPrimary")?.value ?? (typeof layout.contentTypeChartPrimary === "string" ? layout.contentTypeChartPrimary.toLowerCase() : void 0);
1946
+ const chartSide = chartHint === "left" || chartHint === "right" || chartHint === "top" || chartHint === "bottom" ? chartHint : void 0;
1947
+ const chartIndex = pending.findIndex((node) => !node.children && node.field === "chart");
1948
+ const chartPrimary = chartSide !== void 0 && !ownMode && !regions.length && chartIndex >= 0 && pending.some((node) => node.children || node.field !== "chart") ? chartSide : void 0;
1949
+ const rootSettings = chartPrimary ? { ...composition, mode: chartPrimary === "left" || chartPrimary === "right" ? "row" : "column", columns: void 0, weights: chartPrimary === "left" || chartPrimary === "top" ? [3, 2] : [2, 3] } : { ...composition, mode: composition.mode ?? directionMode ?? layoutDirectionMode };
1950
+ if (chartPrimary) {
1951
+ const primary = pending[chartIndex], rest = pending.filter((_, index) => index !== chartIndex);
1952
+ const container = { field: "blocks", type: "group", value: rest.map((node) => node.payload), path, payload: {}, children: rest, composition: {}, synthetic: true };
1953
+ arrange(chartPrimary === "left" || chartPrimary === "top" ? [primary, container] : [container, primary], contentBox, rootSettings, 0);
1954
+ } else arrange(pending, contentBox, rootSettings, composition.mode ? 0 : placeholders.length);
1955
+ if (listBullet?.value === "image" && !bulletImage && items.some((item) => item.field === "items" || item.field === "bullets")) {
1956
+ diagnostics.push({ code: "unresolved-content", path: listBullet.path, message: "Picture bullets (listBullet: image) need design.logo or a primary organization logo; the marker glyph is drawn instead." });
1957
+ }
1760
1958
  for (const item of items) {
1761
1959
  for (const key of ["x", "y", "width", "height"]) item.box[key] = round(item.box[key]);
1762
1960
  if (item.field === "table" && tableOverflows(item.value, item.box, scale, item.composition, options, item.path)) diagnostics.push({ code: "text-overflow", path: item.path, message: "Table cells do not fit; use fewer rows, fewer columns, or split the table across slides." });
@@ -1789,7 +1987,7 @@ function composeSlide(input, options = {}) {
1789
1987
  } : void 0;
1790
1988
  if (failures.length) throw new OPFCompositionError(failures, explanation);
1791
1989
  diagnostics.push(...slideImageDiagnostics);
1792
- return { width, height, contentBox, items, groups, flows, diagnostics, composition, ...furniture ? { furniture } : {}, ...slideImage ? { slideImage } : {}, ...explanation ? { explanation } : {} };
1990
+ return { width, height, contentBox, items, groups, flows, diagnostics, composition, ...furniture ? { furniture } : {}, ...slideImage ? { slideImage } : {}, ...logo ? { logo } : {}, ...explanation ? { explanation } : {} };
1793
1991
  }
1794
1992
  function resolveCanvasDimensions(input) {
1795
1993
  const presets = {
@@ -1809,4 +2007,4 @@ function resolveCanvasDimensions(input) {
1809
2007
  return { width, height };
1810
2008
  }
1811
2009
 
1812
- export { DEFAULT_FONT_SCHEME, DEFAULT_FURNITURE_DATE_FORMAT, DEFAULT_SLIDE_NUMBER_FORMAT, MAX_COMPOSITION_DEPTH, OPFCompositionError, chartColorForFill, colorContrast, composeSlide, fitList, fitRichText, fitText, formatFurnitureDate, formatSlideNumber, layoutCode, layoutFurniture, layoutMetric, layoutQuote, layoutTable, layoutTimeline, measureText, measureTextOutline, normalizeHexColor, placeTextLines, resolveCanvasDimensions, resolveColorRef, resolveFontFamilies, resolveFontSchemeReference, resolveSocialProfile, resolveTextStyle, slideImageShape, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, wrapText };
2010
+ export { DEFAULT_FONT_SCHEME, DEFAULT_FURNITURE_DATE_FORMAT, DEFAULT_SLIDE_NUMBER_FORMAT, MAX_COMPOSITION_DEPTH, OPFCompositionError, PICTURE_BULLET_SCALE, chartColorForFill, colorContrast, composeSlide, fitList, fitRichText, fitText, formatFurnitureDate, formatSlideNumber, layoutCode, layoutFurniture, layoutMetric, layoutQuote, layoutTable, layoutTimeline, measureText, measureTextOutline, normalizeHexColor, placeTextLines, resolveCanvasDimensions, resolveColorRef, resolveFontFamilies, resolveFontSchemeReference, resolveLogo, resolveSocialProfile, resolveTextStyle, slideImageShape, tableGrid, tableRowBoundaries, textColorForFill, textWidthMeasurer, wrapText };
@@ -1,6 +1,6 @@
1
- import { validatePresentation, validationDefinition, validateCatalogRecord } from './chunk-DJRZMGKY.js';
2
- import { schemas, schemaEntries } from './chunk-LTSDDBNW.js';
3
- import { catalogKinds, catalogEntries, catalogSchemaNames } from './chunk-7LF37SG3.js';
1
+ import { validatePresentation, validationDefinition, validateCatalogRecord } from './chunk-FDCWQHWO.js';
2
+ import { schemas, schemaEntries } from './chunk-KJKASEPE.js';
3
+ import { catalogKinds, catalogEntries, catalogSchemaNames } from './chunk-AEUSVEUY.js';
4
4
  import { parseTree, printParseErrorCode, getNodePath } from 'jsonc-parser';
5
5
 
6
6
  var own = (value, key) => Object.hasOwn(value, key);
@@ -447,7 +447,20 @@ function lintPresentation(document, options = {}) {
447
447
  }
448
448
  }
449
449
  for (const issue of validation.warnings) {
450
- const kind = issue.params.kind, id = issue.params.id;
450
+ const kind = issue.params.kind, id = issue.params.id, replacedBy = issue.params.replacedBy;
451
+ if (typeof replacedBy === "string" && typeof id === "string") {
452
+ const target = catalogs.get(kind)?.get(replacedBy);
453
+ diagnostics.push({
454
+ ...schemaDiagnostic(issue),
455
+ ruleId: "opf/deprecated-catalog-id",
456
+ severity: "warning",
457
+ message: `Deprecated ${kind} catalog id ${JSON.stringify(id)}; use ${JSON.stringify(replacedBy)} instead.`,
458
+ help: "The deprecated id still resolves to its original record, so nothing breaks. Switch to the replacement when you next edit this reference.",
459
+ lookup: ["opf", "catalog", kind],
460
+ ...target ? { suggestions: [target] } : {}
461
+ });
462
+ continue;
463
+ }
451
464
  if (seen.has(issue.path) || typeof id === "string" && catalogs.get(kind)?.has(id))
452
465
  continue;
453
466
  diagnostics.push({