voxflow 1.15.0 → 1.15.2

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 (453) hide show
  1. package/README.md +34 -0
  2. package/bin/voxflow.js +27 -0
  3. package/dist/index.js +1 -1
  4. package/dist/remotion-bundle/02a2fb2eb80bc7bf.woff2 +0 -0
  5. package/dist/remotion-bundle/052ca5351e5e06ba.woff2 +0 -0
  6. package/dist/remotion-bundle/05853dd28f4019cb.woff2 +0 -0
  7. package/dist/remotion-bundle/072ead3737f7c0d0.woff2 +0 -0
  8. package/dist/remotion-bundle/07d4248613c86a2e.woff2 +0 -0
  9. package/dist/remotion-bundle/0884a5c2d1d2d99b.woff2 +0 -0
  10. package/dist/remotion-bundle/0b0e185b2752095e.woff2 +0 -0
  11. package/dist/remotion-bundle/0e66c11bde067d91.woff2 +0 -0
  12. package/dist/remotion-bundle/0f7794cfba2c5d21.woff2 +0 -0
  13. package/dist/remotion-bundle/0fdbae5a4365783a.woff2 +0 -0
  14. package/dist/remotion-bundle/112.bundle.js +11 -0
  15. package/dist/remotion-bundle/112.bundle.js.map +1 -0
  16. package/dist/remotion-bundle/113.bundle.js +11 -0
  17. package/dist/remotion-bundle/113.bundle.js.map +1 -0
  18. package/dist/remotion-bundle/119cae0c4c16f7ed.woff2 +0 -0
  19. package/dist/remotion-bundle/14725f649fd1e78c.woff2 +0 -0
  20. package/dist/remotion-bundle/14abe9e3f95f7888.woff2 +0 -0
  21. package/dist/remotion-bundle/163.bundle.js +14678 -0
  22. package/dist/remotion-bundle/163.bundle.js.map +1 -0
  23. package/dist/remotion-bundle/1808c54072bf6d14.woff2 +0 -0
  24. package/dist/remotion-bundle/18948bec3e3012fe.woff2 +0 -0
  25. package/dist/remotion-bundle/1a661c60d0fc84fc.woff2 +0 -0
  26. package/dist/remotion-bundle/1af94941e1bc7e1e.woff2 +0 -0
  27. package/dist/remotion-bundle/1bee0219595f606c.woff2 +0 -0
  28. package/dist/remotion-bundle/1bfd5da7ce9d4ec4.woff2 +0 -0
  29. package/dist/remotion-bundle/1c158d56f1884f3f.woff2 +0 -0
  30. package/dist/remotion-bundle/1cf5e88e667610eb.woff2 +0 -0
  31. package/dist/remotion-bundle/1d431bd10f53c481.woff2 +0 -0
  32. package/dist/remotion-bundle/1d701a81a7670db2.woff2 +0 -0
  33. package/dist/remotion-bundle/1da0fecad4240f16.woff2 +0 -0
  34. package/dist/remotion-bundle/1ed14d3d0c5c63fe.woff2 +0 -0
  35. package/dist/remotion-bundle/1edfecf40e586f53.woff2 +0 -0
  36. package/dist/remotion-bundle/1f479711bc34b054.woff +0 -0
  37. package/dist/remotion-bundle/1f86e54a0ff5fcd1.woff2 +0 -0
  38. package/dist/remotion-bundle/2043ea87d9aabd11.woff2 +0 -0
  39. package/dist/remotion-bundle/20563c39ee8a0e40.woff2 +0 -0
  40. package/dist/remotion-bundle/20c231590fd12c44.woff2 +0 -0
  41. package/dist/remotion-bundle/20ce61713f754c07.woff2 +0 -0
  42. package/dist/remotion-bundle/21eb9306fce24bb1.woff2 +0 -0
  43. package/dist/remotion-bundle/244bf71c0cc851af.woff2 +0 -0
  44. package/dist/remotion-bundle/274d4cfc02bffbcb.woff2 +0 -0
  45. package/dist/remotion-bundle/275.bundle.js +21 -0
  46. package/dist/remotion-bundle/275.bundle.js.map +1 -0
  47. package/dist/remotion-bundle/2958f540b39513dc.woff2 +0 -0
  48. package/dist/remotion-bundle/2a168b98fd97722e.woff2 +0 -0
  49. package/dist/remotion-bundle/2d1f6373937ab55f.woff2 +0 -0
  50. package/dist/remotion-bundle/2d213ae47ff6daa9.woff2 +0 -0
  51. package/dist/remotion-bundle/2e4b1f04fcd05047.woff2 +0 -0
  52. package/dist/remotion-bundle/304170d98f4c4563.woff2 +0 -0
  53. package/dist/remotion-bundle/30d02e136e7a5642.woff2 +0 -0
  54. package/dist/remotion-bundle/3135562b52a714cd.woff2 +0 -0
  55. package/dist/remotion-bundle/313713af2c8144e9.woff2 +0 -0
  56. package/dist/remotion-bundle/325fa4108d2285b9.woff2 +0 -0
  57. package/dist/remotion-bundle/338e927ed3345e0c.woff2 +0 -0
  58. package/dist/remotion-bundle/35fc6b190365bc17.woff2 +0 -0
  59. package/dist/remotion-bundle/37a51f1122d4efc5.woff2 +0 -0
  60. package/dist/remotion-bundle/39a4d63e02736f5e.woff2 +0 -0
  61. package/dist/remotion-bundle/3a00e0d62dfc4171.woff2 +0 -0
  62. package/dist/remotion-bundle/3a6955e6561affe1.woff2 +0 -0
  63. package/dist/remotion-bundle/3c573945aef49b89.woff2 +0 -0
  64. package/dist/remotion-bundle/3cdbfbfa23b516a5.woff2 +0 -0
  65. package/dist/remotion-bundle/3e42f85a9e64ca8a.woff2 +0 -0
  66. package/dist/remotion-bundle/3e83eaf1ec859415.woff2 +0 -0
  67. package/dist/remotion-bundle/3f3c8c90de1250ee.woff2 +0 -0
  68. package/dist/remotion-bundle/434.bundle.js +205 -0
  69. package/dist/remotion-bundle/434.bundle.js.map +1 -0
  70. package/dist/remotion-bundle/44ffc6ca4d781692.woff2 +0 -0
  71. package/dist/remotion-bundle/4670d9c4580b09eb.woff2 +0 -0
  72. package/dist/remotion-bundle/479756881b302824.woff2 +0 -0
  73. package/dist/remotion-bundle/481b82134bfa9c82.woff2 +0 -0
  74. package/dist/remotion-bundle/48d27029626f4328.woff2 +0 -0
  75. package/dist/remotion-bundle/49b7b2a30329c511.woff2 +0 -0
  76. package/dist/remotion-bundle/4c8b25a1a9337045.woff2 +0 -0
  77. package/dist/remotion-bundle/4cba14788ca9259b.woff2 +0 -0
  78. package/dist/remotion-bundle/4cd6c589c004a6a7.woff2 +0 -0
  79. package/dist/remotion-bundle/4cd8d79c1021608d.woff2 +0 -0
  80. package/dist/remotion-bundle/4d8fa99b3f00f9f0.woff2 +0 -0
  81. package/dist/remotion-bundle/4e7805a643f86d53.woff2 +0 -0
  82. package/dist/remotion-bundle/4ff91be454542e3f.woff2 +0 -0
  83. package/dist/remotion-bundle/504cbcba1f63591b.woff2 +0 -0
  84. package/dist/remotion-bundle/5202d792e5791d6c.woff2 +0 -0
  85. package/dist/remotion-bundle/534db5ad4770cc1d.woff2 +0 -0
  86. package/dist/remotion-bundle/53b9568eb85f866b.woff2 +0 -0
  87. package/dist/remotion-bundle/543ad386ca171de9.woff2 +0 -0
  88. package/dist/remotion-bundle/54798e55bbf7976e.woff2 +0 -0
  89. package/dist/remotion-bundle/580.bundle.js +11 -0
  90. package/dist/remotion-bundle/580.bundle.js.map +1 -0
  91. package/dist/remotion-bundle/58d174d1193af6d1.woff2 +0 -0
  92. package/dist/remotion-bundle/591d29ff3ff53c80.woff2 +0 -0
  93. package/dist/remotion-bundle/5c28c4f4824383c6.woff2 +0 -0
  94. package/dist/remotion-bundle/5da9740d2ce894c8.woff2 +0 -0
  95. package/dist/remotion-bundle/6197735364642360.woff2 +0 -0
  96. package/dist/remotion-bundle/6265a4335724080f.woff2 +0 -0
  97. package/dist/remotion-bundle/633f5e4f6394daa7.woff2 +0 -0
  98. package/dist/remotion-bundle/637d95ace6a69c49.woff2 +0 -0
  99. package/dist/remotion-bundle/648e04a04dacff8f.woff2 +0 -0
  100. package/dist/remotion-bundle/64a6e83045a008b2.woff2 +0 -0
  101. package/dist/remotion-bundle/651.bundle.js +11 -0
  102. package/dist/remotion-bundle/651.bundle.js.map +1 -0
  103. package/dist/remotion-bundle/65e2a988c070facc.woff2 +0 -0
  104. package/dist/remotion-bundle/66a2f6ce5cc69105.woff2 +0 -0
  105. package/dist/remotion-bundle/690.bundle.js +3479 -0
  106. package/dist/remotion-bundle/690.bundle.js.map +1 -0
  107. package/dist/remotion-bundle/690ff55252ca715d.woff2 +0 -0
  108. package/dist/remotion-bundle/6a01a1cff49314fc.woff2 +0 -0
  109. package/dist/remotion-bundle/6cbc32670982986c.woff2 +0 -0
  110. package/dist/remotion-bundle/6d3cc42ae547f454.woff2 +0 -0
  111. package/dist/remotion-bundle/6d8f4cfa1ddc0830.woff2 +0 -0
  112. package/dist/remotion-bundle/6e4d7c6ae65e2dc3.woff2 +0 -0
  113. package/dist/remotion-bundle/6e86418bbcefb2e8.woff2 +0 -0
  114. package/dist/remotion-bundle/6ee02884b29cf7fb.woff2 +0 -0
  115. package/dist/remotion-bundle/6f436a74c9e3252c.woff2 +0 -0
  116. package/dist/remotion-bundle/78c8022f1657618b.woff2 +0 -0
  117. package/dist/remotion-bundle/7c5444169792bca4.woff2 +0 -0
  118. package/dist/remotion-bundle/7c86bddd9d997212.woff2 +0 -0
  119. package/dist/remotion-bundle/7e1284684767f584.woff2 +0 -0
  120. package/dist/remotion-bundle/7e81c17522d182b2.woff2 +0 -0
  121. package/dist/remotion-bundle/7eb87be198f7858c.woff2 +0 -0
  122. package/dist/remotion-bundle/8060c928f948aab5.woff2 +0 -0
  123. package/dist/remotion-bundle/80bc9dfbea2b35ae.woff2 +0 -0
  124. package/dist/remotion-bundle/811b83f69963bb48.woff2 +0 -0
  125. package/dist/remotion-bundle/813.bundle.js +117511 -0
  126. package/dist/remotion-bundle/813.bundle.js.map +1 -0
  127. package/dist/remotion-bundle/84df492e349f82e9.woff2 +0 -0
  128. package/dist/remotion-bundle/8501bfd73eb36f2b.woff2 +0 -0
  129. package/dist/remotion-bundle/854236a8376093fe.woff2 +0 -0
  130. package/dist/remotion-bundle/8571d74529082753.woff2 +0 -0
  131. package/dist/remotion-bundle/860bf44f8e6f4b5d.woff2 +0 -0
  132. package/dist/remotion-bundle/879.bundle.js +64 -0
  133. package/dist/remotion-bundle/879.bundle.js.map +1 -0
  134. package/dist/remotion-bundle/887dd482f848d56f.woff2 +0 -0
  135. package/dist/remotion-bundle/89b2132e85fbbb5a.woff2 +0 -0
  136. package/dist/remotion-bundle/8ba60d6c306010c2.woff2 +0 -0
  137. package/dist/remotion-bundle/8c7c4dadea897806.woff2 +0 -0
  138. package/dist/remotion-bundle/8c943f9999706f61.woff2 +0 -0
  139. package/dist/remotion-bundle/8f2a718c90575cc9.woff2 +0 -0
  140. package/dist/remotion-bundle/906b6edb3e1772c9.woff2 +0 -0
  141. package/dist/remotion-bundle/930ff9daccdf14eb.woff2 +0 -0
  142. package/dist/remotion-bundle/934db2f1c403c4d0.woff2 +0 -0
  143. package/dist/remotion-bundle/938.bundle.js +451 -0
  144. package/dist/remotion-bundle/938.bundle.js.map +1 -0
  145. package/dist/remotion-bundle/967.bundle.js +4462 -0
  146. package/dist/remotion-bundle/967.bundle.js.map +1 -0
  147. package/dist/remotion-bundle/9684a1093d3c02ce.woff2 +0 -0
  148. package/dist/remotion-bundle/973dcd0faa6116cc.woff2 +0 -0
  149. package/dist/remotion-bundle/9745400694e76cd8.woff2 +0 -0
  150. package/dist/remotion-bundle/999ef957bed3bdca.woff2 +0 -0
  151. package/dist/remotion-bundle/99a3d67c8b0f43e3.woff2 +0 -0
  152. package/dist/remotion-bundle/a0586c3e03127283.woff2 +0 -0
  153. package/dist/remotion-bundle/a0eb654fdae46269.woff2 +0 -0
  154. package/dist/remotion-bundle/a20e35d3b08f7994.woff2 +0 -0
  155. package/dist/remotion-bundle/a2dcaced7c8c25ab.woff2 +0 -0
  156. package/dist/remotion-bundle/a79255a972a2681a.woff2 +0 -0
  157. package/dist/remotion-bundle/a804b352cb9fec1a.woff2 +0 -0
  158. package/dist/remotion-bundle/aae7117164e1eabc.woff2 +0 -0
  159. package/dist/remotion-bundle/affd121385d0442d.woff2 +0 -0
  160. package/dist/remotion-bundle/b19a6083987ee0d7.woff2 +0 -0
  161. package/dist/remotion-bundle/b1b2bd04d8637981.woff2 +0 -0
  162. package/dist/remotion-bundle/b2c07f341486be87.woff2 +0 -0
  163. package/dist/remotion-bundle/b33d8f82e575c4ce.woff2 +0 -0
  164. package/dist/remotion-bundle/b366c0bed35ef491.woff2 +0 -0
  165. package/dist/remotion-bundle/b41e857ec1b85642.woff2 +0 -0
  166. package/dist/remotion-bundle/b420bb34ccf23e7f.woff2 +0 -0
  167. package/dist/remotion-bundle/b4f7bf4efb0c0ccf.woff2 +0 -0
  168. package/dist/remotion-bundle/b60fe5eca03cff93.woff2 +0 -0
  169. package/dist/remotion-bundle/b6bd31a336e64bce.woff2 +0 -0
  170. package/dist/remotion-bundle/b6d2befba3dfefeb.woff2 +0 -0
  171. package/dist/remotion-bundle/b75f39ab06c43bf4.woff2 +0 -0
  172. package/dist/remotion-bundle/b77880e8c413d4fd.woff2 +0 -0
  173. package/dist/remotion-bundle/b7e38ec441e4a77a.woff2 +0 -0
  174. package/dist/remotion-bundle/b83baa383ff0bf2b.woff2 +0 -0
  175. package/dist/remotion-bundle/b9ad7b6c0a11450a.woff2 +0 -0
  176. package/dist/remotion-bundle/baf84486e8ae3aaf.woff2 +0 -0
  177. package/dist/remotion-bundle/bc047b1f6869cffa.woff2 +0 -0
  178. package/dist/remotion-bundle/bf4f3ac6e93f33aa.woff2 +0 -0
  179. package/dist/remotion-bundle/bf6835ffec5897a2.woff2 +0 -0
  180. package/dist/remotion-bundle/bf8885f581eb1724.woff2 +0 -0
  181. package/dist/remotion-bundle/bundle.js +83376 -0
  182. package/dist/remotion-bundle/bundle.js.map +1 -0
  183. package/dist/remotion-bundle/c03f046bccd789d0.woff2 +0 -0
  184. package/dist/remotion-bundle/c0bb1f8962b73bc3.woff2 +0 -0
  185. package/dist/remotion-bundle/c1003f9a7db6e1cf.woff2 +0 -0
  186. package/dist/remotion-bundle/c15d83fb1e199515.woff2 +0 -0
  187. package/dist/remotion-bundle/c28e7e5d310f73ef.woff2 +0 -0
  188. package/dist/remotion-bundle/c2b840274db78aea.woff2 +0 -0
  189. package/dist/remotion-bundle/c3000e3299d4e45f.woff2 +0 -0
  190. package/dist/remotion-bundle/c83ce886e5288510.woff2 +0 -0
  191. package/dist/remotion-bundle/c87a5a64d4ac0918.woff2 +0 -0
  192. package/dist/remotion-bundle/c8a7e0d049e965fa.woff2 +0 -0
  193. package/dist/remotion-bundle/c949a35d3a3b1faf.woff2 +0 -0
  194. package/dist/remotion-bundle/c9618c9b9ac2bc78.woff2 +0 -0
  195. package/dist/remotion-bundle/ca3add3b84152d5b.woff2 +0 -0
  196. package/dist/remotion-bundle/cad9dd036408d707.woff2 +0 -0
  197. package/dist/remotion-bundle/cbb24916619df439.woff2 +0 -0
  198. package/dist/remotion-bundle/cc054f0b5514e177.woff2 +0 -0
  199. package/dist/remotion-bundle/ccc248ed9312bc71.woff2 +0 -0
  200. package/dist/remotion-bundle/cd9d623aa07af925.woff2 +0 -0
  201. package/dist/remotion-bundle/ce2ba7a321bd1247.woff2 +0 -0
  202. package/dist/remotion-bundle/cf72455f79a29b14.woff2 +0 -0
  203. package/dist/remotion-bundle/d267cbfefab452ac.woff2 +0 -0
  204. package/dist/remotion-bundle/d435cff46a64955f.woff +0 -0
  205. package/dist/remotion-bundle/d494d07f67e363f6.woff2 +0 -0
  206. package/dist/remotion-bundle/d7aa0cc1fa47bf38.woff2 +0 -0
  207. package/dist/remotion-bundle/d7c5ca93d885160a.woff2 +0 -0
  208. package/dist/remotion-bundle/d855d3e252db74e2.woff2 +0 -0
  209. package/dist/remotion-bundle/d8f13d47f02f82c2.woff2 +0 -0
  210. package/dist/remotion-bundle/d9567cce2ee11019.woff2 +0 -0
  211. package/dist/remotion-bundle/db8d4456fc75dd86.woff +0 -0
  212. package/dist/remotion-bundle/dc274628378c47ee.woff2 +0 -0
  213. package/dist/remotion-bundle/dc3e06947bb69903.woff2 +0 -0
  214. package/dist/remotion-bundle/dd67040ac3b6d523.woff2 +0 -0
  215. package/dist/remotion-bundle/e0b04bd488f953f4.woff2 +0 -0
  216. package/dist/remotion-bundle/e2a572ff95089370.woff2 +0 -0
  217. package/dist/remotion-bundle/e2e18a86b1c2b0cc.woff2 +0 -0
  218. package/dist/remotion-bundle/e3a78ee2fc9c6931.woff2 +0 -0
  219. package/dist/remotion-bundle/e654c9d547605a9f.woff2 +0 -0
  220. package/dist/remotion-bundle/e67a3a64c129927c.woff2 +0 -0
  221. package/dist/remotion-bundle/e6be28b4203cd6ce.woff2 +0 -0
  222. package/dist/remotion-bundle/e841907ad9b0a191.woff +0 -0
  223. package/dist/remotion-bundle/e889d1541c69fffa.woff2 +0 -0
  224. package/dist/remotion-bundle/e88ef8c76373a9e2.woff2 +0 -0
  225. package/dist/remotion-bundle/e9c72f4bc37defef.woff2 +0 -0
  226. package/dist/remotion-bundle/e9e35f863403a255.woff2 +0 -0
  227. package/dist/remotion-bundle/eb23b37b009375da.woff2 +0 -0
  228. package/dist/remotion-bundle/ee1342b741625721.woff2 +0 -0
  229. package/dist/remotion-bundle/f07da88543a57ec9.woff2 +0 -0
  230. package/dist/remotion-bundle/f522982115306f8a.woff2 +0 -0
  231. package/dist/remotion-bundle/f8449bd864e6d8bc.woff2 +0 -0
  232. package/dist/remotion-bundle/f906dd5bd95ff9ab.woff2 +0 -0
  233. package/dist/remotion-bundle/f9e9e9413e3c38bb.woff2 +0 -0
  234. package/dist/remotion-bundle/fa5a5b16280994a8.woff2 +0 -0
  235. package/dist/remotion-bundle/favicon.ico +0 -0
  236. package/dist/remotion-bundle/fb19c0517725599b.woff2 +0 -0
  237. package/dist/remotion-bundle/fcaf24232f684b9b.woff2 +0 -0
  238. package/dist/remotion-bundle/fe09e084a3eea8cf.woff2 +0 -0
  239. package/dist/remotion-bundle/ff38d5317df7345a.woff2 +0 -0
  240. package/dist/remotion-bundle/ffe7ea1ea08f455a.woff2 +0 -0
  241. package/dist/remotion-bundle/index.html +49 -0
  242. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/0.mp3 +0 -0
  243. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/1.mp3 +0 -0
  244. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/2.mp3 +0 -0
  245. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/3.mp3 +0 -0
  246. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/0.mp3 +0 -0
  247. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/1.mp3 +0 -0
  248. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/2.mp3 +0 -0
  249. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/3.mp3 +0 -0
  250. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/0.mp3 +0 -0
  251. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/1.mp3 +0 -0
  252. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/2.mp3 +0 -0
  253. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/3.mp3 +0 -0
  254. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/0.mp3 +0 -0
  255. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/1.mp3 +0 -0
  256. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/2.mp3 +0 -0
  257. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/3.mp3 +0 -0
  258. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/0.mp3 +0 -0
  259. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/1.mp3 +0 -0
  260. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/2.mp3 +0 -0
  261. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/3.mp3 +0 -0
  262. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/0.mp3 +0 -0
  263. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/1.mp3 +0 -0
  264. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/2.mp3 +0 -0
  265. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/3.mp3 +0 -0
  266. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/0.mp3 +0 -0
  267. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/1.mp3 +0 -0
  268. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/2.mp3 +0 -0
  269. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/3.mp3 +0 -0
  270. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/0.mp3 +0 -0
  271. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/1.mp3 +0 -0
  272. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/2.mp3 +0 -0
  273. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/3.mp3 +0 -0
  274. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/0.mp3 +0 -0
  275. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/1.mp3 +0 -0
  276. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/2.mp3 +0 -0
  277. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/3.mp3 +0 -0
  278. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/0.mp3 +0 -0
  279. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/1.mp3 +0 -0
  280. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/2.mp3 +0 -0
  281. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/3.mp3 +0 -0
  282. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/0.mp3 +0 -0
  283. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/1.mp3 +0 -0
  284. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/2.mp3 +0 -0
  285. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/3.mp3 +0 -0
  286. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/4.mp3 +0 -0
  287. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/0.mp3 +0 -0
  288. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/1.mp3 +0 -0
  289. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/2.mp3 +0 -0
  290. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/3.mp3 +0 -0
  291. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/0.mp3 +0 -0
  292. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/1.mp3 +0 -0
  293. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/2.mp3 +0 -0
  294. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/3.mp3 +0 -0
  295. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/4.mp3 +0 -0
  296. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/0.mp3 +0 -0
  297. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/1.mp3 +0 -0
  298. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/2.mp3 +0 -0
  299. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/3.mp3 +0 -0
  300. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/4.mp3 +0 -0
  301. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/0.mp3 +0 -0
  302. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/1.mp3 +0 -0
  303. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/2.mp3 +0 -0
  304. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/3.mp3 +0 -0
  305. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/4.mp3 +0 -0
  306. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/0.mp3 +0 -0
  307. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/1.mp3 +0 -0
  308. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/2.mp3 +0 -0
  309. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/3.mp3 +0 -0
  310. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/4.mp3 +0 -0
  311. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/0.mp3 +0 -0
  312. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/1.mp3 +0 -0
  313. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/2.mp3 +0 -0
  314. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/3.mp3 +0 -0
  315. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/0.mp3 +0 -0
  316. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/1.mp3 +0 -0
  317. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/2.mp3 +0 -0
  318. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/3.mp3 +0 -0
  319. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/0.mp3 +0 -0
  320. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/1.mp3 +0 -0
  321. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/2.mp3 +0 -0
  322. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/3.mp3 +0 -0
  323. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/0.mp3 +0 -0
  324. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/1.mp3 +0 -0
  325. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/2.mp3 +0 -0
  326. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/3.mp3 +0 -0
  327. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/4.mp3 +0 -0
  328. package/dist/remotion-bundle/public/voiceover/ai-life/card-0.mp3 +0 -0
  329. package/dist/remotion-bundle/public/voiceover/ai-life/card-1.mp3 +0 -0
  330. package/dist/remotion-bundle/public/voiceover/ai-life/card-2.mp3 +0 -0
  331. package/dist/remotion-bundle/public/voiceover/ai-life/card-3.mp3 +0 -0
  332. package/dist/remotion-bundle/public/voiceover/ai-life/card-4.mp3 +0 -0
  333. package/dist/remotion-bundle/public/voiceover/ai-life/card-5.mp3 +0 -0
  334. package/dist/remotion-bundle/public/voiceover/coffee-science/card-0.mp3 +0 -0
  335. package/dist/remotion-bundle/public/voiceover/coffee-science/card-1.mp3 +0 -0
  336. package/dist/remotion-bundle/public/voiceover/coffee-science/card-2.mp3 +0 -0
  337. package/dist/remotion-bundle/public/voiceover/coffee-science/card-3.mp3 +0 -0
  338. package/dist/remotion-bundle/public/voiceover/coffee-science/card-4.mp3 +0 -0
  339. package/dist/remotion-bundle/public/voiceover/coffee-science/card-5.mp3 +0 -0
  340. package/dist/remotion-bundle/public/voiceover/coffee-science/card-6.mp3 +0 -0
  341. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-0.mp3 +0 -0
  342. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-1.mp3 +0 -0
  343. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-2.mp3 +0 -0
  344. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-3.mp3 +0 -0
  345. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-4.mp3 +0 -0
  346. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-5.mp3 +0 -0
  347. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-6.mp3 +0 -0
  348. package/dist/remotion-bundle/public/voiceover/remote-work/card-0.mp3 +0 -0
  349. package/dist/remotion-bundle/public/voiceover/remote-work/card-1.mp3 +0 -0
  350. package/dist/remotion-bundle/public/voiceover/remote-work/card-2.mp3 +0 -0
  351. package/dist/remotion-bundle/public/voiceover/remote-work/card-3.mp3 +0 -0
  352. package/dist/remotion-bundle/public/voiceover/remote-work/card-4.mp3 +0 -0
  353. package/dist/remotion-bundle/public/voiceover/remote-work/card-5.mp3 +0 -0
  354. package/dist/remotion-bundle/source-map-helper.wasm +0 -0
  355. package/lib/cli.js +270 -0
  356. package/lib/commands/_registry.js +48 -0
  357. package/lib/commands/add.js +242 -0
  358. package/lib/commands/asr/azure-transcribe.js +336 -0
  359. package/lib/commands/asr/cloud-transcribe.js +384 -0
  360. package/lib/commands/asr/helpers.js +76 -0
  361. package/lib/commands/asr/index.js +236 -0
  362. package/lib/commands/asr/local-transcribe.js +125 -0
  363. package/lib/commands/asr-jobs.js +257 -0
  364. package/lib/commands/asr.js +11 -0
  365. package/lib/commands/auth-cmds.js +358 -0
  366. package/lib/commands/dub.js +542 -0
  367. package/lib/commands/explain.js +512 -0
  368. package/lib/commands/feedback.js +152 -0
  369. package/lib/commands/image.js +207 -0
  370. package/lib/commands/mcp-key.js +166 -0
  371. package/lib/commands/narrate.js +639 -0
  372. package/lib/commands/picstory-templates.js +276 -0
  373. package/lib/commands/picstory.js +547 -0
  374. package/lib/commands/podcast/dialogue.js +109 -0
  375. package/lib/commands/podcast/generate.js +127 -0
  376. package/lib/commands/podcast/index.js +561 -0
  377. package/lib/commands/podcast/synthesize.js +188 -0
  378. package/lib/commands/podcast.js +11 -0
  379. package/lib/commands/present.js +519 -0
  380. package/lib/commands/publish.js +415 -0
  381. package/lib/commands/skills.js +473 -0
  382. package/lib/commands/slice-render.js +282 -0
  383. package/lib/commands/slice-stage.js +264 -0
  384. package/lib/commands/slice.js +346 -0
  385. package/lib/commands/slides/constants.js +108 -0
  386. package/lib/commands/slides/html-renderer.js +338 -0
  387. package/lib/commands/slides/index.js +345 -0
  388. package/lib/commands/slides.js +11 -0
  389. package/lib/commands/story.js +302 -0
  390. package/lib/commands/summarize.js +532 -0
  391. package/lib/commands/synthesize.js +261 -0
  392. package/lib/commands/translate.js +593 -0
  393. package/lib/commands/upgrade.js +249 -0
  394. package/lib/commands/video-translate.js +577 -0
  395. package/lib/commands/voices.js +292 -0
  396. package/lib/core/agent-env.js +104 -0
  397. package/lib/core/args.js +107 -0
  398. package/lib/core/asr-client.js +448 -0
  399. package/lib/core/asr-jobs-client.js +126 -0
  400. package/lib/core/asr-jobs-store.js +105 -0
  401. package/lib/core/asr-r2-upload.js +181 -0
  402. package/lib/core/asr-upload.js +132 -0
  403. package/lib/core/audio-extract.js +150 -0
  404. package/lib/core/audio.js +219 -0
  405. package/lib/core/auth.js +880 -0
  406. package/lib/core/config.js +197 -0
  407. package/lib/core/feedback.js +64 -0
  408. package/lib/core/ffmpeg.js +476 -0
  409. package/lib/core/http.js +188 -0
  410. package/lib/core/image-client.js +55 -0
  411. package/lib/core/intent-params.js +11 -0
  412. package/lib/core/llm-client.js +76 -0
  413. package/lib/core/logger.js +208 -0
  414. package/lib/core/mic-recorder.js +182 -0
  415. package/lib/core/pause-markers.js +94 -0
  416. package/lib/core/podcast-pacing.js +118 -0
  417. package/lib/core/spinner.js +33 -0
  418. package/lib/core/srt.js +394 -0
  419. package/lib/core/telemetry.js +100 -0
  420. package/lib/core/timeline.js +92 -0
  421. package/lib/core/tts-synthesizer.js +70 -0
  422. package/lib/core/update-check.js +185 -0
  423. package/lib/core/url-download.js +148 -0
  424. package/lib/core/whisper-local.js +279 -0
  425. package/lib/internal/deck-validator.js +488 -0
  426. package/lib/internal/slice-themes.json +370 -0
  427. package/lib/stage-core/cloud-render.js +170 -0
  428. package/lib/stage-core/deck-format.js +133 -0
  429. package/lib/stage-core/edit-prompt.js +104 -0
  430. package/lib/stage-core/event-bus.js +31 -0
  431. package/lib/stage-core/port.js +46 -0
  432. package/lib/stage-core/server.js +352 -0
  433. package/lib/stage-core/snapshot-store.js +198 -0
  434. package/lib/stage-core/watcher.js +106 -0
  435. package/lib/stage-ui/slice/template.js +1672 -0
  436. package/package.json +9 -4
  437. package/skills/.claude-plugin/marketplace.json +22 -0
  438. package/skills/.claude-plugin/plugin.json +25 -0
  439. package/skills/LICENSE +21 -0
  440. package/skills/README.md +120 -0
  441. package/skills/hub/SKILL.md +317 -0
  442. package/skills/podcast/SKILL.md +146 -0
  443. package/skills/slice/SKILL.md +205 -0
  444. package/skills/slice/agents/openai.yaml +4 -0
  445. package/skills/slice/references/deck-schema.md +183 -0
  446. package/skills/slice/references/example-decks.md +108 -0
  447. package/skills/slice/references/themes.md +172 -0
  448. package/skills/transcribe/SKILL.md +473 -0
  449. package/skills/video/SKILL.md +261 -0
  450. package/skills/voxflow-slice/SKILL.md +271 -0
  451. package/skills/voxflow-slice/examples/article.md +13 -0
  452. package/skills/voxflow-slice/examples/expected-deck.json +39 -0
  453. package/skills/voxflow-slice/examples/validate.mjs +46 -0
