dsh-hyperframes 0.3.2 → 0.4.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 (375) hide show
  1. package/README.en.md +5 -4
  2. package/README.md +6 -5
  3. package/lib/index.d.ts +1 -1
  4. package/lib/index.js +1 -0
  5. package/package.json +2 -2
  6. package/skills/embedded-captions/SKILL.md +28 -3
  7. package/skills/embedded-captions/references/aesthetic-principles.md +2 -3
  8. package/skills/embedded-captions/references/rail.md +1 -1
  9. package/skills/embedded-captions/scripts/check-occlusion.cjs +4 -1
  10. package/skills/embedded-captions/scripts/check-overflow.cjs +1 -0
  11. package/skills/embedded-captions/scripts/check-rail-climax.cjs +5 -1
  12. package/skills/embedded-captions/scripts/fixtures/heroless/theme.json +11 -0
  13. package/skills/embedded-captions/scripts/hf-cli.cjs +33 -0
  14. package/skills/embedded-captions/scripts/make-theme.cjs +95 -57
  15. package/skills/embedded-captions/scripts/make-theme.test.mjs +131 -0
  16. package/skills/embedded-captions/scripts/matte.cjs +1 -13
  17. package/skills/embedded-captions/scripts/measure-layout.cjs +2 -1
  18. package/skills/embedded-captions/scripts/preview-frames.cjs +39 -11
  19. package/skills/embedded-captions/scripts/preview-frames.test.mjs +24 -0
  20. package/skills/embedded-captions/scripts/render-and-composite.sh +8 -52
  21. package/skills/embedded-captions/scripts/safe-zones.cjs +4 -1
  22. package/skills/embedded-captions/scripts/transcribe.cjs +5 -14
  23. package/skills/embedded-captions/scripts/transcribe.test.mjs +41 -0
  24. package/skills/faceless-explainer/SKILL.md +20 -16
  25. package/skills/faceless-explainer/references/story-design.md +1 -1
  26. package/skills/faceless-explainer/scripts/assemble-index.mjs +6 -4
  27. package/skills/faceless-explainer/scripts/assemble-index.test.mjs +57 -2
  28. package/skills/faceless-explainer/scripts/audio.mjs +2 -0
  29. package/skills/faceless-explainer/scripts/build-frame.mjs +6 -3
  30. package/skills/faceless-explainer/scripts/captions.mjs +7 -4
  31. package/skills/faceless-explainer/scripts/frame-packets.mjs +4 -4
  32. package/skills/faceless-explainer/scripts/lib/bgm-volume.mjs +4 -0
  33. package/skills/faceless-explainer/scripts/lib/captured-fonts.mjs +58 -0
  34. package/skills/{hyperframes-core → faceless-explainer}/scripts/lib/frame-packets-core.mjs +5 -23
  35. package/skills/faceless-explainer/scripts/lib/storyboard.mjs +1 -1
  36. package/skills/faceless-explainer/scripts/lib/transitions.json +3 -3
  37. package/skills/faceless-explainer/scripts/transitions.mjs +1 -0
  38. package/skills/faceless-explainer/scripts/transitions.test.mjs +2 -0
  39. package/skills/faceless-explainer/sub-agents/frame-worker.md +1 -1
  40. package/skills/figma/SKILL.md +2 -0
  41. package/skills/general-video/SKILL.md +13 -11
  42. package/skills/general-video/scripts/frame-packets.mjs +4 -4
  43. package/skills/general-video/scripts/lib/frame-packets-core.mjs +194 -0
  44. package/skills/general-video/sub-agents/frame-worker.md +1 -1
  45. package/skills/hyperframes/SKILL.md +40 -30
  46. package/skills/{hyperframes-core → hyperframes}/references/brief-contract.md +15 -15
  47. package/skills/{hyperframes-core → hyperframes}/references/brief-format.md +8 -8
  48. package/skills/hyperframes/references/capability-menu.md +2 -2
  49. package/skills/{hyperframes-core → hyperframes}/references/frame-worker-core.md +1 -1
  50. package/skills/hyperframes/references/intent-interview.md +16 -16
  51. package/skills/hyperframes/references/plugin-installation.md +43 -0
  52. package/skills/{hyperframes-core → hyperframes}/references/production-loop.md +11 -11
  53. package/skills/hyperframes/references/review-loop.md +37 -0
  54. package/skills/hyperframes/references/route-briefs.md +1 -1
  55. package/skills/hyperframes/references/routes/motion-graphics.md +1 -1
  56. package/skills/{hyperframes-core → hyperframes}/references/script-format.md +3 -3
  57. package/skills/hyperframes/references/skill-lifecycle.md +2 -0
  58. package/skills/{hyperframes-core → hyperframes}/references/storyboard-format.md +3 -34
  59. package/skills/hyperframes/scripts/lib/frame-packets-core.mjs +194 -0
  60. package/skills/hyperframes/scripts/plugin-cli.mjs +88 -0
  61. package/skills/hyperframes/scripts/plugin-cli.test.mjs +117 -0
  62. package/skills/hyperframes-animation/SKILL.md +5 -1
  63. package/skills/hyperframes-animation/adapters/gsap.md +1 -1
  64. package/skills/hyperframes-animation/adapters/lottie.md +15 -2
  65. package/skills/hyperframes-animation/adapters/three.md +14 -0
  66. package/skills/hyperframes-animation/blueprints/titlecard-reveal.md +1 -1
  67. package/skills/hyperframes-animation/references/motion-blur.md +133 -0
  68. package/skills/hyperframes-animation/rules/cursor-click-ripple.md +1 -1
  69. package/skills/hyperframes-animation/rules/dynamic-content-sequencing.md +1 -1
  70. package/skills/hyperframes-animation/rules/hacker-flip-3d.md +1 -1
  71. package/skills/hyperframes-animation/rules/kinetic-beat-slam.md +2 -2
  72. package/skills/hyperframes-animation/rules/motion-blur-streak.md +1 -1
  73. package/skills/hyperframes-animation/rules/vertical-spring-ticker.md +1 -1
  74. package/skills/hyperframes-animation/scripts/package-loader.mjs +47 -16
  75. package/skills/hyperframes-animation/scripts/package-loader.test.mjs +52 -0
  76. package/skills/hyperframes-animation/techniques.md +1 -1
  77. package/skills/hyperframes-animation/transitions/TRANSITION-REGISTRY.md +3 -3
  78. package/skills/hyperframes-animation/transitions/css-push.md +4 -4
  79. package/skills/hyperframes-audio/SKILL.md +30 -22
  80. package/skills/hyperframes-audio/scripts/carve.mjs +6 -3
  81. package/skills/hyperframes-audio/scripts/carve.test.mjs +16 -1
  82. package/skills/hyperframes-cli/SKILL.md +44 -35
  83. package/skills/hyperframes-cli/references/init-and-scaffold.md +8 -6
  84. package/skills/hyperframes-cli/references/lint-validate-inspect.md +3 -3
  85. package/skills/hyperframes-cli/references/preview-render.md +35 -32
  86. package/skills/hyperframes-cli/references/upgrade-info-misc.md +50 -1
  87. package/skills/hyperframes-core/SKILL.md +30 -24
  88. package/skills/hyperframes-core/references/composition-patterns.md +3 -42
  89. package/skills/hyperframes-core/references/creator-editing-recipes.md +131 -7
  90. package/skills/hyperframes-core/references/data-attributes.md +11 -11
  91. package/skills/hyperframes-core/references/determinism-rules.md +2 -19
  92. package/skills/hyperframes-core/references/full-screen-motion.md +0 -9
  93. package/skills/hyperframes-core/references/minimal-composition.md +2 -2
  94. package/skills/hyperframes-core/references/sub-compositions.md +3 -66
  95. package/skills/hyperframes-core/references/tailwind.md +1 -20
  96. package/skills/hyperframes-core/references/tracks-and-clips.md +2 -6
  97. package/skills/hyperframes-core/references/variables-and-media.md +3 -3
  98. package/skills/hyperframes-creative/SKILL.md +2 -0
  99. package/skills/hyperframes-creative/references/house-style.md +15 -15
  100. package/skills/hyperframes-creative/references/story-spine.md +3 -1
  101. package/skills/hyperframes-creative/references/storyboard-recipe.md +57 -0
  102. package/skills/hyperframes-creative/references/typography.md +1 -1
  103. package/skills/hyperframes-creative/scripts/package-loader.mjs +47 -16
  104. package/skills/hyperframes-creative/scripts/package-loader.test.mjs +52 -0
  105. package/skills/hyperframes-keyframes/SKILL.md +4 -2
  106. package/skills/hyperframes-registry/SKILL.md +3 -1
  107. package/skills/hyperframes-registry/references/contributing.md +8 -2
  108. package/skills/hyperframes-registry/references/discovery.md +20 -34
  109. package/skills/hyperframes-studio/SKILL.md +77 -0
  110. package/skills/media-use/SKILL.md +14 -12
  111. package/skills/media-use/audio/references/captions/authoring.md +4 -0
  112. package/skills/media-use/audio/references/remove-background.md +1 -1
  113. package/skills/media-use/audio/references/tts-to-captions.md +11 -2
  114. package/skills/media-use/audio/references/tts.md +84 -6
  115. package/skills/media-use/audio/scripts/audio.mjs +15 -6
  116. package/skills/media-use/audio/scripts/gemini-pipeline.test.mjs +113 -0
  117. package/skills/media-use/audio/scripts/lib/audio-meta.mjs +39 -0
  118. package/skills/media-use/audio/scripts/lib/audio-meta.test.mjs +136 -0
  119. package/skills/media-use/audio/scripts/lib/bgm-volume.mjs +4 -0
  120. package/skills/media-use/audio/scripts/lib/bgm.mjs +2 -6
  121. package/skills/media-use/audio/scripts/lib/gemini-auth.mjs +43 -0
  122. package/skills/media-use/audio/scripts/lib/gemini-auth.py +52 -0
  123. package/skills/media-use/audio/scripts/lib/gemini-auth.test.mjs +86 -0
  124. package/skills/media-use/audio/scripts/lib/gemini-auth_test.py +90 -0
  125. package/skills/media-use/audio/scripts/lib/gemini-tts.mjs +122 -0
  126. package/skills/media-use/audio/scripts/lib/gemini-tts.test.mjs +243 -0
  127. package/skills/media-use/audio/scripts/lib/heygen.mjs +2 -1
  128. package/skills/media-use/audio/scripts/lib/tts.mjs +15 -3
  129. package/skills/media-use/audio/scripts/wait-bgm.mjs +8 -4
  130. package/skills/media-use/audio/scripts/wait-bgm.test.mjs +65 -0
  131. package/skills/media-use/references/audio.md +3 -2
  132. package/skills/media-use/references/grading.md +7 -7
  133. package/skills/media-use/references/media-treatment-recipes.md +1 -1
  134. package/skills/media-use/references/memory.md +2 -2
  135. package/skills/media-use/references/meta.md +2 -2
  136. package/skills/media-use/references/operations.md +15 -13
  137. package/skills/media-use/references/resolve.md +22 -11
  138. package/skills/media-use/references/setup-providers.md +11 -11
  139. package/skills/media-use/scripts/audio-duck.mjs +19 -27
  140. package/skills/media-use/scripts/compatibility.test.mjs +96 -0
  141. package/skills/media-use/scripts/lib/config-lock.mjs +45 -0
  142. package/skills/media-use/scripts/lib/duck.mjs +16 -10
  143. package/skills/media-use/scripts/lib/media-fetch.mjs +85 -0
  144. package/skills/media-use/scripts/lib/media-home.mjs +20 -0
  145. package/skills/media-use/scripts/lib/npx-sync.mjs +28 -2
  146. package/skills/media-use/scripts/lib/prefs-store.mjs +2 -2
  147. package/skills/media-use/scripts/lib/recipe-store.mjs +2 -2
  148. package/skills/media-use/scripts/lib/telemetry.mjs +29 -41
  149. package/skills/media-use/scripts/lib/transcriptCutFade.mjs +21 -0
  150. package/skills/media-use/scripts/prefs.mjs +1 -1
  151. package/skills/media-use/scripts/recipe.mjs +1 -1
  152. package/skills/media-use/scripts/resolve-plugin.test.mjs +62 -0
  153. package/skills/media-use/scripts/resolve.mjs +27 -1267
  154. package/skills/media-use/scripts/transcribe.mjs +39 -25
  155. package/skills/media-use/scripts/transcribe.test.mjs +112 -0
  156. package/skills/media-use/scripts/transcript-cut.mjs +39 -8
  157. package/skills/media-use/scripts/transcript-cut.test.mjs +117 -0
  158. package/skills/motion-graphics/SKILL.md +3 -1
  159. package/skills/motion-graphics/agents/builder.md +1 -0
  160. package/skills/motion-graphics/agents/director.md +2 -1
  161. package/skills/motion-graphics/catalog-map.md +25 -13
  162. package/skills/motion-graphics/categories/charts/module.md +2 -1
  163. package/skills/motion-graphics/categories/lower-thirds/module.md +4 -0
  164. package/skills/motion-graphics/categories/news/module.md +1 -1
  165. package/skills/motion-graphics/references/motion-vocabulary.md +4 -0
  166. package/skills/music-to-video/SKILL.md +8 -6
  167. package/skills/music-to-video/references/montage.md +3 -6
  168. package/skills/music-to-video/references/motion-primitives/3d-card-flip/index.html +10 -111
  169. package/skills/music-to-video/references/motion-primitives/3d-card-flip/scene.html +119 -0
  170. package/skills/music-to-video/references/motion-primitives/bg-flow-field/index.html +11 -284
  171. package/skills/music-to-video/references/motion-primitives/bg-flow-field/scene.html +294 -0
  172. package/skills/music-to-video/references/motion-primitives/binary-decrypt/index.html +10 -76
  173. package/skills/music-to-video/references/motion-primitives/binary-decrypt/scene.html +85 -0
  174. package/skills/music-to-video/references/motion-primitives/blur-resolve/index.html +10 -52
  175. package/skills/music-to-video/references/motion-primitives/blur-resolve/scene.html +60 -0
  176. package/skills/music-to-video/references/motion-primitives/braam-punch/index.html +10 -92
  177. package/skills/music-to-video/references/motion-primitives/braam-punch/scene.html +100 -0
  178. package/skills/music-to-video/references/motion-primitives/chromatic-split/index.html +10 -103
  179. package/skills/music-to-video/references/motion-primitives/chromatic-split/scene.html +111 -0
  180. package/skills/music-to-video/references/motion-primitives/chrome-sweep/index.html +10 -60
  181. package/skills/music-to-video/references/motion-primitives/chrome-sweep/scene.html +68 -0
  182. package/skills/music-to-video/references/motion-primitives/counting-punch/index.html +10 -110
  183. package/skills/music-to-video/references/motion-primitives/counting-punch/scene.html +119 -0
  184. package/skills/music-to-video/references/motion-primitives/crash-zoom-in/index.html +10 -112
  185. package/skills/music-to-video/references/motion-primitives/crash-zoom-in/scene.html +120 -0
  186. package/skills/music-to-video/references/motion-primitives/datamosh-smear/index.html +10 -121
  187. package/skills/music-to-video/references/motion-primitives/datamosh-smear/scene.html +131 -0
  188. package/skills/music-to-video/references/motion-primitives/directional-fill/index.html +10 -90
  189. package/skills/music-to-video/references/motion-primitives/directional-fill/scene.html +103 -0
  190. package/skills/music-to-video/references/motion-primitives/dolly-zoom/index.html +10 -94
  191. package/skills/music-to-video/references/motion-primitives/dolly-zoom/scene.html +102 -0
  192. package/skills/music-to-video/references/motion-primitives/electric-arc/index.html +10 -100
  193. package/skills/music-to-video/references/motion-primitives/electric-arc/scene.html +105 -0
  194. package/skills/music-to-video/references/motion-primitives/flash-cut/index.html +10 -89
  195. package/skills/music-to-video/references/motion-primitives/flash-cut/scene.html +97 -0
  196. package/skills/music-to-video/references/motion-primitives/gooey-metaball/index.html +10 -197
  197. package/skills/music-to-video/references/motion-primitives/gooey-metaball/scene.html +206 -0
  198. package/skills/music-to-video/references/motion-primitives/hard-cut/index.html +10 -74
  199. package/skills/music-to-video/references/motion-primitives/hard-cut/scene.html +82 -0
  200. package/skills/music-to-video/references/motion-primitives/hypercut-whip/index.html +10 -36
  201. package/skills/music-to-video/references/motion-primitives/hypercut-whip/scene.html +44 -0
  202. package/skills/music-to-video/references/motion-primitives/iris-open/index.html +10 -61
  203. package/skills/music-to-video/references/motion-primitives/iris-open/scene.html +69 -0
  204. package/skills/music-to-video/references/motion-primitives/kinetic-letter-in/index.html +10 -55
  205. package/skills/music-to-video/references/motion-primitives/kinetic-letter-in/scene.html +64 -0
  206. package/skills/music-to-video/references/motion-primitives/liquid-morph/index.html +10 -88
  207. package/skills/music-to-video/references/motion-primitives/liquid-morph/scene.html +96 -0
  208. package/skills/music-to-video/references/motion-primitives/mask-reveal/index.html +10 -66
  209. package/skills/music-to-video/references/motion-primitives/mask-reveal/scene.html +74 -0
  210. package/skills/music-to-video/references/motion-primitives/mosaic-pack/index.html +10 -81
  211. package/skills/music-to-video/references/motion-primitives/mosaic-pack/scene.html +89 -0
  212. package/skills/music-to-video/references/motion-primitives/neon-flicker/index.html +10 -65
  213. package/skills/music-to-video/references/motion-primitives/neon-flicker/scene.html +73 -0
  214. package/skills/music-to-video/references/motion-primitives/outline-to-fill/index.html +10 -74
  215. package/skills/music-to-video/references/motion-primitives/outline-to-fill/scene.html +82 -0
  216. package/skills/music-to-video/references/motion-primitives/palette-flip/index.html +10 -89
  217. package/skills/music-to-video/references/motion-primitives/palette-flip/scene.html +97 -0
  218. package/skills/music-to-video/references/motion-primitives/particle-burst/index.html +10 -98
  219. package/skills/music-to-video/references/motion-primitives/particle-burst/scene.html +106 -0
  220. package/skills/music-to-video/references/motion-primitives/pixel-dissolve/index.html +10 -78
  221. package/skills/music-to-video/references/motion-primitives/pixel-dissolve/scene.html +86 -0
  222. package/skills/music-to-video/references/motion-primitives/radial-burst-lines/index.html +10 -117
  223. package/skills/music-to-video/references/motion-primitives/radial-burst-lines/scene.html +130 -0
  224. package/skills/music-to-video/references/motion-primitives/screen-shake/index.html +10 -70
  225. package/skills/music-to-video/references/motion-primitives/screen-shake/scene.html +78 -0
  226. package/skills/music-to-video/references/motion-primitives/slot-machine-reveal/index.html +10 -87
  227. package/skills/music-to-video/references/motion-primitives/slot-machine-reveal/scene.html +100 -0
  228. package/skills/music-to-video/references/motion-primitives/spotlight-sweep/index.html +10 -94
  229. package/skills/music-to-video/references/motion-primitives/spotlight-sweep/scene.html +102 -0
  230. package/skills/music-to-video/references/motion-primitives/staggered-exit/index.html +10 -74
  231. package/skills/music-to-video/references/motion-primitives/staggered-exit/scene.html +82 -0
  232. package/skills/music-to-video/references/motion-primitives/text-spectral-rays/index.html +11 -271
  233. package/skills/music-to-video/references/motion-primitives/text-spectral-rays/scene.html +280 -0
  234. package/skills/music-to-video/references/motion-primitives/text-wave-distort/index.html +10 -92
  235. package/skills/music-to-video/references/motion-primitives/text-wave-distort/scene.html +106 -0
  236. package/skills/music-to-video/references/motion-primitives/tile-mosaic/index.html +10 -101
  237. package/skills/music-to-video/references/motion-primitives/tile-mosaic/scene.html +109 -0
  238. package/skills/music-to-video/references/motion-primitives/typewriter-reveal/index.html +10 -79
  239. package/skills/music-to-video/references/motion-primitives/typewriter-reveal/scene.html +91 -0
  240. package/skills/music-to-video/references/motion-primitives/word-grid-burst/index.html +10 -83
  241. package/skills/music-to-video/references/motion-primitives/word-grid-burst/scene.html +96 -0
  242. package/skills/music-to-video/references/planning.md +2 -2
  243. package/skills/music-to-video/scripts/lib/storyboard.mjs +1 -1
  244. package/skills/music-to-video/sub-agents/frame-worker.md +2 -2
  245. package/skills/pr-to-video/SKILL.md +20 -16
  246. package/skills/pr-to-video/references/code-vocabulary.md +14 -12
  247. package/skills/pr-to-video/references/story-design.md +1 -1
  248. package/skills/pr-to-video/scripts/assemble-index.mjs +6 -4
  249. package/skills/pr-to-video/scripts/assemble-index.test.mjs +57 -2
  250. package/skills/pr-to-video/scripts/audio.mjs +2 -0
  251. package/skills/pr-to-video/scripts/build-frame.mjs +6 -3
  252. package/skills/pr-to-video/scripts/captions.mjs +7 -4
  253. package/skills/pr-to-video/scripts/captions.test.mjs +41 -0
  254. package/skills/pr-to-video/scripts/frame-packets.mjs +4 -4
  255. package/skills/pr-to-video/scripts/lib/bgm-volume.mjs +4 -0
  256. package/skills/pr-to-video/scripts/lib/captured-fonts.mjs +58 -0
  257. package/skills/pr-to-video/scripts/lib/frame-packets-core.mjs +194 -0
  258. package/skills/pr-to-video/scripts/lib/storyboard.mjs +1 -1
  259. package/skills/pr-to-video/scripts/lib/transitions.json +3 -3
  260. package/skills/pr-to-video/scripts/transitions.mjs +7 -5
  261. package/skills/pr-to-video/sub-agents/frame-worker.md +1 -1
  262. package/skills/product-launch-video/SKILL.md +20 -16
  263. package/skills/product-launch-video/references/story-design.md +1 -1
  264. package/skills/product-launch-video/scripts/assemble-index.mjs +6 -4
  265. package/skills/product-launch-video/scripts/assemble-index.test.mjs +57 -2
  266. package/skills/product-launch-video/scripts/audio.mjs +2 -0
  267. package/skills/product-launch-video/scripts/audio.test.mjs +16 -0
  268. package/skills/product-launch-video/scripts/build-frame-subsets.test.mjs +152 -0
  269. package/skills/product-launch-video/scripts/build-frame.mjs +6 -3
  270. package/skills/product-launch-video/scripts/captions.mjs +7 -4
  271. package/skills/product-launch-video/scripts/frame-packets.mjs +4 -4
  272. package/skills/product-launch-video/scripts/lib/bgm-volume.mjs +4 -0
  273. package/skills/product-launch-video/scripts/lib/captured-fonts.mjs +58 -0
  274. package/skills/product-launch-video/scripts/lib/frame-packets-core.mjs +194 -0
  275. package/skills/product-launch-video/scripts/lib/storyboard.mjs +1 -1
  276. package/skills/product-launch-video/scripts/lib/transitions.json +3 -3
  277. package/skills/product-launch-video/scripts/transitions.mjs +7 -5
  278. package/skills/product-launch-video/scripts/transitions.test.mjs +68 -0
  279. package/skills/product-launch-video/sub-agents/frame-worker.md +1 -1
  280. package/skills/remotion-to-hyperframes/SKILL.md +7 -4
  281. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/expected.json +5 -5
  282. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/compositions/title.html +31 -0
  283. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-1-title-card/hf-src/index.html +10 -25
  284. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/README.md +8 -8
  285. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/expected.json +9 -9
  286. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-1.html +35 -0
  287. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-2.html +35 -0
  288. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/compositions/scene-3.html +36 -0
  289. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-2-multi-scene/hf-src/index.html +28 -71
  290. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/expected.json +3 -3
  291. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-1.html +52 -0
  292. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-2.html +99 -0
  293. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/compositions/scene-3.html +55 -0
  294. package/skills/remotion-to-hyperframes/assets/test-corpus/tier-3-data-driven/hf-src/index.html +13 -212
  295. package/skills/remotion-to-hyperframes/references/api-map.md +16 -16
  296. package/skills/remotion-to-hyperframes/references/limitations.md +7 -7
  297. package/skills/remotion-to-hyperframes/references/lottie.md +5 -7
  298. package/skills/remotion-to-hyperframes/references/media.md +3 -3
  299. package/skills/remotion-to-hyperframes/references/parameters.md +5 -0
  300. package/skills/remotion-to-hyperframes/references/sequencing.md +71 -18
  301. package/skills/slideshow/SKILL.md +4 -1
  302. package/skills/slideshow/references/standalone-harness.md +0 -3
  303. package/skills/talking-head-recut/SKILL.md +10 -1
  304. package/skills/hyperframes-core/references/review-loop.md +0 -41
  305. package/skills/media-use/scripts/lib/adopt.mjs +0 -128
  306. package/skills/media-use/scripts/lib/adopt.test.mjs +0 -95
  307. package/skills/media-use/scripts/lib/bgm-provider.mjs +0 -20
  308. package/skills/media-use/scripts/lib/brand-provider.mjs +0 -59
  309. package/skills/media-use/scripts/lib/bundled-sfx-provider.mjs +0 -108
  310. package/skills/media-use/scripts/lib/bundled-sfx-provider.test.mjs +0 -85
  311. package/skills/media-use/scripts/lib/cache.mjs +0 -150
  312. package/skills/media-use/scripts/lib/candidates.mjs +0 -91
  313. package/skills/media-use/scripts/lib/candidates.test.mjs +0 -155
  314. package/skills/media-use/scripts/lib/codex-provider.mjs +0 -161
  315. package/skills/media-use/scripts/lib/codex-provider.test.mjs +0 -34
  316. package/skills/media-use/scripts/lib/coverage.test.mjs +0 -117
  317. package/skills/media-use/scripts/lib/cube-build.mjs +0 -181
  318. package/skills/media-use/scripts/lib/cube-build.test.mjs +0 -80
  319. package/skills/media-use/scripts/lib/cube-validate.mjs +0 -198
  320. package/skills/media-use/scripts/lib/cube-validate.test.mjs +0 -125
  321. package/skills/media-use/scripts/lib/cutlist.test.mjs +0 -148
  322. package/skills/media-use/scripts/lib/duck.test.mjs +0 -118
  323. package/skills/media-use/scripts/lib/error-diffusion.test.mjs +0 -102
  324. package/skills/media-use/scripts/lib/freeze.mjs +0 -70
  325. package/skills/media-use/scripts/lib/freeze.test.mjs +0 -46
  326. package/skills/media-use/scripts/lib/grade-analyzer.mjs +0 -264
  327. package/skills/media-use/scripts/lib/grade-analyzer.test.mjs +0 -285
  328. package/skills/media-use/scripts/lib/heygen-cli.mjs +0 -191
  329. package/skills/media-use/scripts/lib/heygen-cli.test.mjs +0 -301
  330. package/skills/media-use/scripts/lib/heygen-search.mjs +0 -45
  331. package/skills/media-use/scripts/lib/heygen-search.test.mjs +0 -49
  332. package/skills/media-use/scripts/lib/heygen-video-provider.mjs +0 -127
  333. package/skills/media-use/scripts/lib/heygen-video-provider.test.mjs +0 -344
  334. package/skills/media-use/scripts/lib/image-provider.mjs +0 -44
  335. package/skills/media-use/scripts/lib/local-models.mjs +0 -318
  336. package/skills/media-use/scripts/lib/local-models.test.mjs +0 -220
  337. package/skills/media-use/scripts/lib/local-run.mjs +0 -78
  338. package/skills/media-use/scripts/lib/local-run.test.mjs +0 -121
  339. package/skills/media-use/scripts/lib/logo-provider.mjs +0 -222
  340. package/skills/media-use/scripts/lib/logo-provider.test.mjs +0 -137
  341. package/skills/media-use/scripts/lib/ltx-video-provider.mjs +0 -94
  342. package/skills/media-use/scripts/lib/ltx-video-provider.test.mjs +0 -316
  343. package/skills/media-use/scripts/lib/lut-preset-provider.mjs +0 -266
  344. package/skills/media-use/scripts/lib/lut-preset-provider.test.mjs +0 -176
  345. package/skills/media-use/scripts/lib/manifest.test.mjs +0 -388
  346. package/skills/media-use/scripts/lib/match.mjs +0 -47
  347. package/skills/media-use/scripts/lib/mflux-provider.mjs +0 -128
  348. package/skills/media-use/scripts/lib/mflux-provider.test.mjs +0 -174
  349. package/skills/media-use/scripts/lib/misses.mjs +0 -49
  350. package/skills/media-use/scripts/lib/misses.test.mjs +0 -78
  351. package/skills/media-use/scripts/lib/npx-sync.test.mjs +0 -50
  352. package/skills/media-use/scripts/lib/parakeet-words.test.mjs +0 -44
  353. package/skills/media-use/scripts/lib/prefs-store.test.mjs +0 -189
  354. package/skills/media-use/scripts/lib/probe.mjs +0 -39
  355. package/skills/media-use/scripts/lib/probe.test.mjs +0 -31
  356. package/skills/media-use/scripts/lib/providers.mjs +0 -5
  357. package/skills/media-use/scripts/lib/recipe-store.test.mjs +0 -367
  358. package/skills/media-use/scripts/lib/registry.mjs +0 -218
  359. package/skills/media-use/scripts/lib/registry.test.mjs +0 -261
  360. package/skills/media-use/scripts/lib/search.mjs +0 -24
  361. package/skills/media-use/scripts/lib/search.test.mjs +0 -32
  362. package/skills/media-use/scripts/lib/sfx-provider.mjs +0 -23
  363. package/skills/media-use/scripts/lib/specs.mjs +0 -132
  364. package/skills/media-use/scripts/lib/specs.test.mjs +0 -157
  365. package/skills/media-use/scripts/lib/stats.mjs +0 -117
  366. package/skills/media-use/scripts/lib/stats.test.mjs +0 -165
  367. package/skills/media-use/scripts/lib/telemetry.test.mjs +0 -373
  368. package/skills/media-use/scripts/lib/tts-local-provider.mjs +0 -92
  369. package/skills/media-use/scripts/lib/tts-local-provider.test.mjs +0 -72
  370. package/skills/media-use/scripts/lib/usage.mjs +0 -57
  371. package/skills/media-use/scripts/lib/usage.test.mjs +0 -54
  372. package/skills/media-use/scripts/lib/voice-provider.mjs +0 -57
  373. package/skills/media-use/scripts/lib/voice-provider.test.mjs +0 -46
  374. package/skills/media-use/scripts/resolve.test.mjs +0 -1120
  375. /package/skills/{hyperframes-core → hyperframes}/references/subagent-dispatch.md +0 -0
