hyperframes 0.8.184 → 0.8.185

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 (309) hide show
  1. package/dist/{add-5ZTD3MUZ.js → add-ODTNPRGG.js} +14 -14
  2. package/dist/{agentPromptGenerator-VIIPZCJ3.js → agentPromptGenerator-KYO5MKTW.js} +1 -1
  3. package/dist/{animatedGifPrep-AGASIWVA.js → animatedGifPrep-PEP5GWOJ.js} +9 -9
  4. package/dist/api.js +17 -17
  5. package/dist/{asset-LSTEMPIB.js → asset-EZG42XPA.js} +2 -2
  6. package/dist/{assetCataloger-BKBMNL3A.js → assetCataloger-OHAJFCA7.js} +9 -9
  7. package/dist/{audio-KGK44XE4.js → audio-65R4T74I.js} +16 -15
  8. package/dist/{auth-MJWKYK77.js → auth-VP2SIZFN.js} +1 -1
  9. package/dist/{autoUpdate-2GGDKYR2.js → autoUpdate-RVSUX4SS.js} +2 -2
  10. package/dist/{backgroundChecks-7GRX5VQY.js → backgroundChecks-LEHK74NS.js} +3 -3
  11. package/dist/backgroundChecksWorker.js +3 -3
  12. package/dist/{batchRender-BCV2LELR.js → batchRender-3X6CL7PK.js} +5 -5
  13. package/dist/{beats-7BCFRBAL.js → beats-66VPQLSC.js} +6 -6
  14. package/dist/{benchmark-2R6CN5MQ.js → benchmark-PBJVTOJP.js} +10 -10
  15. package/dist/{browser-6AFGOI7G.js → browser-TPL5MWGC.js} +3 -3
  16. package/dist/{bundleWithLocalizedFonts-2EGPA5ZO.js → bundleWithLocalizedFonts-67MLTHKM.js} +2 -2
  17. package/dist/{capture-HOIAW6MO.js → capture-F3GNMRZM.js} +13 -13
  18. package/dist/{capture-HA2UHLIU.js → capture-YYRE7EV3.js} +2 -2
  19. package/dist/{captureCompositionFrame-4236FVWJ.js → captureCompositionFrame-PXV2ISHD.js} +13 -13
  20. package/dist/{catalog-A6CZAWW4.js → catalog-6SDD3OHF.js} +14 -14
  21. package/dist/{catch-up-TNA4DJOA.js → catch-up-CWIVA5VG.js} +4 -4
  22. package/dist/{check-5E5U2EQZ.js → check-YQ6REK2S.js} +34 -34
  23. package/dist/{checkBrowser-FNINXBMZ.js → checkBrowser-5XZCBRXM.js} +31 -31
  24. package/dist/{chunk-2ULXZ4PG.js → chunk-2TAS5O4P.js} +56 -11
  25. package/dist/{chunk-3TXNNBI2.js → chunk-37CNYTQQ.js} +2 -2
  26. package/dist/chunk-3NPOFC43.js +3805 -0
  27. package/dist/{chunk-EEP5Y5IE.js → chunk-46HEFGXJ.js} +1 -1
  28. package/dist/{chunk-2XA7KXQO.js → chunk-4CTNDBE3.js} +2 -2
  29. package/dist/{chunk-MFAETWIF.js → chunk-55YELV7B.js} +1 -1
  30. package/dist/{chunk-LRGYPXHF.js → chunk-5GUKZRQN.js} +1 -1
  31. package/dist/{chunk-SAX7HQ5E.js → chunk-6ASIXMFP.js} +1 -1
  32. package/dist/{chunk-SN7KI5JA.js → chunk-6GPR3G3C.js} +2 -2
  33. package/dist/{chunk-E32ZQ2WO.js → chunk-6RIZY7OP.js} +1 -1
  34. package/dist/{chunk-5CS4T6C6.js → chunk-7L37AB57.js} +1 -1
  35. package/dist/{chunk-BCHSTE7U.js → chunk-7PS6RVWD.js} +1 -1
  36. package/dist/{chunk-X3CG5YQA.js → chunk-7RAY4WZP.js} +3 -3
  37. package/dist/{chunk-YWHCXIMD.js → chunk-A6QCO474.js} +5 -5
  38. package/dist/{chunk-YMS5FOMC.js → chunk-B2A56S5C.js} +1 -1
  39. package/dist/{chunk-IWTNTUPQ.js → chunk-BH7TTIPN.js} +11 -13
  40. package/dist/{chunk-DWUGKEHR.js → chunk-BRAPBUPQ.js} +1 -1
  41. package/dist/{chunk-R2BFTECP.js → chunk-CX3USX23.js} +3 -3
  42. package/dist/{chunk-JB6ARMQS.js → chunk-D42NU3DZ.js} +1 -1
  43. package/dist/{chunk-GDEV42FA.js → chunk-EMAWIZ54.js} +481 -36
  44. package/dist/{chunk-UNNXFKVG.js → chunk-EMSDWBG3.js} +2 -2
  45. package/dist/{chunk-CXABAXB3.js → chunk-ESBMUZXZ.js} +1 -1
  46. package/dist/{chunk-EM7C7H7S.js → chunk-F3OPUWQX.js} +7 -7
  47. package/dist/{chunk-YOZPQCSR.js → chunk-FX6RUJUY.js} +3 -3
  48. package/dist/{chunk-PSKEKWVU.js → chunk-G2UANIDX.js} +1 -1
  49. package/dist/{chunk-UKOTVJFV.js → chunk-H5SM5PJI.js} +1 -1
  50. package/dist/{chunk-YWNJTZGN.js → chunk-H6UY725W.js} +2 -2
  51. package/dist/{chunk-PFYWVUHA.js → chunk-HDIRZGK4.js} +1 -1
  52. package/dist/{chunk-HR23IKLZ.js → chunk-HHSJJFVM.js} +2 -2
  53. package/dist/{chunk-LLC4CS6I.js → chunk-HXUTX5GR.js} +3 -3
  54. package/dist/{chunk-RGXVEYHI.js → chunk-I3IH5MV5.js} +6 -10
  55. package/dist/{chunk-Q33P4EAC.js → chunk-I45HL3VP.js} +3 -3
  56. package/dist/{chunk-TYXH3EKP.js → chunk-I5JRXMCV.js} +16 -1
  57. package/dist/{chunk-4N4P3ZWL.js → chunk-I7QMXZSB.js} +2 -2
  58. package/dist/{chunk-ICJEAFPD.js → chunk-IDFNKFPL.js} +7 -7
  59. package/dist/{chunk-VNVQGDPC.js → chunk-IDGLGXPA.js} +1 -1
  60. package/dist/{chunk-2PVE4WS2.js → chunk-IPDKZ5IN.js} +33 -6
  61. package/dist/{chunk-ABZYYUUH.js → chunk-ISTRDGIX.js} +1 -1
  62. package/dist/{chunk-3MDJIWOV.js → chunk-IVEFOUZI.js} +46 -30
  63. package/dist/{chunk-4KKKBEP3.js → chunk-J5XEZ24I.js} +3 -3
  64. package/dist/{chunk-7HJALUIO.js → chunk-K2ZKXTVL.js} +1 -1
  65. package/dist/{chunk-TE427YID.js → chunk-K73RJB23.js} +2 -2
  66. package/dist/{chunk-3QVMJ7GX.js → chunk-KQ4JEJLF.js} +9 -9
  67. package/dist/{chunk-J3QM7AHP.js → chunk-KROXPHXX.js} +9 -3
  68. package/dist/{chunk-OLZENUPI.js → chunk-LLFQA7XF.js} +1 -1
  69. package/dist/chunk-LLMSX6V6.js +17 -0
  70. package/dist/{chunk-WTFUDNW2.js → chunk-LYKSTRAY.js} +3 -3
  71. package/dist/{chunk-VDM3FEDP.js → chunk-OAG47RE6.js} +1 -1
  72. package/dist/{chunk-FGJEUV4S.js → chunk-PHMBMOXF.js} +1 -1
  73. package/dist/{chunk-7MB2ZAQF.js → chunk-PMMQRJDE.js} +1 -1
  74. package/dist/{chunk-ADDHW2FS.js → chunk-Q447CX26.js} +82 -12
  75. package/dist/{chunk-YALM6XN4.js → chunk-Q47FAYYK.js} +3 -3
  76. package/dist/{chunk-ZOCHDXDG.js → chunk-RTBSA3EA.js} +1 -1
  77. package/dist/{chunk-UJKUB2LH.js → chunk-RUEGYANA.js} +11 -11
  78. package/dist/{chunk-OYLO6TYK.js → chunk-RUOG4VB7.js} +1 -1
  79. package/dist/{chunk-ZRHO6YMM.js → chunk-SKYPMHQC.js} +4 -4
  80. package/dist/{chunk-ES426XRJ.js → chunk-SQVRXHOW.js} +3 -3
  81. package/dist/{chunk-QL2PNIXC.js → chunk-TAX2ZJAP.js} +24 -16
  82. package/dist/{chunk-KN46TGHV.js → chunk-TOI35GN2.js} +1 -1
  83. package/dist/{chunk-74JMKLVU.js → chunk-UPR3MU6Z.js} +1 -1
  84. package/dist/{chunk-JIUTXKWA.js → chunk-WQLKDL35.js} +2 -1
  85. package/dist/{chunk-WHPN2TIT.js → chunk-X6L6MD5B.js} +5 -5
  86. package/dist/{chunk-7NRI7HYX.js → chunk-XWC7CNPY.js} +1 -1
  87. package/dist/{clean-HGASSMWI.js → clean-7J6ZCCGV.js} +18 -18
  88. package/dist/cli.js +53 -53
  89. package/dist/{cloud-JPDVNXHN.js → cloud-7IJCJCDD.js} +4 -4
  90. package/dist/{cloudrun-AMMGSDRP.js → cloudrun-UNZRIIDB.js} +7 -7
  91. package/dist/commands/contrast-audit.browser.js +1 -2
  92. package/dist/commands/layout-audit.browser.js +196 -0
  93. package/dist/{compare-LHHVHUN3.js → compare-Q7VW5JZT.js} +28 -28
  94. package/dist/{compiler-Q3N6QXMT.js → compiler-KPHYGFXL.js} +6 -6
  95. package/dist/{component-D72J32Q7.js → component-UUBREJKN.js} +3 -3
  96. package/dist/{compositions-UQRJ5ROG.js → compositions-T4FTKIDP.js} +13 -13
  97. package/dist/{delete-P42VIDDA.js → delete-WZQF2IIA.js} +2 -2
  98. package/dist/{deploy-WZTH57O4.js → deploy-JU7A3XNC.js} +1 -1
  99. package/dist/{deterministicFonts-VQ5UBMXY.js → deterministicFonts-EGAAE7QL.js} +3 -3
  100. package/dist/{dist-SENYPQGW.js → dist-LM7AVHOU.js} +3 -3
  101. package/dist/{dist-DYS24AMA.js → dist-ULGTPSU4.js} +8 -4
  102. package/dist/docs/data-attributes.md +1 -1
  103. package/dist/{doctor-KEJIG2SK.js → doctor-VSNGRKXJ.js} +13 -13
  104. package/dist/{events-CYKSNR7N.js → events-CRW2DYUT.js} +3 -3
  105. package/dist/{events-UZXFQPMT.js → events-E63YBUL2.js} +3 -3
  106. package/dist/{feedback-LXNQF7CA.js → feedback-OSZWVR6N.js} +8 -8
  107. package/dist/{ffmpeg-BYHON577.js → ffmpeg-JLH3Z7G7.js} +9 -9
  108. package/dist/{figma-EIQ35PS6.js → figma-2BQNNR53.js} +3 -3
  109. package/dist/fontLocalizeCli.js +12 -12
  110. package/dist/{get-CC2YL7QO.js → get-P2XI53QW.js} +2 -2
  111. package/dist/{gpuPolicy-VFSACUQV.js → gpuPolicy-NTKQJY75.js} +9 -9
  112. package/dist/{grade-compare-TUQL7IV2.js → grade-compare-OYASIHIB.js} +29 -29
  113. package/dist/{gsapParserAcorn-7NCK2LVT.js → gsapParserAcorn-SKODXC2N.js} +2 -2
  114. package/dist/{help-ROROO5K6.js → help-HK6K2N4G.js} +1 -1
  115. package/dist/{history-PNELYLXX.js → history-JMTYKLXW.js} +6 -6
  116. package/dist/hyperframe.manifest.json +1 -1
  117. package/dist/hyperframe.runtime.iife.js +63 -63
  118. package/dist/hyperframes-player.global.js +1 -1
  119. package/dist/{info-4FZTH6AS.js → info-AW7CIVRE.js} +9 -9
  120. package/dist/{init-I43GHHVP.js → init-6KIDG4LB.js} +30 -30
  121. package/dist/{inspect-IT4TYIZ3.js → inspect-B7BLYMRZ.js} +29 -29
  122. package/dist/{installer-CWOJVK5K.js → installer-ZP2LQZUQ.js} +7 -7
  123. package/dist/{keyframes-3FBOGVD4.js → keyframes-EQ36A5JE.js} +7 -7
  124. package/dist/{lambda-H3V32I7M.js → lambda-E6VURLIS.js} +9 -9
  125. package/dist/{layout-BZ5LYJ5C.js → layout-XYC64QQQ.js} +29 -29
  126. package/dist/{lint-ZTNSGP2S.js → lint-V5DJYWBX.js} +9 -9
  127. package/dist/{lintProject-C2ONSH2U.js → lintProject-JATQKR64.js} +3 -3
  128. package/dist/{list-XM4GWJYY.js → list-RUGVRTVD.js} +2 -2
  129. package/dist/{login-H6HYKMWX.js → login-ZTKGKXGS.js} +4 -4
  130. package/dist/{manager-TWVDSJXO.js → manager-JUP7PM6N.js} +10 -10
  131. package/dist/{media-treatment-N7MY5NXB.js → media-treatment-VIUPK4OR.js} +15 -15
  132. package/dist/{models-WEMIMYBD.js → models-LJS2HBOZ.js} +13 -13
  133. package/dist/{motionShot-RHICTVPU.js → motionShot-3UAXZ7SN.js} +16 -16
  134. package/dist/{normalize-audio-ZFUZCJE3.js → normalize-audio-SBYI4YIH.js} +4 -4
  135. package/dist/{open-IBJ4FL3G.js → open-S27TUD3A.js} +14 -14
  136. package/dist/{parakeetRunner-XXO4WSLR.js → parakeetRunner-ETLMLJ4L.js} +14 -14
  137. package/dist/{pipeline-6ML3LUVK.js → pipeline-I2M22M24.js} +10 -10
  138. package/dist/{play-RV4NAVF5.js → play-COJU6VJK.js} +21 -21
  139. package/dist/{preflight-R3DEFMLA.js → preflight-FZ24NCHH.js} +10 -10
  140. package/dist/{present-5TBJGE6B.js → present-YAHKLC5D.js} +17 -17
  141. package/dist/{preview-FLKZ5CXJ.js → preview-KQOEU5YE.js} +20 -20
  142. package/dist/{producer-7XYSPVKH.js → producer-DTNNG4T6.js} +1 -1
  143. package/dist/{projectConfig-TBU2VWZX.js → projectConfig-4A76VYUP.js} +9 -9
  144. package/dist/{publication-CWWUJYJ5.js → publication-SQPMC2W3.js} +4 -4
  145. package/dist/{publish-FSNE4FEB.js → publish-QUEJBGGT.js} +11 -11
  146. package/dist/{remove-background-MDFWRWCT.js → remove-background-FP2ETAQH.js} +11 -11
  147. package/dist/{render-F4JQ553X.js → render-BIFIL4U6.js} +31 -31
  148. package/dist/{render-DBGQZAZT.js → render-GQFXJWLV.js} +17 -29
  149. package/dist/{render-LWZKXCLY.js → render-P664SMC7.js} +5 -5
  150. package/dist/{render-batch-PU7NMLIJ.js → render-batch-ZCPMYH6Z.js} +5 -5
  151. package/dist/renderSetupWorker.js +3 -3
  152. package/dist/{resolver-RUBGFXPC.js → resolver-BUC2GFZQ.js} +6 -6
  153. package/dist/{sherpa-X2TBNO6P.js → sherpa-HFT6XEP6.js} +13 -13
  154. package/dist/skills/hyperframes/SKILL.md +52 -174
  155. package/dist/skills/hyperframes/assets/frame-presets/biennale-yellow/FRAME.md +289 -0
  156. package/dist/skills/hyperframes/assets/frame-presets/biennale-yellow/caption-skin.html +220 -0
  157. package/dist/skills/hyperframes/assets/frame-presets/biennale-yellow/frame-showcase.html +1214 -0
  158. package/dist/skills/hyperframes/assets/frame-presets/blockframe/FRAME.md +275 -0
  159. package/dist/skills/hyperframes/assets/frame-presets/blockframe/caption-skin.html +223 -0
  160. package/dist/skills/hyperframes/assets/frame-presets/blockframe/frame-showcase.html +1347 -0
  161. package/dist/skills/hyperframes/assets/frame-presets/blue-professional/FRAME.md +307 -0
  162. package/dist/skills/hyperframes/assets/frame-presets/blue-professional/caption-skin.html +222 -0
  163. package/dist/skills/hyperframes/assets/frame-presets/blue-professional/frame-showcase.html +1372 -0
  164. package/dist/skills/hyperframes/assets/frame-presets/bold-poster/FRAME.md +275 -0
  165. package/dist/skills/hyperframes/assets/frame-presets/bold-poster/caption-skin.html +229 -0
  166. package/dist/skills/hyperframes/assets/frame-presets/bold-poster/frame-showcase.html +1140 -0
  167. package/dist/skills/hyperframes/assets/frame-presets/broadside/FRAME.md +284 -0
  168. package/dist/skills/hyperframes/assets/frame-presets/broadside/caption-skin.html +224 -0
  169. package/dist/skills/hyperframes/assets/frame-presets/broadside/frame-showcase.html +1166 -0
  170. package/dist/skills/hyperframes/assets/frame-presets/capsule/FRAME.md +284 -0
  171. package/dist/skills/hyperframes/assets/frame-presets/capsule/caption-skin.html +223 -0
  172. package/dist/skills/hyperframes/assets/frame-presets/capsule/frame-showcase.html +1465 -0
  173. package/dist/skills/hyperframes/assets/frame-presets/cartesian/FRAME.md +278 -0
  174. package/dist/skills/hyperframes/assets/frame-presets/cartesian/caption-skin.html +221 -0
  175. package/dist/skills/hyperframes/assets/frame-presets/cartesian/frame-showcase.html +1263 -0
  176. package/dist/skills/hyperframes/assets/frame-presets/cobalt-grid/FRAME.md +265 -0
  177. package/dist/skills/hyperframes/assets/frame-presets/cobalt-grid/caption-skin.html +237 -0
  178. package/dist/skills/hyperframes/assets/frame-presets/cobalt-grid/frame-showcase.html +1468 -0
  179. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/FRAME.md +277 -0
  180. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/caption-skin.html +223 -0
  181. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/fonts/EBGaramond-400.woff2 +0 -0
  182. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/fonts/EBGaramond-700.woff2 +0 -0
  183. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/fonts/Inter-400.woff2 +0 -0
  184. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/fonts/Inter-700.woff2 +0 -0
  185. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/fonts/JetBrainsMono-400.woff2 +0 -0
  186. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/fonts/JetBrainsMono-700.woff2 +0 -0
  187. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/fonts/OFL-eb-garamond.txt +93 -0
  188. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/fonts/OFL-inter.txt +93 -0
  189. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/fonts/OFL-jetbrains-mono.txt +93 -0
  190. package/dist/skills/hyperframes/assets/frame-presets/code-editorial/frame-showcase.html +1438 -0
  191. package/dist/skills/hyperframes/assets/frame-presets/coral/FRAME.md +314 -0
  192. package/dist/skills/hyperframes/assets/frame-presets/coral/caption-skin.html +225 -0
  193. package/dist/skills/hyperframes/assets/frame-presets/coral/frame-showcase.html +1268 -0
  194. package/dist/skills/hyperframes/assets/frame-presets/creative-mode/FRAME.md +333 -0
  195. package/dist/skills/hyperframes/assets/frame-presets/creative-mode/caption-skin.html +226 -0
  196. package/dist/skills/hyperframes/assets/frame-presets/creative-mode/frame-showcase.html +1330 -0
  197. package/dist/skills/hyperframes/assets/frame-presets/daisy-days/FRAME.md +274 -0
  198. package/dist/skills/hyperframes/assets/frame-presets/daisy-days/caption-skin.html +229 -0
  199. package/dist/skills/hyperframes/assets/frame-presets/daisy-days/frame-showcase.html +1444 -0
  200. package/dist/skills/hyperframes/assets/frame-presets/editorial-forest/FRAME.md +265 -0
  201. package/dist/skills/hyperframes/assets/frame-presets/editorial-forest/caption-skin.html +222 -0
  202. package/dist/skills/hyperframes/assets/frame-presets/editorial-forest/frame-showcase.html +1273 -0
  203. package/dist/skills/hyperframes/references/captions.md +46 -0
  204. package/dist/skills/hyperframes/references/film.md +47 -0
  205. package/dist/skills/hyperframes/references/format.md +157 -0
  206. package/dist/skills/hyperframes/references/look.md +69 -0
  207. package/dist/skills/hyperframes/references/motion.md +94 -0
  208. package/dist/skills/hyperframes/references/overlays.md +39 -0
  209. package/dist/skills/hyperframes/references/plugin-installation.md +3 -3
  210. package/dist/skills/hyperframes/references/review.md +51 -0
  211. package/dist/skills/hyperframes/references/runtimes.md +43 -0
  212. package/dist/skills/hyperframes/references/skill-lifecycle.md +21 -13
  213. package/dist/skills/hyperframes/references/sound.md +43 -0
  214. package/dist/skills/hyperframes/references/spec.md +76 -0
  215. package/dist/skills/hyperframes/references/story.md +103 -0
  216. package/dist/skills/hyperframes/references/storyboard-format.md +30 -22
  217. package/dist/skills/hyperframes/references/storyboard.md +36 -0
  218. package/dist/skills/hyperframes/references/types/3d.md +25 -0
  219. package/dist/skills/hyperframes/references/types/character.md +20 -0
  220. package/dist/skills/hyperframes/references/types/data.md +19 -0
  221. package/dist/skills/hyperframes/references/types/explainer.md +34 -0
  222. package/dist/skills/hyperframes/references/types/launch.md +31 -0
  223. package/dist/skills/hyperframes/references/types/music.md +24 -0
  224. package/dist/skills/hyperframes/references/types/vertical.md +23 -0
  225. package/dist/skills/hyperframes/scripts/bake-basemap.mjs +324 -0
  226. package/dist/skills/hyperframes/scripts/lib/main-module.test.mjs +1 -6
  227. package/dist/skills/hyperframes/scripts/locate.mjs +228 -0
  228. package/dist/skills/hyperframes-cli/SKILL.md +76 -179
  229. package/dist/skills/hyperframes-cli/references/beats.md +1 -1
  230. package/dist/skills/hyperframes-cli/references/cloud.md +2 -2
  231. package/dist/skills/hyperframes-cli/references/cloudrun.md +1 -1
  232. package/dist/skills/hyperframes-cli/references/conventions.md +17 -0
  233. package/dist/skills/hyperframes-cli/references/feedback.md +14 -0
  234. package/dist/skills/hyperframes-cli/references/init-and-scaffold.md +11 -7
  235. package/dist/skills/hyperframes-cli/references/lambda.md +1 -1
  236. package/dist/skills/hyperframes-cli/references/lint-validate-inspect.md +19 -8
  237. package/dist/skills/hyperframes-cli/references/preview-render.md +8 -2
  238. package/dist/skills/hyperframes-cli/references/upgrade-info-misc.md +8 -0
  239. package/dist/skills/media-use/SKILL.md +47 -91
  240. package/dist/skills/media-use/audio/references/bgm.md +1 -1
  241. package/dist/skills/media-use/audio/references/captions/authoring.md +1 -2
  242. package/dist/skills/media-use/audio/references/captions/motion.md +5 -5
  243. package/dist/skills/media-use/audio/references/tts.md +0 -3
  244. package/dist/skills/media-use/audio/scripts/audio.mjs +5 -6
  245. package/dist/skills/media-use/audio/scripts/carve.mjs +560 -0
  246. package/dist/skills/media-use/audio/scripts/carve.test.mjs +254 -0
  247. package/dist/skills/media-use/audio/scripts/extract-audio-data.py +199 -0
  248. package/dist/skills/media-use/references/local.md +24 -0
  249. package/dist/skills/media-use/references/memory.md +4 -4
  250. package/dist/skills/media-use/references/mixing.md +71 -0
  251. package/dist/skills/media-use/references/operations.md +1 -1
  252. package/dist/skills/media-use/scripts/compatibility.test.mjs +1 -56
  253. package/dist/skills/media-use/scripts/lib/prefs-store.mjs +1 -1
  254. package/dist/skills/media-use/scripts/lib/recipe-store.mjs +1 -1
  255. package/dist/skills/media-use/scripts/prefs.mjs +2 -3
  256. package/dist/skills/media-use/scripts/recipe.mjs +1 -3
  257. package/dist/{skills-WHKMWO2D.js → skills-3WEVO3NU.js} +20 -13
  258. package/dist/{skillsUpdateCheck-XQGZGJJG.js → skillsUpdateCheck-BFPWFOK5.js} +3 -3
  259. package/dist/{snapshot-M4NUUSNU.js → snapshot-WDQ6S7CJ.js} +457 -98
  260. package/dist/{src-6MSC62U2.js → src-NPR2JZOJ.js} +11 -11
  261. package/dist/{src-4UYM2K3S.js → src-RZ574OKP.js} +8 -8
  262. package/dist/{staticProjectServer-ERDTFP45.js → staticProjectServer-FTGTEZI4.js} +17 -17
  263. package/dist/studio/assets/{hyperframes-player-DEncBxR7.js → hyperframes-player-CoOdf435.js} +1 -1
  264. package/dist/studio/assets/{index-Cc22mgvY.js → index-CR85xg2x.js} +1 -1
  265. package/dist/studio/assets/{index-BABXr78Q.js → index-C_6rTpaB.js} +5 -5
  266. package/dist/studio/index.html +1 -1
  267. package/dist/{studio-api-SAKD3VKC.js → studio-api-TAWB7B23.js} +11 -11
  268. package/dist/{studioServer-IRI7EVOM.js → studioServer-HRQGJTBU.js} +48 -48
  269. package/dist/{telemetry-F4XADVAF.js → telemetry-QZ6HJF4W.js} +3 -3
  270. package/dist/templates/_shared/AGENTS.md +14 -19
  271. package/dist/templates/_shared/CLAUDE.md +14 -19
  272. package/dist/templates/blank/index.html +10 -4
  273. package/dist/{timeline-5I444RRE.js → timeline-IKUEW42T.js} +20 -19
  274. package/dist/{tokens-LRRYM35A.js → tokens-AUGUEWHS.js} +2 -2
  275. package/dist/{transcribe-44XDFNVU.js → transcribe-7734CG3N.js} +18 -18
  276. package/dist/{transcribe-WHYHWAB2.js → transcribe-FWU6EMEO.js} +12 -12
  277. package/dist/{updateCheck-7CPH3CAZ.js → updateCheck-VIPQ3FPW.js} +2 -2
  278. package/dist/{upgrade-WW4GQ2RI.js → upgrade-4VNDQTVO.js} +2 -2
  279. package/dist/{urlDownloader-OHLLP45A.js → urlDownloader-6CXL4CL7.js} +8 -8
  280. package/dist/{validate-VNCDH2LW.js → validate-QWCMVWID.js} +23 -23
  281. package/dist/{video-5OHTLL2D.js → video-245Q5WN4.js} +9 -9
  282. package/dist/warmCheck.js +36 -36
  283. package/package.json +1 -1
  284. package/dist/chunk-R2SQBPMH.js +0 -3805
  285. package/dist/skills/hyperframes/references/brief-contract.md +0 -108
  286. package/dist/skills/hyperframes/references/brief-format.md +0 -72
  287. package/dist/skills/hyperframes/references/capability-menu.md +0 -47
  288. package/dist/skills/hyperframes/references/frame-worker-core.md +0 -82
  289. package/dist/skills/hyperframes/references/intent-interview.md +0 -73
  290. package/dist/skills/hyperframes/references/pitch-round.md +0 -40
  291. package/dist/skills/hyperframes/references/production-loop.md +0 -26
  292. package/dist/skills/hyperframes/references/review-loop.md +0 -37
  293. package/dist/skills/hyperframes/references/route-briefs.md +0 -11
  294. package/dist/skills/hyperframes/references/routes/embedded-captions.md +0 -11
  295. package/dist/skills/hyperframes/references/routes/faceless-explainer.md +0 -12
  296. package/dist/skills/hyperframes/references/routes/general-video.md +0 -12
  297. package/dist/skills/hyperframes/references/routes/motion-graphics.md +0 -11
  298. package/dist/skills/hyperframes/references/routes/music-to-video.md +0 -12
  299. package/dist/skills/hyperframes/references/routes/pr-to-video.md +0 -28
  300. package/dist/skills/hyperframes/references/routes/product-launch-video.md +0 -13
  301. package/dist/skills/hyperframes/references/routes/remotion-to-hyperframes.md +0 -9
  302. package/dist/skills/hyperframes/references/routes/slideshow.md +0 -11
  303. package/dist/skills/hyperframes/references/routes/talking-head-recut.md +0 -11
  304. package/dist/skills/hyperframes/references/script-format.md +0 -52
  305. package/dist/skills/hyperframes/references/subagent-dispatch.md +0 -35
  306. package/dist/skills/hyperframes/references/workflow-catalog.md +0 -10
  307. package/dist/skills/hyperframes/scripts/lib/frame-packets-core.mjs +0 -176
  308. package/dist/skills/media-use/audio/scripts/lib/host-audio.mjs +0 -28
  309. package/dist/skills/media-use/audio/scripts/lib/host-audio.test.mjs +0 -106
