@bendyline/squisq 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/dist/{Doc-BeSeH2iX.d.ts → Doc-BsMkmzH8.d.ts} +838 -268
  2. package/dist/{ImageEditDoc-BTIvw0oq.d.ts → ImageEditDoc-DvoeHMP9.d.ts} +11 -4
  3. package/dist/{chunk-ZTX4PFFT.js → chunk-2OIBZYKP.js} +75 -2
  4. package/dist/chunk-2OIBZYKP.js.map +1 -0
  5. package/dist/chunk-2UQOHYE2.js +45 -0
  6. package/dist/chunk-2UQOHYE2.js.map +1 -0
  7. package/dist/{chunk-B4EEON3N.js → chunk-4AKUQPEE.js} +536 -103
  8. package/dist/chunk-4AKUQPEE.js.map +1 -0
  9. package/dist/chunk-4ZFRZBU2.js +6582 -0
  10. package/dist/chunk-4ZFRZBU2.js.map +1 -0
  11. package/dist/{chunk-57CVQCFX.js → chunk-7MQGNR76.js} +79 -3
  12. package/dist/chunk-7MQGNR76.js.map +1 -0
  13. package/dist/{chunk-5WFRKURX.js → chunk-DLXZMS5K.js} +4 -2
  14. package/dist/chunk-DLXZMS5K.js.map +1 -0
  15. package/dist/{chunk-MYIH7FWD.js → chunk-FBKP5CAO.js} +1 -1
  16. package/dist/chunk-FBKP5CAO.js.map +1 -0
  17. package/dist/chunk-KZNXZ5SM.js +333 -0
  18. package/dist/chunk-KZNXZ5SM.js.map +1 -0
  19. package/dist/{chunk-WMOI7FN3.js → chunk-LRCV7N7F.js} +12 -2
  20. package/dist/{chunk-WMOI7FN3.js.map → chunk-LRCV7N7F.js.map} +1 -1
  21. package/dist/{chunk-3K5OG6KI.js → chunk-OH43RPTP.js} +403 -187
  22. package/dist/chunk-OH43RPTP.js.map +1 -0
  23. package/dist/{chunk-H3AXU4MJ.js → chunk-OKJOQXK4.js} +149 -2
  24. package/dist/chunk-OKJOQXK4.js.map +1 -0
  25. package/dist/chunk-PGGGKYWG.js +1 -0
  26. package/dist/chunk-PGGGKYWG.js.map +1 -0
  27. package/dist/{chunk-7UDSRZKG.js → chunk-Q3ROPT5H.js} +35 -14
  28. package/dist/chunk-Q3ROPT5H.js.map +1 -0
  29. package/dist/chunk-QE7TWGVT.js +178 -0
  30. package/dist/chunk-QE7TWGVT.js.map +1 -0
  31. package/dist/chunk-RUDYOTA6.js +345 -0
  32. package/dist/chunk-RUDYOTA6.js.map +1 -0
  33. package/dist/chunk-TVSGLUID.js +596 -0
  34. package/dist/chunk-TVSGLUID.js.map +1 -0
  35. package/dist/chunk-ZLQKNOZQ.js +1 -0
  36. package/dist/chunk-ZLQKNOZQ.js.map +1 -0
  37. package/dist/doc/index.d.ts +4 -4
  38. package/dist/doc/index.js +124 -6
  39. package/dist/generate/index.d.ts +9 -2
  40. package/dist/generate/index.js +1 -1
  41. package/dist/icons/index.d.ts +3 -26
  42. package/dist/icons/index.js +13 -0
  43. package/dist/icons/inlineIconMarker.d.ts +1 -0
  44. package/dist/icons/inlineIconMarker.js +15 -0
  45. package/dist/icons/inlineIconMarker.js.map +1 -0
  46. package/dist/imageEdit/index.d.ts +11 -5
  47. package/dist/imageEdit/index.js +4 -1
  48. package/dist/index.d.ts +11 -8
  49. package/dist/index.js +243 -24
  50. package/dist/inlineIconMarker-CiE8Hlvz.d.ts +59 -0
  51. package/dist/markdown/index.d.ts +212 -3
  52. package/dist/markdown/index.js +40 -7
  53. package/dist/recommend/index.d.ts +1 -1
  54. package/dist/recommend/index.js +3 -2
  55. package/dist/schemas/index.d.ts +28 -8
  56. package/dist/schemas/index.js +35 -5
  57. package/dist/spatial/index.d.ts +9 -4
  58. package/dist/spatial/index.js +1 -1
  59. package/dist/story/index.d.ts +906 -40
  60. package/dist/story/index.js +124 -6
  61. package/dist/{themeLibrary-BHjJ0Qx6.d.ts → themeLibrary-DQQKuRMx.d.ts} +1 -1
  62. package/dist/transform/index.d.ts +48 -10
  63. package/dist/transform/index.js +17 -481
  64. package/dist/transform/index.js.map +1 -1
  65. package/dist/{types-BdZkdJ3z.d.ts → types-DlAZ7MW4.d.ts} +115 -1
  66. package/package.json +6 -1
  67. package/src/__tests__/animationUtils.test.ts +40 -0
  68. package/src/__tests__/annotationCoercion.test.ts +243 -0
  69. package/src/__tests__/applyRenderStyle.test.ts +146 -0
  70. package/src/__tests__/atmosphereLayers.test.ts +108 -0
  71. package/src/__tests__/attrTokens.test.ts +162 -0
  72. package/src/__tests__/customTemplateSample.test.ts +83 -0
  73. package/src/__tests__/customTemplatesFrontmatter.test.ts +205 -0
  74. package/src/__tests__/customThemesFrontmatter.test.ts +118 -0
  75. package/src/__tests__/diagramBlock.test.ts +122 -0
  76. package/src/__tests__/diagramLayout.test.ts +134 -0
  77. package/src/__tests__/drawing.test.ts +109 -0
  78. package/src/__tests__/drawingBlock.test.ts +124 -0
  79. package/src/__tests__/drawingLayout.test.ts +171 -0
  80. package/src/__tests__/geohash.test.ts +6 -5
  81. package/src/__tests__/getLayers.test.ts +6 -2
  82. package/src/__tests__/getLayersFallback.test.ts +103 -0
  83. package/src/__tests__/imageEditExportSvg.test.ts +70 -0
  84. package/src/__tests__/imageEditVersions.test.ts +72 -0
  85. package/src/__tests__/inlineIconMarker.test.ts +36 -0
  86. package/src/__tests__/layoutBlock.test.ts +83 -0
  87. package/src/__tests__/layoutLayout.test.ts +169 -0
  88. package/src/__tests__/markdown.test.ts +23 -0
  89. package/src/__tests__/markdownSanitize.test.ts +64 -0
  90. package/src/__tests__/markdownToDoc.test.ts +223 -2
  91. package/src/__tests__/mediaAnnotations.test.ts +64 -0
  92. package/src/__tests__/mediaSchedule.test.ts +118 -0
  93. package/src/__tests__/pandocAttrParse.test.ts +187 -0
  94. package/src/__tests__/resolveDocTheme.test.ts +55 -0
  95. package/src/__tests__/rootExports.test.ts +43 -0
  96. package/src/__tests__/shapeGeometry.test.ts +89 -0
  97. package/src/__tests__/shapeVocabulary.test.ts +133 -0
  98. package/src/__tests__/structuredData.test.ts +179 -0
  99. package/src/__tests__/templateAnnotationParse.test.ts +108 -0
  100. package/src/__tests__/templateMetadata.test.ts +21 -0
  101. package/src/__tests__/templates.test.ts +145 -4
  102. package/src/__tests__/themeCompile.test.ts +42 -0
  103. package/src/__tests__/themeValidator.test.ts +6 -0
  104. package/src/__tests__/transformV2.test.ts +137 -0
  105. package/src/__tests__/transitionNormalize.test.ts +61 -0
  106. package/src/__tests__/validateDoc.test.ts +100 -0
  107. package/src/doc/customTemplatesFrontmatter.ts +308 -0
  108. package/src/doc/customThemesFrontmatter.ts +90 -0
  109. package/src/doc/docToMarkdown.ts +73 -4
  110. package/src/doc/getLayers.ts +40 -17
  111. package/src/doc/index.ts +35 -1
  112. package/src/doc/markdownToDoc.ts +333 -12
  113. package/src/doc/mediaAnnotations.ts +118 -0
  114. package/src/doc/resolveDocTheme.ts +55 -0
  115. package/src/doc/structuredData.ts +230 -0
  116. package/src/doc/templateInputs.ts +240 -0
  117. package/src/doc/templates/__tests__/customTemplate.test.ts +151 -0
  118. package/src/doc/templates/__tests__/customTemplateRegistry.test.ts +110 -0
  119. package/src/doc/templates/accentImage.ts +30 -6
  120. package/src/doc/templates/captionUtils.ts +23 -0
  121. package/src/doc/templates/comparisonBar.ts +26 -18
  122. package/src/doc/templates/coverBlock.ts +7 -4
  123. package/src/doc/templates/customTemplate.ts +45 -0
  124. package/src/doc/templates/dataTable.ts +37 -19
  125. package/src/doc/templates/dateEvent.ts +41 -20
  126. package/src/doc/templates/definitionCard.ts +26 -18
  127. package/src/doc/templates/diagramBlock.ts +204 -0
  128. package/src/doc/templates/diagramLayout.ts +159 -0
  129. package/src/doc/templates/drawingBlock.ts +325 -0
  130. package/src/doc/templates/drawingLayout.ts +539 -0
  131. package/src/doc/templates/factCard.ts +51 -25
  132. package/src/doc/templates/fallbackBlock.ts +123 -0
  133. package/src/doc/templates/featureBlock.ts +20 -13
  134. package/src/doc/templates/fullBleedQuote.ts +18 -8
  135. package/src/doc/templates/imageWithCaption.ts +22 -9
  136. package/src/doc/templates/index.ts +215 -46
  137. package/src/doc/templates/layoutBlock.ts +43 -0
  138. package/src/doc/templates/layoutLayout.ts +243 -0
  139. package/src/doc/templates/listBlock.ts +56 -30
  140. package/src/doc/templates/mapBlock.ts +15 -12
  141. package/src/doc/templates/metadata.ts +120 -0
  142. package/src/doc/templates/persistentLayers.ts +129 -11
  143. package/src/doc/templates/photoGrid.ts +23 -12
  144. package/src/doc/templates/pullQuote.ts +30 -12
  145. package/src/doc/templates/quoteBlock.ts +54 -29
  146. package/src/doc/templates/sectionHeader.ts +25 -11
  147. package/src/doc/templates/statHighlight.ts +39 -22
  148. package/src/doc/templates/titleBlock.ts +59 -21
  149. package/src/doc/templates/tokens/__tests__/resolveTokens.test.ts +221 -0
  150. package/src/doc/templates/tokens/resolveTokens.ts +0 -0
  151. package/src/doc/templates/twoColumn.ts +48 -30
  152. package/src/doc/templates/videoPullQuote.ts +22 -12
  153. package/src/doc/templates/videoWithCaption.ts +13 -8
  154. package/src/doc/utils/animationUtils.ts +267 -3
  155. package/src/doc/utils/applyRenderStyle.ts +113 -0
  156. package/src/doc/utils/imageTreatment.ts +70 -0
  157. package/src/doc/utils/shapeGeometry.ts +464 -0
  158. package/src/doc/utils/themeUtils.ts +75 -1
  159. package/src/doc/validate.ts +476 -0
  160. package/src/generate/slideshowGenerator.ts +7 -0
  161. package/src/icons/index.ts +8 -0
  162. package/src/icons/inlineIconMarker.ts +73 -0
  163. package/src/imageEdit/export.ts +85 -2
  164. package/src/imageEdit/index.ts +1 -0
  165. package/src/imageEdit/persistence.ts +51 -0
  166. package/src/index.ts +2 -0
  167. package/src/markdown/annotationCoercion.ts +295 -0
  168. package/src/markdown/attrTokens.ts +222 -0
  169. package/src/markdown/convert.ts +229 -26
  170. package/src/markdown/index.ts +36 -0
  171. package/src/markdown/sanitize.ts +298 -0
  172. package/src/markdown/stringify.ts +84 -3
  173. package/src/markdown/types.ts +66 -0
  174. package/src/markdown/utils.ts +49 -1
  175. package/src/recommend/templates.ts +24 -0
  176. package/src/schemas/BlockTemplates.ts +133 -4
  177. package/src/schemas/CustomTemplates.ts +74 -0
  178. package/src/schemas/Doc.ts +353 -29
  179. package/src/schemas/ImageEditDoc.ts +11 -4
  180. package/src/schemas/Media.ts +169 -0
  181. package/src/schemas/Theme.ts +23 -1
  182. package/src/schemas/Transitions.ts +224 -0
  183. package/src/schemas/colorUtils.ts +33 -0
  184. package/src/schemas/fontStacks.ts +5 -2
  185. package/src/schemas/index.ts +3 -0
  186. package/src/schemas/themeCompile.ts +23 -4
  187. package/src/schemas/themeValidator.ts +49 -11
  188. package/src/schemas/themes/cinematic.json +69 -12
  189. package/src/schemas/themes/documentary.json +58 -11
  190. package/src/schemas/themes/gezellig.json +60 -11
  191. package/src/schemas/themes/magazine.json +54 -9
  192. package/src/schemas/themes/morning-light.json +52 -9
  193. package/src/schemas/themes/standard-dark.json +48 -13
  194. package/src/schemas/themes/standard.json +44 -12
  195. package/src/schemas/themes/tech-dark.json +65 -12
  196. package/src/spatial/Geohash.ts +52 -21
  197. package/src/transform/applyTransform.ts +43 -4
  198. package/src/transform/index.ts +1 -0
  199. package/src/transform/registry.ts +44 -13
  200. package/src/transform/styles/dataDriven.ts +2 -1
  201. package/src/transform/styles/documentary.ts +2 -1
  202. package/src/transform/styles/magazine.ts +3 -1
  203. package/src/transform/styles/minimal.ts +2 -1
  204. package/src/transform/styles/narrative.ts +3 -1
  205. package/src/transform/templateSelector.ts +87 -11
  206. package/src/transform/types.ts +35 -2
  207. package/dist/chunk-3K5OG6KI.js.map +0 -1
  208. package/dist/chunk-57CVQCFX.js.map +0 -1
  209. package/dist/chunk-5WFRKURX.js.map +0 -1
  210. package/dist/chunk-7UDSRZKG.js.map +0 -1
  211. package/dist/chunk-B4EEON3N.js.map +0 -1
  212. package/dist/chunk-H3AXU4MJ.js.map +0 -1
  213. package/dist/chunk-KSWJR66U.js +0 -3706
  214. package/dist/chunk-KSWJR66U.js.map +0 -1
  215. package/dist/chunk-MYIH7FWD.js.map +0 -1
  216. package/dist/chunk-ZTX4PFFT.js.map +0 -1