@@ -1,6 +1,6 @@
1
1
  # Composition Patterns
2
2
 
3
- How to architect a project — when to inline everything in one HTML, when to split into sub-compositions, what the `index.html` orchestrator looks like at scale, and the common sub-composition archetypes seen in real projects. Pair with `minimal-composition.md` (single-file shape) and `sub-compositions.md` (mechanics of a sub-comp file).
3
+ How to architect a project: the `index.html` orchestrator at scale, and the common sub-composition archetypes. Pair with `minimal-composition.md` (single-file shape) and `sub-compositions.md` (mechanics of a sub-comp file).
4
4
 
5
5
  ## Two Architectures
6
6
 
@@ -13,27 +13,6 @@ How to architect a project — when to inline everything in one HTML, when to sp
13
13
 
14
14
  Both architectures use the same runtime contract — `data-*` attributes + `window.__timelines[id]`. The choice is structural, not behavioral.
15
15
 
16
- ### Pick monolithic when
17
-
18
- - The whole video is one continuous scene with no hard cuts.
19
- - Scenes share heavy state (one canvas/WebGL context spanning the whole video, a single SVG that morphs across all beats).
20
- - Total scope is small (~200–400 lines of markup + script).
21
- - No scene is reused across projects.
22
-
23
- ### Pick modular when
24
-
25
- - The video has clear scene cuts — each scene is its own segment of the timeline.
26
- - Some scenes are large (>100 lines of markup or significant scripted animation).
27
- - A scene is reusable (kinetic intro, end-card logo lockup, a transition).
28
- - The video has a continuous audio track over multiple visual segments. Keep audio at the root, visual segments as sub-comps.
29
- - You want to author/iterate on scenes in isolation (preview a single sub-comp file directly).
30
-
31
- ### Refactor between them
32
-
33
- Conversion is mechanical and reversible. To lift a monolithic scene into a sub-comp: wrap the scene's markup + scoped CSS + its slice of the parent timeline into a `<template>`, save as `compositions/<scene>.html`, replace the inline content in `index.html` with a slot `<div data-composition-src="compositions/<scene>.html">`, and have the sub-comp register its own timeline at `window.__timelines["<scene>"]`. The parent timeline shrinks accordingly.
34
-
35
- If a monolithic project is approaching three or more scene cuts, prefer modularizing _before_ adding the next scene. Mixed projects where some scenes are inline and siblings are in `compositions/` are the hardest to maintain.
36
-
37
16
  ## Modular Orchestrator Pattern