@@ -7,6 +7,9 @@ var __dirname = __hf_dirname(__filename);
7
7
  import {
8
8
  writeCaptureFileSync
9
9
  } from "./chunk-MRF4AMKD.js";
10
+ import {
11
+ toDomNode
12
+ } from "./chunk-LLMSX6V6.js";
10
13
  import {
11
14
  parseAngle
12
15
  } from "./chunk-KZI44OPA.js";
@@ -22,7 +25,7 @@ import {
22
25
  findFFmpeg,
23
26
  findFFprobe,
24
27
  getFFmpegInstallHint
25
- } from "./chunk-7HJALUIO.js";
28
+ } from "./chunk-K2ZKXTVL.js";
26
29
  import {
27
30
  pngDimensions,
28
31
  reportArtifacts,
@@ -30,20 +33,20 @@ import {
30
33
  } from "./chunk-57GN6KWT.js";
31
34
  import {
32
35
  captureArtifactRevision
33
- } from "./chunk-VDM3FEDP.js";
34
- import "./chunk-FGJEUV4S.js";
36
+ } from "./chunk-OAG47RE6.js";
37
+ import "./chunk-PHMBMOXF.js";
35
38
  import "./chunk-X7MTZ4T2.js";
36
39
  import {
37
40
  resolveProject
38
- } from "./chunk-LRGYPXHF.js";
41
+ } from "./chunk-5GUKZRQN.js";
39
42
  import {
40
43
  serveStaticProjectHtml
41
- } from "./chunk-4KKKBEP3.js";
42
- import "./chunk-YWNJTZGN.js";
44
+ } from "./chunk-J5XEZ24I.js";
45
+ import "./chunk-H6UY725W.js";
43
46
  import "./chunk-X7MTZ4T2.js";
