@tscaps/engine 0.1.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 (483) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +349 -0
  3. package/dist/index.d.ts +12 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +12 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/modules/css/BrowserCssResourceEmbedder.d.ts +5 -0
  8. package/dist/modules/css/BrowserCssResourceEmbedder.d.ts.map +1 -0
  9. package/dist/modules/css/BrowserCssResourceEmbedder.js +41 -0
  10. package/dist/modules/css/BrowserCssResourceEmbedder.js.map +1 -0
  11. package/dist/modules/css/CssMinifier.d.ts +12 -0
  12. package/dist/modules/css/CssMinifier.d.ts.map +1 -0
  13. package/dist/modules/css/CssMinifier.js +47 -0
  14. package/dist/modules/css/CssMinifier.js.map +1 -0
  15. package/dist/modules/css/CssResourceEmbedder.d.ts +4 -0
  16. package/dist/modules/css/CssResourceEmbedder.d.ts.map +1 -0
  17. package/dist/modules/css/CssResourceEmbedder.js +2 -0
  18. package/dist/modules/css/CssResourceEmbedder.js.map +1 -0
  19. package/dist/modules/css/CssScoper.d.ts +16 -0
  20. package/dist/modules/css/CssScoper.d.ts.map +1 -0
  21. package/dist/modules/css/CssScoper.js +109 -0
  22. package/dist/modules/css/CssScoper.js.map +1 -0
  23. package/dist/modules/css/NoopCssResourceEmbedder.d.ts +12 -0
  24. package/dist/modules/css/NoopCssResourceEmbedder.d.ts.map +1 -0
  25. package/dist/modules/css/NoopCssResourceEmbedder.js +13 -0
  26. package/dist/modules/css/NoopCssResourceEmbedder.js.map +1 -0
  27. package/dist/modules/css/index.d.ts +6 -0
  28. package/dist/modules/css/index.d.ts.map +1 -0
  29. package/dist/modules/css/index.js +5 -0
  30. package/dist/modules/css/index.js.map +1 -0
  31. package/dist/modules/document/CssVariable.d.ts +46 -0
  32. package/dist/modules/document/CssVariable.d.ts.map +1 -0
  33. package/dist/modules/document/CssVariable.js +47 -0
  34. package/dist/modules/document/CssVariable.js.map +1 -0
  35. package/dist/modules/document/Document.d.ts +43 -0
  36. package/dist/modules/document/Document.d.ts.map +1 -0
  37. package/dist/modules/document/Document.js +86 -0
  38. package/dist/modules/document/Document.js.map +1 -0
  39. package/dist/modules/document/DocumentEditor.d.ts +111 -0
  40. package/dist/modules/document/DocumentEditor.d.ts.map +1 -0
  41. package/dist/modules/document/DocumentEditor.js +555 -0
  42. package/dist/modules/document/DocumentEditor.js.map +1 -0
  43. package/dist/modules/document/Letter.d.ts +17 -0
  44. package/dist/modules/document/Letter.d.ts.map +1 -0
  45. package/dist/modules/document/Letter.js +17 -0
  46. package/dist/modules/document/Letter.js.map +1 -0
  47. package/dist/modules/document/Line.d.ts +31 -0
  48. package/dist/modules/document/Line.d.ts.map +1 -0
  49. package/dist/modules/document/Line.js +94 -0
  50. package/dist/modules/document/Line.js.map +1 -0
  51. package/dist/modules/document/LineState.d.ts +6 -0
  52. package/dist/modules/document/LineState.d.ts.map +1 -0
  53. package/dist/modules/document/LineState.js +7 -0
  54. package/dist/modules/document/LineState.js.map +1 -0
  55. package/dist/modules/document/NarrationPace.d.ts +20 -0
  56. package/dist/modules/document/NarrationPace.d.ts.map +1 -0
  57. package/dist/modules/document/NarrationPace.js +60 -0
  58. package/dist/modules/document/NarrationPace.js.map +1 -0
  59. package/dist/modules/document/Section.d.ts +45 -0
  60. package/dist/modules/document/Section.d.ts.map +1 -0
  61. package/dist/modules/document/Section.js +94 -0
  62. package/dist/modules/document/Section.js.map +1 -0
  63. package/dist/modules/document/Segment.d.ts +40 -0
  64. package/dist/modules/document/Segment.d.ts.map +1 -0
  65. package/dist/modules/document/Segment.js +85 -0
  66. package/dist/modules/document/Segment.js.map +1 -0
  67. package/dist/modules/document/Tag.d.ts +24 -0
  68. package/dist/modules/document/Tag.d.ts.map +1 -0
  69. package/dist/modules/document/Tag.js +37 -0
  70. package/dist/modules/document/Tag.js.map +1 -0
  71. package/dist/modules/document/TimeFragment.d.ts +11 -0
  72. package/dist/modules/document/TimeFragment.d.ts.map +1 -0
  73. package/dist/modules/document/TimeFragment.js +29 -0
  74. package/dist/modules/document/TimeFragment.js.map +1 -0
  75. package/dist/modules/document/Word.d.ts +40 -0
  76. package/dist/modules/document/Word.d.ts.map +1 -0
  77. package/dist/modules/document/Word.js +114 -0
  78. package/dist/modules/document/Word.js.map +1 -0
  79. package/dist/modules/document/WordState.d.ts +6 -0
  80. package/dist/modules/document/WordState.d.ts.map +1 -0
  81. package/dist/modules/document/WordState.js +7 -0
  82. package/dist/modules/document/WordState.js.map +1 -0
  83. package/dist/modules/document/index.d.ts +14 -0
  84. package/dist/modules/document/index.d.ts.map +1 -0
  85. package/dist/modules/document/index.js +14 -0
  86. package/dist/modules/document/index.js.map +1 -0
  87. package/dist/modules/effect/CarryQuotesEffect.d.ts +47 -0
  88. package/dist/modules/effect/CarryQuotesEffect.d.ts.map +1 -0
  89. package/dist/modules/effect/CarryQuotesEffect.js +181 -0
  90. package/dist/modules/effect/CarryQuotesEffect.js.map +1 -0
  91. package/dist/modules/effect/Effect.d.ts +16 -0
  92. package/dist/modules/effect/Effect.d.ts.map +1 -0
  93. package/dist/modules/effect/Effect.js +2 -0
  94. package/dist/modules/effect/Effect.js.map +1 -0
  95. package/dist/modules/effect/GapFreeEffect.d.ts +24 -0
  96. package/dist/modules/effect/GapFreeEffect.d.ts.map +1 -0
  97. package/dist/modules/effect/GapFreeEffect.js +67 -0
  98. package/dist/modules/effect/GapFreeEffect.js.map +1 -0
  99. package/dist/modules/effect/RemovePunctuationEffect.d.ts +21 -0
  100. package/dist/modules/effect/RemovePunctuationEffect.d.ts.map +1 -0
  101. package/dist/modules/effect/RemovePunctuationEffect.js +52 -0
  102. package/dist/modules/effect/RemovePunctuationEffect.js.map +1 -0
  103. package/dist/modules/effect/SmartLowercaseEffect.d.ts +21 -0
  104. package/dist/modules/effect/SmartLowercaseEffect.d.ts.map +1 -0
  105. package/dist/modules/effect/SmartLowercaseEffect.js +45 -0
  106. package/dist/modules/effect/SmartLowercaseEffect.js.map +1 -0
  107. package/dist/modules/effect/SmartPunctuationEffect.d.ts +25 -0
  108. package/dist/modules/effect/SmartPunctuationEffect.d.ts.map +1 -0
  109. package/dist/modules/effect/SmartPunctuationEffect.js +68 -0
  110. package/dist/modules/effect/SmartPunctuationEffect.js.map +1 -0
  111. package/dist/modules/effect/index.d.ts +7 -0
  112. package/dist/modules/effect/index.d.ts.map +1 -0
  113. package/dist/modules/effect/index.js +6 -0
  114. package/dist/modules/effect/index.js.map +1 -0
  115. package/dist/modules/pipeline/PipelineProgress.d.ts +30 -0
  116. package/dist/modules/pipeline/PipelineProgress.d.ts.map +1 -0
  117. package/dist/modules/pipeline/PipelineProgress.js +2 -0
  118. package/dist/modules/pipeline/PipelineProgress.js.map +1 -0
  119. package/dist/modules/pipeline/RenderPipeline.d.ts +105 -0
  120. package/dist/modules/pipeline/RenderPipeline.d.ts.map +1 -0
  121. package/dist/modules/pipeline/RenderPipeline.js +232 -0
  122. package/dist/modules/pipeline/RenderPipeline.js.map +1 -0
  123. package/dist/modules/pipeline/RenderPipelineBuilder.d.ts +143 -0
  124. package/dist/modules/pipeline/RenderPipelineBuilder.d.ts.map +1 -0
  125. package/dist/modules/pipeline/RenderPipelineBuilder.js +317 -0
  126. package/dist/modules/pipeline/RenderPipelineBuilder.js.map +1 -0
  127. package/dist/modules/pipeline/index.d.ts +6 -0
  128. package/dist/modules/pipeline/index.d.ts.map +1 -0
  129. package/dist/modules/pipeline/index.js +3 -0
  130. package/dist/modules/pipeline/index.js.map +1 -0
  131. package/dist/modules/profiling/Profiler.d.ts +115 -0
  132. package/dist/modules/profiling/Profiler.d.ts.map +1 -0
  133. package/dist/modules/profiling/Profiler.js +316 -0
  134. package/dist/modules/profiling/Profiler.js.map +1 -0
  135. package/dist/modules/profiling/index.d.ts +2 -0
  136. package/dist/modules/profiling/index.d.ts.map +1 -0
  137. package/dist/modules/profiling/index.js +2 -0
  138. package/dist/modules/profiling/index.js.map +1 -0
  139. package/dist/modules/rendering/AlignmentConfig.d.ts +21 -0
  140. package/dist/modules/rendering/AlignmentConfig.d.ts.map +1 -0
  141. package/dist/modules/rendering/AlignmentConfig.js +2 -0
  142. package/dist/modules/rendering/AlignmentConfig.js.map +1 -0
  143. package/dist/modules/rendering/BoxEdges.d.ts +8 -0
  144. package/dist/modules/rendering/BoxEdges.d.ts.map +1 -0
  145. package/dist/modules/rendering/BoxEdges.js +2 -0
  146. package/dist/modules/rendering/BoxEdges.js.map +1 -0
  147. package/dist/modules/rendering/BrowserOverlayFrameRenderer.d.ts +12 -0
  148. package/dist/modules/rendering/BrowserOverlayFrameRenderer.d.ts.map +1 -0
  149. package/dist/modules/rendering/BrowserOverlayFrameRenderer.js +31 -0
  150. package/dist/modules/rendering/BrowserOverlayFrameRenderer.js.map +1 -0
  151. package/dist/modules/rendering/BrowserSubtitleFrameRenderer.d.ts +57 -0
  152. package/dist/modules/rendering/BrowserSubtitleFrameRenderer.d.ts.map +1 -0
  153. package/dist/modules/rendering/BrowserSubtitleFrameRenderer.js +775 -0
  154. package/dist/modules/rendering/BrowserSubtitleFrameRenderer.js.map +1 -0
  155. package/dist/modules/rendering/ElementRenderOverrides.d.ts +15 -0
  156. package/dist/modules/rendering/ElementRenderOverrides.d.ts.map +1 -0
  157. package/dist/modules/rendering/ElementRenderOverrides.js +24 -0
  158. package/dist/modules/rendering/ElementRenderOverrides.js.map +1 -0
  159. package/dist/modules/rendering/InlineStyleMap.d.ts +9 -0
  160. package/dist/modules/rendering/InlineStyleMap.d.ts.map +1 -0
  161. package/dist/modules/rendering/InlineStyleMap.js +2 -0
  162. package/dist/modules/rendering/InlineStyleMap.js.map +1 -0
  163. package/dist/modules/rendering/LayeredSubtitleFrame.d.ts +20 -0
  164. package/dist/modules/rendering/LayeredSubtitleFrame.d.ts.map +1 -0
  165. package/dist/modules/rendering/LayeredSubtitleFrame.js +36 -0
  166. package/dist/modules/rendering/LayeredSubtitleFrame.js.map +1 -0
  167. package/dist/modules/rendering/OverlayFrameRenderer.d.ts +27 -0
  168. package/dist/modules/rendering/OverlayFrameRenderer.d.ts.map +1 -0
  169. package/dist/modules/rendering/OverlayFrameRenderer.js +2 -0
  170. package/dist/modules/rendering/OverlayFrameRenderer.js.map +1 -0
  171. package/dist/modules/rendering/RenderingConfig.d.ts +22 -0
  172. package/dist/modules/rendering/RenderingConfig.d.ts.map +1 -0
  173. package/dist/modules/rendering/RenderingConfig.js +2 -0
  174. package/dist/modules/rendering/RenderingConfig.js.map +1 -0
  175. package/dist/modules/rendering/ScopedRenderOverride.d.ts +19 -0
  176. package/dist/modules/rendering/ScopedRenderOverride.d.ts.map +1 -0
  177. package/dist/modules/rendering/ScopedRenderOverride.js +2 -0
  178. package/dist/modules/rendering/ScopedRenderOverride.js.map +1 -0
  179. package/dist/modules/rendering/SegmentPaintRegionResolver.d.ts +41 -0
  180. package/dist/modules/rendering/SegmentPaintRegionResolver.d.ts.map +1 -0
  181. package/dist/modules/rendering/SegmentPaintRegionResolver.js +47 -0
  182. package/dist/modules/rendering/SegmentPaintRegionResolver.js.map +1 -0
  183. package/dist/modules/rendering/SegmentSubtreeHtmlBuilder.d.ts +58 -0
  184. package/dist/modules/rendering/SegmentSubtreeHtmlBuilder.d.ts.map +1 -0
  185. package/dist/modules/rendering/SegmentSubtreeHtmlBuilder.js +132 -0
  186. package/dist/modules/rendering/SegmentSubtreeHtmlBuilder.js.map +1 -0
  187. package/dist/modules/rendering/SubtitleFrameRenderer.d.ts +111 -0
  188. package/dist/modules/rendering/SubtitleFrameRenderer.d.ts.map +1 -0
  189. package/dist/modules/rendering/SubtitleFrameRenderer.js +2 -0
  190. package/dist/modules/rendering/SubtitleFrameRenderer.js.map +1 -0
  191. package/dist/modules/rendering/VideoFrameLayerClass.d.ts +21 -0
  192. package/dist/modules/rendering/VideoFrameLayerClass.d.ts.map +1 -0
  193. package/dist/modules/rendering/VideoFrameLayerClass.js +21 -0
  194. package/dist/modules/rendering/VideoFrameLayerClass.js.map +1 -0
  195. package/dist/modules/rendering/VideoFrameSource.d.ts +29 -0
  196. package/dist/modules/rendering/VideoFrameSource.d.ts.map +1 -0
  197. package/dist/modules/rendering/VideoFrameSource.js +2 -0
  198. package/dist/modules/rendering/VideoFrameSource.js.map +1 -0
  199. package/dist/modules/rendering/index.d.ts +14 -0
  200. package/dist/modules/rendering/index.d.ts.map +1 -0
  201. package/dist/modules/rendering/index.js +6 -0
  202. package/dist/modules/rendering/index.js.map +1 -0
  203. package/dist/modules/splitting/BalancedLineSplitter.d.ts +24 -0
  204. package/dist/modules/splitting/BalancedLineSplitter.d.ts.map +1 -0
  205. package/dist/modules/splitting/BalancedLineSplitter.js +81 -0
  206. package/dist/modules/splitting/BalancedLineSplitter.js.map +1 -0
  207. package/dist/modules/splitting/BalancedPixelWidthLineSplitter.d.ts +31 -0
  208. package/dist/modules/splitting/BalancedPixelWidthLineSplitter.d.ts.map +1 -0
  209. package/dist/modules/splitting/BalancedPixelWidthLineSplitter.js +99 -0
  210. package/dist/modules/splitting/BalancedPixelWidthLineSplitter.js.map +1 -0
  211. package/dist/modules/splitting/BoundarySegmentSplitter.d.ts +19 -0
  212. package/dist/modules/splitting/BoundarySegmentSplitter.d.ts.map +1 -0
  213. package/dist/modules/splitting/BoundarySegmentSplitter.js +38 -0
  214. package/dist/modules/splitting/BoundarySegmentSplitter.js.map +1 -0
  215. package/dist/modules/splitting/CompositeSegmentSplitter.d.ts +8 -0
  216. package/dist/modules/splitting/CompositeSegmentSplitter.d.ts.map +1 -0
  217. package/dist/modules/splitting/CompositeSegmentSplitter.js +12 -0
  218. package/dist/modules/splitting/CompositeSegmentSplitter.js.map +1 -0
  219. package/dist/modules/splitting/DomProbeCanvasTextMeasurer.d.ts +56 -0
  220. package/dist/modules/splitting/DomProbeCanvasTextMeasurer.d.ts.map +1 -0
  221. package/dist/modules/splitting/DomProbeCanvasTextMeasurer.js +197 -0
  222. package/dist/modules/splitting/DomProbeCanvasTextMeasurer.js.map +1 -0
  223. package/dist/modules/splitting/GraphemeWordSplitter.d.ts +11 -0
  224. package/dist/modules/splitting/GraphemeWordSplitter.d.ts.map +1 -0
  225. package/dist/modules/splitting/GraphemeWordSplitter.js +12 -0
  226. package/dist/modules/splitting/GraphemeWordSplitter.js.map +1 -0
  227. package/dist/modules/splitting/LimitByCharsSegmentSplitter.d.ts +15 -0
  228. package/dist/modules/splitting/LimitByCharsSegmentSplitter.d.ts.map +1 -0
  229. package/dist/modules/splitting/LimitByCharsSegmentSplitter.js +64 -0
  230. package/dist/modules/splitting/LimitByCharsSegmentSplitter.js.map +1 -0
  231. package/dist/modules/splitting/LimitByScaledCharsSegmentSplitter.d.ts +30 -0
  232. package/dist/modules/splitting/LimitByScaledCharsSegmentSplitter.d.ts.map +1 -0
  233. package/dist/modules/splitting/LimitByScaledCharsSegmentSplitter.js +66 -0
  234. package/dist/modules/splitting/LimitByScaledCharsSegmentSplitter.js.map +1 -0
  235. package/dist/modules/splitting/LimitByWordsSegmentSplitter.d.ts +12 -0
  236. package/dist/modules/splitting/LimitByWordsSegmentSplitter.d.ts.map +1 -0
  237. package/dist/modules/splitting/LimitByWordsSegmentSplitter.js +25 -0
  238. package/dist/modules/splitting/LimitByWordsSegmentSplitter.js.map +1 -0
  239. package/dist/modules/splitting/LineSplitter.d.ts +10 -0
  240. package/dist/modules/splitting/LineSplitter.d.ts.map +1 -0
  241. package/dist/modules/splitting/LineSplitter.js +2 -0
  242. package/dist/modules/splitting/LineSplitter.js.map +1 -0
  243. package/dist/modules/splitting/PauseBasedSegmentSplitter.d.ts +24 -0
  244. package/dist/modules/splitting/PauseBasedSegmentSplitter.d.ts.map +1 -0
  245. package/dist/modules/splitting/PauseBasedSegmentSplitter.js +43 -0
  246. package/dist/modules/splitting/PauseBasedSegmentSplitter.js.map +1 -0
  247. package/dist/modules/splitting/SegmentSplitter.d.ts +10 -0
  248. package/dist/modules/splitting/SegmentSplitter.d.ts.map +1 -0
  249. package/dist/modules/splitting/SegmentSplitter.js +2 -0
  250. package/dist/modules/splitting/SegmentSplitter.js.map +1 -0
  251. package/dist/modules/splitting/SpeakerChangeSegmentSplitter.d.ts +17 -0
  252. package/dist/modules/splitting/SpeakerChangeSegmentSplitter.d.ts.map +1 -0
  253. package/dist/modules/splitting/SpeakerChangeSegmentSplitter.js +46 -0
  254. package/dist/modules/splitting/SpeakerChangeSegmentSplitter.js.map +1 -0
  255. package/dist/modules/splitting/TextMeasurer.d.ts +15 -0
  256. package/dist/modules/splitting/TextMeasurer.d.ts.map +1 -0
  257. package/dist/modules/splitting/TextMeasurer.js +2 -0
  258. package/dist/modules/splitting/TextMeasurer.js.map +1 -0
  259. package/dist/modules/splitting/WordSplitter.d.ts +8 -0
  260. package/dist/modules/splitting/WordSplitter.d.ts.map +1 -0
  261. package/dist/modules/splitting/WordSplitter.js +2 -0
  262. package/dist/modules/splitting/WordSplitter.js.map +1 -0
  263. package/dist/modules/splitting/index.d.ts +24 -0
  264. package/dist/modules/splitting/index.d.ts.map +1 -0
  265. package/dist/modules/splitting/index.js +12 -0
  266. package/dist/modules/splitting/index.js.map +1 -0
  267. package/dist/modules/svg-filter/SvgFilter.d.ts +16 -0
  268. package/dist/modules/svg-filter/SvgFilter.d.ts.map +1 -0
  269. package/dist/modules/svg-filter/SvgFilter.js +28 -0
  270. package/dist/modules/svg-filter/SvgFilter.js.map +1 -0
  271. package/dist/modules/svg-filter/SvgFilterBundle.d.ts +16 -0
  272. package/dist/modules/svg-filter/SvgFilterBundle.d.ts.map +1 -0
  273. package/dist/modules/svg-filter/SvgFilterBundle.js +20 -0
  274. package/dist/modules/svg-filter/SvgFilterBundle.js.map +1 -0
  275. package/dist/modules/svg-filter/SvgFilterDefinitions.d.ts +16 -0
  276. package/dist/modules/svg-filter/SvgFilterDefinitions.d.ts.map +1 -0
  277. package/dist/modules/svg-filter/SvgFilterDefinitions.js +25 -0
  278. package/dist/modules/svg-filter/SvgFilterDefinitions.js.map +1 -0
  279. package/dist/modules/svg-filter/SvgFilterDefinitionsParser.d.ts +21 -0
  280. package/dist/modules/svg-filter/SvgFilterDefinitionsParser.d.ts.map +1 -0
  281. package/dist/modules/svg-filter/SvgFilterDefinitionsParser.js +50 -0
  282. package/dist/modules/svg-filter/SvgFilterDefinitionsParser.js.map +1 -0
  283. package/dist/modules/svg-filter/SvgFilterLengthResolver.d.ts +17 -0
  284. package/dist/modules/svg-filter/SvgFilterLengthResolver.d.ts.map +1 -0
  285. package/dist/modules/svg-filter/SvgFilterLengthResolver.js +27 -0
  286. package/dist/modules/svg-filter/SvgFilterLengthResolver.js.map +1 -0
  287. package/dist/modules/svg-filter/SvgFilterScope.d.ts +18 -0
  288. package/dist/modules/svg-filter/SvgFilterScope.d.ts.map +1 -0
  289. package/dist/modules/svg-filter/SvgFilterScope.js +28 -0
  290. package/dist/modules/svg-filter/SvgFilterScope.js.map +1 -0
  291. package/dist/modules/svg-filter/SvgFilterScopeProvider.d.ts +52 -0
  292. package/dist/modules/svg-filter/SvgFilterScopeProvider.d.ts.map +1 -0
  293. package/dist/modules/svg-filter/SvgFilterScopeProvider.js +2 -0
  294. package/dist/modules/svg-filter/SvgFilterScopeProvider.js.map +1 -0
  295. package/dist/modules/svg-filter/SvgFilterScoper.d.ts +60 -0
  296. package/dist/modules/svg-filter/SvgFilterScoper.d.ts.map +1 -0
  297. package/dist/modules/svg-filter/SvgFilterScoper.js +58 -0
  298. package/dist/modules/svg-filter/SvgFilterScoper.js.map +1 -0
  299. package/dist/modules/svg-filter/index.d.ts +10 -0
  300. package/dist/modules/svg-filter/index.d.ts.map +1 -0
  301. package/dist/modules/svg-filter/index.js +8 -0
  302. package/dist/modules/svg-filter/index.js.map +1 -0
  303. package/dist/modules/tagging/RegexTagger.d.ts +11 -0
  304. package/dist/modules/tagging/RegexTagger.d.ts.map +1 -0
  305. package/dist/modules/tagging/RegexTagger.js +32 -0
  306. package/dist/modules/tagging/RegexTagger.js.map +1 -0
  307. package/dist/modules/tagging/SpanTagger.d.ts +25 -0
  308. package/dist/modules/tagging/SpanTagger.d.ts.map +1 -0
  309. package/dist/modules/tagging/SpanTagger.js +73 -0
  310. package/dist/modules/tagging/SpanTagger.js.map +1 -0
  311. package/dist/modules/tagging/StructureTagger.d.ts +20 -0
  312. package/dist/modules/tagging/StructureTagger.d.ts.map +1 -0
  313. package/dist/modules/tagging/StructureTagger.js +74 -0
  314. package/dist/modules/tagging/StructureTagger.js.map +1 -0
  315. package/dist/modules/tagging/Tagger.d.ts +5 -0
  316. package/dist/modules/tagging/Tagger.d.ts.map +1 -0
  317. package/dist/modules/tagging/Tagger.js +5 -0
  318. package/dist/modules/tagging/Tagger.js.map +1 -0
  319. package/dist/modules/tagging/WordlistTagger.d.ts +11 -0
  320. package/dist/modules/tagging/WordlistTagger.d.ts.map +1 -0
  321. package/dist/modules/tagging/WordlistTagger.js +31 -0
  322. package/dist/modules/tagging/WordlistTagger.js.map +1 -0
  323. package/dist/modules/tagging/index.d.ts +6 -0
  324. package/dist/modules/tagging/index.d.ts.map +1 -0
  325. package/dist/modules/tagging/index.js +6 -0
  326. package/dist/modules/tagging/index.js.map +1 -0
  327. package/dist/modules/transcription/AudioDecoder.d.ts +11 -0
  328. package/dist/modules/transcription/AudioDecoder.d.ts.map +1 -0
  329. package/dist/modules/transcription/AudioDecoder.js +2 -0
  330. package/dist/modules/transcription/AudioDecoder.js.map +1 -0
  331. package/dist/modules/transcription/BrowserAudioDecoder.d.ts +13 -0
  332. package/dist/modules/transcription/BrowserAudioDecoder.d.ts.map +1 -0
  333. package/dist/modules/transcription/BrowserAudioDecoder.js +34 -0
  334. package/dist/modules/transcription/BrowserAudioDecoder.js.map +1 -0
  335. package/dist/modules/transcription/PassthroughTranscriber.d.ts +20 -0
  336. package/dist/modules/transcription/PassthroughTranscriber.d.ts.map +1 -0
  337. package/dist/modules/transcription/PassthroughTranscriber.js +23 -0
  338. package/dist/modules/transcription/PassthroughTranscriber.js.map +1 -0
  339. package/dist/modules/transcription/PreDecodedAudioDecoder.d.ts +12 -0
  340. package/dist/modules/transcription/PreDecodedAudioDecoder.d.ts.map +1 -0
  341. package/dist/modules/transcription/PreDecodedAudioDecoder.js +14 -0
  342. package/dist/modules/transcription/PreDecodedAudioDecoder.js.map +1 -0
  343. package/dist/modules/transcription/SrtTranscriber.d.ts +33 -0
  344. package/dist/modules/transcription/SrtTranscriber.d.ts.map +1 -0
  345. package/dist/modules/transcription/SrtTranscriber.js +111 -0
  346. package/dist/modules/transcription/SrtTranscriber.js.map +1 -0
  347. package/dist/modules/transcription/Transcriber.d.ts +23 -0
  348. package/dist/modules/transcription/Transcriber.d.ts.map +1 -0
  349. package/dist/modules/transcription/Transcriber.js +2 -0
  350. package/dist/modules/transcription/Transcriber.js.map +1 -0
  351. package/dist/modules/transcription/WhisperTranscriber.d.ts +44 -0
  352. package/dist/modules/transcription/WhisperTranscriber.d.ts.map +1 -0
  353. package/dist/modules/transcription/WhisperTranscriber.js +162 -0
  354. package/dist/modules/transcription/WhisperTranscriber.js.map +1 -0
  355. package/dist/modules/transcription/index.d.ts +8 -0
  356. package/dist/modules/transcription/index.d.ts.map +1 -0
  357. package/dist/modules/transcription/index.js +6 -0
  358. package/dist/modules/transcription/index.js.map +1 -0
  359. package/dist/modules/video/RenderJob.d.ts +95 -0
  360. package/dist/modules/video/RenderJob.d.ts.map +1 -0
  361. package/dist/modules/video/RenderJob.js +2 -0
  362. package/dist/modules/video/RenderJob.js.map +1 -0
  363. package/dist/modules/video/VideoRenderer.d.ts +5 -0
  364. package/dist/modules/video/VideoRenderer.d.ts.map +1 -0
  365. package/dist/modules/video/VideoRenderer.js +2 -0
  366. package/dist/modules/video/VideoRenderer.js.map +1 -0
  367. package/dist/modules/video/index.d.ts +33 -0
  368. package/dist/modules/video/index.d.ts.map +1 -0
  369. package/dist/modules/video/index.js +17 -0
  370. package/dist/modules/video/index.js.map +1 -0
  371. package/dist/modules/video/mediabunny/MediaBunnyVideoRenderer.d.ts +69 -0
  372. package/dist/modules/video/mediabunny/MediaBunnyVideoRenderer.d.ts.map +1 -0
  373. package/dist/modules/video/mediabunny/MediaBunnyVideoRenderer.js +206 -0
  374. package/dist/modules/video/mediabunny/MediaBunnyVideoRenderer.js.map +1 -0
  375. package/dist/modules/video/mediabunny/audio/AudioTrackBridge.d.ts +24 -0
  376. package/dist/modules/video/mediabunny/audio/AudioTrackBridge.d.ts.map +1 -0
  377. package/dist/modules/video/mediabunny/audio/AudioTrackBridge.js +2 -0
  378. package/dist/modules/video/mediabunny/audio/AudioTrackBridge.js.map +1 -0
  379. package/dist/modules/video/mediabunny/audio/AudioTrackBridgeFactory.d.ts +20 -0
  380. package/dist/modules/video/mediabunny/audio/AudioTrackBridgeFactory.d.ts.map +1 -0
  381. package/dist/modules/video/mediabunny/audio/AudioTrackBridgeFactory.js +2 -0
  382. package/dist/modules/video/mediabunny/audio/AudioTrackBridgeFactory.js.map +1 -0
  383. package/dist/modules/video/mediabunny/audio/DefaultAudioTrackBridgeFactory.d.ts +11 -0
  384. package/dist/modules/video/mediabunny/audio/DefaultAudioTrackBridgeFactory.d.ts.map +1 -0
  385. package/dist/modules/video/mediabunny/audio/DefaultAudioTrackBridgeFactory.js +32 -0
  386. package/dist/modules/video/mediabunny/audio/DefaultAudioTrackBridgeFactory.js.map +1 -0
  387. package/dist/modules/video/mediabunny/audio/DiscardAudioTrackBridge.d.ts +12 -0
  388. package/dist/modules/video/mediabunny/audio/DiscardAudioTrackBridge.d.ts.map +1 -0
  389. package/dist/modules/video/mediabunny/audio/DiscardAudioTrackBridge.js +14 -0
  390. package/dist/modules/video/mediabunny/audio/DiscardAudioTrackBridge.js.map +1 -0
  391. package/dist/modules/video/mediabunny/audio/PassthroughAudioTrackBridge.d.ts +28 -0
  392. package/dist/modules/video/mediabunny/audio/PassthroughAudioTrackBridge.d.ts.map +1 -0
  393. package/dist/modules/video/mediabunny/audio/PassthroughAudioTrackBridge.js +52 -0
  394. package/dist/modules/video/mediabunny/audio/PassthroughAudioTrackBridge.js.map +1 -0
  395. package/dist/modules/video/mediabunny/audio/TranscodeAudioTrackBridge.d.ts +25 -0
  396. package/dist/modules/video/mediabunny/audio/TranscodeAudioTrackBridge.d.ts.map +1 -0
  397. package/dist/modules/video/mediabunny/audio/TranscodeAudioTrackBridge.js +52 -0
  398. package/dist/modules/video/mediabunny/audio/TranscodeAudioTrackBridge.js.map +1 -0
  399. package/dist/modules/video/mediabunny/caption/BatchedSubtitleLayerSource.d.ts +32 -0
  400. package/dist/modules/video/mediabunny/caption/BatchedSubtitleLayerSource.d.ts.map +1 -0
  401. package/dist/modules/video/mediabunny/caption/BatchedSubtitleLayerSource.js +60 -0
  402. package/dist/modules/video/mediabunny/caption/BatchedSubtitleLayerSource.js.map +1 -0
  403. package/dist/modules/video/mediabunny/caption/ComposedSubtitleLayerSource.d.ts +27 -0
  404. package/dist/modules/video/mediabunny/caption/ComposedSubtitleLayerSource.d.ts.map +1 -0
  405. package/dist/modules/video/mediabunny/caption/ComposedSubtitleLayerSource.js +72 -0
  406. package/dist/modules/video/mediabunny/caption/ComposedSubtitleLayerSource.js.map +1 -0
  407. package/dist/modules/video/mediabunny/caption/SubtitleLayerSource.d.ts +32 -0
  408. package/dist/modules/video/mediabunny/caption/SubtitleLayerSource.d.ts.map +1 -0
  409. package/dist/modules/video/mediabunny/caption/SubtitleLayerSource.js +2 -0
  410. package/dist/modules/video/mediabunny/caption/SubtitleLayerSource.js.map +1 -0
  411. package/dist/modules/video/mediabunny/caption/VideoBoundSubtitleLayerSource.d.ts +25 -0
  412. package/dist/modules/video/mediabunny/caption/VideoBoundSubtitleLayerSource.d.ts.map +1 -0
  413. package/dist/modules/video/mediabunny/caption/VideoBoundSubtitleLayerSource.js +37 -0
  414. package/dist/modules/video/mediabunny/caption/VideoBoundSubtitleLayerSource.js.map +1 -0
  415. package/dist/modules/video/mediabunny/codec/CodecPolicy.d.ts +32 -0
  416. package/dist/modules/video/mediabunny/codec/CodecPolicy.d.ts.map +1 -0
  417. package/dist/modules/video/mediabunny/codec/CodecPolicy.js +2 -0
  418. package/dist/modules/video/mediabunny/codec/CodecPolicy.js.map +1 -0
  419. package/dist/modules/video/mediabunny/codec/DefaultCodecPolicy.d.ts +59 -0
  420. package/dist/modules/video/mediabunny/codec/DefaultCodecPolicy.d.ts.map +1 -0
  421. package/dist/modules/video/mediabunny/codec/DefaultCodecPolicy.js +110 -0
  422. package/dist/modules/video/mediabunny/codec/DefaultCodecPolicy.js.map +1 -0
  423. package/dist/modules/video/mediabunny/encoder/MediaBunnyCanvasVideoTrackEncoder.d.ts +27 -0
  424. package/dist/modules/video/mediabunny/encoder/MediaBunnyCanvasVideoTrackEncoder.d.ts.map +1 -0
  425. package/dist/modules/video/mediabunny/encoder/MediaBunnyCanvasVideoTrackEncoder.js +44 -0
  426. package/dist/modules/video/mediabunny/encoder/MediaBunnyCanvasVideoTrackEncoder.js.map +1 -0
  427. package/dist/modules/video/mediabunny/encoder/MediaBunnyCanvasVideoTrackEncoderFactory.d.ts +10 -0
  428. package/dist/modules/video/mediabunny/encoder/MediaBunnyCanvasVideoTrackEncoderFactory.d.ts.map +1 -0
  429. package/dist/modules/video/mediabunny/encoder/MediaBunnyCanvasVideoTrackEncoderFactory.js +15 -0
  430. package/dist/modules/video/mediabunny/encoder/MediaBunnyCanvasVideoTrackEncoderFactory.js.map +1 -0
  431. package/dist/modules/video/mediabunny/encoder/VideoTrackEncoder.d.ts +29 -0
  432. package/dist/modules/video/mediabunny/encoder/VideoTrackEncoder.d.ts.map +1 -0
  433. package/dist/modules/video/mediabunny/encoder/VideoTrackEncoder.js +2 -0
  434. package/dist/modules/video/mediabunny/encoder/VideoTrackEncoder.js.map +1 -0
  435. package/dist/modules/video/mediabunny/encoder/VideoTrackEncoderFactory.d.ts +16 -0
  436. package/dist/modules/video/mediabunny/encoder/VideoTrackEncoderFactory.d.ts.map +1 -0
  437. package/dist/modules/video/mediabunny/encoder/VideoTrackEncoderFactory.js +2 -0
  438. package/dist/modules/video/mediabunny/encoder/VideoTrackEncoderFactory.js.map +1 -0
  439. package/dist/modules/video/mediabunny/frame/CurrentFrameVideoSource.d.ts +25 -0
  440. package/dist/modules/video/mediabunny/frame/CurrentFrameVideoSource.d.ts.map +1 -0
  441. package/dist/modules/video/mediabunny/frame/CurrentFrameVideoSource.js +74 -0
  442. package/dist/modules/video/mediabunny/frame/CurrentFrameVideoSource.js.map +1 -0
  443. package/dist/modules/video/mediabunny/frame/CustomWebCodecsVideoFrameDecoder.d.ts +26 -0
  444. package/dist/modules/video/mediabunny/frame/CustomWebCodecsVideoFrameDecoder.d.ts.map +1 -0
  445. package/dist/modules/video/mediabunny/frame/CustomWebCodecsVideoFrameDecoder.js +147 -0
  446. package/dist/modules/video/mediabunny/frame/CustomWebCodecsVideoFrameDecoder.js.map +1 -0
  447. package/dist/modules/video/mediabunny/frame/DefaultVideoFrameDecoderFactory.d.ts +12 -0
  448. package/dist/modules/video/mediabunny/frame/DefaultVideoFrameDecoderFactory.d.ts.map +1 -0
  449. package/dist/modules/video/mediabunny/frame/DefaultVideoFrameDecoderFactory.js +25 -0
  450. package/dist/modules/video/mediabunny/frame/DefaultVideoFrameDecoderFactory.js.map +1 -0
  451. package/dist/modules/video/mediabunny/frame/FrameCompositor.d.ts +30 -0
  452. package/dist/modules/video/mediabunny/frame/FrameCompositor.d.ts.map +1 -0
  453. package/dist/modules/video/mediabunny/frame/FrameCompositor.js +2 -0
  454. package/dist/modules/video/mediabunny/frame/FrameCompositor.js.map +1 -0
  455. package/dist/modules/video/mediabunny/frame/HtmlVideoElementVideoFrameDecoder.d.ts +44 -0
  456. package/dist/modules/video/mediabunny/frame/HtmlVideoElementVideoFrameDecoder.d.ts.map +1 -0
  457. package/dist/modules/video/mediabunny/frame/HtmlVideoElementVideoFrameDecoder.js +334 -0
  458. package/dist/modules/video/mediabunny/frame/HtmlVideoElementVideoFrameDecoder.js.map +1 -0
  459. package/dist/modules/video/mediabunny/frame/LayeredFrameCompositor.d.ts +10 -0
  460. package/dist/modules/video/mediabunny/frame/LayeredFrameCompositor.d.ts.map +1 -0
  461. package/dist/modules/video/mediabunny/frame/LayeredFrameCompositor.js +17 -0
  462. package/dist/modules/video/mediabunny/frame/LayeredFrameCompositor.js.map +1 -0
  463. package/dist/modules/video/mediabunny/frame/VideoFrameDecoder.d.ts +30 -0
  464. package/dist/modules/video/mediabunny/frame/VideoFrameDecoder.d.ts.map +1 -0
  465. package/dist/modules/video/mediabunny/frame/VideoFrameDecoder.js +2 -0
  466. package/dist/modules/video/mediabunny/frame/VideoFrameDecoder.js.map +1 -0
  467. package/dist/modules/video/mediabunny/frame/VideoFrameDecoderFactory.d.ts +22 -0
  468. package/dist/modules/video/mediabunny/frame/VideoFrameDecoderFactory.d.ts.map +1 -0
  469. package/dist/modules/video/mediabunny/frame/VideoFrameDecoderFactory.js +2 -0
  470. package/dist/modules/video/mediabunny/frame/VideoFrameDecoderFactory.js.map +1 -0
  471. package/dist/modules/video/mediabunny/frame/WebCodecsVideoFrameDecoder.d.ts +17 -0
  472. package/dist/modules/video/mediabunny/frame/WebCodecsVideoFrameDecoder.d.ts.map +1 -0
  473. package/dist/modules/video/mediabunny/frame/WebCodecsVideoFrameDecoder.js +48 -0
  474. package/dist/modules/video/mediabunny/frame/WebCodecsVideoFrameDecoder.js.map +1 -0
  475. package/dist/modules/video/mediabunny/output/MediaBunnyOutputTargetBuilder.d.ts +11 -0
  476. package/dist/modules/video/mediabunny/output/MediaBunnyOutputTargetBuilder.d.ts.map +1 -0
  477. package/dist/modules/video/mediabunny/output/MediaBunnyOutputTargetBuilder.js +22 -0
  478. package/dist/modules/video/mediabunny/output/MediaBunnyOutputTargetBuilder.js.map +1 -0
  479. package/dist/modules/video/mediabunny/output/OutputTargetBuilder.d.ts +27 -0
  480. package/dist/modules/video/mediabunny/output/OutputTargetBuilder.d.ts.map +1 -0
  481. package/dist/modules/video/mediabunny/output/OutputTargetBuilder.js +2 -0
  482. package/dist/modules/video/mediabunny/output/OutputTargetBuilder.js.map +1 -0
  483. package/package.json +67 -0