@@ -0,0 +1,596 @@
1
+ import {
2
+ extractContent,
3
+ mapElementToBlock,
4
+ stripMarkdown
5
+ } from "./chunk-FR2RBTKO.js";
6
+ import {
7
+ SeededRandom,
8
+ hashString
9
+ } from "./chunk-FVTQSNR7.js";
10
+ import {
11
+ isTemplateBlock
12
+ } from "./chunk-DLXZMS5K.js";
13
+ import {
14
+ extractPlainText,
15
+ getChildren
16
+ } from "./chunk-OKJOQXK4.js";
17
+
18
+ // src/transform/styles/documentary.ts
19
+ var documentaryStyle = {
20
+ id: "documentary",
21
+ name: "Documentary",
22
+ description: "Conservative \u2014 highlights only the strongest stats, dates, and quotes",
23
+ minConfidence: 0.5,
24
+ transformRatio: 0.3,
25
+ preferredTypes: ["stat", "date", "quote", "fact"],
26
+ colorSchemes: ["blue", "green", "purple"],
27
+ insertSectionHeaders: false,
28
+ interleaveImages: true,
29
+ blocksPerSection: { max: 3 },
30
+ transitionStyle: "fade",
31
+ suggestedThemeId: "documentary"
32
+ };
33
+
34
+ // src/transform/styles/magazine.ts
35
+ var magazineStyle = {
36
+ id: "magazine",
37
+ name: "Magazine",
38
+ description: "Visual variety with pull quotes, facts, and rich image layouts",
39
+ minConfidence: 0.4,
40
+ transformRatio: 0.6,
41
+ preferredTypes: ["quote", "fact", "list", "definition", "stat", "impactLine"],
42
+ colorSchemes: ["blue", "green", "purple", "orange", "red"],
43
+ insertSectionHeaders: true,
44
+ interleaveImages: true,
45
+ blocksPerSection: { max: 5 },
46
+ transitionStyle: "dissolve",
47
+ suggestedThemeId: "magazine",
48
+ templateMap: { quote: "pullQuote", impactLine: "fullBleedQuote" }
49
+ };
50
+
51
+ // src/transform/styles/dataDriven.ts
52
+ var dataDrivenStyle = {
53
+ id: "data-driven",
54
+ name: "Data-Driven",
55
+ description: "Aggressively highlights statistics, comparisons, and timelines",
56
+ minConfidence: 0.3,
57
+ transformRatio: 0.8,
58
+ preferredTypes: ["stat", "comparison", "date", "list", "fact"],
59
+ colorSchemes: ["blue", "orange", "green", "purple", "red"],
60
+ insertSectionHeaders: true,
61
+ interleaveImages: false,
62
+ blocksPerSection: { max: 6 },
63
+ transitionStyle: "cut",
64
+ suggestedThemeId: "tech-dark"
65
+ };
66
+
67
+ // src/transform/styles/narrative.ts
68
+ var narrativeStyle = {
69
+ id: "narrative",
70
+ name: "Narrative",
71
+ description: "Story-driven with timeline events, quotes, and impact moments",
72
+ minConfidence: 0.4,
73
+ transformRatio: 0.5,
74
+ preferredTypes: ["date", "quote", "impactLine", "fact", "stat"],
75
+ colorSchemes: ["blue", "purple", "green"],
76
+ insertSectionHeaders: true,
77
+ interleaveImages: true,
78
+ blocksPerSection: { max: 4 },
79
+ transitionStyle: "fade",
80
+ suggestedThemeId: "cinematic",
81
+ pacing: { intro: true, outro: true }
82
+ };
83
+
84
+ // src/transform/styles/minimal.ts
85
+ var minimalStyle = {
86
+ id: "minimal",
87
+ name: "Minimal",
88
+ description: "Light touch \u2014 only the strongest highlights, mostly text",
89
+ minConfidence: 0.6,
90
+ transformRatio: 0.2,
91
+ preferredTypes: ["stat", "quote"],
92
+ colorSchemes: ["blue", "green"],
93
+ insertSectionHeaders: false,
94
+ interleaveImages: false,
95
+ blocksPerSection: { max: 2 },
96
+ transitionStyle: "fade",
97
+ suggestedThemeId: "minimalist"
98
+ };
99
+
100
+ // src/transform/registry.ts
101
+ var TRANSFORM_STYLES = {
102
+ [documentaryStyle.id]: documentaryStyle,
103
+ [magazineStyle.id]: magazineStyle,
104
+ [dataDrivenStyle.id]: dataDrivenStyle,
105
+ [narrativeStyle.id]: narrativeStyle,
106
+ [minimalStyle.id]: minimalStyle
107
+ };
108
+ var STYLE_ALIASES = {
109
+ dataDriven: "data-driven"
110
+ };
111
+ var registeredStyles = /* @__PURE__ */ new Map();
112
+ var DEFAULT_TRANSFORM_STYLE_ID = "documentary";
113
+ function registerTransformStyle(style) {
114
+ registeredStyles.set(style.id, style);
115
+ }
116
+ function unregisterTransformStyle(id) {
117
+ registeredStyles.delete(id);
118
+ }
119
+ function resolveTransformStyle(id) {
120
+ const canonical = STYLE_ALIASES[id] ?? id;
121
+ return registeredStyles.get(canonical) ?? TRANSFORM_STYLES[canonical] ?? TRANSFORM_STYLES[DEFAULT_TRANSFORM_STYLE_ID];
122
+ }
123
+ function getTransformStyleIds() {
124
+ return Array.from(/* @__PURE__ */ new Set([...Object.keys(TRANSFORM_STYLES), ...registeredStyles.keys()]));
125
+ }
126
+ function getTransformStyleSummaries() {
127
+ return getTransformStyleIds().map((id) => {
128
+ const { name, description } = resolveTransformStyle(id);
129
+ return { id, name, description };
130
+ });
131
+ }
132
+
133
+ // src/transform/blockAnalyzer.ts
134
+ function contentsToPlainText(contents) {
135
+ return contents.map((node) => extractPlainText(node)).join("\n");
136
+ }
137
+ function collectImages(node) {
138
+ const results = [];
139
+ if ("type" in node && node.type === "image" && "url" in node) {
140
+ const img = node;
141
+ if (img.url) {
142
+ results.push({ src: img.url, alt: img.alt ?? "" });
143
+ }
144
+ }
145
+ for (const child of getChildren(node)) {
146
+ results.push(...collectImages(child));
147
+ }
148
+ return results;
149
+ }
150
+ function extractBlockImages(block, blockIndex) {
151
+ if (!block.contents || block.contents.length === 0) return [];
152
+ const images = [];
153
+ for (const node of block.contents) {
154
+ for (const img of collectImages(node)) {
155
+ images.push({ ...img, sourceBlockIndex: blockIndex });
156
+ }
157
+ }
158
+ return images;
159
+ }
160
+ function extractDocImages(blocks) {
161
+ const seen = /* @__PURE__ */ new Set();
162
+ const images = [];
163
+ function walk(blockList) {
164
+ for (const block of blockList) {
165
+ if (block.contents) {
166
+ for (const node of block.contents) {
167
+ for (const img of collectImages(node)) {
168
+ if (!seen.has(img.src)) {
169
+ seen.add(img.src);
170
+ images.push({ src: img.src, alt: img.alt });
171
+ }
172
+ }
173
+ }
174
+ }
175
+ if (block.children) {
176
+ walk(block.children);
177
+ }
178
+ }
179
+ }
180
+ walk(blocks);
181
+ return images;
182
+ }
183
+ function collectLeafBlocks(blocks) {
184
+ const result = [];
185
+ for (const block of blocks) {
186
+ if (block.children && block.children.length > 0) {
187
+ if (!block.contents || block.contents.length === 0) {
188
+ result.push(block);
189
+ }
190
+ result.push(...collectLeafBlocks(block.children));
191
+ } else {
192
+ result.push(block);
193
+ }
194
+ }
195
+ return result;
196
+ }
197
+ function analyzeBlocks(blocks, options) {
198
+ const flat = collectLeafBlocks(blocks);
199
+ const results = [];
200
+ for (const block of flat) {
201
+ let plainText = "";
202
+ if (block.contents && block.contents.length > 0) {
203
+ plainText = contentsToPlainText(block.contents);
204
+ } else if (block.title) {
205
+ plainText = block.title;
206
+ }
207
+ const stripped = stripMarkdown(plainText);
208
+ const bodyWordCount = stripped.split(/\s+/).filter((w) => w.length > 0).length;
209
+ const extraction = extractContent(stripped, options);
210
+ const images = extractBlockImages(block, results.length);
211
+ results.push({
212
+ block,
213
+ plainText: stripped,
214
+ extractions: extraction.elements,
215
+ hasChildren: !!(block.children && block.children.length > 0),
216
+ bodyWordCount,
217
+ images
218
+ });
219
+ }
220
+ return results;
221
+ }
222
+
223
+ // src/transform/templateSelector.ts
224
+ var ACCENT_POSITIONS = [
225
+ "left-strip",
226
+ "right-strip",
227
+ "bottom-strip",
228
+ "corner-inset"
229
+ ];
230
+ var AMBIENT_MOTIONS = [
231
+ "zoomIn",
232
+ "zoomOut",
233
+ "panLeft",
234
+ "panRight"
235
+ ];
236
+ function selectAndBuild(analyzed, config, images, seed) {
237
+ const rng = new SeededRandom(seed);
238
+ const candidates = collectCandidates(analyzed, config);
239
+ const ratioCap = Math.max(1, Math.floor(analyzed.length * config.transformRatio));
240
+ const slidesPerMinute = config.budget?.slidesPerMinute;
241
+ const totalDurationSeconds = analyzed.reduce((sum, ab) => sum + (ab.block.duration || 0), 0);
242
+ const budgetCap = slidesPerMinute && totalDurationSeconds > 0 ? Math.max(1, Math.round(totalDurationSeconds / 60 * slidesPerMinute)) : Infinity;
243
+ const maxTransforms = Math.min(ratioCap, budgetCap);
244
+ const selected = candidates.slice(0, maxTransforms);
245
+ const result = buildBlockSequence(analyzed, selected, config, images, rng);
246
+ return result;
247
+ }
248
+ function collectCandidates(analyzed, config) {
249
+ const candidates = [];
250
+ for (let i = 0; i < analyzed.length; i++) {
251
+ const ab = analyzed[i];
252
+ if (ab.bodyWordCount < 5) continue;
253
+ for (const element of ab.extractions) {
254
+ if (element.confidence < config.minConfidence) continue;
255
+ candidates.push({
256
+ element,
257
+ sourceBlock: ab.block,
258
+ sourceIndex: i
259
+ });
260
+ }
261
+ }
262
+ const typeRank = new Map(config.preferredTypes.map((t, i) => [t, i]));
263
+ const maxRank = config.preferredTypes.length;
264
+ candidates.sort((a, b) => {
265
+ const rankA = typeRank.get(a.element.type) ?? maxRank;
266
+ const rankB = typeRank.get(b.element.type) ?? maxRank;
267
+ if (rankA !== rankB) return rankA - rankB;
268
+ return b.element.confidence - a.element.confidence;
269
+ });
270
+ const seenTexts = /* @__PURE__ */ new Set();
271
+ const deduplicated = [];
272
+ for (const candidate of candidates) {
273
+ const key = candidate.element.text.trim().toLowerCase();
274
+ if (seenTexts.has(key)) continue;
275
+ seenTexts.add(key);
276
+ deduplicated.push(candidate);
277
+ }
278
+ return deduplicated;
279
+ }
280
+ function buildBlockSequence(analyzed, selected, config, images, rng) {
281
+ const blockExtractions = /* @__PURE__ */ new Map();
282
+ for (const sel of selected) {
283
+ const list = blockExtractions.get(sel.sourceIndex) ?? [];
284
+ list.push(sel);
285
+ blockExtractions.set(sel.sourceIndex, list);
286
+ }
287
+ for (const [idx, list] of blockExtractions) {
288
+ const diverse = [];
289
+ const usedTypes = /* @__PURE__ */ new Set();
290
+ for (const sel of list) {
291
+ if (usedTypes.has(sel.element.type)) continue;
292
+ usedTypes.add(sel.element.type);
293
+ diverse.push(sel);
294
+ if (diverse.length >= config.blocksPerSection.max) break;
295
+ }
296
+ blockExtractions.set(idx, diverse);
297
+ }
298
+ const blocks = [];
299
+ let transformedCount = 0;
300
+ let insertedCount = 0;
301
+ let colorIndex = 0;
302
+ let blockIdCounter = 0;
303
+ let imageIndex = 0;
304
+ let accentPositionIndex = 0;
305
+ let elementsSinceImage = 0;
306
+ let prevTemplateType = "";
307
+ const totalTransformed = Array.from(blockExtractions.values()).reduce(
308
+ (sum, list) => sum + list.length,
309
+ 0
310
+ );
311
+ const imageInterval = config.interleaveImages && images.length > 0 && totalTransformed > 0 ? Math.max(2, Math.floor(totalTransformed / (images.length + 1))) : Infinity;
312
+ for (let i = 0; i < analyzed.length; i++) {
313
+ const ab = analyzed[i];
314
+ const extractions = blockExtractions.get(i);
315
+ if (!extractions || extractions.length === 0) {
316
+ blocks.push(ab.block);
317
+ continue;
318
+ }
319
+ if (config.insertSectionHeaders && ab.block.title) {
320
+ const headerBlock = {
321
+ template: "sectionHeader",
322
+ id: `transform-header-${blockIdCounter++}`,
323
+ duration: ab.block.duration > 0 ? Math.min(3, ab.block.duration * 0.2) : 3,
324
+ audioSegment: ab.block.audioSegment,
325
+ title: ab.block.title,
326
+ colorScheme: config.colorSchemes[colorIndex % config.colorSchemes.length]
327
+ };
328
+ blocks.push(headerBlock);
329
+ insertedCount++;
330
+ }
331
+ for (const sel of extractions) {
332
+ const colorScheme = config.colorSchemes[colorIndex % config.colorSchemes.length];
333
+ let accentImage;
334
+ if (images.length > 0 && config.interleaveImages) {
335
+ const img = images[imageIndex % images.length];
336
+ accentImage = {
337
+ src: img.src,
338
+ alt: img.alt ?? "",
339
+ position: ACCENT_POSITIONS[accentPositionIndex % ACCENT_POSITIONS.length],
340
+ ambientMotion: rng.pick(AMBIENT_MOTIONS) ?? "zoomIn",
341
+ credit: img.credit,
342
+ license: img.license
343
+ };
344
+ accentPositionIndex++;
345
+ }
346
+ let templateBlock = mapElementToBlock(sel.element, {
347
+ id: `transform-${blockIdCounter++}`,
348
+ duration: ab.block.duration > 0 ? ab.block.duration / extractions.length : 6,
349
+ audioSegment: ab.block.audioSegment,
350
+ colorScheme,
351
+ accentImage,
352
+ sourceStartTime: sel.element.sourcePosition
353
+ });
354
+ const remapTarget = config.templateMap?.[sel.element.data.type];
355
+ if (remapTarget && remapTarget !== templateBlock.template) {
356
+ const translated = translateTemplateBlock(templateBlock, remapTarget);
357
+ if (translated) templateBlock = translated;
358
+ }
359
+ if (config.transitionStyle !== "cut") {
360
+ const transitionType = config.transitionStyle === "mixed" ? rng.pick(["fade", "dissolve"]) ?? "fade" : config.transitionStyle;
361
+ templateBlock.transition = {
362
+ type: transitionType,
363
+ duration: 0.5
364
+ };
365
+ }
366
+ if (templateBlock.template === prevTemplateType) {
367
+ colorIndex++;
368
+ }
369
+ prevTemplateType = templateBlock.template;
370
+ blocks.push(templateBlock);
371
+ transformedCount++;
372
+ colorIndex++;
373
+ elementsSinceImage++;
374
+ if (config.interleaveImages && imageIndex < images.length && elementsSinceImage >= imageInterval) {
375
+ const img = images[imageIndex];
376
+ const imageBlock = {
377
+ template: "imageWithCaption",
378
+ id: `transform-img-${blockIdCounter++}`,
379
+ duration: 4,
380
+ audioSegment: ab.block.audioSegment,
381
+ imageSrc: img.src,
382
+ imageAlt: img.alt ?? "",
383
+ ambientMotion: rng.pick(AMBIENT_MOTIONS) ?? "zoomIn"
384
+ };
385
+ blocks.push(imageBlock);
386
+ imageIndex++;
387
+ insertedCount++;
388
+ elementsSinceImage = 0;
389
+ }
390
+ }
391
+ }
392
+ return { blocks, transformedCount, insertedCount };
393
+ }
394
+ function translateTemplateBlock(block, target) {
395
+ const base = {
396
+ id: block.id,
397
+ duration: block.duration,
398
+ audioSegment: block.audioSegment,
399
+ sourceStartTime: block.sourceStartTime,
400
+ sourceDuration: block.sourceDuration
401
+ };
402
+ if (block.template === "quote") {
403
+ const quote = block;
404
+ if (target === "fullBleedQuote") {
405
+ return { ...base, template: "fullBleedQuote", text: quote.quote };
406
+ }
407
+ if (target === "pullQuote") {
408
+ const bg = quote.accentImage;
409
+ if (!bg) return null;
410
+ return {
411
+ ...base,
412
+ template: "pullQuote",
413
+ text: quote.quote,
414
+ attribution: quote.attribution,
415
+ backgroundImage: { src: bg.src, alt: bg.alt, credit: bg.credit, license: bg.license }
416
+ };
417
+ }
418
+ return null;
419
+ }
420
+ if (block.template === "fullBleedQuote" && target === "quote") {
421
+ const fbq = block;
422
+ return { ...base, template: "quote", quote: fbq.text };
423
+ }
424
+ if (block.template === "statHighlight" && target === "factCard") {
425
+ const stat = block;
426
+ return {
427
+ ...base,
428
+ template: "factCard",
429
+ fact: stat.stat,
430
+ explanation: stat.description,
431
+ accentImage: stat.accentImage
432
+ };
433
+ }
434
+ return null;
435
+ }
436
+
437
+ // src/transform/timingAllocator.ts
438
+ var MIN_BLOCK_DURATION = 3;
439
+ var MAX_BLOCK_DURATION = 20;
440
+ var DEFAULT_BLOCK_DURATION = 6;
441
+ function allocateTiming(blocks, totalDuration) {
442
+ if (blocks.length === 0) return [];
443
+ const effectiveDuration = totalDuration > 0 ? totalDuration : estimateDefaultDuration(blocks);
444
+ const durations = blocks.map((block) => {
445
+ if (!isTemplateBlock(block) && block.duration > 0) {
446
+ return block.duration;
447
+ }
448
+ if (isTemplateBlock(block) && block.duration > 0) {
449
+ return block.duration;
450
+ }
451
+ return DEFAULT_BLOCK_DURATION;
452
+ });
453
+ const rawTotal = durations.reduce((sum, d) => sum + d, 0);
454
+ if (rawTotal > 0 && effectiveDuration > 0) {
455
+ const scale = effectiveDuration / rawTotal;
456
+ for (let i = 0; i < durations.length; i++) {
457
+ durations[i] = clampDuration(durations[i] * scale);
458
+ }
459
+ }
460
+ let currentTime = 0;
461
+ const result = [];
462
+ for (let i = 0; i < blocks.length; i++) {
463
+ const block = blocks[i];
464
+ const duration = durations[i];
465
+ if (isTemplateBlock(block)) {
466
+ const outputBlock = {
467
+ ...block,
468
+ startTime: currentTime,
469
+ duration,
470
+ title: getTemplateTitle(block)
471
+ };
472
+ result.push(outputBlock);
473
+ } else {
474
+ result.push({
475
+ ...block,
476
+ startTime: currentTime,
477
+ duration
478
+ });
479
+ }
480
+ currentTime += duration;
481
+ }
482
+ return result;
483
+ }
484
+ function clampDuration(d) {
485
+ return Math.max(MIN_BLOCK_DURATION, Math.min(MAX_BLOCK_DURATION, d));
486
+ }
487
+ function estimateDefaultDuration(blocks) {
488
+ let totalSeconds = 0;
489
+ for (const block of blocks) {
490
+ if (!isTemplateBlock(block) && block.duration > 0) {
491
+ totalSeconds += block.duration;
492
+ } else {
493
+ totalSeconds += DEFAULT_BLOCK_DURATION;
494
+ }
495
+ }
496
+ return Math.max(totalSeconds, blocks.length * MIN_BLOCK_DURATION);
497
+ }
498
+ function getTemplateTitle(block) {
499
+ if ("title" in block && typeof block.title === "string") {
500
+ return block.title;
501
+ }
502
+ if ("stat" in block && typeof block.stat === "string") {
503
+ return block.stat;
504
+ }
505
+ if ("fact" in block && typeof block.fact === "string") {
506
+ return block.fact;
507
+ }
508
+ if ("quote" in block && typeof block.quote === "string") {
509
+ return block.quote.slice(0, 60);
510
+ }
511
+ return void 0;
512
+ }
513
+
514
+ // src/transform/applyTransform.ts
515
+ function applyTransform(doc, styleId, options) {
516
+ const baseConfig = resolveTransformStyle(styleId);
517
+ const config = options?.overrides ? { ...baseConfig, ...options.overrides, id: baseConfig.id } : baseConfig;
518
+ const seed = options?.seed ?? hashString(doc.articleId || "transform");
519
+ const images = options?.images ?? extractDocImages(doc.blocks);
520
+ const analyzed = analyzeBlocks(doc.blocks, {
521
+ minConfidence: config.minConfidence,
522
+ types: config.preferredTypes
523
+ });
524
+ const hasContent = analyzed.some((ab) => ab.bodyWordCount >= 5);
525
+ if (!hasContent) {
526
+ return {
527
+ doc: { ...doc },
528
+ stats: {
529
+ totalInputBlocks: doc.blocks.length,
530
+ transformedBlocks: 0,
531
+ insertedBlocks: 0
532
+ }
533
+ };
534
+ }
535
+ const selection = selectAndBuild(analyzed, config, images, seed);
536
+ let paced = selection.blocks;
537
+ let pacingInserted = 0;
538
+ const docTitle = firstTitle(doc);
539
+ if (config.pacing?.intro && docTitle && !doc.startBlock) {
540
+ const intro = {
541
+ template: "title",
542
+ id: "transform-intro",
543
+ duration: 4,
544
+ audioSegment: 0,
545
+ title: docTitle
546
+ };
547
+ paced = [intro, ...paced];
548
+ pacingInserted++;
549
+ }
550
+ if (config.pacing?.outro && docTitle) {
551
+ const outro = {
552
+ template: "sectionHeader",
553
+ id: "transform-outro",
554
+ duration: 3,
555
+ audioSegment: 0,
556
+ title: docTitle,
557
+ colorScheme: config.colorSchemes[0]
558
+ };
559
+ paced = [...paced, outro];
560
+ pacingInserted++;
561
+ }
562
+ const timedBlocks = allocateTiming(paced, doc.duration);
563
+ const transformedDoc = {
564
+ ...doc,
565
+ blocks: timedBlocks,
566
+ themeId: options?.themeId ?? doc.themeId ?? config.suggestedThemeId
567
+ };
568
+ return {
569
+ doc: transformedDoc,
570
+ stats: {
571
+ totalInputBlocks: doc.blocks.length,
572
+ transformedBlocks: selection.transformedCount,
573
+ insertedBlocks: selection.insertedCount + pacingInserted
574
+ }
575
+ };
576
+ }
577
+ function firstTitle(doc) {
578
+ if (doc.startBlock?.title) return doc.startBlock.title;
579
+ for (const block of doc.blocks) {
580
+ if (block.title) return block.title;
581
+ }
582
+ return void 0;
583
+ }
584
+
585
+ export {
586
+ DEFAULT_TRANSFORM_STYLE_ID,
587
+ registerTransformStyle,
588
+ unregisterTransformStyle,
589
+ resolveTransformStyle,
590
+ getTransformStyleIds,
591
+ getTransformStyleSummaries,
592
+ extractDocImages,
593
+ analyzeBlocks,
594
+ applyTransform
595
+ };
596
+ //# sourceMappingURL=chunk-TVSGLUID.js.map