44
47
  import "./chunk-XUHOIDKG.js";
45
- import "./chunk-3QVMJ7GX.js";
46
- import "./chunk-ZRHO6YMM.js";
48
+ import "./chunk-KQ4JEJLF.js";
49
+ import "./chunk-SKYPMHQC.js";
47
50
  import "./chunk-6JR2XUEE.js";
48
51
  import "./chunk-TTVHYD5R.js";
49
52
  import {
@@ -53,7 +56,7 @@ import {
53
56
  recordArtifacts,
54
57
  retainSnapshotOutput
55
58
  } from "./chunk-U6RXZNZE.js";
56
- import "./chunk-SN7KI5JA.js";
59
+ import "./chunk-6GPR3G3C.js";
57
60
  import "./chunk-ALVXFXUG.js";
58
61
  import {
59
62
  DEFAULT_ZOOM_SCALE,
@@ -63,43 +66,44 @@ import {
63
66
  resolveCropRegion,
64
67
  runFfmpegOnce,
65
68
  seekCompositionTimeline
66
- } from "./chunk-ICJEAFPD.js";
69
+ } from "./chunk-IDFNKFPL.js";
67
70
  import "./chunk-FH7WODJH.js";
68
- import "./chunk-KN46TGHV.js";
71
+ import "./chunk-TOI35GN2.js";
69
72
  import "./chunk-MOEGCMKI.js";
70
- import "./chunk-ES426XRJ.js";
73
+ import "./chunk-SQVRXHOW.js";
71
74
  import "./chunk-DKPNNYBZ.js";
72
- import "./chunk-PSKEKWVU.js";
75
+ import "./chunk-G2UANIDX.js";
73
76
  import "./chunk-K2FLNJVB.js";
74
77
  import "./chunk-442JNXRM.js";
75
78
  import "./chunk-EHSBBBR2.js";
76
79
  import "./chunk-DQBY7FRR.js";
77
80
  import {
78
81
  resolveLocalBrowserGpuMode
79
- } from "./chunk-UNNXFKVG.js";
80
- import "./chunk-PFYWVUHA.js";
82
+ } from "./chunk-EMSDWBG3.js";
83
+ import "./chunk-HDIRZGK4.js";
81
84
  import "./chunk-XT7X6QCP.js";
