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,181 +0,0 @@
1
- const DEFAULT_SIZE = 33;
2
- const MAX_SIZE = 64;
3
-
4
- function clamp(value, min, max) {
5
- if (!Number.isFinite(value)) return 0;
6
- return Math.min(max, Math.max(min, value));
7
- }
8
-
9
- function clampUnit(value) {
10
- return clamp(value, 0, 1);
11
- }
12
-
13
- function readParam(params, key, min, max) {
14
- return clamp(Number(params?.[key] ?? 0), min, max);
15
- }
16
-
17
- function luma([r, g, b]) {
18
- // Rec.709 luma weightings (matches the color space the grading runtime uses).
19
- return r * 0.2126 + g * 0.7152 + b * 0.0722;
20
- }
21
-
22
- function smoothstep(edge0, edge1, value) {
23
- const t = clampUnit((value - edge0) / (edge1 - edge0));
24
- return t * t * (3 - 2 * t);
25
- }
26
-
27
- function applyLiftGain(color, params) {
28
- const y = luma(color);
29
- const blacks = readParam(params, "blacks", -1, 1);
30
- const shadows = readParam(params, "shadows", -1, 1);
31
- const highlights = readParam(params, "highlights", -1, 1);
32
- const whites = readParam(params, "whites", -1, 1);
33
- const shadowMask = 1 - smoothstep(0.18, 0.62, y);
34
- const highlightMask = smoothstep(0.38, 0.82, y);
35
- const offset =
36
- blacks * 0.08 + shadows * 0.12 * shadowMask + highlights * 0.12 * highlightMask + whites * 0.08;
37
- return color.map((channel) => clampUnit(channel + offset));
38
- }
39
-
40
- function applyExposure(color, params) {
41
- const exposure = readParam(params, "exposure", -2, 2);
42
- const gain = 2 ** exposure;
43
- const lift = Math.max(0, exposure) * 0.015;
44
- return color.map((channel) => clampUnit(channel * gain + lift));
45
- }
46
-
47
- function applyContrast(color, params) {
48
- const contrast = readParam(params, "contrast", -1, 1);
49
- if (contrast === 0) return color;
50
- const factor = 1 + contrast * 1.2;
51
- return color.map((channel) => clampUnit(0.5 + (channel - 0.5) * factor));
52
- }
53
-
54
- function applyWhiteBalance(color, params) {
55
- const temperature = readParam(params, "temperature", -1, 1);
56
- const tint = readParam(params, "tint", -1, 1);
57
- const redScale = 1 + temperature * 0.28 + tint * 0.08;
58
- const greenScale = 1 - Math.abs(tint) * 0.1 - tint * 0.08;
59
- const blueScale = 1 - temperature * 0.28 + tint * 0.08;
60
- return [
61
- clampUnit(color[0] * redScale),
62
- clampUnit(color[1] * greenScale),
63
- clampUnit(color[2] * blueScale),
64
- ];
65
- }
66
-
67
- function applySplitTone(color, params) {
68
- const split = params?.splitTone;
69
- if (!split) return color;
70
- const intensity = clampUnit(Number(split.intensity ?? 0));
71
- if (intensity === 0) return color;
72
- const balance = clampUnit(Number(split.balance ?? 0.5));
73
- const y = luma(color);
74
- const shadowMask = 1 - smoothstep(balance - 0.25, balance + 0.2, y);
75
- const highlightMask = smoothstep(balance - 0.2, balance + 0.25, y);
76
- const shadows = Array.isArray(split.shadows) ? split.shadows : [0, 0, 0];
77
- const highlights = Array.isArray(split.highlights) ? split.highlights : [0, 0, 0];
78
- return color.map((channel, i) =>
79
- clampUnit(
80
- channel +
81
- Number(shadows[i] ?? 0) * shadowMask * intensity +
82
- Number(highlights[i] ?? 0) * highlightMask * intensity,
83
- ),
84
- );
85
- }
86
-
87
- function applySaturation(color, params) {
88
- const saturation = readParam(params, "saturation", -1, 1);
89
- const vibrance = readParam(params, "vibrance", -1, 1);
90
- if (saturation === 0 && vibrance === 0) return color;
91
- const y = luma(color);
92
- const currentSat = Math.max(
93
- Math.abs(color[0] - y),
94
- Math.abs(color[1] - y),
95
- Math.abs(color[2] - y),
96
- );
97
- const vibranceWeight = 1 - clampUnit(currentSat * 2);
98
- const factor = clamp(1 + saturation + vibrance * vibranceWeight, 0, 2.5);
99
- return color.map((channel) => clampUnit(y + (channel - y) * factor));
100
- }
101
-
102
- function applyParams(color, params) {
103
- let out = applyLiftGain(color, params);
104
- out = applyExposure(out, params);
105
- out = applyContrast(out, params);
106
- out = applyWhiteBalance(out, params);
107
- out = applySplitTone(out, params);
108
- out = applySaturation(out, params);
109
- return out;
110
- }
111
-
112
- function formatNumber(value) {
113
- return clampUnit(value).toFixed(6);
114
- }
115
-
116
- export function buildCube(params = {}, size = DEFAULT_SIZE) {
117
- if (!Number.isInteger(size) || size < 2 || size > MAX_SIZE) {
118
- throw new Error(`LUT size must be an integer from 2 to ${MAX_SIZE}`);
119
- }
120
- const lines = [
121
- `TITLE "media-use parametric grade"`,
122
- "DOMAIN_MIN 0 0 0",
123
- "DOMAIN_MAX 1 1 1",
124
- `LUT_3D_SIZE ${size}`,
125
- ];
126
- const denom = size - 1;
127
- for (let b = 0; b < size; b++) {
128
- for (let g = 0; g < size; g++) {
129
- for (let r = 0; r < size; r++) {
130
- const out = applyParams([r / denom, g / denom, b / denom], params);
131
- lines.push(`${formatNumber(out[0])} ${formatNumber(out[1])} ${formatNumber(out[2])}`);
132
- }
133
- }
134
- }
135
- return `${lines.join("\n")}\n`;
136
- }
137
-
138
- export function paramsFromIntent(intent) {
139
- const text = String(intent ?? "").toLowerCase();
140
- const params = {};
141
- let matched = false;
142
-
143
- if (/\b(warm|golden|sunlit|sunny)\b/.test(text)) {
144
- params.temperature = 0.18;
145
- matched = true;
146
- } else if (/\b(cool|blue|icy|crisp)\b/.test(text)) {
147
- params.temperature = -0.16;
148
- matched = true;
149
- }
150
-
151
- if (/\b(cinematic|film|movie)\b/.test(text)) {
152
- params.contrast = 0.08;
153
- params.saturation = 0.04;
154
- matched = true;
155
- }
156
- if (/\b(punchy|contrast|dramatic|bold)\b/.test(text)) {
157
- params.contrast = Math.max(params.contrast ?? 0, 0.22);
158
- matched = true;
159
- }
160
- if (/\b(bright|airy|lift)\b/.test(text)) {
161
- params.exposure = 0.16;
162
- params.shadows = 0.08;
163
- matched = true;
164
- }
165
- if (/\b(dark|moody|low-key)\b/.test(text)) {
166
- params.exposure = -0.12;
167
- params.contrast = Math.max(params.contrast ?? 0, 0.12);
168
- matched = true;
169
- }
170
- if (/\b(vibrant|saturated|colorful)\b/.test(text)) {
171
- params.saturation = Math.max(params.saturation ?? 0, 0.16);
172
- params.vibrance = 0.12;
173
- matched = true;
174
- }
175
- if (/\b(muted|desaturated|washed)\b/.test(text)) {
176
- params.saturation = Math.min(params.saturation ?? 0, -0.16);
177
- matched = true;
178
- }
179
-
180
- return matched ? params : null;
181
- }
@@ -1,80 +0,0 @@
1
- import { strict as assert } from "node:assert";
2
- import { test } from "node:test";
3
- import { buildCube, paramsFromIntent } from "./cube-build.mjs";
4
- import { validateCube } from "./cube-validate.mjs";
5
-
6
- function rows(cube) {
7
- return cube
8
- .split(/\r?\n/)
9
- .map((line) => line.trim())
10
- .filter((line) => /^[+-]?(?:\d|\.\d)/.test(line))
11
- .map((line) => line.split(/\s+/).map(Number));
12
- }
13
-
14
- function rowAt(cubeRows, size, r, g, b) {
15
- return cubeRows[(b * size + g) * size + r];
16
- }
17
-
18
- function luma(row) {
19
- return row[0] * 0.2126 + row[1] * 0.7152 + row[2] * 0.0722;
20
- }
21
-
22
- test("all-zero params produce a near-identity LUT", () => {
23
- const cube = buildCube({}, 3);
24
- assert.equal(validateCube(cube).ok, true);
25
- const parsed = rows(cube);
26
- for (let b = 0; b < 3; b++) {
27
- for (let g = 0; g < 3; g++) {
28
- for (let r = 0; r < 3; r++) {
29
- const row = rowAt(parsed, 3, r, g, b);
30
- assert.ok(Math.abs(row[0] - r / 2) < 0.000001);
31
- assert.ok(Math.abs(row[1] - g / 2) < 0.000001);
32
- assert.ok(Math.abs(row[2] - b / 2) < 0.000001);
33
- }
34
- }
35
- }
36
- });
37
-
38
- test("positive exposure increases unclipped output luma", () => {
39
- const identity = rows(buildCube({}, 5));
40
- const exposed = rows(buildCube({ exposure: 0.3 }, 5));
41
- for (let i = 0; i < identity.length; i++) {
42
- const before = luma(identity[i]);
43
- if (before > 0.02 && before < 0.95) {
44
- assert.ok(luma(exposed[i]) > before, `row ${i} should brighten`);
45
- }
46
- }
47
- });
48
-
49
- test("positive temperature warms mid-gray", () => {
50
- const parsed = rows(buildCube({ temperature: 0.2 }, 3));
51
- const mid = rowAt(parsed, 3, 1, 1, 1);
52
- assert.ok(mid[0] > 0.5, "red channel should rise");
53
- assert.ok(mid[2] < 0.5, "blue channel should fall");
54
- });
55
-
56
- test("positive contrast darkens shadows and brightens highlights", () => {
57
- const parsed = rows(buildCube({ contrast: 0.3 }, 5));
58
- const shadow = rowAt(parsed, 5, 1, 1, 1);
59
- const highlight = rowAt(parsed, 5, 3, 3, 3);
60
- assert.ok(luma(shadow) < 0.25, "below-mid gray should darken");
61
- assert.ok(luma(highlight) > 0.75, "above-mid gray should brighten");
62
- });
63
-
64
- test("outputs validate at the default size and are deterministic", () => {
65
- const params = { exposure: 0.15, contrast: 0.2, temperature: -0.1, saturation: 0.12 };
66
- const a = buildCube(params);
67
- const b = buildCube(params);
68
- assert.equal(a, b);
69
- assert.equal(validateCube(a).ok, true);
70
- assert.equal(validateCube(a).size, 33);
71
- });
72
-
73
- test("paramsFromIntent declines zero-overlap prompts and maps technical words", () => {
74
- assert.equal(paramsFromIntent("zqxv imaginary neutron look"), null);
75
- assert.deepEqual(paramsFromIntent("warm cinematic"), {
76
- temperature: 0.18,
77
- contrast: 0.08,
78
- saturation: 0.04,
79
- });
80
- });
@@ -1,198 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // Standalone mirror of packages/core/src/colorLuts.ts. media-use cannot import
4
- // the TypeScript source at runtime, so cube-validate.test.mjs mirrors core
5
- // parser cases to catch drift in accepted .cube files before freezing them.
6
-
7
- import { readFileSync } from "node:fs";
8
- import { resolve as resolvePath } from "node:path";
9
- import { fileURLToPath } from "node:url";
10
-
11
- export const DEFAULT_MAX_CUBE_LUT_SIZE = 64;
12
-
13
- const DEFAULT_DOMAIN_MIN = [0, 0, 0];
14
- const DEFAULT_DOMAIN_MAX = [1, 1, 1];
15
-
16
- class CubeValidateError extends Error {
17
- constructor(message, lineNumber = null) {
18
- super(lineNumber == null ? message : `${message} at line ${lineNumber}`);
19
- this.name = "CubeValidateError";
20
- this.lineNumber = lineNumber;
21
- }
22
- }
23
-
24
- function stripComment(line) {
25
- let inQuote = false;
26
- for (let i = 0; i < line.length; i++) {
27
- const char = line[i];
28
- if (char === '"') inQuote = !inQuote;
29
- if (char === "#" && !inQuote) return line.slice(0, i);
30
- }
31
- return line;
32
- }
33
-
34
- function parseFiniteNumber(value, lineNumber) {
35
- const parsed = Number(value);
36
- if (!Number.isFinite(parsed)) {
37
- throw new CubeValidateError(`Invalid number "${value}"`, lineNumber);
38
- }
39
- return parsed;
40
- }
41
-
42
- function parseVec3(parts, keyword, lineNumber) {
43
- if (parts.length !== 3) {
44
- throw new CubeValidateError(`${keyword} expects three numbers`, lineNumber);
45
- }
46
- return [
47
- parseFiniteNumber(parts[0], lineNumber),
48
- parseFiniteNumber(parts[1], lineNumber),
49
- parseFiniteNumber(parts[2], lineNumber),
50
- ];
51
- }
52
-
53
- function parseSize(value, keyword, lineNumber) {
54
- if (!value) throw new CubeValidateError(`${keyword} expects a size`, lineNumber);
55
- const parsed = Number(value);
56
- if (!Number.isInteger(parsed) || parsed < 2) {
57
- throw new CubeValidateError(`${keyword} must be an integer greater than 1`, lineNumber);
58
- }
59
- return parsed;
60
- }
61
-
62
- function validateDomain(domainMin, domainMax) {
63
- if (
64
- domainMax[0] <= domainMin[0] ||
65
- domainMax[1] <= domainMin[1] ||
66
- domainMax[2] <= domainMin[2]
67
- ) {
68
- throw new CubeValidateError("DOMAIN_MAX values must be greater than DOMAIN_MIN values");
69
- }
70
- }
71
-
72
- function isNumericDataLine(token) {
73
- return /^[+-]?(?:\d|\.\d)/.test(token);
74
- }
75
-
76
- function parseCube(input, options = {}) {
77
- const maxSize = options.maxSize ?? DEFAULT_MAX_CUBE_LUT_SIZE;
78
- let domainMin = DEFAULT_DOMAIN_MIN;
79
- let domainMax = DEFAULT_DOMAIN_MAX;
80
- let lut1dSize = null;
81
- let lut3dSize = null;
82
- let rows = 0;
83
-
84
- const lines = String(input)
85
- .replace(/^\uFEFF/, "")
86
- .split(/\r?\n/);
87
- for (let i = 0; i < lines.length; i++) {
88
- const lineNumber = i + 1;
89
- const line = stripComment(lines[i] ?? "").trim();
90
- if (!line) continue;
91
- const parts = line.split(/\s+/);
92
- const keyword = (parts[0] ?? "").toUpperCase();
93
- const rest = parts.slice(1);
94
-
95
- if (keyword === "TITLE") continue;
96
- if (keyword === "DOMAIN_MIN") {
97
- domainMin = parseVec3(rest, keyword, lineNumber);
98
- continue;
99
- }
100
- if (keyword === "DOMAIN_MAX") {
101
- domainMax = parseVec3(rest, keyword, lineNumber);
102
- continue;
103
- }
104
- if (keyword === "LUT_3D_INPUT_RANGE") {
105
- if (rest.length !== 2) {
106
- throw new CubeValidateError(`${keyword} expects two numbers`, lineNumber);
107
- }
108
- const min = parseFiniteNumber(rest[0], lineNumber);
109
- const max = parseFiniteNumber(rest[1], lineNumber);
110
- if (max <= min) {
111
- throw new CubeValidateError("LUT_3D_INPUT_RANGE max must exceed min", lineNumber);
112
- }
113
- domainMin = [min, min, min];
114
- domainMax = [max, max, max];
115
- continue;
116
- }
117
- if (keyword === "LUT_1D_SIZE") {
118
- lut1dSize = parseSize(rest[0], keyword, lineNumber);
119
- continue;
120
- }
121
- if (keyword === "LUT_3D_SIZE") {
122
- lut3dSize = parseSize(rest[0], keyword, lineNumber);
123
- if (lut3dSize > maxSize) {
124
- throw new CubeValidateError(`LUT_3D_SIZE ${lut3dSize} exceeds max ${maxSize}`, lineNumber);
125
- }
126
- continue;
127
- }
128
-
129
- if (!isNumericDataLine(keyword)) {
130
- if (keyword.startsWith("LUT_")) {
131
- throw new CubeValidateError(`Unsupported cube keyword ${keyword}`, lineNumber);
132
- }
133
- continue;
134
- }
135
- if (!lut3dSize) {
136
- if (lut1dSize) {
137
- throw new CubeValidateError("1D cube LUTs are not supported yet", lineNumber);
138
- }
139
- throw new CubeValidateError("LUT data appears before LUT_3D_SIZE", lineNumber);
140
- }
141
- if (parts.length !== 3) {
142
- throw new CubeValidateError("LUT data rows must contain three numbers", lineNumber);
143
- }
144
- parseFiniteNumber(parts[0], lineNumber);
145
- parseFiniteNumber(parts[1], lineNumber);
146
- parseFiniteNumber(parts[2], lineNumber);
147
- rows++;
148
- }
149
-
150
- if (lut1dSize && lut3dSize) {
151
- throw new CubeValidateError("Mixed 1D and 3D cube LUTs are not supported yet");
152
- }
153
- if (!lut3dSize) {
154
- if (lut1dSize) throw new CubeValidateError("1D cube LUTs are not supported yet");
155
- throw new CubeValidateError("Missing LUT_3D_SIZE");
156
- }
157
- validateDomain(domainMin, domainMax);
158
-
159
- const expectedRows = lut3dSize * lut3dSize * lut3dSize;
160
- if (rows !== expectedRows) {
161
- throw new CubeValidateError(
162
- `Expected ${expectedRows} LUT rows for size ${lut3dSize}, found ${rows}`,
163
- );
164
- }
165
-
166
- return { size: lut3dSize };
167
- }
168
-
169
- export function validateCube(input, options = {}) {
170
- try {
171
- const parsed = parseCube(input, options);
172
- return { ok: true, size: parsed.size };
173
- } catch (err) {
174
- return { ok: false, error: err.message };
175
- }
176
- }
177
-
178
- export function validateCubeFile(filePath, options = {}) {
179
- return validateCube(readFileSync(filePath, "utf8"), options);
180
- }
181
-
182
- function main(argv) {
183
- const file = argv[2];
184
- if (!file) {
185
- console.error("usage: cube-validate.mjs <file.cube>");
186
- process.exit(2);
187
- }
188
- const result = validateCubeFile(file);
189
- if (!result.ok) {
190
- console.error(`error: ${result.error}`);
191
- process.exit(1);
192
- }
193
- console.log(`ok: LUT_3D_SIZE ${result.size}`);
194
- }
195
-
196
- if (process.argv[1] && resolvePath(process.argv[1]) === fileURLToPath(import.meta.url)) {
197
- main(process.argv);
198
- }
@@ -1,125 +0,0 @@
1
- import { strict as assert } from "node:assert";
2
- import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
- import { execFileSync } from "node:child_process";
4
- import { join } from "node:path";
5
- import { tmpdir } from "node:os";
6
- import { test } from "node:test";
7
- import { validateCube } from "./cube-validate.mjs";
8
-
9
- const IDENTITY_2 = `
10
- # comment
11
- TITLE "Identity 2"
12
- DOMAIN_MIN 0 0 0
13
- DOMAIN_MAX 1 1 1
14
- LUT_3D_SIZE 2
15
- 0 0 0
16
- 1 0 0
17
- 0 1 0
18
- 1 1 0
19
- 0 0 1
20
- 1 0 1
21
- 0 1 1
22
- 1 1 1
23
- `;
24
-
25
- test("accepts a valid minimal 3D cube LUT", () => {
26
- const result = validateCube(IDENTITY_2);
27
- assert.deepEqual(result, { ok: true, size: 2 });
28
- });
29
-
30
- test("rejects oversize LUTs with the core parser message", () => {
31
- const result = validateCube("LUT_3D_SIZE 65", { maxSize: 64 });
32
- assert.equal(result.ok, false);
33
- assert.match(result.error, /LUT_3D_SIZE 65 exceeds max 64/);
34
- });
35
-
36
- test("rejects data rows before LUT_3D_SIZE", () => {
37
- const result = validateCube("0 0 0\nLUT_3D_SIZE 2");
38
- assert.equal(result.ok, false);
39
- assert.match(result.error, /LUT data appears before LUT_3D_SIZE/);
40
- });
41
-
42
- test("rejects missing LUT_3D_SIZE", () => {
43
- const result = validateCube('TITLE "No Size"');
44
- assert.equal(result.ok, false);
45
- assert.match(result.error, /Missing LUT_3D_SIZE/);
46
- });
47
-
48
- test("rejects row count mismatches with the core parser message", () => {
49
- const result = validateCube("LUT_3D_SIZE 2\n0 0 0");
50
- assert.equal(result.ok, false);
51
- assert.match(result.error, /Expected 8 LUT rows/);
52
- });
53
-
54
- test("rejects inverted domains", () => {
55
- const result = validateCube(`
56
- DOMAIN_MIN 0 0 0
57
- DOMAIN_MAX 1 0 1
58
- LUT_3D_SIZE 2
59
- 0 0 0
60
- 1 0 0
61
- 0 1 0
62
- 1 1 0
63
- 0 0 1
64
- 1 0 1
65
- 0 1 1
66
- 1 1 1
67
- `);
68
- assert.equal(result.ok, false);
69
- assert.match(result.error, /DOMAIN_MAX values must be greater than DOMAIN_MIN values/);
70
- });
71
-
72
- test("rejects unsupported 1D and mixed cube LUTs", () => {
73
- const oneD = validateCube("LUT_1D_SIZE 2\n0 0 0\n1 1 1");
74
- assert.equal(oneD.ok, false);
75
- assert.match(oneD.error, /1D cube LUTs are not supported yet/);
76
-
77
- const mixed = validateCube(`
78
- LUT_1D_SIZE 2
79
- LUT_3D_SIZE 2
80
- 0 0 0
81
- 1 0 0
82
- 0 1 0
83
- 1 1 0
84
- 0 0 1
85
- 1 0 1
86
- 0 1 1
87
- 1 1 1
88
- `);
89
- assert.equal(mixed.ok, false);
90
- assert.match(mixed.error, /Mixed 1D and 3D cube LUTs are not supported yet/);
91
- });
92
-
93
- test("CLI exits zero for valid files and non-zero for invalid files", () => {
94
- const dir = mkdtempSync(join(tmpdir(), "mu-cube-validate-"));
95
- try {
96
- const valid = join(dir, "valid.cube");
97
- const invalid = join(dir, "invalid.cube");
98
- writeFileSync(valid, IDENTITY_2);
99
- writeFileSync(invalid, "LUT_3D_SIZE 65");
100
-
101
- const out = execFileSync(
102
- process.execPath,
103
- [new URL("./cube-validate.mjs", import.meta.url).pathname, valid],
104
- {
105
- encoding: "utf8",
106
- },
107
- );
108
- assert.match(out, /ok: LUT_3D_SIZE 2/);
109
-
110
- assert.throws(
111
- () =>
112
- execFileSync(
113
- process.execPath,
114
- [new URL("./cube-validate.mjs", import.meta.url).pathname, invalid],
115
- {
116
- encoding: "utf8",
117
- stdio: "pipe",
118
- },
119
- ),
120
- (err) => err.status === 1 && String(err.stderr).includes("exceeds max 64"),
121
- );
122
- } finally {
123
- rmSync(dir, { recursive: true, force: true });
124
- }
125
- });