38
17
 
39
18
  When using sub-compositions, `index.html` should be **thin**. Its job is to declare slots, lay them out in time, mount the audio track, and register a (usually empty) root timeline. All scene animation lives inside the sub-comps.
@@ -51,8 +30,8 @@ When using sub-compositions, `index.html` should be **thin**. Its job is to decl
51
30
  }
52
31
  #root {
53
32
  position: relative;
54
- width: 1920px;
55
- height: 1080px;
33
+ width: 100%;
34
+ height: 100%;
56
35
  overflow: hidden;
57
36
  }
58
37
  /* Sub-comp slots stretch to fill the root. */
@@ -110,7 +89,6 @@ When using sub-compositions, `index.html` should be **thin**. Its job is to decl
110
89
  </div>
111
90
 
112
91
  <script>
113
- window.__timelines = window.__timelines || {};
114
92
  window.__timelines["root"] = gsap.timeline({ paused: true });
115
93
  </script>
116
94
  </body>
@@ -162,7 +140,6 @@ The reason to reach for it: a sub-comp timeline **cannot** drive host elements (
162
140
 
163
141
  <script>
164
142
  // MAIN timeline drives the host video. Global time: scene starts at 20.
165
- window.__timelines = window.__timelines || {};
166
143
  const main = window.__timelines["main"];
167
144
  main.fromTo(
168
145
  "#final-video",
@@ -182,7 +159,6 @@ The reason to reach for it: a sub-comp timeline **cannot** drive host elements (
182
159
  style="position:absolute; inset:0; pointer-events:none;"
183
160
  >
184
161
  <script>
185
- window.__timelines = window.__timelines || {};
186
162
  const tl = gsap.timeline({ paused: true });
187
163
  // animate ONLY this sub-comp's own elements here (labels, frame, overlays)
188
164
  window.__timelines["final-anim"] = tl;
@@ -216,7 +192,6 @@ When several beat-level scenes share continuous state — a chat thread that gro
216
192
  <div class="phase" id="phase-b">…</div>
217
193
  <div class="phase" id="phase-c">…</div>
218
194
  <script>
219
- window.__timelines = window.__timelines || {};
220
195
  const tl = gsap.timeline({ paused: true });
221
196
  tl.set("#phase-a", { opacity: 1 }, 0);
222
197
  tl.to("#phase-a", { opacity: 0, duration: 0.4 }, 3.0);
@@ -247,17 +222,3 @@ Audio always lives at the host (`index.html`) as a root-level `<audio>` so playb
247
222
  | Audio at root | `data-track-index` well above visual tracks | `10` while visuals use `1` |
248
223
 
249
224
  The `-template` suffix on `<template>` is conventional but not required — the runtime extracts contents from whichever `<template>` is in `<body>`, regardless of id. The prefix on inner element ids is the only safeguard against id collisions when multiple sub-comps are mounted into the same host page at once.
250
-
251
- ## Editing Existing Projects
252
-
253
- Before adding or modifying scenes, identify which architecture is in use:
254
-
255
- ```bash
256
- ls compositions/ 2>/dev/null && echo "modular" || echo "monolithic"
257
- ```
258
-
259
- - In a **monolithic** project, add new scenes as inline `<section class="clip">` elements with a deliberate `data-start` and a sensible `data-track-index`, and extend the existing single timeline.
260
- - In a **modular** project, match the pattern: add a new file under `compositions/`, add a slot in `index.html`, keep the root timeline thin. Do **not** start inlining new scenes into `index.html` when sibling scenes are sub-comps — the inconsistency is the worst of both worlds.
261
- - If a monolithic project needs a third or fourth scene cut, lift each scene into a sub-comp before adding more. The conversion is mechanical (see "Refactor between them" above).
262
-
263
- When picking the slot's `data-start`/`data-duration`, prefer continuing the existing sequencing convention (adjoining starts, deliberate overlaps for cross-fades). Don't introduce a new track index unless you actually need parallel visual layers — most sequential-scene projects use exactly one visual track.
@@ -2,6 +2,8 @@
2
2
 
3
3
  Use these copyable contracts after `tracks-and-clips.md`. Global math: **consumed source = timeline duration × rate**; **natural timeline duration = remaining source / rate**.
4
4
 
5
+ Before any edit, run `npx hyperframes timeline` (add `--json` for a machine-readable list) to see the project's tracks and clips instead of reading the HTML.
6
+
5
7
  These recipes keep sound on a separate `<audio>` element with the `<video>` muted, which is the pattern to reach for when picture and sound are cut independently. An unmuted `<video>` that declares `data-has-audio="true"` is also mixed, so a separate track is a choice, not a requirement.
6
8
 
7
9
  **Every `<video>` and `<audio>` below carries an `id`, and that is not cosmetic**: `lint` errors with `media_missing_id` on timed media without one, and an id-less `<audio>` is never picked up by the mixer, so the render comes out silent. Keep the ids when you copy a recipe.
@@ -47,7 +49,7 @@ These recipes keep sound on a separate `<audio>` element with the `<video>` mute
47
49
  ></audio>
48
50
  ```
49
51
 
50
- Timeline math: B starts at A start + duration. Source math: each range starts at `data-media-start`; consumed source = timeline duration × rate. Audio follows: duplicate matching `<audio>` ranges/timing. Owner: `/hyperframes-core`. Limit: adjacent windows only; author the two windows edge to edge.
52
+ Timeline math: B starts at A start + duration. Source math: each range starts at `data-media-start`; consumed source = timeline duration × rate. Audio follows: duplicate matching `<audio>` ranges/timing. Owner: `/hyperframes-core`. Limit: adjacent windows only; author the two windows edge to edge. Same-track overlap is valid; both clips paint in CSS order.
51
53
 
52
54
  ## Trim in/out
53
55
 
@@ -227,7 +229,7 @@ Timeline math: the still owns its hold duration. Source math: final-source frame
227
229
  ></video>
228
230
  ```
229
231
 
230
- Timeline math: duration is authored timeline time. Source math: consumed source = timeline duration × rate; natural timeline duration = remaining source / rate. Audio follows: matching separate audio track uses the same constant rate. Owner: `/hyperframes-core`. Limit: normalized 0.1..5 constant only; no speed ramp envelope.
232
+ Timeline math: duration is authored timeline time. Source math: consumed source = timeline duration × rate; natural timeline duration = remaining source / rate. Audio follows: matching separate audio track uses the same constant rate. Owner: `/hyperframes-core`. Limit: normalized 0.1..10. For a speed ramp put a `rate` lane in `data-automation`, e.g. `{"version":1,"lanes":[{"target":"rate","points":[{"t":0,"v":1},{"t":2,"v":4}]}]}`; it wins over the constant.
231
233
 
232
234
  ## Zoom / punch
233
235
 
@@ -314,11 +316,10 @@ Timeline math: overlap placed clips for the 0.5s handoff. Source math: each clip
314
316
  ></audio>
315
317
  <script>
316
318
  const tl = gsap.timeline({ paused: true });
317
- tl.set("#b-visual", { autoAlpha: 0 }, 0)
318
- .to("#a-visual", { autoAlpha: 0, duration: 0.5 }, 2.5)
319
- .to("#b-visual", { autoAlpha: 1, duration: 0.5 }, 2.5);
320
- window.__timelines = window.__timelines || {};
321
- window.__timelines.main = tl;
319
+ tl.set("#b-visual", { opacity: 0 }, 0)
320
+ .to("#a-visual", { opacity: 0, duration: 0.5 }, 2.5)
321
+ .to("#b-visual", { opacity: 1, duration: 0.5 }, 2.5);
322
+ window.__timelines["main"] = tl;
322
323
  </script>
323
324
  ```
324
325
 
@@ -339,6 +340,8 @@ Timeline math: distinct tracks overlap by 0.5s with opposing opacity envelopes.
339
340
 
340
341
  Timeline math: lane `t` is clip-local authored time: fade-in 0–1, duck down 2–2.2, hold 2.2–3, duck up 3–3.2, fade-out 4–5. Source math: source selection still uses core attributes. Audio follows: the explicit down-hold-up envelope affects this separate audio track. Owner: `/hyperframes-audio`. Limit: automation is not source retiming.
341
342
 
343
+ **One rule for volume over time: use the lane.** `lint` accepts a timeline tween on `volume` too, but when a track has both, the lane wins and the tween is ignored (`audio_volume_double_automation`). Never add a lane to a track that already has a `volume` tween, and never add a tween to a track that has a lane; edit the one that exists. To ramp 0.1 to 0.5 over ten seconds, write `{"t":0,"v":0.1},{"t":10,"v":0.5}`. `t` is seconds from the clip's own start, so a ramp past `data-duration` never finishes: check the clip's length before choosing the times. `data-volume` stays as the static level of the clip and combines with nothing else you author here.
344
+
342
345
  ## Audio alignment
343
346
 
344
347
  ```html
@@ -365,3 +368,124 @@ Timeline math: lane `t` is clip-local authored time: fade-in 0–1, duck down 2
365
368
  ```
366
369
 
367
370
  Timeline math: picture and sound share start/duration. Source math: both consume four source seconds. Audio follows: identical timing, range, and rate on the separate audio track. Owner: `/hyperframes-core` + `/hyperframes-audio`. Limit: no waveform auto-sync or drift correction.
371
+
372
+ ## Align a sound to an on-screen event
373
+
374
+ ```html
375
+ <audio
376
+ id="sfx-click-3"
377
+ src="click.mp3"
378
+ data-start="7.48"
379
+ data-duration="0.07"
380
+ data-track-index="103"
381
+ data-volume="0.85"
382
+ ></audio>
383
+ ```
384
+
385
+ Timeline math: an audio element in the root composition has `data-start` in absolute root time; audio inside a scene file uses scene-local time and the host's `data-start` is added for you. An event inside a sub-composition happens at the host's `data-start` plus the event's local time in that sub-composition's own timeline, so `data-start = host start + local time`. Move only the audio's `data-start`; leave the picture alone. Source math: if the sound's transient is not at the file's first sample, subtract that lead-in from `data-start` (or trim it with `data-media-start`). Audio follows: nothing links audio to picture, so re-derive after every retime of the host. Owner: `/hyperframes-core`. Limit: no waveform auto-sync; for a beat grid use `hyperframes beats` and place each start on a beat time.
386
+
387
+ ## Copy a group of clips to another time
388
+
389
+ ```html
390
+ <audio
391
+ id="sfx-click-0"
392
+ src="click.mp3"
393
+ data-start="1.6"
394
+ data-duration="0.07"
395
+ data-track-index="100"
396
+ ></audio>
397
+ <audio
398
+ id="sfx-type-0"
399
+ src="typenew.mp3"
400
+ data-start="7.8"
401
+ data-duration="0.57"
402
+ data-track-index="109"
403
+ ></audio>
404
+ <audio
405
+ id="sfx-click-0-copy"
406
+ src="click.mp3"
407
+ data-start="41.6"
408
+ data-duration="0.07"
409
+ data-track-index="186"
410
+ ></audio>
411
+ <audio
412
+ id="sfx-type-0-copy"
413
+ src="typenew.mp3"
414
+ data-start="47.8"
415
+ data-duration="0.57"
416
+ data-track-index="187"
417
+ ></audio>
418
+ ```
419
+
420
+ Timeline math: pick the clips first and say which ones you picked (by id) if the request does not match the file exactly; then add one `delta` to every member's `data-start`, so relative spacing is preserved (here `delta = 40`). Give each copy a new unique `id` and the next unused `data-track-index`; keep `src`, `data-duration`, `data-media-start`, `data-volume` and any `data-automation` as they are. Leave the originals untouched. Check the copies still end inside the composition's duration. Owner: `/hyperframes-core`. Limit: copies of a `<video>` or a sub-composition host follow the same rule, and a copied sub-composition needs its own host `id`.
421
+
422
+ ## Add media (image, video, audio)
423
+
424
+ Write what Studio writes when a person drops a file on the timeline, so an agent-added clip behaves the same as a dropped one; the one difference is that video and audio need no `data-duration`. Studio's source of truth is `DEFAULT_TIMELINE_ASSET_DURATION` in `packages/studio/src/utils/studioHelpers.ts` and `buildTimelineAssetInsertHtml` in `packages/studio/src/utils/timelineAssetDrop.ts`; a test keeps this section equal to them.
425
+
426
+ - **Image: `data-duration` is optional and defaults to 3 seconds**, the same as a dropped image, because a still has no length of its own. Write it only for another length. A test keeps the 3 equal to the default in code.
427
+ - **Video and audio: `data-start` is enough.** The length comes from the media itself. An authored `data-duration` shorter than the file is a trim, never a requirement; leave it out unless the request asks for a shorter clip.
428
+ - **Start: the playhead or the requested time, never a silent `0`.** Studio's asset-panel Add uses the playhead time on track `0`; a drop uses the drop point.
429
+ - Give every clip `id`, `class="clip"`, `data-start` and `data-track-index`. Video is `muted playsinline`; audio carries `data-volume="1"`.
430
+ - Then make sure the root composition's `data-duration` is at least the clip's end (`data-start` plus its length: 3 for an image unless you set another, the media's length for video and audio): Studio raises a declared root duration to cover the new clip, so an agent must too, or the clip lies past the end and never plays.
431
+ - **Images and video fill the whole frame**: absolutely positioned at `left: 0; top: 0`, `width` and `height` equal to the composition's `data-width` and `data-height`, `object-fit: contain`. Studio does not know a dropped file's natural size, so it does not centre a smaller one.
432
+ - `z-index` is the number of top-level clips already in that file plus one (at least `1`); later clips stack above earlier ones.
433
+ - Several files dropped together share the drop's track and run end to end.
434
+
435
+ ```html
436
+ <img
437
+ id="photo"
438
+ class="clip"
439
+ src="assets/photo.png"
440
+ data-start="4"
441
+ data-track-index="1"
442
+ style="position: absolute; left: 0px; top: 0px; width: 1920px; height: 1080px; object-fit: contain; z-index: 2"
443
+ />
444
+ ```
445
+
446
+ ```html
447
+ <video
448
+ id="broll"
449
+ class="clip"
450
+ src="assets/broll.mp4"
451
+ data-start="4"
452
+ data-track-index="2"
453
+ muted
454
+ playsinline
455
+ style="position: absolute; left: 0px; top: 0px; width: 1920px; height: 1080px; object-fit: contain; z-index: 3"
456
+ ></video>
457
+ ```
458
+
459
+ ```html
460
+ <audio
461
+ id="whoosh"
462
+ class="clip"
463
+ src="assets/whoosh.mp3"
464
+ data-start="4"
465
+ data-track-index="3"
466
+ data-volume="1"
467
+ ></audio>
468
+ ```
469
+
470
+ Inside a sub-composition file, `data-start` is scene-local (see `## Align a sound to an on-screen event`). Owner: `/hyperframes-core`.
471
+
472
+ ## Swap a media file
473
+
474
+ ```html
475
+ <video
476
+ id="hero"
477
+ src="assets/product-v2.mp4"
478
+ data-start="2"
479
+ data-duration="4"
480
+ data-media-start="0"
481
+ data-track-index="0"
482
+ muted
483
+ playsinline
484
+ ></video>
485
+ ```
486
+
487
+ Timeline math: change only `src`. Source math: reset `data-media-start` to the offset you want in the NEW file, and set `data-duration` no longer than the new file's remaining length (probe it with `ffprobe`). Audio follows: a separate `<audio>` that pointed at the old file needs the same `src` swap. Keep `id`, `data-start`, `data-track-index` and any `data-automation` so nothing else moves. Run `lint`: `audio_src_not_found` and `media_src_kind_mismatch` catch a wrong path or kind. Owner: `/hyperframes-core`. Limit: a still swapped for a video (or the reverse) is a tag change, not a swap.
488
+
489
+ ## Split a section and change its speed
490
+
491
+ Timeline math: a section that is a sub-composition or a group of clips has no `data-playback-rate` of its own to set; split it by giving each half its own host or clips and shift everything after the cut by the length change. New length of a part = old length / rate. Every later `data-start` (clips, audio, root-timeline tweens) moves by the same delta. Source math: `<video>` and `<audio>` parts use `data-playback-rate` (0.1 to 10, constant) per the constant-speed recipe above, with matching audio. A speed ramp (a rate that changes within one clip) is a `rate` lane in `data-automation` on the `<video>`/`<audio>`; see `docs/reference/speed-ramps`. Say which you did.
@@ -14,7 +14,7 @@ Every renderable composition needs one root element:
14
14
  | `data-fps` | No | Optional frame rate hint. CLI render flags can override output fps. |
15
15
  | `data-composition-variables` | No | JSON array of variable declarations (on `<html>`). See `variables-and-media.md`. |
16
16
 
17
- \*`data-duration` is optional whenever the runtime can auto-infer duration: a registered GSAP timeline, a finite CSS animation, a finite WAAPI `element.animate()`, or a registered Lottie animation. It is **required** for Three.js (no auto-inference), for infinite/unbounded CSS or WAAPI animations, and for any composition with no GSAP timeline and no animation signal at all. `npx hyperframes lint` enforces this (`root_composition_missing_duration_source`). See `determinism-rules.md` → "Duration Contract For Non-GSAP Runtimes" for the per-runtime breakdown.
17
+ \*`data-duration` is optional whenever the runtime can auto-infer duration: a registered GSAP timeline, a finite CSS animation, a finite WAAPI `element.animate()`, a registered Lottie animation, or timed clips (the root then ends at its latest clip end, and lint only warns `root_composition_duration_derived`). It is **required** for Three.js (no auto-inference), for infinite/unbounded CSS or WAAPI animations, and for any composition with no GSAP timeline, no animation signal and no timed clip with a length. `npx hyperframes lint` enforces this (`root_composition_missing_duration_source`). Per-runtime inference lives in `hyperframes-animation/adapters/`.
18
18
 
19
19
  The root should be `position: relative`, have explicit pixel dimensions, and hide overflow unless intentionally composing outside the frame.
20
20
 
@@ -22,19 +22,19 @@ The root should be `position: relative`, have explicit pixel dimensions, and hid
22
22
 
23
23
  **`data-start` is what makes an element a clip.** The runtime collects `[data-start]` and drives visibility off that attribute, so any element carrying it is timed.
24
24
 
25
- `class="clip"` is a **convention, not a requirement**: the runtime never reads it. Keep writing it, because the scaffold's shared `.clip { position: absolute; inset: 0 }` rule is what gives a scene its full-frame box, Studio uses it as an edit hint, and `lint` warns (`timed_element_missing_clip_class`) when a timed element lacks it. If you drop the class you must supply that layout yourself. Omit it on `<video>` and `<audio>`.
25
+ `class="clip"` is a **convention, not a requirement**: the runtime never reads it. Keep writing it: drop it and the full-frame box collapses unless you supply that layout yourself, Studio uses it as an edit hint, and `lint` warns (`timed_element_missing_clip_class`) when a timed element lacks it. Omit it on `<video>` and `<audio>`.
26
26
 
27
27
  **Nesting is allowed.** A timed element inside a wrapper is still timed, and a timed ancestor clamps its descendants: a child cannot be visible while its timed ancestor is hidden. Direct children of the root get automatic layout (see "Root-level clips get automatic layout" below); nested ones do not, so give them their own positioning.
28
28
 
29
- | Attribute | Required | Meaning |
30
- | ------------------ | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
31
- | `id` | Yes on `<video>`/`<audio>`, else recommended | `lint` errors with `media_missing_id` on media without one, and an id-less `<audio>` is never mixed, so the render is **silent**. Elsewhere it is a warning (`studio_missing_editable_id`): Studio needs a stable edit target, and timeline targets reference it. |
32
- | `data-start` | Yes | Start time in seconds, or a supported clip-time reference. This attribute is what marks the element as timed. |
33
- | `data-duration` | Required for `div`, `img`, and sub-compositions | Duration in seconds. Video/audio can default to media duration when known. Without any resolvable duration the element has no end and stays visible for the rest of the composition. |
34
- | `data-track-index` | No | Studio timeline lane, display only. The render never reads it, and clips on one track may overlap in time. Absent, the parser defaults it and Studio lays out one lane per clip. Two `<audio>` elements on the same index that overlap in time raise a `lint` warning. |
35
- | `data-media-start` | No | Offset into the media source, in seconds. |
36
- | `data-volume` | No | Static audio gain, default `1` (0 dB). `0` is silence and values above `1` boost, up to `3.98` (+12 dB) — Studio's fader writes this. For fades, animate `volume` on the timeline instead (see `variables-and-media.md`); a tween's own values replace this baseline entirely. |
37
- | `data-has-audio` | No (`<video>` only) | `"true"` to declare the video carries an audio track when auto-detection would miss it. |
29
+ | Attribute | Required | Meaning |
30
+ | ------------------ | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31
+ | `id` | Yes on `<video>`/`<audio>`, else recommended | `lint` errors with `media_missing_id` on media without one, and an id-less `<audio>` is never mixed, so the render is **silent**. Elsewhere it is a warning (`studio_missing_editable_id`): Studio needs a stable edit target, and timeline targets reference it. |
32
+ | `data-start` | Yes | Start time in seconds, or a supported clip-time reference. This attribute is what marks the element as timed. |
33
+ | `data-duration` | Required for `div` and sub-compositions | Duration in seconds. An `img` with `data-start` defaults to 3 s; video/audio default to the source length (less the playback offset, over the rate) once known; an authored value trims. Without any resolvable duration the element has no end and stays visible for the rest of the composition. |
34
+ | `data-track-index` | No | Studio timeline lane, display only. The render never reads it, and clips on one track may overlap in time. Absent, the parser defaults it and Studio lays out one lane per clip. Two `<audio>` elements on the same index that overlap in time raise a `lint` warning. |
35
+ | `data-media-start` | No | Offset into the media source, in seconds. |
36
+ | `data-volume` | No | Static audio gain, default `1` (0 dB). `0` is silence and values above `1` boost, up to `3.98` (+12 dB) — Studio's fader writes this. For fades and ducking, use the `data-automation` volume lane (see `creator-editing-recipes.md`). |
37
+ | `data-has-audio` | No (`<video>` only) | `"true"` to declare the video carries an audio track when auto-detection would miss it. |
38
38
 
39
39
  **The visibility window is half-open: `[start, start + duration)`.** A clip shows while `start ≤ t < start + duration` and is hidden at exactly `t = start + duration`. Land an animation's resolved end state slightly **before** `data-duration`, not on it, or its last frame is never rendered. Two clips can therefore be authored back to back (`b.start === a.start + a.duration`) with no overlapping frame.
40
40
 
@@ -15,18 +15,7 @@ For GSAP:
15
15
  - **Do not** call `tl.play()` for render-critical motion.
16
16
  - **Do not** create empty tweens only to set duration; use `data-duration` on the clip instead.
17
17
 
18
- Use the `hyperframes-animation` skill for tween syntax, position parameters, eases, and performance rules.
19
-
20
- ### Duration Contract For Non-GSAP Runtimes
21
-
22
- The render engine needs a positive total duration before it will capture a single frame — without one, capture fails outright with "Composition has zero duration." A GSAP timeline supplies this automatically. CSS, WAAPI, and Lottie compositions have no timeline object, so the runtime infers duration itself:
23
-
24
- - **CSS**: longest `animation-delay` + `animation-duration` × finite `animation-iteration-count` across animated elements (offset by each element's `data-start`). `animation-iteration-count: infinite` cannot be inferred.
25
- - **WAAPI**: longest `element.animate()` effect's `getComputedTiming().endTime`. Infinite `iterations` cannot be inferred.
26
- - **Lottie**: the registered animation's native length (`totalFrames / frameRate`, or the dotLottie player's own `duration`) — always finite regardless of `loop`.
27
- - **Three.js**: **not inferable**. The `three` adapter only forwards time via `hf-seek` — it has no `AnimationClip`/`AnimationMixer` inspection.
28
-
29
- `data-duration` on the root `[data-composition-id]` element is therefore optional whenever every non-GSAP animation on the page is finite (CSS/WAAPI with finite iteration counts, or Lottie). It is **required** when: the composition has an infinite/unbounded CSS or WAAPI animation, the composition uses Three.js, or there is no GSAP timeline and no animation signal at all for any adapter to discover. `npx hyperframes lint` enforces exactly this (`root_composition_missing_duration_source`) — see the runtime/adapter-specific docs under `hyperframes-animation/adapters/` for the full contract per runtime.
18
+ Use the `hyperframes-animation` skill for tween syntax, position parameters, eases, and performance rules. Non-GSAP duration inference lives in `hyperframes-animation/adapters/`.
30
19
 
31
20
  ## Determinism Rules
32
21
 
@@ -40,7 +29,7 @@ Rendered frames must be reproducible from the requested time. Do **not** use any
40
29
 
41
30
  Also avoid:
42
31
 
43
- - Tweening `display` or raw `visibility` **on a clip element**: HyperFrames timing owns a clip's visibility, and `lint` rejects it. Use GSAP `autoAlpha` (it interpolates opacity and flips visibility only at the hidden endpoint) or a zero-duration `tl.set(..., { visibility: "hidden" | "visible" })` at an explicit beat boundary for a deterministic hard kill. Animating a clip element's ordinary visual properties (`opacity`, transforms, `filter`, …) is fine and the shipped catalog does it constantly; what is forbidden is taking over its visibility.
32
+ - Tweening `display`, raw `visibility`, or `autoAlpha` **on a clip element**: HyperFrames timing owns a clip's visibility, and `lint` rejects it (`gsap_animates_clip_element`). Fade with `opacity`, or tween a child wrapper. Do not tween `class="clip"`.
44
33
  - There is no fixed allowlist of animatable properties. `lint` enforces a **denylist**, so `filter`, `clipPath`, `strokeDashoffset`, `width`, `height` and similar are all legitimate targets. Prefer transforms and opacity where you have the choice, for performance rather than correctness. The per-runtime detail lives in `hyperframes-animation/adapters/`.
45
34
  - Animating the same property on the same element from multiple timelines at the same time — GSAP's overwrite behavior is order-dependent and can flip between renders.
46
35
 
@@ -62,9 +51,3 @@ Build the visible end-state in static HTML and CSS first, then animate from/to t
62
51
  - **Do not** use `<br>` in body text. Forced breaks ignore the actual rendered font width and produce an extra break when the line already wraps naturally, causing overlap. Let text wrap via `max-width`. Exception: short display titles where each word is deliberately on its own line.
63
52
  - **Transformed elements must be block-level + sized.** `transform`/`scaleX`/`scaleY` is a no-op on an inline `<span>`, and scaling an auto-width (0px) element shows nothing → invisible bars/fills. Give them `display: block`/`inline-block`/flex-item **and** a real `width`/`height` (e.g. `width: 100%` inside a sized parent). _(Silent — automated gates may miss it.)_
64
53
  - **Absolutely-positioned decoratives that pulse or overshoot** (`yoyo` scale, `back.out`) need clearance at their **peak** size and must not straddle an `overflow: hidden` edge — else they overlap a neighbor or get clipped. Position for the largest frame, not the resting one. _(silent.)_
65
-
66
- ## Why This Matters
67
-
68
- The renderer takes a time value and produces a pixel buffer. There is no notion of "playback" — every frame is a fresh seek. Any state that depends on having reached this frame _through_ a prior frame (timers, accumulated state, event-driven animations) will desync when the renderer samples out of order or in parallel.
69
-
70
- If you find yourself reaching for `setTimeout`, `requestAnimationFrame`, or `addEventListener` to drive a visual, rebuild it as a tween on the timeline instead.
@@ -2,10 +2,6 @@
2
2
 
3
3
  For full-frame motion (continuous backgrounds, color washes, full-bleed visual states that span multiple clips), prefer a **shared background layer + transparent timed content layers** over stacked opaque scene backgrounds.
4
4
 
5
- ## Why
6
-
7
- Stacking opaque scene divs means every scene change has to repaint the entire frame, every cross-scene visual continuity has to be faked, and every "global" state (a hue shift, a vignette, a film grain) has to be duplicated on every scene. A shared background layer driven by the seekable timeline gives you one continuous visual surface and makes scenes themselves cheap and transparent.
8
-
9
5
  ## Pattern
10
6
 
11
7
  ```html
@@ -48,7 +44,6 @@ Stacking opaque scene divs means every scene change has to repaint the entire fr
48
44
  </div>
49
45
 
50
46
  <script>
51
- window.__timelines = window.__timelines || {};
52
47
  const tl = gsap.timeline({ paused: true });
53
48
 
54
49
  // Drive the shared background from the seekable timeline.
@@ -70,7 +65,3 @@ Stacking opaque scene divs means every scene change has to repaint the entire fr
70
65
  - **Drive global state from the shared layer.** Hue shifts, vignettes, grain, film-look filters — animate them once on the shared layer, not per-scene.
71
66
  - **Do not animate visibility on `.clip` elements.** HyperFrames already shows/hides clips based on `data-start` and `data-duration`. Animating `display` / `visibility` on the clip itself races with the framework's own show/hide. Animate a _child wrapper_ inside the clip instead.
72
67
  - **Verify intentional overflow with snapshots.** Before adding `data-layout-allow-overflow` to silence an inspect warning, run `npx hyperframes snapshot` and confirm the overflow is what you want.
73
-
74
- ## When Not to Use This Pattern
75
-
76
- If scenes really are visually disjoint — hard cuts between distinct color worlds with no continuity — the stacked-opaque pattern is fine. The shared-background pattern is for compositions where the background **is part of the motion language**, not just backdrop.
@@ -19,8 +19,8 @@ The smallest renderable HyperFrames composition — a standalone (top-level) roo
19
19
  }
20
20
  #root {
21
21
  position: relative;
22
- width: 1920px;
23
- height: 1080px;
22
+ width: 100%;
23
+ height: 100%;
24
24
  overflow: hidden;
25
25
  }
26
26
  .clip {
@@ -48,8 +48,7 @@ So `<template>` is not just a wrapper — it is the **transport container**. If
48
48
  <template>
49
49
  <!-- EVERYTHING the runtime needs goes here: styles, markup, scripts -->
50
50
  <style>
51
- /* Root: style by #root, never a class. (At render the CSS is scoped to
52
- data-composition-id, so a class on the root stops matching — see Pitfall 3.) */
51
+ /* Root: style by #root, never a class. lint: subcomposition_root_styled_by_class. See Pitfall 3. */
53
52
  #root {
54
53
  position: absolute;
55
54
  inset: 0;
@@ -63,7 +62,6 @@ So `<template>` is not just a wrapper — it is the **transport container**. If
63
62
  </div>
64
63
 
65
64
  <script>
66
- window.__timelines = window.__timelines || {};
67
65
  const tl = gsap.timeline({ paused: true });
68
66
  // ... build timeline ...
69
67
  window.__timelines["data-chart"] = tl;
@@ -77,7 +75,7 @@ Contrast with **standalone** compositions, which put the root directly in `<body
77
75
 
78
76
  ## Common pitfalls that pass static checks but break at render
79
77
 
80
- Static file checks cannot prove the **cross-file mount contract**. These failures appear only when the runtime mounts the sub-composition. Watch for them at author time and verify with the pre-render snapshot checklist below.
78
+ Static file checks cannot prove the **cross-file mount contract**. These failures appear only when the runtime mounts the sub-composition.
81
79
 
82
80
  ### Pitfall 1 — `<style>` in `<head>` instead of inside `<template>`
83
81
 
@@ -137,68 +135,7 @@ Static file checks cannot prove the **cross-file mount contract**. These failure
137
135
 
138
136
  ### Pitfall 3 — Styling the root by a class instead of `#root`
139
137
 
140
- ```html
141
- <!-- ❌ WRONG — class on the root, stylesheet keyed off it -->
142
- <template>
143
- <style>
144
- .frame {
145
- position: absolute;
146
- inset: 0;
147
- background: #faf9f5;
148
- }
149
- .frame .title {
150
- font-size: 120px;
151
- }
152
- </style>
153
- <div id="root" class="frame" data-composition-id="03-scene" ...>
154
- <div class="title">…</div>
155
- </div>
156
- </template>
157
-
158
- <!-- ✅ RIGHT — root styled by #root, descendants by plain selectors -->
159
- <template>
160
- <style>
161
- #root {
162
- position: absolute;
163
- inset: 0;
164
- background: #faf9f5;
165
- }
166
- .title {
167
- font-size: 120px;
168
- }
169
- </style>
170
- <div id="root" data-composition-id="03-scene" ...>
171
- <div class="title">…</div>
172
- </div>
173
- </template>
174
- ```
175
-
176
- **Why this happens:** when sub-compositions are inlined into one composited render, the compiler **scopes each file's CSS to its own `data-composition-id`** so scenes can't leak styles into each other: every rule `S` becomes `[data-composition-id="<id>"] S` (a _descendant_ selector). A rule whose leftmost selector is the **root's own class** (`.frame`) therefore becomes `[data-composition-id="<id>"] .frame`, which cannot match the scoped element itself. `#root` is special-cased by the scoper and keeps matching the root; plain descendant selectors (`.title`) match normally. The per-scene class namespace is also just redundant, since the `data-composition-id` scope already isolates each scene's styles.
177
-
178
- **What actually happens today:** the render no longer drops these rules. Since #1886 the producer preserves the authored root as a `data-hf-inner-root` wrapper _inside_ the scoped element, so a class on the root still matches as a descendant and preview and render agree. The regression is pinned by `packages/producer/tests/sub-comp-class-selector/`.
179
-
180
- **So why still use `#root`?** Because `lint` rejects the class form outright (`subcomposition_root_styled_by_class`, **error**), and it is the pattern the registry blocks model (e.g. `apple-money-count`). Treat this as a lint constraint you must satisfy, not as a render bug you are avoiding. If you inherited a composition that styles its root by class, it renders correctly; you still have to convert it to `#root` to get a green `lint`.
181
-
182
- ### Verification checklist before render
183
-
184
- ```bash
185
- # For every sub-composition file in compositions/:
186
- # 1) <style> + <script> + main markup all live INSIDE <template>
187
- grep -n "<style\|<script\|<template" compositions/<scene>.html
188
- # → first <style>/<script>/<div data-composition-id> should be AFTER <template>, before </template>
189
-
190
- # 2) host data-composition-id == internal data-composition-id == window.__timelines key
191
- grep "data-composition-id" index.html
192
- grep "data-composition-id\|__timelines\[" compositions/<scene>.html
193
- # → all three strings must match exactly per scene
194
-
195
- # 3) the root is styled by #root, not by a class on the data-composition-id element
196
- grep -n 'data-composition-id=' compositions/<scene>.html
197
- # → that element should NOT also carry a class="…" that the <style> keys off
198
- # (e.g. `.frame { … }`); scoping drops it. Style the root via #root. See Pitfall 3.
199
- ```
200
-
201
- For the runtime end-to-end check (a fast `snapshot` pass + per-scene frame eyeball), see the **Visual smoke test** step in `hyperframes-cli`'s Minimum Completion Gate — that is the only gate that catches these three pitfalls.
138
+ `lint` still errors (`subcomposition_root_styled_by_class`) if a sub-composition styles the host root's own class. Style the root with `#root`.
202
139
 
203
140
  ## What HyperFrames Does With the Sub-Composition
204
141
 
@@ -2,13 +2,6 @@
2
2
 
3
3
  HyperFrames `init --tailwind` uses the Tailwind browser runtime pinned by the scaffold. Treat it as Tailwind v4, not Studio's Tailwind v3 setup.
4
4
 
5
- ## When To Use
6
-
7
- - The project was scaffolded with `npx hyperframes init --tailwind`.
8
- - `index.html` contains `window.__tailwindReady`.
9
- - The task asks for Tailwind utility classes, `@theme`, custom utilities, or v3-to-v4 fixes in a composition.
10
- - Rendered frames have missing Tailwind styles or frame-0 flashes.
11
-
12
5
  ## Version Contract
13
6
 
14
7
  - **Pinned: `@tailwindcss/browser@4.2.4`** (source of truth: `packages/cli/src/commands/init.ts` `TAILWIND_BROWSER_VERSION`).
@@ -44,7 +37,7 @@ Avoid v3-only patterns in browser-runtime compositions:
44
37
 
45
38
  Do not add `tailwind.config.js` only for composition colors, fonts, spacing, or utilities. Use `@theme` and `@utility`.
46
39
 
47
- **Migrating from v3?** Load an existing JS config explicitly: put `@config "./tailwind.config.js";` inside a `text/tailwindcss` block. v4 does **not** auto-detect v3 config files.
40
+ **`@config` / `@plugin` abort the browser compile.** The pinned `@tailwindcss/browser` build does not support JS config or plugins. Theme and utilities stay in a `text/tailwindcss` block.
48
41
 
49
42
  ## Composition Pattern
50
43
 
@@ -109,15 +102,3 @@ npx hyperframes check
109
102
  # Render proof — frame 0 must NOT flash unstyled content. Preview alone can hide this.
110
103
  npx hyperframes render . --workers 1 --quality draft --output tailwind-proof.mp4
111
104
  ```
112
-
113
- ## Quick Debug Checklist
114
-
115
- When Tailwind styles don't apply in a render, check in order:
116
-
117
- 1. Project scaffolded with `npx hyperframes init --tailwind`?
118
- 2. `index.html` `<head>` has `<script src="…@tailwindcss/browser@4.2.4…">` (not `cdn.tailwindcss.com`)?
119
- 3. `window.__tailwindReady` Promise present in `<head>`?
120
- 4. No v3 directives (`@tailwind base/components/utilities`) in the file?
121
- 5. Tokens moved from `tailwind.config.js` to `@theme` (or `@config` reference for v3 migration)?
122
- 6. Every render-critical class appears as a complete static token (no `bg-${color}-500` style assembly)?
123
- 7. Re-run `npx hyperframes check`, then the render proof above.
@@ -10,7 +10,7 @@ A clip is any DOM element with `data-start` and, where required, `data-duration`
10
10
  - **Sub-composition hosts** — `<div>` with `data-composition-src`. Always require `data-duration`.
11
11
  - **Video clips** — `<video>` with `muted` and `playsinline`. Duration can default to media length.
12
12
  - **Audio clips** — `<audio>`. Duration can default to media length.
13
- - **Image clips** — `<img>`. Always require `data-duration`.
13
+ - **Image clips** — `<img>`. `data-duration` is optional and defaults to 3 seconds; write it only for another length.
14
14
 
15
15
  Add `class="clip"` to authored visual clips. The runtime does not read it, but the scaffold's shared `.clip { position: absolute; inset: 0 }` rule is what gives a scene its full-frame box, Studio treats it as an edit hint, and `lint` warns without it.
16
16
 
@@ -28,11 +28,7 @@ The one place the value carries meaning: two `<audio>` elements that share a tra
28
28
 
29
29
  ## Picking a Track Index
30
30
 
31
- Purely a readability choice for whoever opens the file in Studio. Common patterns:
32
-
33
- - **Track 0** — base video (e.g. an A-roll).
34
- - **Track 1+** — visual scenes, overlays, captions.
35
- - **Higher tracks (e.g. 10+)**: audio clips, separated from visual tracks.
31
+ Purely a readability choice for whoever opens the file in Studio. Common patterns, owned by `/hyperframes-studio` (one caption track, one element kind per track).
36
32
 
37
33
  When adding a clip to an existing composition, set its `data-start`/`data-duration` against the clips around it. You do not need to hunt for a free lane, and you never need to renumber tracks after a retime.
38
34
 
@@ -58,11 +58,11 @@ document.getElementById("title").textContent = title;
58
58
  ### Two JSON Shapes (Easy to Confuse)
59
59
 
60
60
  - `data-composition-variables` is an **array of declarations** (the schema): `[{id, type, label, default}, ...]`
61
- - `--variables` and `data-variable-values` are **objects keyed by id** (the values): `{ title: "Q4", accent: "#fff" }`
61
+ - `--variables` and `data-variable-values` are **objects keyed by id** (the values): `{"title":"Q4","accent":"#fff"}`
62
62
 
63
63
  ## Media
64
64
 
65
- **`<video>`/`<audio>` work at any nesting depth, including inside a sub-composition `<template>` or a wrapper `<div>`.** The runtime discovers media with a flat `document.querySelectorAll("video, audio")`, resolves each element's host composition via `element.closest("[data-composition-id]")`, and rebases its local `data-start` by the accumulated absolute start of every ancestor composition (`packages/core/src/runtime/{media,startResolver}.ts`). So a scene-specific clip can live in its scene's sub-comp with scene-local `data-start`, and it seeks/decodes correctly. If a panel renders blank after a render, that is a real bug: capture a per-frame `snapshot` and treat it as render-blocking.
65
+ **`<video>`/`<audio>` work at any nesting depth, including inside a sub-composition `<template>` or a wrapper `<div>`.** The runtime discovers media with a flat `document.querySelectorAll("video, audio")`, resolves each element's host composition via `element.closest("[data-composition-id]")`, and rebases its local `data-start` by the accumulated absolute start of every ancestor composition. A host at `2` with child media at `2` therefore starts that media at root time `4`, consistently in preview, snapshot, extraction, and render. Legacy projects that deliberately authored a media start in root time must mark that element with `data-hf-media-start-basis="global"`; never infer the basis from overlapping numbers. New compositions should always use scene-local `data-start`. If a panel renders blank after a render, capture a per-frame `snapshot` and treat it as render-blocking.
66
66
 
67
67
  The one real constraint is about **timelines, not media placement**: a sub-composition timeline **cannot reach or animate host elements** — neither `document.querySelector("#host-id")` nor a gsap selector string (`tl.to("#host-id", …)`) resolves across the boundary; a sub-comp timeline only drives its own subtree. So if a media element lives at the host root, **its per-scene motion (scale/opacity/morph/tilt/breathing) must be authored on the MAIN timeline in `index.html`, at GLOBAL time** (scene-local time + the scene slot's `data-start`). Keeping the media inside the scene sub-comp instead lets that sub-comp's own timeline animate it with scene-local time. For 3D tilt without a perspective parent, use gsap `transformPerspective` on the element. See `composition-patterns.md` archetype B.
68
68
 
@@ -100,7 +100,7 @@ Video elements must be muted and inline. Audio must be a separate `<audio>` elem
100
100
  - **Never** add `crossorigin` to `<video>`/`<audio>`. `lint` rejects it unconditionally (`media_crossorigin_breaks_preview`, error) because a media host without `Access-Control-Allow-Origin` then fails silently in preview while renders still work, hiding the bug. There is no suppression, so this holds even for the canvas/WebGL/WebAudio readback case.
101
101
  - **Every `<audio>` needs an `id`.** The mixer selects `audio[id][src]`, so an id-less `<audio>` is never mixed and the render is **silent**. `lint` catches it as `media_missing_id`.
102
102
  - Audio always lives on a separate `<audio>` element — even if its source file is the same as a `<video>`. The `<video>` is muted; the `<audio>` carries sound.
103
- - For volume fades/ducking, animate `volume` on the timeline (`tl.to("#bgm", { volume: 0, duration: 1 }, "outro")`) rather than swapping `data-volume`. The runtime probes the timeline's volume keyframes and applies them identically in preview and render; `data-volume` is the static baseline for elements no tween touches. A tween's values REPLACE that baseline rather than scaling it, so on a clip whose gain is not `1` you scale the tween's targets instead (`{ volume: 1.95 }`, not `{ volume: 1 }`) — `lint` warns with `audio_volume_tween_overrides_gain` when the two disagree.
103
+ - For volume fades and ducking, use the `data-automation` volume lane; the exact form is in `creator-editing-recipes.md`. `data-volume` is the static baseline. A timeline `volume` tween is ignored when a lane is present.
104
104
 
105
105
  For media duration: `<video>` and `<audio>` can omit `data-duration` if the media's intrinsic length is known and you want the full clip. Otherwise provide `data-duration` explicitly.
106
106
 
@@ -3,6 +3,8 @@ name: hyperframes-creative
3
3
  description: Non-animation creative direction for HyperFrames videos. Use for design spec (frame.md / design.md) handling, palettes, typography, narration, beat planning, audio-reactive visuals, composition patterns, and brand / style decisions. For atomic motion patterns and scene blueprints, use `hyperframes-animation`.
4
4
  ---
5
5
 
6
+ **Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below.
7
+
6
8
  # HyperFrames Creative
7
9
 
8
10
  Brand, pacing, style, narration, and composition direction. Use after the technical contract from `hyperframes-core` is in place.