82
85
  import "./chunk-4YYJOFKS.js";
83
86
  import "./chunk-HVW7ABXD.js";
84
- import "./chunk-ZOCHDXDG.js";
87
+ import "./chunk-RTBSA3EA.js";
85
88
  import {
86
89
  runWithConcurrencyLimit
87
90
  } from "./chunk-JHOCK3DG.js";
88
- import "./chunk-LLC4CS6I.js";
89
- import "./chunk-7MB2ZAQF.js";
91
+ import "./chunk-HXUTX5GR.js";
92
+ import "./chunk-PMMQRJDE.js";
90
93
  import "./chunk-AWDFDFI3.js";
91
94
  import "./chunk-P6LJ4Y32.js";
92
95
  import "./chunk-GMECXFAN.js";
93
- import "./chunk-5CS4T6C6.js";
96
+ import "./chunk-7L37AB57.js";
94
97
  import "./chunk-EOVYQ3IG.js";
95
- import "./chunk-JIUTXKWA.js";
96
- import "./chunk-UKOTVJFV.js";
98
+ import "./chunk-WQLKDL35.js";
99
+ import "./chunk-H5SM5PJI.js";
97
100
  import "./chunk-VWG7OQKU.js";
98
101
  import {
99
102
  defineCommand
100
103
  } from "./chunk-S3S4TGEH.js";
101
104
  import "./chunk-54HJ63Q2.js";
102
105
  import {
106
+ CliRuntimeError,
103
107
  failCommand
104
108
  } from "./chunk-JVBRJMOP.js";
105
109
  import {
@@ -108,23 +112,26 @@ import {
108
112
  import "./chunk-KLIKCX6Q.js";
109
113
  import "./chunk-EVMY7IPL.js";
110
114
  import "./chunk-7FMUTGOH.js";
111
- import "./chunk-RGXVEYHI.js";
115
+ import "./chunk-I3IH5MV5.js";
112
116
  import "./chunk-IZCW2NKT.js";
113
117
  import {
114
118
  createRuntimeStartTimeResolver,
115
119
  isClipVisibleAt,
116
120
  isInClipWindow
117
- } from "./chunk-J3QM7AHP.js";
121
+ } from "./chunk-KROXPHXX.js";
118
122
  import "./chunk-RQVDTPGD.js";
119
- import "./chunk-YALM6XN4.js";
123
+ import "./chunk-Q47FAYYK.js";
120
124
  import {
121
125
  readElementRateSpec,
122
126
  sourceTimeAt,
123
127
  timeAtSourceTime
124
- } from "./chunk-R2SQBPMH.js";
128
+ } from "./chunk-3NPOFC43.js";
125
129
  import "./chunk-VC76R6W2.js";
126
130
  import "./chunk-UJKQWGFF.js";
127
- import "./chunk-YMS5FOMC.js";
131
+ import {
132
+ topLevelElements,
133
+ trackKindOf
134
+ } from "./chunk-B2A56S5C.js";
128
135
  import "./chunk-OFQWTKPW.js";
129
136
  import "./chunk-DPVXBVLL.js";
130
137
  import "./chunk-3GXQPCJC.js";
@@ -142,23 +149,26 @@ import "./chunk-XT6MXC7A.js";
142
149
  import "./chunk-4UMPIPCQ.js";
143
150
  import {
144
151
  lintProject
145
- } from "./chunk-ADDHW2FS.js";
152
+ } from "./chunk-Q447CX26.js";
146
153
  import "./chunk-VAS6HQX3.js";
147
154
  import "./chunk-6BNTGNGD.js";
148
- import "./chunk-OYLO6TYK.js";
155
+ import "./chunk-RUOG4VB7.js";
149
156
  import "./chunk-MMQ52NIZ.js";
150
157
  import "./chunk-BSQ7XANM.js";
151
158
  import {
152
159
  parseHTML
153
160
  } from "./chunk-SDYDNHEE.js";
154
161
  import "./chunk-IXLUTE6N.js";
155
- import "./chunk-TYXH3EKP.js";
162
+ import "./chunk-I5JRXMCV.js";
156
163
  import "./chunk-W2LGJSEN.js";
164
+ import {
165
+ withMeta
166
+ } from "./chunk-ISTRDGIX.js";
157
167
  import "./chunk-K4WIOZFP.js";
158
168
  import "./chunk-XUXGIA7H.js";
