@mevdragon/vidfarm-devcli 0.14.0 → 0.15.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 (418) hide show
  1. package/.agents/skills/embedded-captions/CATALOG.md +93 -0
  2. package/.agents/skills/embedded-captions/SKILL.md +286 -0
  3. package/.agents/skills/embedded-captions/assets/brand/CDPR-fankit-terms.txt +35 -0
  4. package/.agents/skills/embedded-captions/assets/brand/cyberpunk-widths.json +115 -0
  5. package/.agents/skills/embedded-captions/assets/fonts/char-widths.json +2211 -0
  6. package/.agents/skills/embedded-captions/assets/strokefonts/HersheyScript1.svg +242 -0
  7. package/.agents/skills/embedded-captions/assets/strokefonts/HersheyScriptMed.svg +242 -0
  8. package/.agents/skills/embedded-captions/dna/README.md +148 -0
  9. package/.agents/skills/embedded-captions/dna/chrome.json +68 -0
  10. package/.agents/skills/embedded-captions/dna/cream.json +69 -0
  11. package/.agents/skills/embedded-captions/dna/documentary.json +62 -0
  12. package/.agents/skills/embedded-captions/dna/editorial.json +70 -0
  13. package/.agents/skills/embedded-captions/dna/glitch.json +82 -0
  14. package/.agents/skills/embedded-captions/dna/ink.json +65 -0
  15. package/.agents/skills/embedded-captions/dna/keynote.json +62 -0
  16. package/.agents/skills/embedded-captions/dna/loud.json +75 -0
  17. package/.agents/skills/embedded-captions/dna/neon.json +58 -0
  18. package/.agents/skills/embedded-captions/dna/velocity.json +89 -0
  19. package/.agents/skills/embedded-captions/modes/cinematic/README.md +48 -0
  20. package/.agents/skills/embedded-captions/modes/cinematic/_archive/champion/spec.md +98 -0
  21. package/.agents/skills/embedded-captions/modes/cinematic/_archive/champion/template.html +188 -0
  22. package/.agents/skills/embedded-captions/modes/cinematic/_archive/memory-wall/spec.md +134 -0
  23. package/.agents/skills/embedded-captions/modes/cinematic/_archive/memory-wall/template.html +179 -0
  24. package/.agents/skills/embedded-captions/modes/cinematic/_archive/portrait-header/spec.md +81 -0
  25. package/.agents/skills/embedded-captions/modes/cinematic/_archive/portrait-header/template.html +170 -0
  26. package/.agents/skills/embedded-captions/modes/cinematic/cinematic-cream/spec.md +16 -0
  27. package/.agents/skills/embedded-captions/modes/cinematic/cinematic-cream/template.html +226 -0
  28. package/.agents/skills/embedded-captions/modes/cinematic/engine.html +355 -0
  29. package/.agents/skills/embedded-captions/modes/standard/_anatomy.md +233 -0
  30. package/.agents/skills/embedded-captions/modes/standard/_motion.md +192 -0
  31. package/.agents/skills/embedded-captions/modes/standard/fonts/build-fonts-css.cjs +97 -0
  32. package/.agents/skills/embedded-captions/references/aesthetic-principles.md +168 -0
  33. package/.agents/skills/embedded-captions/references/anti-patterns.md +188 -0
  34. package/.agents/skills/embedded-captions/references/bespoke-vs-presets.md +178 -0
  35. package/.agents/skills/embedded-captions/references/caption-grouping.md +130 -0
  36. package/.agents/skills/embedded-captions/references/composition-craft.md +522 -0
  37. package/.agents/skills/embedded-captions/references/direction-catalog.md +138 -0
  38. package/.agents/skills/embedded-captions/references/example-renders/champion.html +371 -0
  39. package/.agents/skills/embedded-captions/references/example-renders/memory-wall.html +334 -0
  40. package/.agents/skills/embedded-captions/references/failure-modes.md +145 -0
  41. package/.agents/skills/embedded-captions/references/layout-heuristics.md +238 -0
  42. package/.agents/skills/embedded-captions/references/motion-vocabulary.md +148 -0
  43. package/.agents/skills/embedded-captions/references/rail.md +68 -0
  44. package/.agents/skills/embedded-captions/references/reference-bar.md +47 -0
  45. package/.agents/skills/embedded-captions/references/scene-types.md +94 -0
  46. package/.agents/skills/embedded-captions/references/test-set.md +66 -0
  47. package/.agents/skills/embedded-captions/references/typographic-moves.md +266 -0
  48. package/.agents/skills/embedded-captions/references/typography-presets.md +63 -0
  49. package/.agents/skills/embedded-captions/scripts/audio-envelope.cjs +95 -0
  50. package/.agents/skills/embedded-captions/scripts/check-occlusion.cjs +250 -0
  51. package/.agents/skills/embedded-captions/scripts/check-overflow.cjs +194 -0
  52. package/.agents/skills/embedded-captions/scripts/check-rail-climax.cjs +233 -0
  53. package/.agents/skills/embedded-captions/scripts/check-timing.cjs +173 -0
  54. package/.agents/skills/embedded-captions/scripts/fill-timings.cjs +124 -0
  55. package/.agents/skills/embedded-captions/scripts/fit-fonts.cjs +158 -0
  56. package/.agents/skills/embedded-captions/scripts/gen-stroke-path.py +46 -0
  57. package/.agents/skills/embedded-captions/scripts/inject-fonts.cjs +169 -0
  58. package/.agents/skills/embedded-captions/scripts/lib-dna.cjs +201 -0
  59. package/.agents/skills/embedded-captions/scripts/make-cinematic.cjs +1205 -0
  60. package/.agents/skills/embedded-captions/scripts/make-composition.cjs +404 -0
  61. package/.agents/skills/embedded-captions/scripts/make-theme.cjs +8781 -0
  62. package/.agents/skills/embedded-captions/scripts/matte.cjs +294 -0
  63. package/.agents/skills/embedded-captions/scripts/measure-layout.cjs +293 -0
  64. package/.agents/skills/embedded-captions/scripts/prepare.sh +39 -0
  65. package/.agents/skills/embedded-captions/scripts/preview-frames.cjs +268 -0
  66. package/.agents/skills/embedded-captions/scripts/render-and-composite.sh +509 -0
  67. package/.agents/skills/embedded-captions/scripts/render-theme.sh +34 -0
  68. package/.agents/skills/embedded-captions/scripts/safe-zones.cjs +813 -0
  69. package/.agents/skills/embedded-captions/scripts/transcribe.cjs +349 -0
  70. package/.agents/skills/embedded-captions/themes/PORTING.md +49 -0
  71. package/.agents/skills/embedded-captions/themes/README.md +244 -0
  72. package/.agents/skills/embedded-captions/themes/anchor.json +27 -0
  73. package/.agents/skills/embedded-captions/themes/arcade.json +51 -0
  74. package/.agents/skills/embedded-captions/themes/aurora.json +41 -0
  75. package/.agents/skills/embedded-captions/themes/biolume.json +35 -0
  76. package/.agents/skills/embedded-captions/themes/brush.json +74 -0
  77. package/.agents/skills/embedded-captions/themes/chalkboard.json +52 -0
  78. package/.agents/skills/embedded-captions/themes/dossier.json +56 -0
  79. package/.agents/skills/embedded-captions/themes/graffiti.json +62 -0
  80. package/.agents/skills/embedded-captions/themes/hologram.json +41 -0
  81. package/.agents/skills/embedded-captions/themes/inkwater.json +43 -0
  82. package/.agents/skills/embedded-captions/themes/laser.json +46 -0
  83. package/.agents/skills/embedded-captions/themes/lastpage.json +33 -0
  84. package/.agents/skills/embedded-captions/themes/neonsign.json +53 -0
  85. package/.agents/skills/embedded-captions/themes/nightcity.json +56 -0
  86. package/.agents/skills/embedded-captions/themes/ordnance.json +54 -0
  87. package/.agents/skills/embedded-captions/themes/papercut.json +37 -0
  88. package/.agents/skills/embedded-captions/themes/popup.json +46 -0
  89. package/.agents/skills/embedded-captions/themes/ransom.json +62 -0
  90. package/.agents/skills/embedded-captions/themes/scoreboard.json +54 -0
  91. package/.agents/skills/embedded-captions/themes/spectrum.json +46 -0
  92. package/.agents/skills/embedded-captions/themes/stardust.json +29 -0
  93. package/.agents/skills/embedded-captions/themes/stomp.json +43 -0
  94. package/.agents/skills/embedded-captions/themes/terminal.json +49 -0
  95. package/.agents/skills/embedded-captions/themes/thunder.json +44 -0
  96. package/.agents/skills/embedded-captions/themes/transit.json +44 -0
  97. package/.agents/skills/embedded-captions/themes/vhs.json +47 -0
  98. package/.agents/skills/faceless-explainer/SKILL.md +209 -0
  99. package/.agents/skills/faceless-explainer/references/cut-catalog.md +215 -0
  100. package/.agents/skills/faceless-explainer/references/motion-language.md +156 -0
  101. package/.agents/skills/faceless-explainer/references/story-design.md +248 -0
  102. package/.agents/skills/faceless-explainer/references/visual-design.md +146 -0
  103. package/.agents/skills/faceless-explainer/scripts/assemble-index.mjs +570 -0
  104. package/.agents/skills/faceless-explainer/scripts/audio.mjs +260 -0
  105. package/.agents/skills/faceless-explainer/scripts/build-frame.mjs +536 -0
  106. package/.agents/skills/faceless-explainer/scripts/captions.mjs +508 -0
  107. package/.agents/skills/faceless-explainer/scripts/lib/assets.mjs +55 -0
  108. package/.agents/skills/faceless-explainer/scripts/lib/dimensions.mjs +45 -0
  109. package/.agents/skills/faceless-explainer/scripts/lib/storyboard.mjs +249 -0
  110. package/.agents/skills/faceless-explainer/scripts/lib/tokens.mjs +204 -0
  111. package/.agents/skills/faceless-explainer/scripts/lib/transition-registry.mjs +38 -0
  112. package/.agents/skills/faceless-explainer/scripts/lib/transitions.json +71 -0
  113. package/.agents/skills/faceless-explainer/scripts/transitions.mjs +320 -0
  114. package/.agents/skills/faceless-explainer/sub-agents/frame-worker.md +75 -0
  115. package/.agents/skills/flockposter/LICENSE +17 -0
  116. package/.agents/skills/flockposter/README.md +72 -0
  117. package/.agents/skills/flockposter/SKILL.md +562 -0
  118. package/.agents/skills/flockposter/examples/cli-instagram-reel.sh +21 -0
  119. package/.agents/skills/flockposter/examples/cli-multi-platform.json +69 -0
  120. package/.agents/skills/flockposter/examples/cli-tiktok-upload-only.sh +22 -0
  121. package/.agents/skills/flockposter/examples/cli-x-thread.sh +17 -0
  122. package/.agents/skills/flockposter/examples/cli-youtube-short.sh +21 -0
  123. package/.agents/skills/flockposter/examples/public-api-instagram-reel.json +28 -0
  124. package/.agents/skills/general-video/SKILL.md +143 -0
  125. package/.agents/skills/hyperframes/SKILL.md +160 -0
  126. package/.agents/skills/hyperframes-animation/SKILL.md +84 -0
  127. package/.agents/skills/hyperframes-animation/adapters/animate-text.md +64 -0
  128. package/.agents/skills/hyperframes-animation/adapters/animejs.md +114 -0
  129. package/.agents/skills/hyperframes-animation/adapters/css-animations.md +143 -0
  130. package/.agents/skills/hyperframes-animation/adapters/gsap-easing-and-stagger.md +118 -0
  131. package/.agents/skills/hyperframes-animation/adapters/gsap-timeline-and-labels.md +96 -0
  132. package/.agents/skills/hyperframes-animation/adapters/gsap-transforms-and-perf.md +96 -0
  133. package/.agents/skills/hyperframes-animation/adapters/gsap.md +105 -0
  134. package/.agents/skills/hyperframes-animation/adapters/html-in-canvas-patterns.md +507 -0
  135. package/.agents/skills/hyperframes-animation/adapters/lottie.md +117 -0
  136. package/.agents/skills/hyperframes-animation/adapters/three.md +131 -0
  137. package/.agents/skills/hyperframes-animation/adapters/typegpu.md +178 -0
  138. package/.agents/skills/hyperframes-animation/adapters/waapi.md +101 -0
  139. package/.agents/skills/hyperframes-animation/blueprints/comparison-split.md +27 -0
  140. package/.agents/skills/hyperframes-animation/blueprints/constellation-hub.md +44 -0
  141. package/.agents/skills/hyperframes-animation/blueprints/cta-morph-press.md +28 -0
  142. package/.agents/skills/hyperframes-animation/blueprints/cursor-ui-demo.md +57 -0
  143. package/.agents/skills/hyperframes-animation/blueprints/dataviz-countup.md +46 -0
  144. package/.agents/skills/hyperframes-animation/blueprints/device-surface-showcase.md +53 -0
  145. package/.agents/skills/hyperframes-animation/blueprints/grid-card-assemble.md +66 -0
  146. package/.agents/skills/hyperframes-animation/blueprints/kinetic-type-beats.md +78 -0
  147. package/.agents/skills/hyperframes-animation/blueprints/logo-assemble-lockup.md +69 -0
  148. package/.agents/skills/hyperframes-animation/blueprints/overwhelm-surround.md +28 -0
  149. package/.agents/skills/hyperframes-animation/blueprints/spatial-pan-stations.md +37 -0
  150. package/.agents/skills/hyperframes-animation/blueprints/ticker-takeover.md +29 -0
  151. package/.agents/skills/hyperframes-animation/blueprints/titlecard-reveal.md +42 -0
  152. package/.agents/skills/hyperframes-animation/blueprints/typewriter-reveal.md +51 -0
  153. package/.agents/skills/hyperframes-animation/blueprints/video-text-pivot.md +30 -0
  154. package/.agents/skills/hyperframes-animation/blueprints-index.md +146 -0
  155. package/.agents/skills/hyperframes-animation/examples/assets/avatars/02.avif +0 -0
  156. package/.agents/skills/hyperframes-animation/examples/assets/brands/github.avif +0 -0
  157. package/.agents/skills/hyperframes-animation/examples/assets/brands/nvidia.avif +0 -0
  158. package/.agents/skills/hyperframes-animation/examples/assets/brands/visa.avif +0 -0
  159. package/.agents/skills/hyperframes-animation/examples/assets/brands/zoominfo.avif +0 -0
  160. package/.agents/skills/hyperframes-animation/examples/brand-reveal-assemble-zoom.html +382 -0
  161. package/.agents/skills/hyperframes-animation/examples/comparison-split-cards.html +649 -0
  162. package/.agents/skills/hyperframes-animation/examples/concept-demo-decode-pan.html +520 -0
  163. package/.agents/skills/hyperframes-animation/examples/cta-morph-press.html +468 -0
  164. package/.agents/skills/hyperframes-animation/examples/cta-orbit-collapse.html +1298 -0
  165. package/.agents/skills/hyperframes-animation/examples/demo-page-scroll-spotlight.html +759 -0
  166. package/.agents/skills/hyperframes-animation/examples/hook-counter-burst.html +729 -0
  167. package/.agents/skills/hyperframes-animation/examples/messaging-multi-phrase.html +352 -0
  168. package/.agents/skills/hyperframes-animation/examples/metric-video-text-pivot.html +779 -0
  169. package/.agents/skills/hyperframes-animation/examples/problem-mockup-overwhelm.html +1374 -0
  170. package/.agents/skills/hyperframes-animation/examples/proof-logo-chain.html +861 -0
  171. package/.agents/skills/hyperframes-animation/examples/takeover-ticker-displace.html +347 -0
  172. package/.agents/skills/hyperframes-animation/examples/workflow-approve-press.html +606 -0
  173. package/.agents/skills/hyperframes-animation/rules/3d-page-scroll.md +227 -0
  174. package/.agents/skills/hyperframes-animation/rules/3d-text-depth-layers.md +297 -0
  175. package/.agents/skills/hyperframes-animation/rules/ai-tracking-box.md +382 -0
  176. package/.agents/skills/hyperframes-animation/rules/ambient-glow-bloom.md +305 -0
  177. package/.agents/skills/hyperframes-animation/rules/asr-keyword-glow.md +286 -0
  178. package/.agents/skills/hyperframes-animation/rules/avatar-cloud-network.md +371 -0
  179. package/.agents/skills/hyperframes-animation/rules/camera-cursor-tracking.md +246 -0
  180. package/.agents/skills/hyperframes-animation/rules/card-morph-anchor.md +267 -0
  181. package/.agents/skills/hyperframes-animation/rules/center-outward-expansion.md +227 -0
  182. package/.agents/skills/hyperframes-animation/rules/context-sensitive-cursor.md +257 -0
  183. package/.agents/skills/hyperframes-animation/rules/coordinate-target-zoom.md +332 -0
  184. package/.agents/skills/hyperframes-animation/rules/counting-dynamic-scale.md +283 -0
  185. package/.agents/skills/hyperframes-animation/rules/css-marker-patterns.md +373 -0
  186. package/.agents/skills/hyperframes-animation/rules/cursor-click-ripple.md +262 -0
  187. package/.agents/skills/hyperframes-animation/rules/depth-of-field-blur.md +313 -0
  188. package/.agents/skills/hyperframes-animation/rules/depth-scatter-assemble.md +303 -0
  189. package/.agents/skills/hyperframes-animation/rules/discrete-text-sequence.md +273 -0
  190. package/.agents/skills/hyperframes-animation/rules/dynamic-content-sequencing.md +307 -0
  191. package/.agents/skills/hyperframes-animation/rules/gsap-effects.md +299 -0
  192. package/.agents/skills/hyperframes-animation/rules/hacker-flip-3d.md +223 -0
  193. package/.agents/skills/hyperframes-animation/rules/kinetic-beat-slam.md +183 -0
  194. package/.agents/skills/hyperframes-animation/rules/motion-blur-streak.md +328 -0
  195. package/.agents/skills/hyperframes-animation/rules/multi-phase-camera.md +273 -0
  196. package/.agents/skills/hyperframes-animation/rules/orbit-3d-entry.md +301 -0
  197. package/.agents/skills/hyperframes-animation/rules/physics-press-reaction.md +350 -0
  198. package/.agents/skills/hyperframes-animation/rules/press-release-spring.md +296 -0
  199. package/.agents/skills/hyperframes-animation/rules/reactive-displacement.md +277 -0
  200. package/.agents/skills/hyperframes-animation/rules/scale-swap-transition.md +298 -0
  201. package/.agents/skills/hyperframes-animation/rules/sine-wave-loop.md +278 -0
  202. package/.agents/skills/hyperframes-animation/rules/split-tilt-cards.md +277 -0
  203. package/.agents/skills/hyperframes-animation/rules/spring-pop-entrance.md +273 -0
  204. package/.agents/skills/hyperframes-animation/rules/stat-bars-and-fills.md +156 -0
  205. package/.agents/skills/hyperframes-animation/rules/svg-icon-enrichment.md +329 -0
  206. package/.agents/skills/hyperframes-animation/rules/svg-path-draw.md +274 -0
  207. package/.agents/skills/hyperframes-animation/rules/vertical-spring-ticker.md +239 -0
  208. package/.agents/skills/hyperframes-animation/rules/viewport-change.md +349 -0
  209. package/.agents/skills/hyperframes-animation/rules-index.md +86 -0
  210. package/.agents/skills/hyperframes-animation/scripts/animation-map.mjs +606 -0
  211. package/.agents/skills/hyperframes-animation/scripts/package-loader.mjs +288 -0
  212. package/.agents/skills/hyperframes-animation/scripts/package-loader.test.mjs +62 -0
  213. package/.agents/skills/hyperframes-animation/techniques.md +507 -0
  214. package/.agents/skills/hyperframes-animation/transitions/TRANSITION-REGISTRY.md +167 -0
  215. package/.agents/skills/hyperframes-animation/transitions/catalog.md +127 -0
  216. package/.agents/skills/hyperframes-animation/transitions/css-3d.md +12 -0
  217. package/.agents/skills/hyperframes-animation/transitions/css-blur.md +51 -0
  218. package/.agents/skills/hyperframes-animation/transitions/css-cover.md +43 -0
  219. package/.agents/skills/hyperframes-animation/transitions/css-destruction.md +95 -0
  220. package/.agents/skills/hyperframes-animation/transitions/css-dissolve.md +66 -0
  221. package/.agents/skills/hyperframes-animation/transitions/css-distortion.md +45 -0
  222. package/.agents/skills/hyperframes-animation/transitions/css-grid.md +10 -0
  223. package/.agents/skills/hyperframes-animation/transitions/css-light.md +49 -0
  224. package/.agents/skills/hyperframes-animation/transitions/css-mechanical.md +30 -0
  225. package/.agents/skills/hyperframes-animation/transitions/css-other.md +25 -0
  226. package/.agents/skills/hyperframes-animation/transitions/css-push.md +41 -0
  227. package/.agents/skills/hyperframes-animation/transitions/css-radial.md +37 -0
  228. package/.agents/skills/hyperframes-animation/transitions/css-scale.md +24 -0
  229. package/.agents/skills/hyperframes-animation/transitions/overview.md +153 -0
  230. package/.agents/skills/hyperframes-cli/SKILL.md +113 -0
  231. package/.agents/skills/hyperframes-cli/references/doctor-browser.md +45 -0
  232. package/.agents/skills/hyperframes-cli/references/init-and-scaffold.md +55 -0
  233. package/.agents/skills/hyperframes-cli/references/lambda.md +132 -0
  234. package/.agents/skills/hyperframes-cli/references/lint-validate-inspect.md +121 -0
  235. package/.agents/skills/hyperframes-cli/references/preview-render.md +149 -0
  236. package/.agents/skills/hyperframes-cli/references/upgrade-info-misc.md +75 -0
  237. package/.agents/skills/hyperframes-core/SKILL.md +78 -0
  238. package/.agents/skills/hyperframes-core/references/composition-patterns.md +263 -0
  239. package/.agents/skills/hyperframes-core/references/data-attributes.md +70 -0
  240. package/.agents/skills/hyperframes-core/references/determinism-rules.md +68 -0
  241. package/.agents/skills/hyperframes-core/references/full-screen-motion.md +62 -0
  242. package/.agents/skills/hyperframes-core/references/minimal-composition.md +66 -0
  243. package/.agents/skills/hyperframes-core/references/script-format.md +49 -0
  244. package/.agents/skills/hyperframes-core/references/storyboard-format.md +95 -0
  245. package/.agents/skills/hyperframes-core/references/sub-compositions.md +237 -0
  246. package/.agents/skills/hyperframes-core/references/subagent-dispatch.md +41 -0
  247. package/.agents/skills/hyperframes-core/references/tailwind.md +125 -0
  248. package/.agents/skills/hyperframes-core/references/tracks-and-clips.md +76 -0
  249. package/.agents/skills/hyperframes-core/references/variables-and-media.md +90 -0
  250. package/.agents/skills/hyperframes-creative/SKILL.md +70 -0
  251. package/.agents/skills/hyperframes-creative/frame-presets/biennale-yellow/FRAME.md +289 -0
  252. package/.agents/skills/hyperframes-creative/frame-presets/biennale-yellow/caption-skin.html +221 -0
  253. package/.agents/skills/hyperframes-creative/frame-presets/biennale-yellow/frame-showcase.html +1214 -0
  254. package/.agents/skills/hyperframes-creative/frame-presets/blockframe/FRAME.md +275 -0
  255. package/.agents/skills/hyperframes-creative/frame-presets/blockframe/caption-skin.html +224 -0
  256. package/.agents/skills/hyperframes-creative/frame-presets/blockframe/frame-showcase.html +1347 -0
  257. package/.agents/skills/hyperframes-creative/frame-presets/blue-professional/FRAME.md +307 -0
  258. package/.agents/skills/hyperframes-creative/frame-presets/blue-professional/caption-skin.html +223 -0
  259. package/.agents/skills/hyperframes-creative/frame-presets/blue-professional/frame-showcase.html +1372 -0
  260. package/.agents/skills/hyperframes-creative/frame-presets/bold-poster/FRAME.md +275 -0
  261. package/.agents/skills/hyperframes-creative/frame-presets/bold-poster/caption-skin.html +230 -0
  262. package/.agents/skills/hyperframes-creative/frame-presets/bold-poster/frame-showcase.html +1140 -0
  263. package/.agents/skills/hyperframes-creative/frame-presets/broadside/FRAME.md +284 -0
  264. package/.agents/skills/hyperframes-creative/frame-presets/broadside/caption-skin.html +225 -0
  265. package/.agents/skills/hyperframes-creative/frame-presets/broadside/frame-showcase.html +1166 -0
  266. package/.agents/skills/hyperframes-creative/frame-presets/capsule/FRAME.md +284 -0
  267. package/.agents/skills/hyperframes-creative/frame-presets/capsule/caption-skin.html +224 -0
  268. package/.agents/skills/hyperframes-creative/frame-presets/capsule/frame-showcase.html +1465 -0
  269. package/.agents/skills/hyperframes-creative/frame-presets/cartesian/FRAME.md +278 -0
  270. package/.agents/skills/hyperframes-creative/frame-presets/cartesian/caption-skin.html +222 -0
  271. package/.agents/skills/hyperframes-creative/frame-presets/cartesian/frame-showcase.html +1263 -0
  272. package/.agents/skills/hyperframes-creative/frame-presets/claude/FRAME.md +277 -0
  273. package/.agents/skills/hyperframes-creative/frame-presets/claude/caption-skin.html +224 -0
  274. package/.agents/skills/hyperframes-creative/frame-presets/claude/frame-showcase.html +1439 -0
  275. package/.agents/skills/hyperframes-creative/frame-presets/cobalt-grid/FRAME.md +265 -0
  276. package/.agents/skills/hyperframes-creative/frame-presets/cobalt-grid/caption-skin.html +238 -0
  277. package/.agents/skills/hyperframes-creative/frame-presets/cobalt-grid/frame-showcase.html +1468 -0
  278. package/.agents/skills/hyperframes-creative/frame-presets/coral/FRAME.md +314 -0
  279. package/.agents/skills/hyperframes-creative/frame-presets/coral/caption-skin.html +226 -0
  280. package/.agents/skills/hyperframes-creative/frame-presets/coral/frame-showcase.html +1268 -0
  281. package/.agents/skills/hyperframes-creative/frame-presets/creative-mode/FRAME.md +333 -0
  282. package/.agents/skills/hyperframes-creative/frame-presets/creative-mode/caption-skin.html +227 -0
  283. package/.agents/skills/hyperframes-creative/frame-presets/creative-mode/frame-showcase.html +1330 -0
  284. package/.agents/skills/hyperframes-creative/frame-presets/daisy-days/FRAME.md +274 -0
  285. package/.agents/skills/hyperframes-creative/frame-presets/daisy-days/caption-skin.html +230 -0
  286. package/.agents/skills/hyperframes-creative/frame-presets/daisy-days/frame-showcase.html +1444 -0
  287. package/.agents/skills/hyperframes-creative/frame-presets/editorial-forest/FRAME.md +265 -0
  288. package/.agents/skills/hyperframes-creative/frame-presets/editorial-forest/caption-skin.html +223 -0
  289. package/.agents/skills/hyperframes-creative/frame-presets/editorial-forest/frame-showcase.html +1273 -0
  290. package/.agents/skills/hyperframes-creative/palettes/bold-energetic.md +14 -0
  291. package/.agents/skills/hyperframes-creative/palettes/clean-corporate.md +14 -0
  292. package/.agents/skills/hyperframes-creative/palettes/dark-premium.md +14 -0
  293. package/.agents/skills/hyperframes-creative/palettes/jewel-rich.md +14 -0
  294. package/.agents/skills/hyperframes-creative/palettes/monochrome.md +14 -0
  295. package/.agents/skills/hyperframes-creative/palettes/nature-earth.md +14 -0
  296. package/.agents/skills/hyperframes-creative/palettes/neon-electric.md +14 -0
  297. package/.agents/skills/hyperframes-creative/palettes/pastel-soft.md +14 -0
  298. package/.agents/skills/hyperframes-creative/palettes/warm-editorial.md +14 -0
  299. package/.agents/skills/hyperframes-creative/references/audio-reactive.md +76 -0
  300. package/.agents/skills/hyperframes-creative/references/beat-direction.md +164 -0
  301. package/.agents/skills/hyperframes-creative/references/composition-patterns.md +199 -0
  302. package/.agents/skills/hyperframes-creative/references/data-in-motion.md +19 -0
  303. package/.agents/skills/hyperframes-creative/references/design-adherence.md +19 -0
  304. package/.agents/skills/hyperframes-creative/references/design-picker.md +123 -0
  305. package/.agents/skills/hyperframes-creative/references/design-spec.md +55 -0
  306. package/.agents/skills/hyperframes-creative/references/house-style.md +73 -0
  307. package/.agents/skills/hyperframes-creative/references/motion-principles.md +150 -0
  308. package/.agents/skills/hyperframes-creative/references/narration.md +92 -0
  309. package/.agents/skills/hyperframes-creative/references/prompt-expansion.md +68 -0
  310. package/.agents/skills/hyperframes-creative/references/typography.md +218 -0
  311. package/.agents/skills/hyperframes-creative/references/video-composition.md +63 -0
  312. package/.agents/skills/hyperframes-creative/references/visual-styles.md +457 -0
  313. package/.agents/skills/hyperframes-creative/scripts/contrast-report.mjs +353 -0
  314. package/.agents/skills/hyperframes-creative/scripts/extract-audio-data.py +188 -0
  315. package/.agents/skills/hyperframes-creative/scripts/package-loader.mjs +288 -0
  316. package/.agents/skills/hyperframes-creative/scripts/package-loader.test.mjs +62 -0
  317. package/.agents/skills/hyperframes-creative/templates/design-picker.html +1432 -0
  318. package/.agents/skills/hyperframes-keyframes/SKILL.md +237 -0
  319. package/.agents/skills/hyperframes-keyframes/agents/openai.yaml +4 -0
  320. package/.agents/skills/hyperframes-keyframes/references/keyframe-patterns.md +106 -0
  321. package/.agents/skills/motion-graphics/SKILL.md +172 -0
  322. package/.agents/skills/motion-graphics/agents/builder.md +40 -0
  323. package/.agents/skills/motion-graphics/agents/director.md +53 -0
  324. package/.agents/skills/motion-graphics/agents/finalize.md +17 -0
  325. package/.agents/skills/motion-graphics/catalog-map.md +40 -0
  326. package/.agents/skills/motion-graphics/categories/asset-fusion/module.md +37 -0
  327. package/.agents/skills/motion-graphics/categories/charts/module.md +21 -0
  328. package/.agents/skills/motion-graphics/categories/kinetic-type/module.md +19 -0
  329. package/.agents/skills/motion-graphics/categories/logo-reveal/module.md +17 -0
  330. package/.agents/skills/motion-graphics/categories/lower-thirds/module.md +16 -0
  331. package/.agents/skills/motion-graphics/categories/maps/bake-basemap.mjs +273 -0
  332. package/.agents/skills/motion-graphics/categories/maps/module.md +61 -0
  333. package/.agents/skills/motion-graphics/categories/news/module.md +56 -0
  334. package/.agents/skills/motion-graphics/categories/stat/module.md +21 -0
  335. package/.agents/skills/motion-graphics/categories/tweet/module.md +16 -0
  336. package/.agents/skills/motion-graphics/categories/webpage/module.md +21 -0
  337. package/.agents/skills/motion-graphics/grounding/PROTOCOL.md +64 -0
  338. package/.agents/skills/motion-graphics/grounding/locate.mjs +227 -0
  339. package/.agents/skills/motion-graphics/phases/source/guide.md +21 -0
  340. package/.agents/skills/motion-graphics/references/builder-contract.md +38 -0
  341. package/.agents/skills/motion-graphics/references/motion-vocabulary.md +43 -0
  342. package/.agents/skills/motion-graphics/references/shot-plan-ir.md +54 -0
  343. package/.agents/skills/motion-graphics/samples/asset-fusion/_ref-circle-highlight.html +306 -0
  344. package/.agents/skills/product-launch-video/SKILL.md +208 -0
  345. package/.agents/skills/product-launch-video/references/cut-catalog.md +220 -0
  346. package/.agents/skills/product-launch-video/references/motion-language.md +156 -0
  347. package/.agents/skills/product-launch-video/references/story-design.md +373 -0
  348. package/.agents/skills/product-launch-video/references/visual-design.md +126 -0
  349. package/.agents/skills/product-launch-video/scripts/assemble-index.mjs +570 -0
  350. package/.agents/skills/product-launch-video/scripts/audio.mjs +260 -0
  351. package/.agents/skills/product-launch-video/scripts/build-frame.mjs +536 -0
  352. package/.agents/skills/product-launch-video/scripts/captions.mjs +508 -0
  353. package/.agents/skills/product-launch-video/scripts/lib/assets.mjs +55 -0
  354. package/.agents/skills/product-launch-video/scripts/lib/dimensions.mjs +45 -0
  355. package/.agents/skills/product-launch-video/scripts/lib/pad-frame-duration.mjs +36 -0
  356. package/.agents/skills/product-launch-video/scripts/lib/pad-frame-duration.test.mjs +76 -0
  357. package/.agents/skills/product-launch-video/scripts/lib/storyboard.mjs +249 -0
  358. package/.agents/skills/product-launch-video/scripts/lib/tokens.mjs +204 -0
  359. package/.agents/skills/product-launch-video/scripts/lib/transition-registry.mjs +38 -0
  360. package/.agents/skills/product-launch-video/scripts/lib/transitions.json +71 -0
  361. package/.agents/skills/product-launch-video/scripts/stage-assets.mjs +39 -0
  362. package/.agents/skills/product-launch-video/scripts/transitions.mjs +327 -0
  363. package/.agents/skills/product-launch-video/sub-agents/frame-worker.md +75 -0
  364. package/.agents/skills/slideshow/SKILL.md +550 -0
  365. package/.agents/skills/slideshow/references/standalone-harness.md +1020 -0
  366. package/.agents/skills/talking-head-recut/NOTICE.md +39 -0
  367. package/.agents/skills/talking-head-recut/SKILL.md +1195 -0
  368. package/.agents/skills/talking-head-recut/assets/vendor/gsap.min.js +10 -0
  369. package/.agents/skills/talking-head-recut/references/DESIGN_INDEX.md +145 -0
  370. package/.agents/skills/talking-head-recut/references/frames/clean.html +92 -0
  371. package/.agents/skills/talking-head-recut/references/frames/hairline.html +171 -0
  372. package/.agents/skills/talking-head-recut/references/frames/polaroid.html +131 -0
  373. package/.agents/skills/talking-head-recut/references/layouts/overlay.html +164 -0
  374. package/.agents/skills/talking-head-recut/references/layouts/pip.html +143 -0
  375. package/.agents/skills/talking-head-recut/references/layouts/split.html +121 -0
  376. package/.agents/skills/talking-head-recut/references/layouts/stack.html +109 -0
  377. package/.agents/skills/talking-head-recut/references/styles/academic.html +172 -0
  378. package/.agents/skills/talking-head-recut/references/styles/audit.html +170 -0
  379. package/.agents/skills/talking-head-recut/references/styles/editorial.html +189 -0
  380. package/.agents/skills/talking-head-recut/references/styles/geom.html +181 -0
  381. package/.agents/skills/talking-head-recut/references/styles/minimal.html +130 -0
  382. package/.agents/skills/talking-head-recut/references/styles/spotlight.html +142 -0
  383. package/.agents/skills/talking-head-recut/references/styles/swiss.html +188 -0
  384. package/.agents/skills/talking-head-recut/references/styles/terminal.html +196 -0
  385. package/.agents/skills/talking-head-recut/references/styles/whiteboard.html +170 -0
  386. package/.agents/skills/talking-head-recut/references/styles/xhs.html +206 -0
  387. package/.agents/skills/vidfarm-media/SKILL.md +85 -0
  388. package/.agents/skills/vidfarm-media/references/tts.md +54 -0
  389. package/.agents/skills/vidfarm-media/scripts/audio.mjs +628 -0
  390. package/.agents/skills/vidfarm-media/scripts/wait-bgm.mjs +31 -0
  391. package/.agents/skills/website-to-video/SKILL.md +145 -0
  392. package/.agents/skills/website-to-video/assets/sfx/CREDITS.md +35 -0
  393. package/.agents/skills/website-to-video/assets/sfx/manifest.json +97 -0
  394. package/.agents/skills/website-to-video/references/beat-builder-guide.md +263 -0
  395. package/.agents/skills/website-to-video/references/capabilities.md +713 -0
  396. package/.agents/skills/website-to-video/references/step-0-capture.md +55 -0
  397. package/.agents/skills/website-to-video/references/step-1-design.md +333 -0
  398. package/.agents/skills/website-to-video/references/step-2-brief.md +178 -0
  399. package/.agents/skills/website-to-video/references/step-3-storyboard.md +572 -0
  400. package/.agents/skills/website-to-video/references/step-4-vo.md +225 -0
  401. package/.agents/skills/website-to-video/references/step-5-build.md +483 -0
  402. package/.agents/skills/website-to-video/references/step-6-validate.md +341 -0
  403. package/.agents/skills/website-to-video/scripts/w2h-verify.mjs +759 -0
  404. package/SKILL.director.md +37 -0
  405. package/SKILL.platform.md +6 -1
  406. package/demo/dist/app.js +295 -11
  407. package/dist/src/app.js +317 -133
  408. package/dist/src/cli.js +529 -12
  409. package/dist/src/composition-runtime.js +262 -3
  410. package/dist/src/devcli/doctor.js +192 -0
  411. package/dist/src/devcli/hyperframes-cli.js +118 -0
  412. package/dist/src/devcli/skills.js +476 -0
  413. package/dist/src/devcli/stills.js +249 -0
  414. package/dist/src/editor-chat.js +255 -0
  415. package/dist/src/hyperframes/composition.js +5 -5
  416. package/dist/src/services/composition-lint.js +199 -0
  417. package/dist/src/services/hyperframes.js +67 -13
  418. package/package.json +2 -1