@@ -0,0 +1,775 @@
1
+ import { CssScoper } from '../css/CssScoper';
2
+ import { CssMinifier } from '../css/CssMinifier';
3
+ import { CssVariable } from '../document/CssVariable';
4
+ import { ElementRenderOverrides } from '../rendering/ElementRenderOverrides';
5
+ import { SvgFilterBundle } from '../svg-filter/SvgFilterBundle';
6
+ import { SvgFilterScope } from '../svg-filter/SvgFilterScope';
7
+ import { SvgFilterScoper } from '../svg-filter/SvgFilterScoper';
8
+ import { SvgFilterLengthResolver } from '../svg-filter/SvgFilterLengthResolver';
9
+ import { SegmentPaintRegionResolver } from '../rendering/SegmentPaintRegionResolver';
10
+ import { SegmentSubtreeHtmlBuilder } from '../rendering/SegmentSubtreeHtmlBuilder';
11
+ import { VIDEO_FRAME_LAYER_BASELINE_CSS } from '../rendering/VideoFrameLayerClass';
12
+ const FINGERPRINT_BASE_S = 10000;
13
+ const NUMBER_OF_TILES_TO_TEST = [1, 2, 4, 6, 8, 10, 12, 15, 18, 21, 25, 30];
14
+ // 10 frames at 1080×1920 ≈ 80 MB of pixel buffer.
15
+ // At 4K the same budget admits only ~2 tiles per batch.
16
+ const DEFAULT_MAX_BUFFER_PIXELS = 10 * 1080 * 1920;
17
+ const NO_EXCLUDED_WORDS = new Set();
18
+ // Safety bleed applied to the painted region for video-frame styles
19
+ // that don't declare a `rendering.padding`. 0.25em ≈ enough to cover
20
+ // a typical text-shadow, kept narrow so the crop still pays off.
21
+ const UNDECLARED_PADDING_SAFETY_EM = 0.25;
22
+ const BASELINE_CSS = `html { font-size: 16px; text-rendering: geometricPrecision; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: 100%; }
23
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; animation-fill-mode: both; animation-play-state: paused !important; }
24
+ .line { white-space: nowrap; }
25
+ ${VIDEO_FRAME_LAYER_BASELINE_CSS}`;
26
+ /**
27
+ * Browser-native `SubtitleFrameRenderer` that builds each batch as a
28
+ * single SVG holding one `foreignObject` per unique visual state, and
29
+ * decodes it through an `<img>` so the resulting bitmap paints into
30
+ * any 2D context — including OffscreenCanvas — without tainting the
31
+ * canvas.
32
+ *
33
+ * Inside a batch, timestamps that hit the same visual state collapse
34
+ * into a single sprite tile; tiles stack along the smaller dimension
35
+ * axis (portrait → row, landscape → column) so the sprite sheet stays
36
+ * under whatever sprite-length cap the host's 2D-canvas backend
37
+ * actually honors. That cap is probed empirically on first `open`
38
+ * — no public browser API surfaces it directly and reported
39
+ * limits (WebGL `MAX_TEXTURE_SIZE`, `MAX_VIEWPORT_DIMS`) bear no
40
+ * stable relationship to what `img.decode` / `drawImage` accept.
41
+ */
42
+ export class BrowserSubtitleFrameRenderer {
43
+ cssEmbedder;
44
+ wordSplitter;
45
+ maxBufferPixels;
46
+ session = null;
47
+ cssScoper = new CssScoper();
48
+ cssMinifier = new CssMinifier();
49
+ svgFilterScoper = new SvgFilterScoper();
50
+ width;
51
+ height;
52
+ /**
53
+ * @param cssEmbedder Inlines external resources (fonts, images)
54
+ * referenced by each style's CSS so the resulting stylesheet is
55
+ * self-contained inside the sandboxed render SVG.
56
+ * @param wordSplitter Splits words into letter units when a style's
57
+ * `RenderingConfig.splitWordsIntoLetters` is set.
58
+ * @param maxBufferPixels Per-batch memory budget in pixels. With
59
+ * the probed sprite-length cap, drives `getMaxBatchSize`. The
60
+ * default sizes a batch to ~80 MB of pixel buffer.
61
+ */
62
+ constructor(cssEmbedder, wordSplitter, maxBufferPixels = DEFAULT_MAX_BUFFER_PIXELS) {
63
+ this.cssEmbedder = cssEmbedder;
64
+ this.wordSplitter = wordSplitter;
65
+ this.maxBufferPixels = maxBufferPixels;
66
+ }
67
+ async open(doc, styles, width, height, videoFrameSource) {
68
+ const requiresFrame = Object.values(styles).some((s) => s.rendering.videoFrame.required);
69
+ if (requiresFrame && !videoFrameSource) {
70
+ throw new Error('A SubtitleStyle declares videoFrame.required=true but no VideoFrameSource was supplied to open().');
71
+ }
72
+ this.width = width;
73
+ this.height = height;
74
+ const prepared = {};
75
+ for (const [kind, style] of Object.entries(styles)) {
76
+ prepared[kind] = await this.prepareStyle(kind, style);
77
+ }
78
+ this.session = new ActiveRenderSession(doc, prepared, this.wordSplitter, this.width, this.height, videoFrameSource ?? null);
79
+ }
80
+ async getMaxBatchSize() {
81
+ try {
82
+ for (const numberOfTiles of NUMBER_OF_TILES_TO_TEST) {
83
+ const isPortrait = this.width < this.height;
84
+ const scaledWidth = isPortrait ? this.width * numberOfTiles : this.width;
85
+ const scaledHeight = isPortrait ? this.height : this.height * numberOfTiles;
86
+ const length = scaledWidth * scaledHeight;
87
+ if (length > this.maxBufferPixels) {
88
+ return numberOfTiles;
89
+ }
90
+ if (!(await this.testTileSize(scaledWidth, scaledHeight))) {
91
+ return numberOfTiles;
92
+ }
93
+ }
94
+ return 1;
95
+ }
96
+ catch {
97
+ return 1;
98
+ }
99
+ }
100
+ /**
101
+ * Renders a green field with a red 2×2 marker in the bottom-right
102
+ * corner at the target size, decodes it, paints it, and reads back
103
+ * the corner pixel. Some hosts decode oversize SVGs but leave the
104
+ * far edges of the raster blank — the readback catches that.
105
+ */
106
+ async testTileSize(width, height) {
107
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}"><rect width="100%" height="100%" fill="green"/><rect x="${width - 2}" y="${height - 2}" width="2" height="2" fill="red"/></svg>`;
108
+ const img = new Image();
109
+ img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
110
+ try {
111
+ await img.decode();
112
+ const canvas = new OffscreenCanvas(width, height);
113
+ const ctx = canvas.getContext('2d');
114
+ if (!ctx)
115
+ return false;
116
+ ctx.drawImage(img, 0, 0);
117
+ const px = ctx.getImageData(width - 1, height - 1, 1, 1).data;
118
+ return px[0] > 200 && px[1] < 80 && px[2] < 80 && px[3] > 200;
119
+ }
120
+ catch {
121
+ return false;
122
+ }
123
+ }
124
+ async getFrames(timestamps) {
125
+ if (!this.session)
126
+ return timestamps.map(() => null);
127
+ return this.session.getFrames(timestamps);
128
+ }
129
+ async getFrame(timestamp) {
130
+ if (!this.session)
131
+ return null;
132
+ const [frame] = await this.session.getFrames([timestamp]);
133
+ return frame ?? null;
134
+ }
135
+ close() {
136
+ this.session?.dispose();
137
+ this.session = null;
138
+ this.width = undefined;
139
+ this.height = undefined;
140
+ }
141
+ async prepareStyle(kind, style) {
142
+ const minified = this.cssMinifier.minify(style.css);
143
+ const embedded = await this.cssEmbedder.embed(minified);
144
+ const scopeClass = `tscaps-render-${kind}-${Math.random().toString(36).slice(2, 8)}`;
145
+ const { css: cssWithIndirectFilters } = this.svgFilterScoper.rewriteCss(embedded);
146
+ const scopedCss = this.cssScoper.scope(cssWithIndirectFilters, `.${scopeClass}`);
147
+ // Probe styles live in `document.head` while the renderer is
148
+ // open; without the scopeClass guard, any `.word`/`.line`/etc on
149
+ // the host page would inherit them.
150
+ const scopedBaseline = this.cssScoper.scope(BASELINE_CSS, `.${scopeClass}`);
151
+ const probeStyleEl = document.createElement('style');
152
+ probeStyleEl.textContent = scopedBaseline + scopedCss;
153
+ document.head.appendChild(probeStyleEl);
154
+ const probeContainer = document.createElement('div');
155
+ probeContainer.className = scopeClass;
156
+ probeContainer.style.cssText = 'position:fixed;left:-99999px;visibility:hidden;pointer-events:none;';
157
+ document.body.appendChild(probeContainer);
158
+ return {
159
+ kind,
160
+ scopedCss,
161
+ filters: style.svgFilters ?? SvgFilterBundle.empty({
162
+ scopeAt: () => SvgFilterScope.empty(),
163
+ lengthFactorsAt: () => ({ pxPerEm: 0, pxPerCqh: 0 }),
164
+ }),
165
+ inlineStyles: style.inlineStyles,
166
+ alignment: style.alignment,
167
+ rendering: style.rendering,
168
+ wordOverrides: style.wordOverrides ?? ElementRenderOverrides.empty(),
169
+ segmentOverrides: style.segmentOverrides ?? ElementRenderOverrides.empty(),
170
+ probeContainer,
171
+ scopeClass,
172
+ probeCache: new Map(),
173
+ };
174
+ }
175
+ }
176
+ class ActiveRenderSession {
177
+ doc;
178
+ styles;
179
+ width;
180
+ height;
181
+ videoFrameSource;
182
+ svgFilterScoper = new SvgFilterScoper();
183
+ svgFilterLengthResolver = new SvgFilterLengthResolver();
184
+ paintRegionResolver = new SegmentPaintRegionResolver();
185
+ paintRegionsBySegmentId = new Map();
186
+ subtreeBuilder;
187
+ constructor(doc, styles, wordSplitter, width, height, videoFrameSource) {
188
+ this.doc = doc;
189
+ this.styles = styles;
190
+ this.width = width;
191
+ this.height = height;
192
+ this.videoFrameSource = videoFrameSource;
193
+ this.subtreeBuilder = new SegmentSubtreeHtmlBuilder(wordSplitter);
194
+ }
195
+ async getFrames(timestamps) {
196
+ if (timestamps.length === 0)
197
+ return [];
198
+ const plan = this.planBatch(timestamps);
199
+ if (plan.groups.size === 0)
200
+ return timestamps.map(() => null);
201
+ const sprites = await this.renderGroups(plan.groups);
202
+ return this.buildFrames(plan.assignments, sprites);
203
+ }
204
+ dispose() {
205
+ for (const style of Object.values(this.styles)) {
206
+ style.probeContainer.remove();
207
+ style.probeCache.clear();
208
+ }
209
+ this.paintRegionsBySegmentId.clear();
210
+ }
211
+ /**
212
+ * Splits timestamps into per-asset-key groups so each rendered SVG
213
+ * carries only the CSS and font payload its tiles actually need —
214
+ * sharing one data URL across kinds with disjoint assets transfers
215
+ * extra bytes for no amortization gain. Within each group,
216
+ * timestamps sharing a state key collapse into one tile.
217
+ */
218
+ planBatch(timestamps) {
219
+ const groups = new Map();
220
+ const tilesByKey = new Map();
221
+ const assignments = timestamps.map((t) => {
222
+ const items = this.itemsAt(t);
223
+ if (items.length === 0)
224
+ return null;
225
+ const assetKey = this.computeAssetKey(items);
226
+ let group = groups.get(assetKey);
227
+ let groupTiles;
228
+ if (!group) {
229
+ group = { assetKey, uniqueTiles: [] };
230
+ groups.set(assetKey, group);
231
+ groupTiles = new Map();
232
+ tilesByKey.set(assetKey, groupTiles);
233
+ }
234
+ else {
235
+ groupTiles = tilesByKey.get(assetKey);
236
+ }
237
+ const stateKey = this.computeStateKey(items, t);
238
+ let tile = groupTiles.get(stateKey);
239
+ if (!tile) {
240
+ tile = { items, tileIndex: group.uniqueTiles.length };
241
+ group.uniqueTiles.push(tile);
242
+ groupTiles.set(stateKey, tile);
243
+ }
244
+ return { assetKey, tileIndex: tile.tileIndex };
245
+ });
246
+ return { groups, assignments };
247
+ }
248
+ async renderGroups(groups) {
249
+ const sprites = new Map();
250
+ for (const group of groups.values()) {
251
+ const img = await this.renderSpriteSheet(group.uniqueTiles);
252
+ sprites.set(group.assetKey, img);
253
+ }
254
+ return sprites;
255
+ }
256
+ buildFrames(assignments, sprites) {
257
+ const isPortrait = this.width < this.height;
258
+ return assignments.map((assn) => {
259
+ if (!assn)
260
+ return null;
261
+ const img = sprites.get(assn.assetKey);
262
+ const sx = isPortrait ? assn.tileIndex * this.width : 0;
263
+ const sy = isPortrait ? 0 : assn.tileIndex * this.height;
264
+ return {
265
+ draw: (ctx, dx, dy, dw, dh) => ctx.drawImage(img, sx, sy, this.width, this.height, dx, dy, dw, dh),
266
+ };
267
+ });
268
+ }
269
+ async renderSpriteSheet(tiles) {
270
+ const isPortrait = this.width < this.height;
271
+ const sheetW = isPortrait ? tiles.length * this.width : this.width;
272
+ const sheetH = isPortrait ? this.height : tiles.length * this.height;
273
+ let itemUid = 0;
274
+ const tileResults = await Promise.all(tiles.map((tile, i) => this.buildTileHtml(tile, i, isPortrait, this.width, this.height, () => itemUid++)));
275
+ const tilesHtml = tileResults.map((r) => r.html).join('');
276
+ const filterDefs = tileResults.map((r) => r.defs).filter(Boolean).join('');
277
+ const sectionCss = this.joinByKind(this.collectActiveKinds(tiles), (style) => style.scopedCss);
278
+ const defsBlock = filterDefs ? `<defs>${filterDefs}</defs>` : '';
279
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${sheetW}" height="${sheetH}">${defsBlock}<style>${BASELINE_CSS}${sectionCss}</style>${tilesHtml}</svg>`;
280
+ return this.decodeSvg(svg, tiles, sheetW, sheetH);
281
+ }
282
+ collectActiveKinds(tiles) {
283
+ const kinds = new Set();
284
+ for (const tile of tiles)
285
+ for (const item of tile.items)
286
+ kinds.add(item.style.kind);
287
+ return kinds;
288
+ }
289
+ joinByKind(kinds, pick) {
290
+ return [...kinds].map((kind) => pick(this.styles[kind])).filter(Boolean).join('\n');
291
+ }
292
+ async buildTileHtml(tile, i, isPortrait, width, height, nextUid) {
293
+ const tx = isPortrait ? i * width : 0;
294
+ const ty = isPortrait ? 0 : i * height;
295
+ const wrapperResults = await Promise.all(tile.items.map(({ style, seg, t }) => this.buildWrapperHtml(style, seg, t, width, height, nextUid)));
296
+ const wrappers = wrapperResults.map((r) => r.html).join('');
297
+ const defs = wrapperResults.map((r) => r.defs).filter(Boolean).join('');
298
+ const html = `<foreignObject x="${tx}" y="${ty}" width="${width}" height="${height}"><div xmlns="http://www.w3.org/1999/xhtml" style="position:relative;width:${width}px;height:${height}px;overflow:hidden;container-type:size;">${wrappers}</div></foreignObject>`;
299
+ return { html, defs };
300
+ }
301
+ async decodeSvg(svg, tiles, sheetW, sheetH) {
302
+ const img = new Image();
303
+ img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
304
+ // `onload` can fire before custom fonts have finished applying,
305
+ // producing visually-laggy captures with foreignObject + @font-face.
306
+ // `decode()` only resolves once the image is fully paint-ready.
307
+ try {
308
+ await img.decode();
309
+ }
310
+ catch (e) {
311
+ const kinds = new Set();
312
+ for (const tile of tiles)
313
+ for (const item of tile.items)
314
+ kinds.add(item.style.kind);
315
+ console.error('[tscaps] subtitle SVG decode failed', {
316
+ error: e instanceof Error ? e.message : String(e),
317
+ kinds: [...kinds],
318
+ tiles: tiles.length,
319
+ sheetW,
320
+ sheetH,
321
+ svgLength: svg.length,
322
+ svgHead: svg.slice(0, 400),
323
+ });
324
+ throw new Error(`Subtitle render failed: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
325
+ }
326
+ return img;
327
+ }
328
+ itemsAt(t) {
329
+ const items = [];
330
+ for (const seg of this.doc.getActiveSegments(t)) {
331
+ const style = this.styles[seg.getSection().kind];
332
+ if (style)
333
+ items.push({ seg, style, t });
334
+ }
335
+ return items;
336
+ }
337
+ computeAssetKey(items) {
338
+ const kinds = new Set();
339
+ for (const it of items)
340
+ kinds.add(it.style.kind);
341
+ return [...kinds].sort().join(',');
342
+ }
343
+ computeStateKey(items, t) {
344
+ return items.map(({ seg, style }) => {
345
+ const base = `${style.kind}:${seg.id}:${this.fingerprintSegmentState(seg, t)}`;
346
+ return this.isItemAnimating(style, seg, t) ? `${base}:${t.toFixed(3)}` : base;
347
+ }).join('|');
348
+ }
349
+ /**
350
+ * Captures every time-varying CSS class the segment subtree exposes
351
+ * (segment, lines, words) so two timestamps that resolve to the same
352
+ * computed style — and therefore the same rendered frame outside
353
+ * active animation windows — share a tile, while two timestamps with
354
+ * different class states (e.g. a line pre-narration vs. already
355
+ * narrated) stay distinct even when no word is active.
356
+ */
357
+ fingerprintSegmentState(seg, t) {
358
+ const segClasses = seg.getCssClasses(t).join(',');
359
+ const lineFingerprints = [...seg.lines].map((line) => this.fingerprintLineState(line, t));
360
+ return `${segClasses}|{${lineFingerprints.join(';')}}`;
361
+ }
362
+ fingerprintLineState(line, t) {
363
+ const lineClasses = line.getCssClasses(t).join(',');
364
+ const wordClasses = [...line.words].map((w) => w.getCssClasses(t).join(',')).join('|');
365
+ return `${lineClasses}[${wordClasses}]`;
366
+ }
367
+ isItemAnimating(style, seg, t) {
368
+ // Letter-level CSS typically combines time vars via calc(), which
369
+ // destroys the fingerprint-based probe; treat the segment as
370
+ // always animating in that mode and redraw every frame.
371
+ if (style.rendering.splitWordsIntoLetters)
372
+ return true;
373
+ // Styles that bake the underlying video frame into their visuals
374
+ // change every tick by definition — no two timestamps share state.
375
+ if (style.rendering.videoFrame.required)
376
+ return true;
377
+ // Filter definitions whose materialized output can vary with
378
+ // `currentTime` likewise force per-frame redraw; conservatively
379
+ // treat any non-empty filter set as time-varying — the renderer
380
+ // has no introspection into the consumer's scope provider.
381
+ if (!style.filters.definitions.isEmpty())
382
+ return true;
383
+ const segClasses = seg.getCssClasses(t);
384
+ if (this.evalAnims(this.segmentTiming(style, segClasses), t, seg.time.start, seg.time.end))
385
+ return true;
386
+ return [...seg.lines].some((line) => {
387
+ const lineClasses = line.getCssClasses(t);
388
+ if (this.evalAnims(this.lineTiming(style, lineClasses, segClasses), t, seg.time.start, seg.time.end, line.time.start, line.time.end))
389
+ return true;
390
+ return [...line.words].some((word) => {
391
+ const wordClasses = word.getCssClasses(t);
392
+ return this.evalAnims(this.wordTiming(style, wordClasses, lineClasses, segClasses), t, seg.time.start, seg.time.end, line.time.start, line.time.end, word.time.start, word.time.end);
393
+ });
394
+ });
395
+ }
396
+ evalAnims(anims, t, segStart, segEnd, lineStart, lineEnd, wordStart, wordEnd) {
397
+ return anims.some((a) => {
398
+ let startT;
399
+ switch (a.cssVar) {
400
+ case CssVariable.SECTION_STARTS:
401
+ case CssVariable.SECTION_ENDS:
402
+ // Section-level animations are not yet wired into per-frame
403
+ // timing — treat them as never active.
404
+ return false;
405
+ case CssVariable.SEGMENT_STARTS:
406
+ startT = segStart;
407
+ break;
408
+ case CssVariable.SEGMENT_ENDS:
409
+ startT = segEnd;
410
+ break;
411
+ case CssVariable.LINE_NOT_NARRATED_YET_STARTS:
412
+ startT = segStart;
413
+ break;
414
+ case CssVariable.LINE_NOT_NARRATED_YET_ENDS:
415
+ startT = lineStart ?? segStart;
416
+ break;
417
+ case CssVariable.LINE_BEING_NARRATED_STARTS:
418
+ startT = lineStart ?? segStart;
419
+ break;
420
+ case CssVariable.LINE_BEING_NARRATED_ENDS:
421
+ startT = lineEnd ?? segEnd;
422
+ break;
423
+ case CssVariable.LINE_ALREADY_NARRATED_STARTS:
424
+ startT = lineEnd ?? segEnd;
425
+ break;
426
+ case CssVariable.LINE_ALREADY_NARRATED_ENDS:
427
+ startT = segEnd;
428
+ break;
429
+ case CssVariable.WORD_NOT_NARRATED_YET_STARTS:
430
+ startT = segStart;
431
+ break;
432
+ case CssVariable.WORD_NOT_NARRATED_YET_ENDS:
433
+ startT = wordStart ?? segStart;
434
+ break;
435
+ case CssVariable.WORD_BEING_NARRATED_STARTS:
436
+ startT = wordStart ?? segStart;
437
+ break;
438
+ case CssVariable.WORD_BEING_NARRATED_ENDS:
439
+ startT = wordEnd ?? segEnd;
440
+ break;
441
+ case CssVariable.WORD_ALREADY_NARRATED_STARTS:
442
+ startT = wordEnd ?? segEnd;
443
+ break;
444
+ case CssVariable.WORD_ALREADY_NARRATED_ENDS:
445
+ startT = segEnd;
446
+ break;
447
+ default: return false;
448
+ }
449
+ return t >= startT && t < startT + a.durationS;
450
+ });
451
+ }
452
+ /**
453
+ * Renders the segment and any words that have been promoted out of
454
+ * flow by a per-word alignment override. The promoted words appear as
455
+ * sibling anchors after the main wrapper; their original slot in the
456
+ * line is omitted so neighbours reflow into the freed space.
457
+ */
458
+ async buildWrapperHtml(style, seg, t, width, height, nextUid) {
459
+ const segmentOverride = style.segmentOverrides.get(seg.id);
460
+ const segmentAlignment = { ...style.alignment, ...segmentOverride?.alignment };
461
+ const segmentInlineStylesOverride = segmentOverride?.inlineStyles;
462
+ const baseInlineStyles = segmentInlineStylesOverride
463
+ ? { ...style.inlineStyles, ...segmentInlineStylesOverride }
464
+ : style.inlineStyles;
465
+ const positionedWords = this.collectPositionedWords(seg, style.wordOverrides);
466
+ const excludedWordIds = new Set(positionedWords.map((w) => w.id));
467
+ // Skip the main segment subtree entirely when every word has been
468
+ // pulled into a positioned-word sibling — otherwise the segment's
469
+ // own decorations (background, ::before chrome, padding) would
470
+ // paint as an empty shell on the frame. Positioned words render
471
+ // below regardless.
472
+ let html = '';
473
+ let defs = '';
474
+ if (!this.allWordsExcluded(seg, excludedWordIds)) {
475
+ const main = await this.buildSegmentSubtreeHtml(style, seg, t, width, height, segmentAlignment, baseInlineStyles, excludedWordIds, nextUid);
476
+ html = main.html;
477
+ defs = main.defs;
478
+ }
479
+ for (const word of positionedWords) {
480
+ const wordAlignmentOverride = style.wordOverrides.get(word.id)?.alignment;
481
+ const wordAlignment = { ...segmentAlignment, ...wordAlignmentOverride };
482
+ const line = this.findLineContainingWord(seg, word);
483
+ const built = await this.buildPositionedWordSubtreeHtml(style, seg, line, word, t, width, height, wordAlignment, baseInlineStyles, nextUid);
484
+ html += built.html;
485
+ defs += built.defs;
486
+ }
487
+ return { html, defs };
488
+ }
489
+ /**
490
+ * Builds the main anchor → wrapper → segment subtree at the
491
+ * segment's effective alignment. Words whose ids appear in
492
+ * `excludedWordIds` are omitted from the line so neighbours reflow;
493
+ * the caller renders them as separate positioned-word subtrees.
494
+ */
495
+ async buildSegmentSubtreeHtml(style, seg, t, width, height, alignment, baseInlineStyles, excludedWordIds, nextUid) {
496
+ const resolved = this.resolveAlignment(alignment, width, height);
497
+ const engineVars = await this.buildEngineVars(style, seg, t, width, height, resolved);
498
+ const { defs, bindings } = this.materializeFilters(style, t, engineVars, nextUid);
499
+ const styleInput = this.composeStyleInput(style, this.mergeExtras(engineVars, bindings, baseInlineStyles));
500
+ const subtreeHtml = this.subtreeBuilder.buildSegmentSubtree(styleInput, seg, t, excludedWordIds);
501
+ const anchorStyle = this.composeAnchorStyle(resolved);
502
+ const html = `<div style="${anchorStyle}">${subtreeHtml}</div>`;
503
+ return { html, defs };
504
+ }
505
+ /**
506
+ * Builds a sibling anchor → wrapper → mini-segment subtree for a
507
+ * single word at its effective alignment. The synthetic chain
508
+ * (segment, single-line, single-word) carries the same time-driven
509
+ * classes and CSS variables as the main render so template rules
510
+ * apply identically; only the position differs.
511
+ */
512
+ async buildPositionedWordSubtreeHtml(style, seg, line, word, t, width, height, alignment, baseInlineStyles, nextUid) {
513
+ const resolved = this.resolveAlignment(alignment, width, height);
514
+ const engineVars = await this.buildEngineVars(style, seg, t, width, height, resolved);
515
+ const { defs, bindings } = this.materializeFilters(style, t, engineVars, nextUid);
516
+ const styleInput = this.composeStyleInput(style, this.mergeExtras(engineVars, bindings, baseInlineStyles));
517
+ const subtreeHtml = this.subtreeBuilder.buildSingleWordSubtree(styleInput, seg, line, word, t);
518
+ const anchorStyle = this.composeAnchorStyle(resolved);
519
+ const html = `<div style="${anchorStyle}">${subtreeHtml}</div>`;
520
+ return { html, defs };
521
+ }
522
+ collectPositionedWords(seg, wordOverrides) {
523
+ const out = [];
524
+ for (const line of seg.lines) {
525
+ for (const word of line.words) {
526
+ if (wordOverrides.get(word.id)?.alignment)
527
+ out.push(word);
528
+ }
529
+ }
530
+ return out;
531
+ }
532
+ allWordsExcluded(seg, excludedWordIds) {
533
+ for (const line of seg.lines) {
534
+ for (const word of line.words) {
535
+ if (!excludedWordIds.has(word.id))
536
+ return false;
537
+ }
538
+ }
539
+ return true;
540
+ }
541
+ findLineContainingWord(seg, word) {
542
+ for (const line of seg.lines) {
543
+ for (const w of line.words) {
544
+ if (w.id === word.id)
545
+ return line;
546
+ }
547
+ }
548
+ throw new Error(`Word ${word.id} not found in segment ${seg.id}`);
549
+ }
550
+ // Zero-sized grid anchor places the wrapper via `place-items`
551
+ // so the wrapper stays transform-free and doesn't form a
552
+ // stacking context (which would trap descendant
553
+ // `mix-blend-mode` away from the layer).
554
+ composeAnchorStyle(resolved) {
555
+ return `position: absolute; top: ${resolved.yPx}px; left: ${resolved.xPx}px; width: 0; height: 0; display: grid; grid-template: 0 / 0; align-items: ${resolved.vGridAlign}; justify-items: ${resolved.hGridAlign};`;
556
+ }
557
+ /**
558
+ * Per-call style input handed to the subtree builder, materialized
559
+ * from the prepared style plus the per-frame wrapper extras.
560
+ * Pulling this together once per call keeps the builder stateless
561
+ * and avoids re-deriving the same flags inside it.
562
+ */
563
+ composeStyleInput(style, extraWrapperStyles) {
564
+ return {
565
+ scopeClass: style.scopeClass,
566
+ baseInlineStyles: style.inlineStyles,
567
+ wordOverrides: style.wordOverrides,
568
+ splitWordsIntoLetters: style.rendering.splitWordsIntoLetters,
569
+ includeVideoFrameLayer: style.rendering.videoFrame.required,
570
+ extraWrapperStyles,
571
+ };
572
+ }
573
+ /**
574
+ * Folds the engine-injected video-frame vars and filter-id bindings
575
+ * onto the base inline styles in cascade order so per-segment
576
+ * overrides win over the style's defaults when both are present.
577
+ */
578
+ mergeExtras(engineVars, bindings, baseInlineStyles) {
579
+ return { ...baseInlineStyles, ...engineVars, ...Object.fromEntries(bindings) };
580
+ }
581
+ /**
582
+ * Reduces an `AlignmentConfig` to the concrete numbers the wrapper
583
+ * HTML and video-frame vars consume: the anchor point in pixels, the
584
+ * anchor-edge fraction in percent, and the matching grid keywords.
585
+ * Called per segment with the segment's effective alignment so that
586
+ * per-segment overrides flow through every downstream consumer.
587
+ */
588
+ resolveAlignment(alignment, width, height) {
589
+ return {
590
+ yPx: Math.round(alignment.verticalOffset * height),
591
+ xPx: Math.round(alignment.horizontalOffset * width),
592
+ vAnchorPct: alignment.verticalAlign === 'top' ? 0 : alignment.verticalAlign === 'center' ? 50 : 100,
593
+ hAnchorPct: alignment.horizontalAlign === 'left' ? 0 : alignment.horizontalAlign === 'center' ? 50 : 100,
594
+ vGridAlign: alignment.verticalAlign === 'top' ? 'start' : alignment.verticalAlign === 'center' ? 'center' : 'end',
595
+ hGridAlign: alignment.horizontalAlign === 'left' ? 'start' : alignment.horizontalAlign === 'center' ? 'center' : 'end',
596
+ };
597
+ }
598
+ async buildEngineVars(style, seg, t, width, height, resolved) {
599
+ if (!style.rendering.videoFrame.required)
600
+ return {};
601
+ const region = this.resolvePaintRegion(style, seg, resolved, width, height);
602
+ // Offset vars assume the consuming element is a direct child of
603
+ // the wrapper: `%` resolves against the wrapper. The `region.x`/`y`
604
+ // bias positions the layer so the cropped frame's (0,0) lands on
605
+ // viewport `(region.x, region.y)`; when no crop applies the bias
606
+ // is zero and the layer covers the full viewport.
607
+ return {
608
+ [CssVariable.VIDEO_FRAME]: `url("${await this.videoFrameSource.getFrameAt(t, style.rendering.videoFrame.jpegQuality, region)}")`,
609
+ [CssVariable.SUBTITLE_REGION_WIDTH]: `${region.width}px`,
610
+ [CssVariable.SUBTITLE_REGION_HEIGHT]: `${region.height}px`,
611
+ [CssVariable.SUBTITLE_REGION_X]: `calc(${resolved.hAnchorPct}% - ${resolved.xPx - region.x}px)`,
612
+ [CssVariable.SUBTITLE_REGION_Y]: `calc(${resolved.vAnchorPct}% - ${resolved.yPx - region.y}px)`,
613
+ };
614
+ }
615
+ /**
616
+ * Looks up the painted region for `seg` in the per-session cache;
617
+ * on a miss, derives the region (full viewport when any word has a
618
+ * positioned override, otherwise the tight measured bbox) and
619
+ * caches it. Keyed by `${kind}:${seg.id}` to keep two styles'
620
+ * segments from colliding.
621
+ *
622
+ * The tight-crop optimization assumes every word stays inside the
623
+ * segment's in-flow bounding box. As soon as a word is repositioned
624
+ * outside that bbox, the same crop leaves its video-frame backdrop
625
+ * sampling pixels that aren't in the JPEG payload. Falling back to
626
+ * the full viewport for those segments restores correctness; the
627
+ * optimization recovers automatically the moment the user resets
628
+ * the override.
629
+ */
630
+ resolvePaintRegion(style, seg, placement, viewportWidth, viewportHeight) {
631
+ const cacheKey = `${style.kind}:${seg.id}`;
632
+ const cached = this.paintRegionsBySegmentId.get(cacheKey);
633
+ if (cached)
634
+ return cached;
635
+ const region = this.hasPositionedWord(seg, style.wordOverrides)
636
+ ? { x: 0, y: 0, width: viewportWidth, height: viewportHeight }
637
+ : this.measureSegmentPaintRegion(style, seg, placement, viewportWidth, viewportHeight);
638
+ this.paintRegionsBySegmentId.set(cacheKey, region);
639
+ return region;
640
+ }
641
+ measureSegmentPaintRegion(style, seg, placement, viewportWidth, viewportHeight) {
642
+ const segmentHtml = this.subtreeBuilder.buildSegmentSubtree(this.composeStyleInput(style, {}), seg, seg.time.start, NO_EXCLUDED_WORDS);
643
+ return this.paintRegionResolver.resolve({
644
+ segmentHtml,
645
+ probeContainer: style.probeContainer,
646
+ placement,
647
+ viewportWidth,
648
+ viewportHeight,
649
+ safetyBleedEm: style.rendering.padding === null ? UNDECLARED_PADDING_SAFETY_EM : 0,
650
+ });
651
+ }
652
+ hasPositionedWord(seg, wordOverrides) {
653
+ for (const line of seg.lines) {
654
+ for (const word of line.words) {
655
+ if (wordOverrides.get(word.id)?.alignment)
656
+ return true;
657
+ }
658
+ }
659
+ return false;
660
+ }
661
+ materializeFilters(style, t, engineVars, nextUid) {
662
+ const definitions = style.filters.definitions;
663
+ if (definitions.isEmpty())
664
+ return { defs: '', bindings: new Map() };
665
+ const context = { currentTime: t, renderHeightPx: this.height };
666
+ const consumerScope = style.filters.scopeProvider.scopeAt(context);
667
+ const scope = consumerScope.with(SvgFilterScope.fromEntries(Object.entries(engineVars)));
668
+ const lengthFactors = style.filters.scopeProvider.lengthFactorsAt(context);
669
+ const { idByLocal, bindings } = this.svgFilterScoper.scopeIds(definitions.ids, `${style.scopeClass}-${nextUid()}`);
670
+ const defs = definitions.filters
671
+ .map((filter) => {
672
+ const body = this.svgFilterLengthResolver.resolve(filter.materialize(scope), lengthFactors);
673
+ return `<filter id="${idByLocal.get(filter.id)}">${body}</filter>`;
674
+ })
675
+ .join('');
676
+ return { defs, bindings };
677
+ }
678
+ wordTiming(style, wordClasses, lineClasses, segClasses) {
679
+ const key = `w:${wordClasses.join(' ')}|l:${lineClasses.join(' ')}|s:${segClasses.join(' ')}`;
680
+ return this.getOrProbeTiming(style, key, () => {
681
+ const seg = document.createElement('div');
682
+ seg.className = segClasses.join(' ');
683
+ const line = document.createElement('div');
684
+ line.className = lineClasses.join(' ');
685
+ const word = document.createElement('span');
686
+ word.className = wordClasses.join(' ');
687
+ this.injectProbeMagic(word);
688
+ line.appendChild(word);
689
+ seg.appendChild(line);
690
+ style.probeContainer.appendChild(seg);
691
+ const timing = this.readAnimTimings(word);
692
+ style.probeContainer.removeChild(seg);
693
+ return timing;
694
+ });
695
+ }
696
+ lineTiming(style, lineClasses, segClasses) {
697
+ const key = `l:${lineClasses.join(' ')}|s:${segClasses.join(' ')}`;
698
+ return this.getOrProbeTiming(style, key, () => {
699
+ const seg = document.createElement('div');
700
+ seg.className = segClasses.join(' ');
701
+ const line = document.createElement('div');
702
+ line.className = lineClasses.join(' ');
703
+ this.injectProbeMagic(line);
704
+ seg.appendChild(line);
705
+ style.probeContainer.appendChild(seg);
706
+ const timing = this.readAnimTimings(line);
707
+ style.probeContainer.removeChild(seg);
708
+ return timing;
709
+ });
710
+ }
711
+ segmentTiming(style, segClasses) {
712
+ const key = `s:${segClasses.join(' ')}`;
713
+ return this.getOrProbeTiming(style, key, () => {
714
+ const seg = document.createElement('div');
715
+ seg.className = segClasses.join(' ');
716
+ this.injectProbeMagic(seg);
717
+ style.probeContainer.appendChild(seg);
718
+ const timing = this.readAnimTimings(seg);
719
+ style.probeContainer.removeChild(seg);
720
+ return timing;
721
+ });
722
+ }
723
+ // Each CssVariable gets a fingerprint-shaped duration value so the
724
+ // computed `animation-delay` on a probed element identifies which
725
+ // variable a rule consumed.
726
+ injectProbeMagic(el) {
727
+ Object.values(CssVariable).forEach((cssVar, index) => {
728
+ el.style.setProperty(cssVar, `${(index + 1) * FINGERPRINT_BASE_S}s`);
729
+ });
730
+ }
731
+ getOrProbeTiming(style, key, probe) {
732
+ let timing = style.probeCache.get(key);
733
+ if (!timing) {
734
+ timing = probe();
735
+ style.probeCache.set(key, timing);
736
+ }
737
+ return timing;
738
+ }
739
+ readAnimTimings(el) {
740
+ const computed = window.getComputedStyle(el);
741
+ const durations = this.parseDurationsS(computed.animationDuration);
742
+ const delays = this.parseDurationsS(computed.animationDelay);
743
+ const iterations = computed.animationIterationCount.split(',').map((s) => s.trim());
744
+ if (durations.every((d) => d === 0))
745
+ return [];
746
+ const anims = [];
747
+ const variables = Object.values(CssVariable);
748
+ for (let i = 0; i < durations.length; i++) {
749
+ const d = durations[i] ?? 0;
750
+ if (d === 0)
751
+ continue;
752
+ const dl = delays[i] ?? 0;
753
+ const fingerprintIndex = (dl / FINGERPRINT_BASE_S) - 1;
754
+ const cssVar = variables[fingerprintIndex];
755
+ if (!cssVar)
756
+ continue;
757
+ const isInfinite = (iterations[i] ?? '1') === 'infinite';
758
+ anims.push({ cssVar, durationS: isInfinite ? Infinity : d + 0.05 });
759
+ }
760
+ return anims;
761
+ }
762
+ parseDurationsS(value) {
763
+ if (!value)
764
+ return [0];
765
+ return value.split(',').map((s) => {
766
+ s = s.trim();
767
+ if (s.endsWith('ms'))
768
+ return parseFloat(s) / 1000;
769
+ if (s.endsWith('s'))
770
+ return parseFloat(s);
771
+ return 0;
772
+ });
773
+ }
774
+ }
775
+ //# sourceMappingURL=BrowserSubtitleFrameRenderer.js.map