159
169
  import "./chunk-WERLJGIC.js";
160
170
  import "./chunk-ISN5GRYI.js";
161
- import "./chunk-4N4P3ZWL.js";
171
+ import "./chunk-I7QMXZSB.js";
162
172
  import {
163
173
  normalizeErrorMessage
164
174
  } from "./chunk-KNU5COPB.js";
@@ -182,6 +192,15 @@ function sampleSnapshotRange([start, end], frames, duration, exclusiveEnd = fals
182
192
  const last = exclusiveEnd || end === duration ? end - Math.min(1e-3, (end - start) / (frames * 2)) : end;
183
193
  return Array.from({ length: frames }, (_, i) => start + (last - start) * i / (frames - 1));
184
194
  }
195
+ function snapshotStartResolver(document2, durations) {
196
+ return createRuntimeStartTimeResolver({
197
+ documentRef: document2,
198
+ timelineRegistry: Object.fromEntries(
199
+ Object.entries(durations).map(([key, value]) => [key, { duration: () => value }])
200
+ ),
201
+ includeAuthoredTimingAttrs: true
202
+ });
203
+ }
185
204
  function resolveSnapshotCompositionRange(html, durations, id, duration) {
186
205
  const { document: document2 } = parseHTML(html);
187
206
  const matches = Array.from(document2.querySelectorAll("[data-composition-id]")).filter(
@@ -191,14 +210,7 @@ function resolveSnapshotCompositionRange(html, durations, id, duration) {
191
210
  throw new Error(
192
211
  `Composition ID ${JSON.stringify(id)} matched ${matches.length} elements; expected exactly one`
193
212
  );
194
- const registry = Object.fromEntries(
195
- Object.entries(durations).map(([key, value]) => [key, { duration: () => value }])
196
- );
197
- const resolver = createRuntimeStartTimeResolver({
198
- documentRef: document2,
199
- timelineRegistry: registry,
200
- includeAuthoredTimingAttrs: true
201
- });
213
+ const resolver = snapshotStartResolver(document2, durations);
202
214
  let start = 0, end = duration;
203
215
  for (let el = matches[0]; el; el = el.parentElement) {
204
216
  if (!el.hasAttribute("data-composition-id") && !el.hasAttribute("data-start")) continue;
@@ -219,6 +231,208 @@ import { randomUUID } from "crypto";
219
231
  import { existsSync, mkdtempSync, readFileSync, mkdirSync, rmSync, writeFileSync } from "fs";
220
232
  import { tmpdir } from "os";
221
233
  import { resolve, join, relative, isAbsolute, basename, posix } from "path";
234
+
235
+ // src/commands/snapshotReview.ts
236
+ var REVIEW_SETTLE_SEC = 0.3;
237
+ var REVIEW_MID_GAP_SEC = 0.5;
238
+ var REVIEW_SEAM_OFFSETS = [-0.2, -0.1, 0, 0.1, 0.2];
239
+ var REVIEW_PHONE_WIDTH = 360;
240
+ var SAME_TIME_SEC = 0.05;
241
+ var EVEN_BEATS = 5;
242
+ function roundMs(value) {
243
+ return Math.round(value * 1e3) / 1e3;
244
+ }
245
+ function sceneId(el, index) {
246
+ return el.id || el.getAttribute("data-composition-id") || el.getAttribute("data-layout-name") || `${el.tagName.toLowerCase()}-${index + 1}`;
247
+ }
248
+ function resolveReviewTimeline(html, durations, duration, compositionId) {
249
+ const bounds = compositionId ? resolveSnapshotCompositionRange(html, durations, compositionId, duration) : [0, duration];
250
+ const { document: document2 } = parseHTML(html);
251
+ const target = compositionId ? Array.from(document2.querySelectorAll("[data-composition-id]")).find(
252
+ (el) => el.getAttribute("data-composition-id") === compositionId
253
+ ) : document2.querySelector("[data-composition-id]");
254
+ if (!target) {
255
+ return { window: bounds, scenes: [], beats: [], notes: ["No composition root found."] };
256
+ }
257
+ const resolver = snapshotStartResolver(document2, durations);
258
+ const span = (el) => {
259
+ const start = resolver.resolveStartForElement(el, 0);
260
+ if (!Number.isFinite(start)) return null;
261
+ const length = resolver.resolveDurationForElement(el);
262
+ const from = Math.max(bounds[0], start);
263
+ const to = Math.min(bounds[1], length != null ? start + length : bounds[1]);
264
+ return to > from ? [roundMs(from), roundMs(to)] : null;
265
+ };
266
+ const scenes = [];
267
+ const rows = topLevelElements(toDomNode(target)).filter((node) => {
268
+ const kind = trackKindOf(node).kind;
269
+ return kind === "video" || kind === "graphics";
270
+ });
271
+ rows.forEach((node, index) => {
272
+ const interval = span(node.el);
273
+ if (interval)
274
+ scenes.push({ id: sceneId(node.el, index), start: interval[0], end: interval[1] });
275
+ });
276
+ const beats = [];
277
+ const notes = [];
278
+ for (const el of Array.from(target.querySelectorAll("[data-beat]"))) {
279
+ const name = el.getAttribute("data-beat")?.trim() || sceneId(el, beats.length);
280
+ const timed = el.hasAttribute("data-start") || el.hasAttribute("data-composition-id");
281
+ const interval = timed ? span(el) : null;
282
+ if (!interval) {
283
+ notes.push(`data-beat="${name}" was skipped: give it a data-start so the beat has a time.`);
284
+ continue;
285
+ }
286
+ beats.push({ time: settledTime(interval[0], interval[1]), label: `beat:${name}` });
287
+ }
288
+ scenes.sort((a, b) => a.start - b.start || a.end - b.end);
289
+ return { window: bounds, scenes, beats, notes };
290
+ }
291
+ function sceneBeats(scene) {
292
+ const settled = settledTime(scene.start, scene.end);
293
+ const middle = roundMs((scene.start + scene.end) / 2);
294
+ const beats = [{ time: settled, label: scene.id }];
295
+ return middle - settled >= REVIEW_MID_GAP_SEC ? [...beats, { time: middle, label: `${scene.id} mid` }] : beats;
296
+ }
297
+ function settledTime(start, end) {
298
+ return roundMs(Math.min(start + REVIEW_SETTLE_SEC, (start + end) / 2));
299
+ }
300
+ function planReview(timeline, lastFrame) {
301
+ const { window: bounds, scenes } = timeline;
302
+ const notes = [...timeline.notes];
303
+ const clamp = (time) => roundMs(Math.min(Math.max(time, bounds[0]), lastFrame));
304
+ const distinctStarts = mergeBeats(
305
+ scenes.map((scene) => ({ time: scene.start, label: scene.id }))
306
+ );
307
+ const beats = mergeBeats([
308
+ ...distinctStarts.length >= 2 ? scenes.flatMap(sceneBeats) : evenBeats(bounds, notes),
309
+ ...timeline.beats
310
+ ]).map((beat) => ({ ...beat, time: clamp(beat.time) }));
311
+ const seams = seamTimes(scenes, bounds).map((time) => ({
312
+ time,
313
+ ...seamSides(scenes, time),
314
+ times: REVIEW_SEAM_OFFSETS.map((offset) => clamp(time + offset))
315
+ }));
316
+ return { window: bounds, scenes, beats, seams, notes };
317
+ }
318
+ function evenBeats(bounds, notes) {
319
+ notes.push(
320
+ `Fewer than two scene starts; ${EVEN_BEATS} beats are spread evenly instead. Time scenes as sub-composition hosts or clips to review each one.`
321
+ );
322
+ const [start, end] = bounds;
323
+ return Array.from({ length: EVEN_BEATS }, (_, index) => {
324
+ const time = roundMs(start + (index + 0.5) / EVEN_BEATS * (end - start));
325
+ return { time, label: `t=${time}` };
326
+ });
327
+ }
328
+ function mergeBeats(beats) {
329
+ const merged = [];
330
+ for (const beat of [...beats].sort((a, b) => a.time - b.time)) {
331
+ const same = merged.find((entry) => Math.abs(entry.time - beat.time) < SAME_TIME_SEC);
332
+ if (same) same.label = `${same.label} + ${beat.label}`;
333
+ else merged.push({ ...beat });
334
+ }
335
+ return merged;
336
+ }
337
+ function seamTimes(scenes, bounds) {
338
+ const inside = (time) => time > bounds[0] + SAME_TIME_SEC && time < bounds[1] - SAME_TIME_SEC;
339
+ const starts = scenes.map((scene) => scene.start).filter(inside);
340
+ const ends = scenes.map((scene) => scene.end).filter(inside).filter((end) => !starts.some((start) => Math.abs(start - end) < SAME_TIME_SEC));
341
+ const seams = [];
342
+ for (const time of [...starts, ...ends].sort((a, b) => a - b)) {
343
+ if (!seams.some((seam) => Math.abs(seam - time) < SAME_TIME_SEC)) seams.push(time);
344
+ }
345
+ return seams;
346
+ }
347
+ function seamSides(scenes, time) {
348
+ const before = scenes.filter((scene) => scene.start < time - SAME_TIME_SEC && scene.end >= time - SAME_TIME_SEC).sort((a, b) => b.start - a.start)[0];
349
+ const after = scenes.filter((scene) => Math.abs(scene.start - time) < SAME_TIME_SEC).sort((a, b) => a.end - b.end)[0];
350
+ const next = after ?? scenes.find((scene) => scene.start > time);
351
+ return { from: before?.id ?? null, to: next?.id ?? null };
352
+ }
353
+ function pad(index) {
354
+ return String(index).padStart(2, "0");
355
+ }
356
+ function stamp(time) {
357
+ return `${Number(time.toFixed(3))}s`;
358
+ }
359
+ var SEAM_TILE_LABELS = ["cut -0.2", "-0.1", "cut", "+0.1", "+0.2"];
360
+ function reviewFrames(plan) {
361
+ const beats = plan.beats.map((beat, index) => ({
362
+ role: "beat",
363
+ index: index + 1,
364
+ time: beat.time,
365
+ label: beat.label,
366
+ file: `beat-${pad(index + 1)}-at-${stamp(beat.time)}.png`
367
+ }));
368
+ const seams = plan.seams.flatMap(
369
+ (seam, index) => seam.times.map((time, slot) => ({
370
+ role: "seam",
371
+ index: index + 1,
372
+ time,
373
+ label: slot === 2 ? `cut ${seam.from ?? "start"} -> ${seam.to ?? "end"}` : SEAM_TILE_LABELS[slot] ?? "",
374
+ file: `seam-${pad(index + 1)}-${slot + 1}-at-${stamp(time)}.png`
375
+ }))
376
+ );
377
+ return [...beats, ...seams];
378
+ }
379
+ var BEAT_SHEET_PAGE = 16;
380
+ async function writeReviewSheets(dir, frames, written) {
381
+ const { createContactSheet } = await import("./contactSheet-CAGMKI3E.js");
382
+ const { join: join2 } = await import("path");
383
+ const captured = frames.flatMap((frame) => {
384
+ const path = written(frame);
385
+ return path ? [{ frame, path }] : [];
386
+ });
387
+ const beats = captured.filter((entry) => entry.frame.role === "beat");
388
+ const beatLabel = ({ frame }) => `${stamp(frame.time)} ${frame.label}`;
389
+ const pages = async (name, cellWidth, colsFor) => {
390
+ const sheets = [];
391
+ for (let start = 0; start < beats.length; start += BEAT_SHEET_PAGE) {
392
+ const page = beats.slice(start, start + BEAT_SHEET_PAGE);
393
+ const suffix = beats.length > BEAT_SHEET_PAGE ? `-${start / BEAT_SHEET_PAGE + 1}` : "";
394
+ const sheet = await createContactSheet(
395
+ page.map((entry) => entry.path),
396
+ join2(dir, `${name}${suffix}.jpg`),
397
+ {
398
+ cols: colsFor(page.length),
399
+ maxImages: page.length,
400
+ cellWidth,
401
+ labelMode: "custom",
402
+ labels: page.map(beatLabel)
403
+ }
404
+ );
405
+ if (sheet) sheets.push(sheet);
406
+ }
407
+ return sheets;
408
+ };
409
+ const seams = [];
410
+ const seamNumbers = [
411
+ ...new Set(captured.filter((e) => e.frame.role === "seam").map((e) => e.frame.index))
412
+ ];
413
+ for (const index of seamNumbers) {
414
+ const strip = captured.filter((e) => e.frame.role === "seam" && e.frame.index === index);
415
+ const sheet = await createContactSheet(
416
+ strip.map((entry) => entry.path),
417
+ join2(dir, `review-seam-${pad(index)}.jpg`),
418
+ {
419
+ cols: strip.length,
420
+ maxImages: strip.length,
421
+ cellWidth: 384,
422
+ labelMode: "custom",
423
+ labels: strip.map(({ frame }) => `${stamp(frame.time)} ${frame.label}`)
424
+ }
425
+ );
426
+ if (sheet) seams.push(sheet);
427
+ }
428
+ return {
429
+ beats: await pages("review-beats", 480, (count) => count <= 3 ? count : count <= 9 ? 3 : 4),
430
+ seams,
431
+ phone: await pages("review-phone", REVIEW_PHONE_WIDTH, (count) => Math.min(4, count))
432
+ };
433
+ }
434
+
435
+ // src/commands/snapshot.ts
222
436
  function orbitStageSource() {
223
437
  return `function(cam) {
224
438
  var root = document.querySelector("[data-composition-id]")
@@ -479,7 +693,8 @@ function serveCapturePage(projectDir, html, proxy) {
479
693
  return serveStaticProjectHtml(projectDir, html, void 0, [], proxy === true);
480
694
  }
481
695
  async function captureSnapshots(projectDir, opts) {
482
- const { bundleWithLocalizedFonts } = await import("./bundleWithLocalizedFonts-2EGPA5ZO.js");
696
+ const log = opts.log ?? ((message) => console.log(message));
697
+ const { bundleWithLocalizedFonts } = await import("./bundleWithLocalizedFonts-67MLTHKM.js");
483
698
  const capture = await captureArtifactRevision(projectDir).catch(warnSnapshotsNotRecorded);
484
699
  const invocation = randomUUID();
485
700
  const artifacts = [];
@@ -487,6 +702,9 @@ async function captureSnapshots(projectDir, opts) {
487
702
  const warnings = [];
488
703
  let completed = false;
489
704
  const numFrames = opts.frames ?? 5;
705
+ let review;
706
+ let reviewList;
707
+ const reviewPaths = /* @__PURE__ */ new Map();
490
708
  const html = await bundleWithLocalizedFonts(projectDir);
491
709
  const server = await serveCapturePage(projectDir, html, opts.autoProxy);
492
710
  const savedPaths = [];
@@ -518,10 +736,10 @@ async function captureSnapshots(projectDir, opts) {
518
736
  const parts = [`${fontReport.loaded.length} loaded`];
519
737
  if (fontReport.errored.length > 0) parts.push(`${fontReport.errored.length} failed`);
520
738
  if (fontReport.unused.length > 0) parts.push(`${fontReport.unused.length} unused`);
521
- console.log(`
739
+ log(`
522
740
  ${c.dim("Fonts:")} ${parts.join(", ")}`);
523
741
  if (fontReport.errored.length > 0) {
524
- console.log(` ${c.error("Fonts FAILED:")} ${fontReport.errored.join(", ")}`);
742
+ log(` ${c.error("Fonts FAILED:")} ${fontReport.errored.join(", ")}`);
525
743
  }
526
744
  }
527
745
  if (fontReport.errored.length)
@@ -537,19 +755,20 @@ async function captureSnapshots(projectDir, opts) {
537
755
  return 0;
538
756
  });
539
757
  if (duration <= 0 && !opts.at?.length) {
540
- return [];
758
+ return { paths: [] };
541
759
  }
760
+ const readTiming = () => page.evaluate(() => ({
761
+ html: document.documentElement.outerHTML,
762
+ durations: Object.fromEntries(
763
+ Object.entries(window.__timelines ?? {}).map(([id, timeline]) => [
764
+ id,
765
+ Number(timeline.duration?.())
766
+ ])
767
+ )
768
+ }));
542
769
  let range = opts.range;
543
- if (opts.composition) {
544
- const timing = await page.evaluate(() => ({
545
- html: document.documentElement.outerHTML,
546
- durations: Object.fromEntries(
547
- Object.entries(window.__timelines ?? {}).map(([id, timeline]) => [
548
- id,
549
- Number(timeline.duration?.())
550
- ])
551
- )
552
- }));
770
+ if (opts.composition && !opts.review) {
771
+ const timing = await readTiming();
553
772
  range = resolveSnapshotCompositionRange(
554
773
  timing.html,
555
774
  timing.durations,
@@ -557,7 +776,18 @@ async function captureSnapshots(projectDir, opts) {
557
776
  duration
558
777
  );
559
778
  }
560
- const { times: positions, appendedTail } = range ? {
779
+ if (opts.review) {
780
+ const timing = await readTiming();
781
+ const timeline = resolveReviewTimeline(
782
+ timing.html,
783
+ timing.durations,
784
+ duration,
785
+ opts.composition
786
+ );
787
+ review = planReview(timeline, Math.min(timeline.window[1], duration) - 1 / 30);
788
+ reviewList = reviewFrames(review);
789
+ }
790
+ const { times: positions, appendedTail } = reviewList ? { times: reviewList.map((frame) => frame.time), appendedTail: false } : range ? {
561
791
  times: sampleSnapshotRange(range, numFrames, duration, !!opts.composition),
562
792
  appendedTail: false
563
793
  } : computeSnapshotTimes(duration, {
@@ -566,11 +796,15 @@ async function captureSnapshots(projectDir, opts) {
566
796
  includeEnd: opts.includeEnd
567
797
  });
568
798
  if (range)
569
- console.log(
799
+ log(
570
800
  ` Sampling ${positions.length} frames within ${range[0]}\u2013${range[1]}s${opts.composition ? ` (composition ${opts.composition})` : ""}`
571
801
  );
802
+ if (review)
803
+ log(
804
+ ` Review: ${review.beats.length} beat(s) and ${review.seams.length} seam(s) within ${review.window[0]}\u2013${review.window[1]}s${opts.composition ? ` (composition ${opts.composition})` : ""}`
805
+ );
572
806
  if (appendedTail) {
573
- console.log(
807
+ log(
574
808
  ` ${c.dim(`Note: added an end-of-timeline frame at ${positions[positions.length - 1].toFixed(2)}s. Short beats between your --at times may still be skipped \u2014 pass them explicitly.`)}`
575
809
  );
576
810
  }
@@ -592,7 +826,7 @@ async function captureSnapshots(projectDir, opts) {
592
826
  let syncVideoFrameVisibility = null;
593
827
  let extractMediaMetadata = null;
594
828
  try {
595
- const engine = await import("./src-4UYM2K3S.js");
829
+ const engine = await import("./src-RZ574OKP.js");
596
830
  injectVideoFramesBatch = engine.injectVideoFramesBatch;
597
831
  syncVideoFrameVisibility = engine.syncVideoFrameVisibility;
598
832
  extractMediaMetadata = engine.extractMediaMetadata;
@@ -730,7 +964,8 @@ async function captureSnapshots(projectDir, opts) {
730
964
  await recaptureSnapshotComposite(page);
731
965
  const timeLabel = formatSnapshotTimestamp(time);
732
966
  const slot = opts.keep ? `at-${timeLabel}` : `${String(i).padStart(2, "0")}-at-${timeLabel}`;
733
- const filename = opts.keep ? `${slot}.png` : `frame-${slot}.png`;
967
+ const reviewFrame = reviewList?.[i];
968
+ const filename = reviewFrame ? reviewFrame.file : opts.keep ? `${slot}.png` : `frame-${slot}.png`;
734
969
  const framePath = join(snapshotDir, filename);
735
970
  let crop;
736
971
  if (opts.zoom) {
@@ -769,9 +1004,11 @@ async function captureSnapshots(projectDir, opts) {
769
1004
  zoom: opts.zoom,
770
1005
  zoomScale: opts.zoom ? opts.zoomScale ?? DEFAULT_ZOOM_SCALE : 1,
771
1006
  angle: opts.angle,
772
- browserGpuMode: opts.browserGpuMode
1007
+ browserGpuMode: opts.browserGpuMode,
1008
+ ...reviewFrame ? { review: { role: reviewFrame.role, label: reviewFrame.label } } : {}
773
1009
  }
774
1010
  });
1011
+ if (reviewFrame) reviewPaths.set(reviewFrame, framePath);
775
1012
  if (opts.against) {
776
1013
  const refPng = await extractVideoFrameToBuffer(opts.against, time);
777
1014
  if (!refPng) {
@@ -807,20 +1044,32 @@ async function captureSnapshots(projectDir, opts) {
807
1044
  artifacts,
808
1045
  opts.zoom,
809
1046
  warnings,
810
- completed
1047
+ completed,
1048
+ log
811
1049
  ) : false;
812
1050
  if (!recorded) {
813
1051
  const paths = [...replaced, ...artifacts.map((artifact) => artifact.image)];
814
1052
  if (paths.length > 0) await forgetRecordedArtifacts(projectDir, paths).catch(() => void 0);
815
1053
  }
816
1054
  }
817
- return savedPaths;
1055
+ return {
1056
+ paths: savedPaths,
1057
+ ...review && reviewList ? {
1058
+ review: {
1059
+ plan: review,
1060
+ frames: reviewList.flatMap((frame) => {
1061
+ const path = reviewPaths.get(frame);
1062
+ return path ? [{ ...frame, path }] : [];
1063
+ })
1064
+ }
1065
+ } : {}
1066
+ };
818
1067
  }
819
1068
  function warnSnapshotsNotRecorded(error) {
820
1069
  console.warn(` ${c.warn("\u26A0")} Snapshots were not recorded: ${normalizeErrorMessage(error)}`);
821
1070
  return void 0;
822
1071
  }
823
- async function recordSnapshotArtifacts(projectDir, capture, artifacts, zoom, warnings, completed) {
1072
+ async function recordSnapshotArtifacts(projectDir, capture, artifacts, zoom, warnings, completed, log) {
824
1073
  const records = await recordArtifacts(
825
1074
  projectDir,
826
1075
  capture,
@@ -833,10 +1082,65 @@ async function recordSnapshotArtifacts(projectDir, capture, artifacts, zoom, war
833
1082
  ).catch(warnSnapshotsNotRecorded);
834
1083
  const record = records?.[0];
835
1084
  if (!record) return false;
836
- console.log(` Revision: ${record.revision ?? "none"} (${record.attribution})`);
837
- console.log(` State: ${join(projectDir, ".hyperframes", "video-state.json")}`);
1085
+ log(` Revision: ${record.revision ?? "none"} (${record.attribution})`);
1086
+ log(` State: ${join(projectDir, ".hyperframes", "video-state.json")}`);
838
1087
  return true;
839
1088
  }
1089
+ function snapshotEnvelope(projectDir, outputDir, captured, extra) {
1090
+ const review = captured.review;
1091
+ const frames = review ? review.frames.map((frame) => ({
1092
+ path: frame.path,
1093
+ time: frame.time,
1094
+ role: frame.role,
1095
+ label: frame.label
1096
+ })) : captured.paths.map((path) => ({ path: resolve(projectDir, path), time: timeFromName(path) }));
1097
+ return {
1098
+ ok: true,
1099
+ outputDir,
1100
+ frames,
1101
+ contactSheets: extra.contactSheets.map((sheet) => resolve(sheet)),
1102
+ ...extra.describedPath ? { descriptions: extra.describedPath } : {},
1103
+ ...review ? {
1104
+ review: {
1105
+ window: review.plan.window,
1106
+ scenes: review.plan.scenes,
1107
+ beats: review.plan.beats.map((beat, index) => ({
1108
+ ...beat,
1109
+ path: review.frames.find(
1110
+ (frame) => frame.role === "beat" && frame.index === index + 1
1111
+ )?.path
1112
+ })),
1113
+ seams: review.plan.seams.map((seam, index) => ({
1114
+ ...seam,
1115
+ paths: review.frames.filter((frame) => frame.role === "seam" && frame.index === index + 1).map((frame) => frame.path),
1116
+ sheet: extra.reviewSheets?.seams[index]
1117
+ })),
1118
+ sheets: extra.reviewSheets ?? { beats: [], seams: [], phone: [] },
1119
+ notes: review.plan.notes
1120
+ }
1121
+ } : {}
1122
+ };
1123
+ }
1124
+ function timeFromName(path) {
1125
+ const match = /at-([\d.]+)s\.png$/.exec(path);
1126
+ return match ? Number(match[1]) : null;
1127
+ }
1128
+ function printReview(review, sheets, log) {
1129
+ const beats = review.plan.beats.map(
1130
+ (beat) => `${formatSnapshotTimestamp(beat.time)} ${beat.label}`
1131
+ );
1132
+ log(` Beat sheet (settled frame per scene): ${sheets.beats.join(", ") || "none"}`);
1133
+ log(` ${c.dim(beats.join(" \xB7 "))}`);
1134
+ review.plan.seams.forEach((seam, index) => {
1135
+ const sides = `${seam.from ?? "start"} -> ${seam.to ?? "end"}`;
1136
+ const strip = sheets.seams[index] ?? "not written";
1137
+ log(` Seam ${index + 1} at ${formatSnapshotTimestamp(seam.time)} (${sides}): ${strip}`);
1138
+ });
1139
+ log(
1140
+ ` Phone sheet (beats at ${REVIEW_PHONE_WIDTH} px wide): ${sheets.phone.join(", ") || "none"}`
1141
+ );
1142
+ for (const note of review.plan.notes) log(` ${c.dim(note)}`);
1143
+ }
840
1144
  var snapshot_default = defineCommand({
841
1145
  meta: {
842
1146
  name: "snapshot",
@@ -902,6 +1206,16 @@ var snapshot_default = defineCommand({
902
1206
  type: "string",
903
1207
  description: "Reference video (e.g. footage being rebuilt): also save its frame at each captured time and a render|reference pair sheet"
904
1208
  },
1209
+ review: {
1210
+ type: "boolean",
1211
+ description: "Capture the review set instead of evenly spaced frames: each scene at its start + 0.3 s and its middle, and each data-beat at its start + 0.3 s, on review-beats.jpg, a 5-frame strip around every cut (review-seam-NN.jpg), and the beat frames at phone width (review-phone.jpg). Scopes to --composition; ignores --frames.",
1212
+ default: false
1213
+ },
1214
+ json: {
1215
+ type: "boolean",
1216
+ description: "Print one JSON envelope (paths, times, sheets; the review plan under --review)",
1217
+ default: false
1218
+ },
905
1219
  describe: {
906
1220
  type: "string",
907
1221
  description: "Gemini vision frame analysis. Runs by default when GEMINI_API_KEY is set. Pass a custom question (e.g. --describe 'Is the logo visible in every beat?') to override the default prompt, or --describe false to opt out."
@@ -918,8 +1232,22 @@ var snapshot_default = defineCommand({
918
1232
  }
919
1233
  },
920
1234
  async run({ args }) {
1235
+ const asJson = args.json === true;
1236
+ const log = (message) => asJson ? console.error(message) : console.log(message);
1237
+ const fail = (message) => {
1238
+ if (asJson) console.log(JSON.stringify(withMeta({ ok: false, error: message }), null, 2));
1239
+ else log(message);
1240
+ failCommand();
1241
+ };
921
1242
  if ([args.at, args.range, args.composition].filter((value) => value !== void 0).length > 1)
922
1243
  throw new Error("Use only one of --at, --range, or --composition");
1244
+ const reviewMode = args.review === true;
1245
+ if (reviewMode) {
1246
+ const clash = ["at", "range", "zoom", "keep", "against"].find(
1247
+ (flag) => args[flag] !== void 0 && args[flag] !== false
1248
+ );
1249
+ if (clash) throw new Error(`--review picks its own frames; drop --${clash}`);
1250
+ }
923
1251
  const frames = parseInt(args.frames, 10) || 5;
924
1252
  const range = args.range === void 0 ? void 0 : parseSnapshotRange(String(args.range));
925
1253
  const composition = args.composition === void 0 ? void 0 : String(args.composition).trim();
@@ -928,28 +1256,32 @@ var snapshot_default = defineCommand({
928
1256
  const lintResult = await lintProject(project.dir);
929
1257
  if (hasDefinitiveEntryMismatch(lintResult)) {
930
1258
  const candidate = definitiveEntryMismatchComposition(lintResult);
931
- console.log("");
1259
+ log("");
932
1260
  for (const line of formatLintFindings(lintResult, { errorsFirst: true })) {
933
- console.log(line);
1261
+ log(line);
934
1262
  }
935
- console.log("");
936
- console.log(c.error(" Aborting snapshot because the default index.html entry is blank."));
1263
+ log("");
1264
+ log(c.error(" Aborting snapshot because the default index.html entry is blank."));
937
1265
  if (candidate && posix.basename(candidate) === "index.html") {
938
1266
  const candidateDir = posix.dirname(candidate);
939
1267
  const target = `<project>/${candidateDir}`;
940
- console.log(
1268
+ log(
941
1269
  c.dim(
942
1270
  ` Move or mount the authored file, or snapshot its directory directly: hyperframes snapshot ${target}. Only use the directory form when its assets are self-contained under that directory; otherwise mount it from the project root.`
943
1271
  )
944
1272
  );
945
1273
  } else if (candidate) {
946
- console.log(
1274
+ log(
947
1275
  c.dim(
948
1276
  ` Move or mount ${candidate} as a project index.html before snapshotting; snapshot accepts project directories, not individual HTML files.`
949
1277
  )
950
1278
  );
951
1279
  }
952
- console.log("");
1280
+ log("");
1281
+ if (asJson) {
1282
+ const error = "The default index.html entry is blank; see the lint findings on stderr.";
1283
+ console.log(JSON.stringify(withMeta({ ok: false, error }), null, 2));
1284
+ }
953
1285
  failCommand();
954
1286
  }
955
1287
  const timeout = parseInt(args.timeout, 10) || 5e3;
@@ -960,20 +1292,18 @@ var snapshot_default = defineCommand({
960
1292
  const zoomTarget = args.zoom ? parseZoomTarget(String(args.zoom)) : void 0;
961
1293
  const zoomScale = parseZoomScale(args["zoom-scale"]);
962
1294
  const against = args.against ? resolve(String(args.against)) : void 0;
963
- if (against && !existsSync(against)) {
964
- console.log(`${c.error("\u2717")} --against video not found: ${against}`);
965
- failCommand();
966
- }
967
- const label = atTimestamps ? `${atTimestamps.length} frames at [${atTimestamps.map(formatSnapshotTimestamp).join(", ")}]` : `${frames} frames`;
1295
+ if (against && !existsSync(against))
1296
+ fail(`${c.error("\u2717")} --against video not found: ${against}`);
1297
+ const label = reviewMode ? "review frames (beats, seams, phone)" : atTimestamps ? `${atTimestamps.length} frames at [${atTimestamps.map(formatSnapshotTimestamp).join(", ")}]` : `${frames} frames`;
968
1298
  const angleLabel = camera && (camera.yaw !== 0 || camera.pitch !== 0) ? ` ${c.dim(`(angle yaw ${camera.yaw}\xB0 pitch ${camera.pitch}\xB0)`)}` : "";
969
- console.log(`${c.accent("\u25C6")} Capturing ${label} from ${c.accent(project.name)}${angleLabel}`);
1299
+ log(`${c.accent("\u25C6")} Capturing ${label} from ${c.accent(project.name)}${angleLabel}`);
970
1300
  let managedSnapshotDir;
971
1301
  try {
972
1302
  const snapshotDir = args.output ? resolve(String(args.output)) : artifactDirectory(project.dir, "snapshots");
973
1303
  if (!args.output) managedSnapshotDir = snapshotDir;
974
1304
  if (args.output) retainSnapshotOutput(project.dir, snapshotDir);
975
1305
  await withProjectLock(project.dir, "snapshot", async () => {
976
- const paths = await captureSnapshots(project.dir, {
1306
+ const captured = await captureSnapshots(project.dir, {
977
1307
  frames,
978
1308
  range,
979
1309
  composition,
@@ -987,53 +1317,71 @@ var snapshot_default = defineCommand({
987
1317
  autoProxy: args.proxy,
988
1318
  browserGpuMode: resolveLocalBrowserGpuMode(args["browser-gpu"]),
989
1319
  against,
990
- keep: args.keep === true
1320
+ keep: args.keep === true,
1321
+ review: reviewMode,
1322
+ log
991
1323
  });
1324
+ const paths = captured.paths;
992
1325
  reportArtifacts(
993
1326
  "snapshot",
994
1327
  "frame",
995
1328
  paths.map((path) => resolve(project.dir, path))
996
1329
  );
997
1330
  if (paths.length === 0) {
998
- console.log(
999
- `
1000
- ${c.error("\u2717")} Could not determine composition duration \u2014 no frames captured`
1001
- );
1002
- failCommand();
1331
+ fail(`
1332
+ ${c.error("\u2717")} Could not determine composition duration \u2014 no frames captured`);
1003
1333
  }
1004
- console.log(`
1334
+ log(`
1005
1335
  ${c.success("\u25C7")} ${paths.length} snapshots saved to ${snapshotDir}`);
1006
1336
  for (const p of paths) {
1007
- console.log(` ${p}`);
1337
+ log(` ${p}`);
1008
1338
  }
1009
1339
  if (against) {
1010
- console.log(
1340
+ log(
1011
1341
  ` ${c.dim("ref-*.png + pair-*.jpg")} beside each frame (reference frame, render | reference sheet)`
1012
1342
  );
1013
1343
  }
1344
+ let contactSheets = [];
1345
+ let reviewSheets;
1014
1346
  try {
1015
1347
  const { createSnapshotContactSheet } = await import("./contactSheet-CAGMKI3E.js");
1016
- const sheets = args.keep === true ? [] : await createSnapshotContactSheet(
1017
- snapshotDir,
1018
- join(snapshotDir, "contact-sheet.jpg")
1019
- );
1020
- reportArtifacts("snapshot", "contact-sheet", sheets);
1021
- for (const sheet of sheets) {
1022
- console.log(` ${resolve(sheet)} (grid view for AI review)`);
1348
+ if (captured.review) {
1349
+ const written = new Map(
1350
+ captured.review.frames.map((frame) => [frame.file, frame.path])
1351
+ );
1352
+ reviewSheets = await writeReviewSheets(
1353
+ snapshotDir,
1354
+ captured.review.frames,
1355
+ (frame) => written.get(frame.file) ?? null
1356
+ );
1357
+ contactSheets = [...reviewSheets.beats, ...reviewSheets.seams, ...reviewSheets.phone];
1358
+ printReview(captured.review, reviewSheets, log);
1359
+ } else if (args.keep !== true) {
1360
+ contactSheets = await createSnapshotContactSheet(
1361
+ snapshotDir,
1362
+ join(snapshotDir, "contact-sheet.jpg")
1363
+ );
1364
+ }
1365
+ reportArtifacts("snapshot", "contact-sheet", contactSheets);
1366
+ if (!captured.review) {
1367
+ for (const sheet of contactSheets) {
1368
+ log(` ${resolve(sheet)} (grid view for AI review)`);
1369
+ }
1023
1370
  }
1024
1371
  } catch (err) {
1025
1372
  console.warn(
1026
1373
  ` ${c.warn("\u26A0")} Contact sheet skipped: ${normalizeErrorMessage(err)}. Individual snapshot PNGs are still available.`
1027
1374
  );
1028
1375
  }
1376
+ let describedPath;
1029
1377
  if (describeArg !== null) {
1030
1378
  try {
1031
1379
  const geminiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
1032
1380
  if (!geminiKey) {
1033
1381
  if (args.describe !== void 0)
1034
- console.log(` ${c.dim("--describe: GEMINI_API_KEY not set, skipping")}`);
1382
+ log(` ${c.dim("--describe: GEMINI_API_KEY not set, skipping")}`);
1035
1383
  } else if (paths.length > 0) {
1036
- console.log(` ${c.dim("Describing frames with Gemini vision...")}`);
1384
+ log(` ${c.dim("Describing frames with Gemini vision...")}`);
1037
1385
  const { GoogleGenAI } = await loadOptionalPackage("@google/genai", "--describe");
1038
1386
  const ai = new GoogleGenAI({ apiKey: geminiKey });
1039
1387
  const model = process.env.HYPERFRAMES_GEMINI_MODEL || "gemini-3.1-flash-lite-preview";
@@ -1101,17 +1449,28 @@ ${c.success("\u25C7")} ${paths.length} snapshots saved to ${snapshotDir}`);
1101
1449
  }
1102
1450
  const descPath = join(snapshotDir, "descriptions.md");
1103
1451
  writeFileSync(descPath, descriptions.join("\n"));
1104
- console.log(` ${c.dim("descriptions.md")} (Gemini frame analysis)`);
1452
+ describedPath = descPath;
1453
+ log(` ${c.dim("descriptions.md")} (Gemini frame analysis)`);
1105
1454
  }
1106
1455
  } catch (descErr) {
1107
1456
  const msg = normalizeErrorMessage(descErr);
1108
- console.log(` ${c.dim(`--describe failed: ${msg}`)}`);
1457
+ log(` ${c.dim(`--describe failed: ${msg}`)}`);
1109
1458
  }
1110
1459
  }
1460
+ if (asJson) {
1461
+ const envelope = snapshotEnvelope(project.dir, snapshotDir, captured, {
1462
+ contactSheets,
1463
+ reviewSheets,
1464
+ describedPath
1465
+ });
1466
+ console.log(JSON.stringify(withMeta(envelope), null, 2));
1467
+ }
1111
1468
  });
1112
1469
  } catch (err) {
1470
+ if (err instanceof CliRuntimeError && err.result.presented) throw err;
1113
1471
  const msg = normalizeErrorMessage(err);
1114
- console.error(`
1472
+ if (asJson) console.log(JSON.stringify(withMeta({ ok: false, error: msg }), null, 2));
1473
+ else console.error(`
1115
1474
  ${c.error("\u2717")} Snapshot failed: ${msg}`);
1116
1475
  failCommand();
1117
1476
  } finally {