@@ -0,0 +1,249 @@
1
+ // `vidfarm stills` — render PNG still frames of the CURRENT composition
2
+ // in-process, without a full MP4 render. This is the coding agent's visual
3
+ // self-verification loop: edit composition.html, render 3-8 stills, look at
4
+ // them, iterate.
5
+ //
6
+ // Implementation mirrors HyperframesService.renderStill (src/services/
7
+ // hyperframes.ts): stage the composition as a HyperFrames project dir, drive
8
+ // @hyperframes/producer's createRenderJob/executeRenderJob with the
9
+ // "png-sequence" format, then pick the captured frames nearest each requested
10
+ // timestamp. Deliberately NOT importing HyperframesService — that class drags
11
+ // in config.ts (which mkdirs at import) and the whole cloud-render surface;
12
+ // this is a lean local-file implementation.
13
+ //
14
+ // Cost control: png-sequence captures EVERY frame on the fps grid, so we
15
+ // (a) cap the staged composition's duration at the last requested timestamp
16
+ // and (b) render on a coarse integer fps grid (4fps → 0.25s accuracy, dropped
17
+ // to 2/1fps for long timelines) instead of the composition's native 30fps.
18
+ // Requested timestamps are snapped to that grid; the actually-captured time
19
+ // is reported next to each written PNG.
20
+ import { copyFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
21
+ import os from "node:os";
22
+ import path from "node:path";
23
+ import { parseHTML } from "linkedom";
24
+ import { inspectComposition } from "./composition-edit.js";
25
+ const MAX_DEFAULT_STILLS = 8;
26
+ const MAX_GRID_FRAMES = 600;
27
+ // The in-process producer shells out to ffmpeg/ffprobe resolved from
28
+ // HYPERFRAMES_FFMPEG_PATH / HYPERFRAMES_FFPROBE_PATH (then PATH). Point it at
29
+ // the bundled static binaries, same as src/services/hyperframes.ts does.
30
+ async function ensureProducerFfmpegEnv() {
31
+ if (!process.env.HYPERFRAMES_FFMPEG_PATH?.trim()) {
32
+ try {
33
+ const mod = (await import("ffmpeg-static"));
34
+ const resolved = typeof mod === "string" ? mod : mod.default;
35
+ if (typeof resolved === "string" && resolved && existsSync(resolved)) {
36
+ process.env.HYPERFRAMES_FFMPEG_PATH = resolved;
37
+ }
38
+ }
39
+ catch { /* PATH fallback */ }
40
+ }
41
+ if (!process.env.HYPERFRAMES_FFPROBE_PATH?.trim()) {
42
+ try {
43
+ const mod = (await import("ffprobe-static"));
44
+ const resolved = (mod.path ?? mod.default?.path);
45
+ if (typeof resolved === "string" && resolved && existsSync(resolved)) {
46
+ process.env.HYPERFRAMES_FFPROBE_PATH = resolved;
47
+ }
48
+ }
49
+ catch { /* PATH fallback */ }
50
+ }
51
+ }
52
+ /**
53
+ * Default timestamps when --at is omitted: the midpoint of each distinct
54
+ * scene clip on the LOWEST video track (capped at 8), else 0 / mid / end of
55
+ * the composition.
56
+ */
57
+ export function defaultStillTimestamps(layers, durationSeconds) {
58
+ const isSceneClip = (layer) => layer.duration > 0 && (layer.is_timeline_proxy ||
59
+ layer.kind === "video" ||
60
+ layer.kind === "scene" ||
61
+ (layer.src !== null && /\.(mp4|mov|webm|m4v|mkv)(\?|#|$)/i.test(layer.src)) ||
62
+ // Canvas-filling clips of any kind read as scenes (image scenes,
63
+ // solid/title cards) — overlays/captions are never full-canvas.
64
+ layer.is_full_canvas);
65
+ const scenes = layers.filter(isSceneClip);
66
+ if (scenes.length > 0) {
67
+ const lowestTrack = Math.min(...scenes.map((layer) => layer.track));
68
+ const seen = new Set();
69
+ const midpoints = [];
70
+ for (const layer of scenes.filter((l) => l.track === lowestTrack).sort((a, b) => a.start - b.start)) {
71
+ const key = layer.key || `${layer.start}-${layer.duration}`;
72
+ if (seen.has(key))
73
+ continue;
74
+ seen.add(key);
75
+ midpoints.push(layer.start + layer.duration / 2);
76
+ if (midpoints.length >= MAX_DEFAULT_STILLS)
77
+ break;
78
+ }
79
+ if (midpoints.length > 0)
80
+ return midpoints;
81
+ }
82
+ if (durationSeconds <= 0)
83
+ return [0];
84
+ return [...new Set([0, durationSeconds / 2, Math.max(0, durationSeconds - 0.5)])];
85
+ }
86
+ // Pick the coarsest integer fps grid that keeps capture cheap while staying
87
+ // within 0.25s of any requested timestamp where possible.
88
+ function pickGridFps(maxTimestamp) {
89
+ for (const fps of [4, 2, 1]) {
90
+ if ((maxTimestamp + 1) * fps <= MAX_GRID_FRAMES)
91
+ return fps;
92
+ }
93
+ return 1;
94
+ }
95
+ // Cap the staged copy's root data-duration so the producer never captures
96
+ // frames past the last requested still.
97
+ function capCompositionDuration(html, capSeconds) {
98
+ const { document } = parseHTML(html);
99
+ const root = document.querySelector("[data-composition-id]");
100
+ if (!root)
101
+ throw new Error("Composition root ([data-composition-id]) not found in composition.html.");
102
+ const existing = Number.parseFloat(root.getAttribute("data-duration") ?? "");
103
+ const capped = Number.isFinite(existing) && existing > 0 ? Math.min(existing, capSeconds) : capSeconds;
104
+ root.setAttribute("data-duration", String(Number(capped.toFixed(3))));
105
+ return `<!doctype html>\n${document.documentElement.outerHTML}`;
106
+ }
107
+ // Same guard as renderLocal: libx264-adjacent tooling (and the runtime's
108
+ // canvas sizing) prefers even data-width/data-height.
109
+ function forceEvenDimensions(html) {
110
+ return html.replace(/\bdata-(width|height)=(["'])(\d+)\2/gi, (match, dim, quote, value) => {
111
+ const n = Number.parseInt(value, 10);
112
+ if (!Number.isFinite(n) || n % 2 === 0)
113
+ return match;
114
+ return `data-${dim}=${quote}${n - 1}${quote}`;
115
+ });
116
+ }
117
+ /**
118
+ * Stage the composition as a HyperFrames project dir: the (duration-capped)
119
+ * composition.html becomes index.html, and every sibling file/dir is copied in
120
+ * so relative asset references keep resolving. Mirrors stageProject in
121
+ * src/services/hyperframes.ts, but against a local composition dir.
122
+ */
123
+ function stageCompositionProject(htmlPath, stagedHtml) {
124
+ const sourceDir = path.dirname(htmlPath);
125
+ const projectDir = mkdtempSync(path.join(os.tmpdir(), "vidfarm-stills-"));
126
+ writeFileSync(path.join(projectDir, "index.html"), stagedHtml, "utf8");
127
+ for (const entry of readdirSync(sourceDir)) {
128
+ if (entry === path.basename(htmlPath) || entry === "index.html" || entry === "stills")
129
+ continue;
130
+ const from = path.join(sourceDir, entry);
131
+ try {
132
+ const info = statSync(from);
133
+ if (info.isDirectory()) {
134
+ cpSync(from, path.join(projectDir, entry), { recursive: true, force: true });
135
+ }
136
+ else if (info.isFile()) {
137
+ copyFileSync(from, path.join(projectDir, entry));
138
+ }
139
+ }
140
+ catch {
141
+ // best-effort: a missing/unreadable sibling never blocks the stills
142
+ }
143
+ }
144
+ return projectDir;
145
+ }
146
+ export async function renderCompositionStills(input) {
147
+ const html = readFileSync(input.htmlPath, "utf8");
148
+ if (!html.includes("data-composition-id=")) {
149
+ throw new Error(`stills requires composition HTML with data-composition-id (${input.htmlPath}).`);
150
+ }
151
+ const info = inspectComposition(html);
152
+ const durationSeconds = info.duration_seconds > 0 ? info.duration_seconds : 1;
153
+ const requested = (input.at && input.at.length > 0
154
+ ? input.at
155
+ : defaultStillTimestamps(info.layers, durationSeconds))
156
+ .map((t) => Math.min(Math.max(0, t), Math.max(0, durationSeconds - 0.01)))
157
+ .sort((a, b) => a - b);
158
+ if (requested.length === 0)
159
+ throw new Error("stills: no timestamps to render.");
160
+ const gridFps = pickGridFps(requested[requested.length - 1]);
161
+ const capSeconds = Math.min(durationSeconds, requested[requested.length - 1] + 2 / gridFps);
162
+ await ensureProducerFfmpegEnv();
163
+ const stagedHtml = forceEvenDimensions(capCompositionDuration(html, capSeconds));
164
+ const projectDir = stageCompositionProject(input.htmlPath, stagedHtml);
165
+ const framesDir = mkdtempSync(path.join(os.tmpdir(), "vidfarm-stills-frames-"));
166
+ const outDir = path.resolve(input.outDir ?? path.join(path.dirname(input.htmlPath), "stills"));
167
+ mkdirSync(outDir, { recursive: true });
168
+ // Declarative vidfarm compositions carry no scripts (cloud saves strip
169
+ // them), so they can never satisfy the engine's window.__timelines
170
+ // sub-composition poll — which otherwise stalls capture-session init for the
171
+ // full 45s playerReadyTimeout before proceeding anyway. When the html has no
172
+ // timeline registration at all, cap that wait via the engine's env knob.
173
+ // Compositions that DO register GSAP timelines keep the default.
174
+ const priorReadyTimeout = process.env.PRODUCER_PLAYER_READY_TIMEOUT_MS;
175
+ if (!priorReadyTimeout?.trim() && !stagedHtml.includes("__timelines")) {
176
+ process.env.PRODUCER_PLAYER_READY_TIMEOUT_MS = "10000";
177
+ }
178
+ try {
179
+ // Same producer entry points renderStill uses; "png-sequence" writes the
180
+ // frames dir as frame_000001.png … at 1/gridFps intervals.
181
+ const { createRenderJob, executeRenderJob } = await import("@hyperframes/producer");
182
+ // Quiet mode (--json) swaps in a no-op logger so stdout stays parseable —
183
+ // the producer's default logger writes progress lines to stdout.
184
+ const silentLogger = { error() { }, warn() { }, info() { }, debug() { } };
185
+ const job = createRenderJob({
186
+ fps: gridFps,
187
+ quality: "high",
188
+ format: "png-sequence",
189
+ ...(input.quiet ? { logger: silentLogger } : {})
190
+ });
191
+ let lastStatus = null;
192
+ const executeRender = () => executeRenderJob(job, projectDir, framesDir, (progressJob) => {
193
+ if (!input.quiet && progressJob.status !== lastStatus) {
194
+ lastStatus = progressJob.status;
195
+ console.log(`\x1b[2m ${progressJob.status}\x1b[0m`);
196
+ }
197
+ });
198
+ // The engine also logs init/browser lines via console.log directly (they
199
+ // bypass the producer logger); in quiet mode reroute console stdout to
200
+ // stderr for the duration of the render so stdout stays pure JSON.
201
+ const originalConsole = { log: console.log, info: console.info, debug: console.debug };
202
+ if (input.quiet) {
203
+ const toStderr = (...args) => {
204
+ process.stderr.write(`${args.map((value) => (typeof value === "string" ? value : JSON.stringify(value))).join(" ")}\n`);
205
+ };
206
+ console.log = toStderr;
207
+ console.info = toStderr;
208
+ console.debug = toStderr;
209
+ }
210
+ try {
211
+ await executeRender();
212
+ }
213
+ finally {
214
+ console.log = originalConsole.log;
215
+ console.info = originalConsole.info;
216
+ console.debug = originalConsole.debug;
217
+ }
218
+ const frameFiles = readdirSync(framesDir)
219
+ .filter((name) => name.toLowerCase().endsWith(".png"))
220
+ .sort();
221
+ if (frameFiles.length === 0) {
222
+ throw new Error("stills render produced no frames — check the composition with `vidfarm lint` first.");
223
+ }
224
+ const stills = [];
225
+ for (const t of requested) {
226
+ const frameIndex = Math.min(frameFiles.length - 1, Math.max(0, Math.round(t * gridFps)));
227
+ const capturedSec = Number((frameIndex / gridFps).toFixed(3));
228
+ const stillName = `still-${capturedSec.toFixed(2).padStart(6, "0")}s.png`;
229
+ const dest = path.join(outDir, stillName);
230
+ copyFileSync(path.join(framesDir, frameFiles[frameIndex]), dest);
231
+ stills.push({ requested_sec: Number(t.toFixed(3)), captured_sec: capturedSec, path: dest });
232
+ }
233
+ return {
234
+ ok: true,
235
+ composition: input.htmlPath,
236
+ out_dir: outDir,
237
+ grid_fps: gridFps,
238
+ duration_seconds: durationSeconds,
239
+ stills
240
+ };
241
+ }
242
+ finally {
243
+ if (!priorReadyTimeout?.trim())
244
+ delete process.env.PRODUCER_PLAYER_READY_TIMEOUT_MS;
245
+ rmSync(framesDir, { recursive: true, force: true });
246
+ rmSync(projectDir, { recursive: true, force: true });
247
+ }
248
+ }
249
+ //# sourceMappingURL=stills.js.map
@@ -1,3 +1,6 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ import { lintCompositionHtml } from "./services/composition-lint.js";
1
4
  export const EDITOR_CHAT_PROVIDER_PRIORITY = [
2
5
  "openai",
3
6
  "gemini",
@@ -89,6 +92,8 @@ export function buildTemplateEditorChatSystemPrompt(input) {
89
92
  "When you simply want to change a layer's media without moving it, prefer set_layer_media (kind must match: video↔video, image↔image, audio↔audio) — it keeps the same node, id, track, and geometry.",
90
93
  "CONTEXT AVAILABLE FOR USE — this composition can expose TWO video sources plus a text/scene context: (1) the ORIGINAL video — the raw uploaded source, with any baked-in subtitles/captions; (2) the DECOMPOSED video — a processed, caption-free copy Vidfarm produces asynchronously (subtitles removed) that the composition timeline renders from; and (3) the decomposed video context (transcript + per-scene visual descriptions + viral DNA) available via the video_context tool. The original and the decomposed video are NOT the same URL, and you should not carry both URLs in default context. When the user asks to reuse, re-render, or feed the composition's video into a primitive route (image extract, ai video edit, media/dedupe, video trim, video probe, etc.), call GET /api/v1/compositions/:forkId/remove-video-captions FIRST to read { status, original_source_url, mirrored_url } and pick correctly — here original_source_url is the ORIGINAL video and mirrored_url is the DECOMPOSED video: prefer the DECOMPOSED video (mirrored_url) when status=\"done\" and the downstream call should be caption-free (thumbnails, style references, re-cut hooks); prefer the ORIGINAL video (original_source_url) when the user explicitly wants the raw upload (branded intros, provenance, when captions are the subject matter). If status is \"pending\" and the user is not blocked on subtitle removal, use the ORIGINAL video and mention that the decomposed (caption-free) video is still processing. If status is \"none\" or \"failed\", there is no decomposed video yet — use the ORIGINAL video only. Do not call POST /remove-video-captions-poll unless the user explicitly asks to advance the subtitle-removal job. To strip burned-in captions from any OTHER video URL (not this fork's decompose flow), use the POST /api/v1/primitives/videos/remove-captions primitive instead.",
91
94
  "The OPTIONAL video_context tool returns this fork's decomposed video context: the source video's verbatim audio transcript (full text plus timestamped segments), a literal visual description of every scene, per-scene transcript excerpts, and viral DNA. Call it with the fork id from editor_context whenever the user asks what the source video says or shows, when writing/translating captions, hooks, or dubs that must match the spoken audio, or when you need scene-by-scene grounding before planning timeline edits. It is read-only and non-billing, so prefer it over guessing from frames or memory. If it returns status=\"none\", the fork was never smart-decomposed — offer to run POST /api/v1/compositions/:forkId/auto-decompose first. The same data is available via GET /api/v1/compositions/:forkId/video-context.json through http_request (useful for other forks).",
95
+ "KNOWLEDGE ON DEMAND (load_skill): deeper authoring knowledge ships as vidfarm skill packs you can read mid-conversation with the load_skill tool — the composition HTML contract (hyperframes-core), motion/animation craft and transition doctrine (hyperframes-animation), seek-safe keyframe patterns (hyperframes-keyframes), creative direction/palettes/typography (hyperframes-creative), caption identities (embedded-captions), media/audio resolution (vidfarm-media), and end-to-end workflow playbooks (product-launch-video, faceless-explainer, website-to-video, general-video, motion-graphics, slideshow, talking-head-recut). Call load_skill with the pack name to read its SKILL.md; it lists reference files you can then load by relative path (e.g. file='references/step-4-vo.md'). Use it when a request needs craft beyond the built-in preset vocabulary — designing a whole composition via replace_composition_html, picking motion or transition language for a style, or following a named workflow. Load only what the task needs, never bulk-load packs, and never paste large skill content back to the user.",
96
+ "replace_composition_html is validated before it reaches the editor: when the HTML violates the composition contract (missing data-composition-id, invalid clip timing, same-track overlaps, unknown preset names, media without src) the tool result comes back with rejected=true and lint_errors, and NOTHING is applied — fix the reported issues and call it again. lint_warnings are advisory and the action still applies (e.g. <script> tags survive local renders but are stripped on cloud/web saves — keep web-editor compositions declarative).",
92
97
  "TEMPLATE FORMAT — MATCH IT WHEN WRITING TEXT: the COMPOSITION BRIEF (stable template context in the system prompt) carries composition_context — WHAT THIS TEMPLATE IS: its format/genre and narrative arc (trend_tagline, hook, retention, payoff, preserve, avoid). ALWAYS read it before writing or adjusting ANY captions, text layers, or on-screen copy, and make your copy fit the template's format and voice — do NOT default to generic product/app ad copy. The format dictates the caption style: a text-message / DM / iMessage conversation template's captions must read like short back-and-forth chat messages between people (in-character, conversational), a 'story time' / talking-head template reads like first-person spoken narration, a listicle reads like punchy numbered items, a fake-news / headline template reads like a news chyron, etc. `preserve` usually names format cues that must stay (e.g. the messaging-bubble layout); `avoid` names what to keep out. When the template's format is a conversation, skit, or roleplay, keep the captions in that voice and only weave the product in the way that format allows (subtly, in-dialogue) rather than replacing the messages with feature bullets. If the brief's composition_context is thin or you are unsure what the video actually shows, call video_context (scene visual descriptions + transcript) to ground the format BEFORE writing captions.",
93
98
  "MY FILES: the user has a personal file library ('My Files') of uploaded assets — videos (mp4/mov/webm), images (png/jpg/jpeg/gif/webp/svg), audio (mp3/wav/m4a/aac), and documents (pdf/md/txt/csv) — organized into virtual folders. Use the browse_files tool to explore AND write it: call action=list (optionally with folder_path) to see what files and folders exist, action=search with a plain-language query to find files by MEANING (keyword + vector match over every file's name, folder, and metadata notes — prefer it over paging list when the library is large or the user references an asset vaguely), action=read with a file_id (copied from a prior list/search) to fetch one file, action=write with file_name + content (or source_url) to SAVE a file, and action=annotate with file_id + notes to attach metadata notes to any existing file. When the user references their own footage, brand assets, logos, music, a brief, or a script ('use my product photo', 'the video I uploaded', 'my brand logo', 'the script in my files'), browse_files search or list first to find it rather than asking them to re-upload or paste a URL. For text files (md/txt/csv/srt/vtt/json), read returns the full text_content inline so you can read a brief or script directly. For images/video/audio/pdf, read returns only a durable view_url — use that URL as media: drop it into the timeline via editor_action add_layer/set_layer_media, or pass it into primitive routes (images/edit source_image_url, videos/generate input_references, videos/download). Never invent file ids or view URLs — always list or search to discover the real ones first.",
94
99
  "SAVING CONTEXT TO MY FILES: browse_files action=write persists durable, reusable context the user (and future chats/agents) can read back. write saves text files (md/txt/csv/json/srt/vtt) via content, and IMPORTS media into the library via source_url (a durable URL from a finished primitive job or existing asset) — use source_url to persist a generated asset the user will want again (a character sprite card, a recurring background, a logo variant) instead of leaving it stranded in a job result. Use write to capture the Getting Started artifacts as Markdown: a product About.md (basic offer/product context) or a deeper Interview.md, plus awareness-levels.md, persuasive-angles.md, and ad-hooks.md distilled from the matching brainstorm outputs. When a brainstorm job returns strategy the user wants to keep, offer to save it (or save it) as the corresponding .md so it is not lost when the chat ends. Namescope every write under the right product/offer folder (see MY FILES IS MULTI-OFFER) — e.g. folder_path='acme-skincare', file_name='About.md'. Pass notes on write (or annotate afterwards) for any asset worth finding again: notes describe what the file is, who/what it depicts, and when to use it, and they are vector-embedded so action=search finds the file by meaning in future sessions. Before overwriting an existing file of the same name in the same folder, confirm with the user (writing the same name replaces it).",
@@ -451,4 +456,254 @@ export function compactEditorChatModelMessages(messages) {
451
456
  ...recentMessages
452
457
  ];
453
458
  }
459
+ // ——— Skill packs the load_skill chat tool can fetch on demand. Served from
460
+ // GET /skill/:name (SKILL.md) and GET /skill-pack/:name/files/<path>
461
+ // (reference files), vendored under .agents/skills. Keep this list in sync
462
+ // with the packs actually vendored in the repo.
463
+ export const EDITOR_CHAT_SKILL_PACKS = [
464
+ { name: "vidfarm-director", summary: "the full director playbook (REST + devcli flows)" },
465
+ { name: "hyperframes", summary: "workflow router for the HyperFrames skill suite" },
466
+ { name: "hyperframes-cli", summary: "HyperFrames CLI dev loop (init/lint/validate/inspect/snapshot/render)" },
467
+ { name: "hyperframes-core", summary: "composition HTML contract: data-* timing attrs, tracks, clips, determinism rules" },
468
+ { name: "hyperframes-animation", summary: "motion rules catalog, scene blueprints, transition doctrine, runtime adapters" },
469
+ { name: "hyperframes-keyframes", summary: "seek-safe keyframe/CSS animation patterns" },
470
+ { name: "hyperframes-creative", summary: "design specs, palettes, typography, house style, narration craft" },
471
+ { name: "vidfarm-media", summary: "narration TTS/STT, BGM/SFX/media resolution through vidfarm primitives" },
472
+ { name: "embedded-captions", summary: "caption identity catalog + subject-occlusion caption pipeline" },
473
+ { name: "talking-head-recut", summary: "timed graphic overlay cards on existing footage" },
474
+ { name: "product-launch-video", summary: "product/launch promo workflow (time-coded shot sequences)" },
475
+ { name: "faceless-explainer", summary: "text-to-faceless-explainer workflow" },
476
+ { name: "website-to-video", summary: "website capture to video workflow" },
477
+ { name: "general-video", summary: "general multi-scene composition workflow" },
478
+ { name: "motion-graphics", summary: "short motion-graphic workflow with category modules" },
479
+ { name: "slideshow", summary: "interactive slideshow/deck composition contract" }
480
+ ];
481
+ export const EDITOR_CHAT_SKILL_FILE_MAX_CHARS = 24_000;
482
+ // ——— Stream guardrails shared by the in-process chat (src/app.ts) and the
483
+ // deployed Lambda copy (infra/lambda/editor-chat.ts). One implementation so
484
+ // the two stream loops cannot drift.
485
+ export const EDITOR_CHAT_JOB_ID_PATTERN = /\bjob_[a-z0-9]+\b/gi;
486
+ const HTTP_TOOL_CLAIM_PATTERN = /\b(?:http_request|REST API call|API call|GET\s+https?:\/\/|POST\s+https?:\/\/|GET\s+\/api\/|POST\s+\/api\/)/i;
487
+ export function collectJobIdsFromUnknown(value, output = new Set()) {
488
+ if (typeof value === "string") {
489
+ for (const match of value.matchAll(EDITOR_CHAT_JOB_ID_PATTERN)) {
490
+ output.add(match[0]);
491
+ }
492
+ return output;
493
+ }
494
+ if (Array.isArray(value)) {
495
+ for (const entry of value) {
496
+ collectJobIdsFromUnknown(entry, output);
497
+ }
498
+ return output;
499
+ }
500
+ if (value && typeof value === "object") {
501
+ for (const entry of Object.values(value)) {
502
+ collectJobIdsFromUnknown(entry, output);
503
+ }
504
+ }
505
+ return output;
506
+ }
507
+ export function collectVerifiedHttpJobIds(toolResult) {
508
+ if (!toolResult || typeof toolResult !== "object") {
509
+ return new Set();
510
+ }
511
+ const record = toolResult;
512
+ return collectJobIdsFromUnknown(record.response?.body ?? record.result?.body);
513
+ }
514
+ export function readAsyncJobHandoff(output) {
515
+ if (!output || typeof output !== "object") {
516
+ return null;
517
+ }
518
+ const record = output;
519
+ const body = record.response?.body;
520
+ if (!body || typeof body !== "object") {
521
+ return null;
522
+ }
523
+ const job = body;
524
+ if (typeof job.job_id !== "string" || typeof job.status !== "string") {
525
+ return null;
526
+ }
527
+ if (job.status === "succeeded" || job.status === "failed" || job.status === "cancelled") {
528
+ return null;
529
+ }
530
+ return {
531
+ jobId: job.job_id,
532
+ tracer: typeof job.tracer === "string" ? job.tracer : null,
533
+ status: job.status,
534
+ isNewQueuedJob: record.request?.method === "POST" && record.response?.status === 202
535
+ };
536
+ }
537
+ export function buildAsyncJobHandoffText(input) {
538
+ // "Render …" wording is load-bearing: template-editor-chat.tsx emits the
539
+ // identical strings client-side and dedupes with a substring check, so any
540
+ // wording change here double-prints every queued-job handoff.
541
+ if (!input.isNewQueuedJob) {
542
+ return `Render \`${input.jobId}\` is ${input.status}.`;
543
+ }
544
+ return [
545
+ `Started render \`${input.jobId}\`${input.tracer ? ` with tracer \`${input.tracer}\`` : ""}.`,
546
+ "If it takes a while, ask for an update later and I can check it for you."
547
+ ].join("\n\n");
548
+ }
549
+ export function buildEditorChatGuardrailNotice(input) {
550
+ const mentionedJobIds = collectJobIdsFromUnknown(input.assistantText);
551
+ const unverifiedJobIds = [...mentionedJobIds].filter((jobId) => !input.verifiedJobIds.has(jobId));
552
+ const notices = [];
553
+ if (input.redactedJobIds.size) {
554
+ notices.push("Guardrail: I withheld unverified job ID text because it did not come from a visible HTTP response card.");
555
+ }
556
+ else if (unverifiedJobIds.length) {
557
+ notices.push("Guardrail: I mentioned an unverified job ID. Ignore it unless it appears in a visible HTTP response card.");
558
+ }
559
+ if (!input.hasHttpRequestResult && HTTP_TOOL_CLAIM_PATTERN.test(input.assistantText)) {
560
+ notices.push("Guardrail: no `http_request` tool result was recorded for this response, so any claimed REST/API call should be treated as not executed.");
561
+ }
562
+ return notices.length ? `\n\n${notices.join("\n\n")}` : "";
563
+ }
564
+ export function redactUnverifiedJobIds(input) {
565
+ return input.text.replace(EDITOR_CHAT_JOB_ID_PATTERN, (jobId) => {
566
+ if (input.verifiedJobIds.has(jobId)) {
567
+ return jobId;
568
+ }
569
+ input.redactedJobIds.add(jobId);
570
+ return "[unverified job id]";
571
+ });
572
+ }
573
+ const SKILL_PACK_NAME_RE = /^[a-z0-9][a-z0-9_-]*$/;
574
+ const SKILL_PACK_TEXT_FILE_RE = /\.(md|txt|json|ya?ml|mjs|cjs|js|html|css|srt|vtt|sh)$/i;
575
+ // Shared implementation behind the load_skill chat tool (registered in both
576
+ // src/app.ts and infra/lambda/editor-chat.ts). Fetches vendored skill-pack
577
+ // content from the vidfarm host's public /skill + /skill-pack routes.
578
+ export async function loadEditorChatSkillContent(input) {
579
+ const skill = input.skill.trim().toLowerCase().replace(/^\//, "");
580
+ if (!SKILL_PACK_NAME_RE.test(skill)) {
581
+ return { skill: input.skill, error: "Invalid skill pack name." };
582
+ }
583
+ const knownPack = EDITOR_CHAT_SKILL_PACKS.some((pack) => pack.name === skill);
584
+ const file = (input.file ?? "").trim().replace(/^\.?\//, "");
585
+ if (file && (file.includes("..") || !SKILL_PACK_TEXT_FILE_RE.test(file))) {
586
+ return { skill, file, error: "file must be a text file path inside the pack (no '..'; markdown/scripts/data files only)." };
587
+ }
588
+ const base = input.publicBaseUrl.replace(/\/$/, "");
589
+ const url = file
590
+ ? `${base}/skill-pack/${encodeURIComponent(skill)}/files/${file.split("/").map(encodeURIComponent).join("/")}`
591
+ : `${base}/skill/${encodeURIComponent(skill)}`;
592
+ const timeoutController = new AbortController();
593
+ const timeoutId = setTimeout(() => {
594
+ timeoutController.abort(new Error("Skill fetch timed out after 30 seconds."));
595
+ }, 30_000);
596
+ try {
597
+ const signal = input.abortSignal
598
+ ? AbortSignal.any([input.abortSignal, timeoutController.signal])
599
+ : timeoutController.signal;
600
+ const response = await fetch(url, { method: "GET", signal });
601
+ if (!response.ok) {
602
+ return {
603
+ explanation: input.explanation ?? null,
604
+ skill,
605
+ file: file || null,
606
+ status: response.status,
607
+ error: `Skill content unavailable (${response.status}).${knownPack ? "" : " Unknown pack — the tool description lists the available packs."}`
608
+ };
609
+ }
610
+ const raw = await response.text();
611
+ const truncated = raw.length > EDITOR_CHAT_SKILL_FILE_MAX_CHARS;
612
+ return {
613
+ explanation: input.explanation ?? null,
614
+ skill,
615
+ file: file || "SKILL.md",
616
+ truncated,
617
+ content: truncated
618
+ ? `${raw.slice(0, EDITOR_CHAT_SKILL_FILE_MAX_CHARS)}\n\n[truncated — load a specific reference file for the rest]`
619
+ : raw
620
+ };
621
+ }
622
+ catch (error) {
623
+ return {
624
+ explanation: input.explanation ?? null,
625
+ skill,
626
+ file: file || null,
627
+ error: error instanceof Error ? error.message : "Skill fetch failed."
628
+ };
629
+ }
630
+ finally {
631
+ clearTimeout(timeoutId);
632
+ }
633
+ }
634
+ // ——— load_skill tool factory + replace_composition_html lint gate, shared by
635
+ // both chat copies (src/app.ts and infra/lambda/editor-chat.ts) so the tool
636
+ // description, schema, and gating semantics cannot drift between deployments.
637
+ export function createLoadSkillTool(input) {
638
+ return tool({
639
+ description: `OPTIONAL knowledge fetch: load a vidfarm skill pack document on demand instead of guessing at authoring craft. Available packs: ${EDITOR_CHAT_SKILL_PACKS.map((pack) => `${pack.name} — ${pack.summary}`).join("; ")}. Call with just skill=<name> to read the pack's SKILL.md (it lists the pack's reference files), then load a specific reference with file=<relative path> (e.g. 'references/step-4-vo.md', 'rules-index.md'). Text files only; long files are truncated. Read-only and non-billing. Load only what the current task needs.`,
640
+ inputSchema: z.object({
641
+ skill: z.string().min(1).describe("Skill pack name, e.g. 'hyperframes-animation'."),
642
+ file: z.string().optional().describe("Optional text file path inside the pack. Omit to fetch SKILL.md."),
643
+ explanation: z.string().optional().describe("Short reason for loading this knowledge.")
644
+ }),
645
+ execute: async ({ skill, file, explanation }) => loadEditorChatSkillContent({
646
+ skill,
647
+ file,
648
+ explanation,
649
+ publicBaseUrl: input.publicBaseUrl,
650
+ abortSignal: input.abortSignal
651
+ })
652
+ });
653
+ }
654
+ // Lint gate for editor_action's replace_composition_html escape hatch. Returns
655
+ // the tool result to send INSTEAD of the echoed args, or null to pass through.
656
+ // A rejected result omits action_type, so the browser's extractEditorAction
657
+ // never applies it — rejected payloads are inert by construction.
658
+ export function lintReplaceCompositionAction(args) {
659
+ if (args.action_type !== "replace_composition_html") {
660
+ return null;
661
+ }
662
+ const lint = lintCompositionHtml(typeof args.composition_html === "string" ? args.composition_html : "");
663
+ if (!lint.ok) {
664
+ return {
665
+ rejected: true,
666
+ reason: "composition_html failed validation and was NOT applied. Fix the lint_errors and call replace_composition_html again.",
667
+ lint_errors: lint.errors,
668
+ lint_warnings: lint.warnings,
669
+ explanation: args.explanation
670
+ };
671
+ }
672
+ if (lint.warnings.length) {
673
+ return { ...args, lint_warnings: lint.warnings };
674
+ }
675
+ return null;
676
+ }
677
+ // Streaming wrapper around redactUnverifiedJobIds: ai-sdk text deltas split at
678
+ // arbitrary byte boundaries, so a verified id like job_abc123 can arrive as
679
+ // "…job_ab" + "c123…" — redacting each chunk independently mangles the leading
680
+ // fragment. Hold back a trailing potential id fragment until the next delta
681
+ // (or flush) completes it.
682
+ export function createStreamingJobIdRedactor(input) {
683
+ let carry = "";
684
+ const TRAILING_FRAGMENT = /\bjob_[a-z0-9]*$/i;
685
+ const MAX_CARRY_CHARS = 64;
686
+ const redact = (text) => redactUnverifiedJobIds({
687
+ text,
688
+ verifiedJobIds: input.verifiedJobIds,
689
+ redactedJobIds: input.redactedJobIds
690
+ });
691
+ return {
692
+ push(text) {
693
+ let combined = carry + text;
694
+ carry = "";
695
+ const match = combined.match(TRAILING_FRAGMENT);
696
+ if (match && match[0].length <= MAX_CARRY_CHARS) {
697
+ carry = match[0];
698
+ combined = combined.slice(0, combined.length - carry.length);
699
+ }
700
+ return combined ? redact(combined) : "";
701
+ },
702
+ flush() {
703
+ const rest = carry;
704
+ carry = "";
705
+ return rest ? redact(rest) : "";
706
+ }
707
+ };
708
+ }
454
709
  //# sourceMappingURL=editor-chat.js.map
@@ -575,12 +575,12 @@ function renderLayer(layer) {
575
575
  // data-start) and it plays across exactly the clip's span.
576
576
  const vars = kenBurnsInlineVars(kenBurns, positiveNumber(layer.duration, 0));
577
577
  const kbStyle = vars ? `${style};${vars}` : style;
578
- return `<img ${attrs} data-kenburns="${escapeAttr(kenBurns.preset)}" src="${escapeAttr(layer.src || "")}" alt="" style="${escapeAttr(kbStyle)}" />`;
578
+ return `<img class="clip" ${attrs} data-kenburns="${escapeAttr(kenBurns.preset)}" src="${escapeAttr(layer.src || "")}" alt="" style="${escapeAttr(kbStyle)}" />`;
579
579
  }
580
- return `<img ${attrs} src="${escapeAttr(layer.src || "")}" alt="" style="${escapeAttr(style)}" />`;
580
+ return `<img class="clip" ${attrs} src="${escapeAttr(layer.src || "")}" alt="" style="${escapeAttr(style)}" />`;
581
581
  }
582
582
  if (layer.kind === "html") {
583
- return `<div ${attrs} style="${escapeAttr(style)}">${layer.html || ""}</div>`;
583
+ return `<div class="clip" ${attrs} style="${escapeAttr(style)}">${layer.html || ""}</div>`;
584
584
  }
585
585
  const text = layer.text ?? layer.label ?? "";
586
586
  const inlineStyle = textInlineCss(layer.textBackgroundStyle || "plain", layer.color || "#ffffff", layer.background || "#000000", layer.fontFamily || "TikTok Sans", positiveInteger(layer.fontWeight, 700));
@@ -605,10 +605,10 @@ function renderLayer(layer) {
605
605
  const upper = captionConfig.uppercase ? ";text-transform:uppercase" : "";
606
606
  const divStyle = `${style}${vars ? `;${vars}` : ""}${upper};overflow:visible`;
607
607
  const captionAttrs = `${attrs} data-caption-animation="${escapeAttr(captionConfig.animation)}"${captionConfig.uppercase ? " data-caption-uppercase=\"1\"" : ""}`;
608
- return `<div ${captionAttrs} style="${escapeAttr(divStyle)}"><span data-vf-text-lines="true" style="${escapeAttr(linesStyle)}"><span data-vf-text-inline="true" style="${escapeAttr(inlineStyle)}">${captionWordSpansHtml(words)}</span></span></div>`;
608
+ return `<div class="clip" ${captionAttrs} style="${escapeAttr(divStyle)}"><span data-vf-text-lines="true" style="${escapeAttr(linesStyle)}"><span data-vf-text-inline="true" style="${escapeAttr(inlineStyle)}">${captionWordSpansHtml(words)}</span></span></div>`;
609
609
  }
610
610
  }
611
- return `<div ${attrs} style="${escapeAttr(style)}"><span data-vf-text-lines="true" style="${escapeAttr(linesStyle)}"><span data-vf-text-inline="true" style="${escapeAttr(inlineStyle)}">${escapeHtml(text)}</span></span></div>`;
611
+ return `<div class="clip" ${attrs} style="${escapeAttr(style)}"><span data-vf-text-lines="true" style="${escapeAttr(linesStyle)}"><span data-vf-text-inline="true" style="${escapeAttr(inlineStyle)}">${escapeHtml(text)}</span></span></div>`;
612
612
  }
613
613
  // Serializes word windows into the <span data-cap-word> run that the caption
614
614
  // stylesheet animates. Delay/duration are the word window relative to the