@@ -0,0 +1,282 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * `voxflow slice render <deck.json>` — offline local Remotion render.
5
+ *
6
+ * Reads a validator-shaped deck.json, transforms it into PaperSlideDeck
7
+ * composition input props, and renders directly to MP4 via the pre-bundled
8
+ * Remotion serveUrl at `cli/dist/remotion-bundle/`. No VoxFlow API call,
9
+ * no TTS, no quota — Phase 0 is silent video; Phase 1 will add local TTS.
10
+ *
11
+ * No `meta` export — reached only via `slice.js` dispatch (`voxflow slice
12
+ * render <path>`), so it stays out of the registry parity test (the same
13
+ * pattern slice-stage.js uses).
14
+ */
15
+
16
+ const fs = require('node:fs');
17
+ const path = require('node:path');
18
+
19
+ const { validatePaperSlideDeck, isV2LayoutTreeDeck } = require('../internal/deck-validator');
20
+ const SLICE_THEME_REGISTRY = require('../internal/slice-themes.json');
21
+
22
+ const THEME_TO_DECK_ID = Object.fromEntries(
23
+ SLICE_THEME_REGISTRY.themes.map((t) => [t.id, t.remotion.deck])
24
+ );
25
+ const DEFAULT_THEME = SLICE_THEME_REGISTRY.default;
26
+
27
+ // Composition FPS is fixed at 30 across every theme's <Composition> in
28
+ // video-present/src/Root.tsx. Captured here as documentation; the actual
29
+ // frames-per-second comes back from selectComposition() at runtime.
30
+ const DEFAULT_CARD_SEC = 4;
31
+
32
+ /**
33
+ * Transform a validator-shaped deck (cards[].kind/title/caption/...) into
34
+ * PaperSlideDeckProps (cards[].slide + cards[].durationSec) so the same
35
+ * deck schema users author in Claude maps cleanly onto every Remotion deck
36
+ * composition without going through the cloud render-worker pipeline.
37
+ *
38
+ * Phase 0 is silent — every card gets DEFAULT_CARD_SEC. Phase 1 will
39
+ * splice per-card TTS in and replace this with audio-driven durations.
40
+ */
41
+ function buildInputProps(deck) {
42
+ const numberBadge = null;
43
+ const cards = deck.cards.map((card) => {
44
+ const slide = {
45
+ kind: card.kind,
46
+ header: deck.header,
47
+ title: card.kind === 'title' ? card.title || [] : [],
48
+ caption: card.caption ?? null,
49
+ figureKeyword: card.figureKeyword ?? null,
50
+ seriesTitle: deck.seriesTitle,
51
+ seriesTagline: deck.seriesTagline,
52
+ voiceoverSrc: null,
53
+ numberBadge,
54
+ imageUrl: card.imageUrl,
55
+ };
56
+ if (deck.variantId) slide.variantId = deck.variantId;
57
+ return { slide, durationSec: card.durationSec || DEFAULT_CARD_SEC };
58
+ });
59
+ return { cards };
60
+ }
61
+
62
+ function humanSize(bytes) {
63
+ if (bytes < 1024) return `${bytes} B`;
64
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
65
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
66
+ return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;
67
+ }
68
+
69
+ function fmtSec(ms) {
70
+ const s = ms / 1000;
71
+ if (s < 60) return `${s.toFixed(1)}s`;
72
+ const m = Math.floor(s / 60);
73
+ const rem = (s - m * 60).toFixed(0);
74
+ return `${m}m${rem.padStart(2, '0')}s`;
75
+ }
76
+
77
+ function resolveServeUrl() {
78
+ // dist/remotion-bundle is generated by `npm run build:bundle` at CLI
79
+ // prepublish. Local dev: rebuild after a Remotion edit (`cd cli && npm
80
+ // run build:bundle`). Path is intentionally relative to this file so
81
+ // both the unbundled source layout and the ncc-bundled dist/ resolve
82
+ // to the same on-disk artifact (dist/remotion-bundle is shipped flat
83
+ // in the npm tarball).
84
+ // Two layouts to support:
85
+ // - Source/dev (`node bin/voxflow.js`): __dirname = cli/lib/commands/
86
+ // → `../../dist/remotion-bundle` = cli/dist/remotion-bundle
87
+ // - ncc-published (`voxflow ...` from npm tarball): __dirname = <pkg>/dist/
88
+ // → `remotion-bundle` (sibling of bundled index.js)
89
+ const candidates = [
90
+ path.resolve(__dirname, '../../dist/remotion-bundle'),
91
+ path.resolve(__dirname, 'remotion-bundle'),
92
+ ];
93
+ for (const c of candidates) {
94
+ if (fs.existsSync(path.join(c, 'index.html'))) return c;
95
+ }
96
+ throw new Error(
97
+ `Pre-bundled Remotion serveUrl not found at ${candidates.join(' or ')}.\n` +
98
+ ' Run `cd cli && npm run build:bundle` from the monorepo first.'
99
+ );
100
+ }
101
+
102
+ /**
103
+ * Heuristic: Remotion downloads chrome-headless-shell into node_modules/.remotion
104
+ * the FIRST time the renderer is invoked. After that it's cached.
105
+ *
106
+ * We probe for the binary so we can give the user a clear "first-run wait"
107
+ * message instead of leaving them staring at a silent terminal for a minute.
108
+ */
109
+ function chromeBinaryExists() {
110
+ // Remotion stores the downloaded chrome-headless-shell next to whatever
111
+ // node_modules/ contains @remotion/renderer at runtime. In a flat
112
+ // consumer install that's `<pkg>/node_modules/.remotion/`; in a hoisted
113
+ // monorepo it's the hoist root. We probe both CWD-based and renderer-
114
+ // resolved ancestors so a warm install is detected regardless of layout.
115
+ const candidates = new Set();
116
+ const addAncestors = (start) => {
117
+ let dir = start;
118
+ for (let i = 0; i < 12; i++) {
119
+ candidates.add(path.join(dir, 'node_modules', '.remotion', 'chrome-headless-shell'));
120
+ candidates.add(path.join(dir, '.remotion', 'chrome-headless-shell'));
121
+ const parent = path.dirname(dir);
122
+ if (parent === dir) break;
123
+ dir = parent;
124
+ }
125
+ };
126
+ addAncestors(process.cwd());
127
+ addAncestors(__dirname);
128
+ try {
129
+ const rendererPkgPath = require.resolve('@remotion/renderer/package.json');
130
+ addAncestors(path.dirname(rendererPkgPath));
131
+ } catch {
132
+ // renderer not installed — definitely cold
133
+ }
134
+ for (const cand of candidates) {
135
+ if (fs.existsSync(cand)) return true;
136
+ }
137
+ return false;
138
+ }
139
+
140
+ async function render(opts) {
141
+ const deckPath = path.resolve(opts.deckPath);
142
+ if (!fs.existsSync(deckPath)) {
143
+ throw new Error(`Deck file not found: ${deckPath}`);
144
+ }
145
+ const raw = fs.readFileSync(deckPath, 'utf8');
146
+ let deck;
147
+ try {
148
+ deck = JSON.parse(raw);
149
+ } catch (err) {
150
+ throw new Error(`Could not parse deck JSON: ${err.message}`);
151
+ }
152
+
153
+ // V2 LayoutTree decks have `cards[].children[]` — different shape than
154
+ // the V1 PaperSlide deck this renderer expects. V1 validator doesn't
155
+ // catch V2 input (V2 cards happen to satisfy V1's kind/narration checks)
156
+ // so without an explicit guard, render() would silently produce a deck
157
+ // with undefined caption/figureKeyword on every card and Remotion would
158
+ // happily render blank slides. Fail loud instead.
159
+ if (isV2LayoutTreeDeck(deck)) {
160
+ throw new Error(
161
+ 'V2 LayoutTree decks are not yet supported by offline render. ' +
162
+ 'Use voxflow.studio/apps/slice for V2 decks or downgrade to V1 shape.'
163
+ );
164
+ }
165
+
166
+ // Run the same validator the production cloud route uses — so an offline
167
+ // render fails fast on the same shape errors the backend would reject.
168
+ validatePaperSlideDeck(deck);
169
+
170
+ const theme = deck.theme || DEFAULT_THEME;
171
+ const deckId = THEME_TO_DECK_ID[theme];
172
+ if (!deckId) {
173
+ throw new Error(
174
+ `Unknown theme "${theme}". Valid: ${Object.keys(THEME_TO_DECK_ID).join(', ')}`
175
+ );
176
+ }
177
+
178
+ const outputPath = path.resolve(
179
+ opts.output || path.join(process.cwd(), 'out.mp4')
180
+ );
181
+ const outputDir = path.dirname(outputPath);
182
+ if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
183
+
184
+ const serveUrl = resolveServeUrl();
185
+ const inputProps = buildInputProps(deck);
186
+
187
+ // Lazy require so users who never run `slice render` don't pay the
188
+ // remotion install cost at CLI startup (renderer pulls in puppeteer-
189
+ // core + chromium glue, ~30 MB on disk).
190
+ const { selectComposition, renderMedia } = require('@remotion/renderer');
191
+
192
+ const coldStart = !chromeBinaryExists();
193
+ if (coldStart) {
194
+ console.log(' First-run: Remotion will download chrome-headless-shell (~90 MB).');
195
+ console.log(' Allow ~30-60 s for the one-time download, then ~30 s for the render itself.');
196
+ }
197
+ console.log(`[slice render] theme: ${theme}`);
198
+ console.log(`[slice render] composition: ${deckId}`);
199
+ console.log(`[slice render] cards: ${deck.cards.length}`);
200
+ console.log(`[slice render] output: ${outputPath}`);
201
+
202
+ const t0 = Date.now();
203
+
204
+ const composition = await selectComposition({
205
+ serveUrl,
206
+ id: deckId,
207
+ inputProps,
208
+ });
209
+ const totalFrames = composition.durationInFrames;
210
+ const videoSec = totalFrames / composition.fps;
211
+ console.log(
212
+ `[slice render] duration: ${videoSec.toFixed(1)}s ` +
213
+ `(${totalFrames} frames @ ${composition.fps} fps, ` +
214
+ `${composition.width}x${composition.height})`
215
+ );
216
+
217
+ let lastFrame = 0;
218
+ let lastLog = 0;
219
+ await renderMedia({
220
+ composition,
221
+ serveUrl,
222
+ codec: 'h264',
223
+ outputLocation: outputPath,
224
+ inputProps,
225
+ imageFormat: 'jpeg',
226
+ jpegQuality: 85,
227
+ concurrency: 4,
228
+ x264Preset: 'ultrafast',
229
+ chromiumOptions: { gl: 'swiftshader' },
230
+ offthreadVideoCacheSizeInBytes: 512 * 1024 * 1024,
231
+ onProgress: ({ progress, renderedFrames }) => {
232
+ lastFrame = renderedFrames;
233
+ const now = Date.now();
234
+ // Throttle to ~2 lines/sec so the terminal stays scrollable in
235
+ // dev and so AI agents reading stdout aren't flooded.
236
+ if (now - lastLog < 500 && progress < 1) return;
237
+ lastLog = now;
238
+ const elapsed = now - t0;
239
+ const pct = Math.round(progress * 100);
240
+ process.stdout.write(
241
+ `\r[slice render] frame ${renderedFrames}/${totalFrames} ` +
242
+ `(${pct}%) — elapsed ${fmtSec(elapsed)} `
243
+ );
244
+ },
245
+ });
246
+ const totalMs = Date.now() - t0;
247
+ process.stdout.write('\n');
248
+
249
+ const stat = fs.statSync(outputPath);
250
+ console.log(`[slice render] done in ${fmtSec(totalMs)} — ${humanSize(stat.size)}`);
251
+ console.log(`[slice render] saved to ${outputPath}`);
252
+ return { outputPath, totalMs, frames: lastFrame, size: stat.size };
253
+ }
254
+
255
+ async function handle(args) {
256
+ const { parseFlag } = require('../core/args');
257
+ const output = parseFlag(args, '--output', '-o');
258
+ const positional = args.find(
259
+ (a) => !a.startsWith('-') && !a.startsWith('--')
260
+ );
261
+ if (!positional) {
262
+ console.error('Usage: voxflow slice render <deck.json> [--output out.mp4]');
263
+ process.exit(1);
264
+ }
265
+ try {
266
+ await render({ deckPath: positional, output });
267
+ } catch (err) {
268
+ console.error(`\nslice render failed: ${err.message}`);
269
+ if (process.env.VOXFLOW_DEBUG) console.error(err.stack);
270
+ process.exit(1);
271
+ }
272
+ }
273
+
274
+ module.exports = {
275
+ render,
276
+ handle,
277
+ buildInputProps,
278
+ resolveServeUrl,
279
+ chromeBinaryExists,
280
+ THEME_TO_DECK_ID,
281
+ DEFAULT_THEME,
282
+ };
@@ -0,0 +1,264 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * `voxflow slice stage <deck.json>` — boot a local Stage server that watches a
5
+ * deck JSON file and hot-reloads a localhost preview page on every change.
6
+ *
7
+ * No `meta` export — this is reached only via `slice.js` dispatch (first
8
+ * positional arg `stage`), so it stays out of the registry parity test.
9
+ *
10
+ * Phase 1 scope: server + watcher + minimal deck-JSON UI.
11
+ * Phase 1.3+ will add: Remotion `renderStill` thumbnails, exports, publish.
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ const { startStageServer } = require('../stage-core/server');
18
+ const { watchDeckFile } = require('../stage-core/watcher');
19
+ const { createEventBus } = require('../stage-core/event-bus');
20
+ const { createSnapshotStore } = require('../stage-core/snapshot-store');
21
+ const { createCloudRenderClient } = require('../stage-core/cloud-render');
22
+ const { renderSliceStageHtml } = require('../stage-ui/slice/template');
23
+ const { emit: emitTelemetry } = require('../core/telemetry');
24
+
25
+ // Sourced from the canonical registry at repo root. Previously this list
26
+ // silently fell out of sync (lagged at 6 themes while the rest of the repo
27
+ // had 13) — the JSON import makes that impossible.
28
+ const SLICE_THEME_REGISTRY = require('../../../slice-themes.json');
29
+ const VALID_THEMES = SLICE_THEME_REGISTRY.themes.map((t) => t.id);
30
+
31
+ function loadInitialDeck(absPath) {
32
+ try {
33
+ const raw = fs.readFileSync(absPath, 'utf8');
34
+ return { deck: JSON.parse(raw), error: null };
35
+ } catch (err) {
36
+ return { deck: null, error: err.code === 'ENOENT'
37
+ ? `File not found: ${absPath}. Run \`voxflow slice <article>\` first, or pass --output to write a deck.json.`
38
+ : `Could not read deck: ${err.message}` };
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Programmatic entry — used by tests + the CLI handler. Returns a handle that
44
+ * exposes the listening URL and a stop() that tears everything down.
45
+ *
46
+ * @param {object} opts
47
+ * @param {string} opts.deckPath
48
+ * @param {string} [opts.theme] Lock to a single theme (validated)
49
+ * @param {number} [opts.port=5180]
50
+ * @param {number} [opts.maxPortTries=10]
51
+ * @returns {Promise<{url:string, port:number, stop:() => Promise<void>}>}
52
+ */
53
+ async function startSliceStage(opts) {
54
+ const deckPath = path.resolve(opts.deckPath);
55
+ const theme = opts.theme;
56
+ if (theme && !VALID_THEMES.includes(theme)) {
57
+ throw new Error(
58
+ `Unknown theme "${theme}". Valid: ${VALID_THEMES.join(', ')}`
59
+ );
60
+ }
61
+
62
+ const bus = createEventBus();
63
+ let snapshot = loadInitialDeck(deckPath);
64
+ snapshot.sourcePath = deckPath;
65
+
66
+ // Per-deck history. Initial parseable load = baseline snapshot (v0). Every
67
+ // subsequent watcher event with parseable content gets snapshotted; the
68
+ // store dedups by content hash so atomic-write double events stay one
69
+ // version. A `restore` flag rides on the deck event so the UI knows to
70
+ // skip the just-changed diff highlight (otherwise restoring an old deck
71
+ // would flash every card as "changed").
72
+ const snapshotStore = createSnapshotStore(deckPath);
73
+ let pendingRestoreId = null;
74
+ if (snapshot.deck) snapshotStore.snapshotIfChanged(snapshot.deck);
75
+
76
+ const watcher = watchDeckFile({
77
+ filePath: deckPath,
78
+ onChange(deck, err) {
79
+ if (err && !deck) {
80
+ snapshot = { deck: null, error: err.message, sourcePath: deckPath };
81
+ bus.publish({ type: 'error', sourcePath: deckPath, error: err.message, ts: Date.now() });
82
+ return;
83
+ }
84
+ snapshot = { deck, error: null, sourcePath: deckPath };
85
+ snapshotStore.snapshotIfChanged(deck);
86
+ const restoredFromId = pendingRestoreId;
87
+ pendingRestoreId = null;
88
+ bus.publish({
89
+ type: 'deck',
90
+ sourcePath: deckPath,
91
+ deck,
92
+ theme: theme || null,
93
+ ts: Date.now(),
94
+ restoredFrom: restoredFromId,
95
+ });
96
+ },
97
+ });
98
+
99
+ // Server-side restore writer: writes the chosen snapshot bytes back to the
100
+ // live deck file, sets `pendingRestoreId` so the next watcher event tags
101
+ // the deck event with `restoredFrom`. Server.js passes through to this.
102
+ const snapshotsBridge = {
103
+ list: snapshotStore.list,
104
+ load: snapshotStore.load,
105
+ getStorePath: snapshotStore.getStorePath,
106
+ restore(id) {
107
+ const deck = snapshotStore.load(id);
108
+ if (!deck) return null;
109
+ pendingRestoreId = id;
110
+ try {
111
+ fs.writeFileSync(deckPath, JSON.stringify(deck, null, 2) + '\n', 'utf8');
112
+ } catch (err) {
113
+ pendingRestoreId = null;
114
+ throw err;
115
+ }
116
+ return { deck };
117
+ },
118
+ };
119
+
120
+ const preferredPort = Number.isInteger(opts.port) && opts.port > 0 ? opts.port : 5180;
121
+ const maxPortTries = Number.isInteger(opts.maxPortTries) && opts.maxPortTries > 0
122
+ ? opts.maxPortTries : 10;
123
+
124
+ // We have to start the server before knowing the final port (port-conflict
125
+ // retry happens inside startStageServer). So we render the HTML lazily,
126
+ // re-rendering each request with the actual port baked in.
127
+ let actualPort = preferredPort;
128
+ const uiHtml = () => renderSliceStageHtml({ sourcePath: deckPath, port: actualPort });
129
+
130
+ // startStageServer takes a string for uiHtml; we pass a stub that will be
131
+ // replaced once we know the port. Cheap workaround: render once with
132
+ // `preferredPort`, then patch after we get the real port back.
133
+ const initialHtml = renderSliceStageHtml({ sourcePath: deckPath, port: preferredPort });
134
+
135
+ // Cloud render bridge — proxies POST /api/render-mp4 (and friends) to the
136
+ // backend cloud-render endpoint. Token comes from the same CLI cache the
137
+ // user logged into; the page never sees the JWT.
138
+ const cloudRender = opts.cloudRender || createCloudRenderClient();
139
+
140
+ const handle = await startStageServer({
141
+ uiHtml: initialHtml,
142
+ getDeckSnapshot: () => snapshot,
143
+ subscribe: bus.subscribe,
144
+ snapshots: snapshotsBridge,
145
+ cloudRender,
146
+ preferredPort,
147
+ maxPortTries,
148
+ });
149
+ actualPort = handle.port;
150
+
151
+ // Single anonymous launch event — feeds the "should Stage get an MCP?"
152
+ // decision. Fire-and-forget; opt-out via VOXFLOW_TELEMETRY=0 / DO_NOT_TRACK.
153
+ emitTelemetry('cli.slice.stage.launched', { theme: theme || null });
154
+
155
+ // If the port shifted (5180 → 5181 due to conflict), re-render the HTML so
156
+ // the footer hint shows the truthy URL. We swap by replacing the listener
157
+ // on the server: simpler than re-injecting, just update the closure used
158
+ // by startStageServer's GET / branch via a tiny patch.
159
+ if (actualPort !== preferredPort) {
160
+ // The HTML is captured by closure inside startStageServer; rather than
161
+ // wiring a setter, we do nothing — the page only references the port in
162
+ // a footer hint, and the live SSE connection works regardless. This is
163
+ // acceptable for MVP; revisit if/when port mismatch becomes user-visible.
164
+ void uiHtml;
165
+ }
166
+
167
+ return {
168
+ url: handle.url,
169
+ port: handle.port,
170
+ sourcePath: deckPath,
171
+ async stop() {
172
+ watcher.stop();
173
+ await handle.close();
174
+ },
175
+ };
176
+ }
177
+
178
+ // ─── CLI Handler ────────────────────────────────────────────────────────────
179
+
180
+ async function handle(args) {
181
+ const { parseFlag } = require('../core/args');
182
+
183
+ const portFlag = parseFlag(args, '--port');
184
+ const themeFlag = parseFlag(args, '--theme');
185
+ const noOpen = args.includes('--no-open');
186
+
187
+ // First positional arg that isn't a flag value
188
+ const valuedFlags = new Set(['--port', '--theme']);
189
+ let deckPath = null;
190
+ for (let i = 0; i < args.length; i++) {
191
+ if (args[i].startsWith('-')) {
192
+ if (valuedFlags.has(args[i])) i++;
193
+ continue;
194
+ }
195
+ deckPath = args[i];
196
+ break;
197
+ }
198
+ if (!deckPath) {
199
+ console.error('Error: provide a deck JSON file');
200
+ console.error('Usage: voxflow slice stage <deck.json> [--port <n>] [--theme <id>] [--no-open]');
201
+ process.exit(1);
202
+ }
203
+
204
+ let port;
205
+ if (portFlag) {
206
+ const parsed = Number.parseInt(portFlag, 10);
207
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
208
+ console.error(`Error: --port "${portFlag}" is not a valid TCP port`);
209
+ process.exit(1);
210
+ }
211
+ port = parsed;
212
+ }
213
+
214
+ let stage;
215
+ try {
216
+ stage = await startSliceStage({
217
+ deckPath,
218
+ theme: themeFlag || undefined,
219
+ port,
220
+ });
221
+ } catch (err) {
222
+ console.error(`\x1b[31mError:\x1b[0m ${err.message}`);
223
+ process.exit(1);
224
+ }
225
+
226
+ console.log('');
227
+ console.log('\x1b[36m=== VoxFlow Stage — Slice ===\x1b[0m');
228
+ console.log(`Watching: ${stage.sourcePath}`);
229
+ console.log(`Preview: ${stage.url}`);
230
+ if (themeFlag) console.log(`Theme: ${themeFlag}`);
231
+ console.log('');
232
+ console.log('Edit the deck JSON to hot-reload. Ctrl+C to stop.');
233
+ console.log('');
234
+
235
+ if (!noOpen && !process.env.CI) {
236
+ try {
237
+ const open = (await import('open')).default;
238
+ await open(stage.url);
239
+ } catch {
240
+ // Non-fatal — user can open the URL manually.
241
+ }
242
+ }
243
+
244
+ // Keep the process alive until SIGINT.
245
+ let stopping = false;
246
+ const stopAndExit = async (signal) => {
247
+ if (stopping) return;
248
+ stopping = true;
249
+ console.log(`\n\x1b[33m${signal} received — stopping Stage…\x1b[0m`);
250
+ try { await stage.stop(); } catch { /* best-effort */ }
251
+ process.exit(0);
252
+ };
253
+ process.on('SIGINT', () => stopAndExit('SIGINT'));
254
+ process.on('SIGTERM', () => stopAndExit('SIGTERM'));
255
+
256
+ // Park.
257
+ return new Promise(() => { /* never resolves; SIGINT exits */ });
258
+ }
259
+
260
+ module.exports = {
261
+ handle,
262
+ startSliceStage,
263
+ VALID_THEMES,
264
+ };