voxflow 1.14.0 → 1.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/README.md +69 -2
  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-preview.js +266 -0
  383. package/lib/commands/slice-render.js +282 -0
  384. package/lib/commands/slice-stage.js +264 -0
  385. package/lib/commands/slice.js +343 -0
  386. package/lib/commands/slides/constants.js +108 -0
  387. package/lib/commands/slides/html-renderer.js +338 -0
  388. package/lib/commands/slides/index.js +345 -0
  389. package/lib/commands/slides.js +11 -0
  390. package/lib/commands/story.js +302 -0
  391. package/lib/commands/summarize.js +532 -0
  392. package/lib/commands/synthesize.js +261 -0
  393. package/lib/commands/translate.js +593 -0
  394. package/lib/commands/upgrade.js +249 -0
  395. package/lib/commands/video-translate.js +577 -0
  396. package/lib/commands/voices.js +292 -0
  397. package/lib/core/agent-env.js +104 -0
  398. package/lib/core/args.js +107 -0
  399. package/lib/core/asr-client.js +448 -0
  400. package/lib/core/asr-jobs-client.js +126 -0
  401. package/lib/core/asr-jobs-store.js +105 -0
  402. package/lib/core/asr-r2-upload.js +181 -0
  403. package/lib/core/asr-upload.js +132 -0
  404. package/lib/core/audio-extract.js +150 -0
  405. package/lib/core/audio.js +219 -0
  406. package/lib/core/auth.js +880 -0
  407. package/lib/core/config.js +197 -0
  408. package/lib/core/feedback.js +64 -0
  409. package/lib/core/ffmpeg.js +476 -0
  410. package/lib/core/http.js +188 -0
  411. package/lib/core/image-client.js +55 -0
  412. package/lib/core/intent-params.js +11 -0
  413. package/lib/core/llm-client.js +76 -0
  414. package/lib/core/logger.js +208 -0
  415. package/lib/core/mic-recorder.js +182 -0
  416. package/lib/core/pause-markers.js +94 -0
  417. package/lib/core/podcast-pacing.js +118 -0
  418. package/lib/core/spinner.js +33 -0
  419. package/lib/core/srt.js +394 -0
  420. package/lib/core/telemetry.js +100 -0
  421. package/lib/core/timeline.js +92 -0
  422. package/lib/core/tts-synthesizer.js +70 -0
  423. package/lib/core/update-check.js +185 -0
  424. package/lib/core/url-download.js +148 -0
  425. package/lib/core/whisper-local.js +279 -0
  426. package/lib/internal/deck-validator.js +488 -0
  427. package/lib/internal/slice-themes.json +370 -0
  428. package/lib/stage-core/cloud-render.js +170 -0
  429. package/lib/stage-core/deck-format.js +133 -0
  430. package/lib/stage-core/edit-prompt.js +104 -0
  431. package/lib/stage-core/event-bus.js +31 -0
  432. package/lib/stage-core/port.js +46 -0
  433. package/lib/stage-core/server.js +352 -0
  434. package/lib/stage-core/snapshot-store.js +198 -0
  435. package/lib/stage-core/watcher.js +106 -0
  436. package/lib/stage-ui/slice/template.js +1672 -0
  437. package/package.json +9 -4
  438. package/skills/.claude-plugin/marketplace.json +22 -0
  439. package/skills/.claude-plugin/plugin.json +25 -0
  440. package/skills/LICENSE +21 -0
  441. package/skills/README.md +120 -0
  442. package/skills/hub/SKILL.md +317 -0
  443. package/skills/podcast/SKILL.md +146 -0
  444. package/skills/slice/SKILL.md +205 -0
  445. package/skills/slice/agents/openai.yaml +4 -0
  446. package/skills/slice/references/deck-schema.md +183 -0
  447. package/skills/slice/references/example-decks.md +108 -0
  448. package/skills/slice/references/themes.md +172 -0
  449. package/skills/transcribe/SKILL.md +473 -0
  450. package/skills/video/SKILL.md +261 -0
  451. package/skills/voxflow-slice/SKILL.md +271 -0
  452. package/skills/voxflow-slice/examples/article.md +13 -0
  453. package/skills/voxflow-slice/examples/expected-deck.json +39 -0
  454. package/skills/voxflow-slice/examples/validate.mjs +46 -0
@@ -0,0 +1,266 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * `voxflow slice preview <deck.json>` — open a localhost HTML page that
5
+ * shows a midpoint-frame still of every card in the deck so users can
6
+ * see the cards before paying the full render cost (~30s for 20s video).
7
+ *
8
+ * Renders N PNG stills via @remotion/renderer.renderStill() at the
9
+ * midpoint frame of each card, caches them under /tmp/, then serves a
10
+ * minimal HTML page with the grid + auto-opens the browser. The server
11
+ * stays alive until Ctrl+C.
12
+ *
13
+ * Shares buildInputProps / resolveServeUrl / chromeBinaryExists with
14
+ * slice-render so behavior is consistent between preview and render.
15
+ *
16
+ * No `meta` export — reached via `slice.js` dispatch (`voxflow slice
17
+ * preview <path>`), so it stays out of the registry parity test.
18
+ */
19
+
20
+ const fs = require('node:fs');
21
+ const http = require('node:http');
22
+ const path = require('node:path');
23
+ const crypto = require('node:crypto');
24
+ const os = require('node:os');
25
+
26
+ const open = require('open').default;
27
+
28
+ const { validatePaperSlideDeck, isV2LayoutTreeDeck } = require('../internal/deck-validator');
29
+ const {
30
+ buildInputProps,
31
+ resolveServeUrl,
32
+ chromeBinaryExists,
33
+ THEME_TO_DECK_ID,
34
+ DEFAULT_THEME,
35
+ } = require('./slice-render');
36
+
37
+ const FPS = 30;
38
+ const DEFAULT_PORT = 5555;
39
+ const PREVIEW_WIDTH = 540; // half of 1080 — stills don't need full-res for thumbnails
40
+
41
+ function deckHash(deck) {
42
+ // 8-char hash so the cache dir is short but unique per deck content.
43
+ // If the deck changes, we want fresh stills; if it doesn't, reuse cache.
44
+ return crypto.createHash('sha1').update(JSON.stringify(deck)).digest('hex').slice(0, 8);
45
+ }
46
+
47
+ function cardMidpointFrame(deck, cardIndex) {
48
+ // Composition plays cards sequentially. To get card K's midpoint
49
+ // frame we sum previous durations then add half of this one.
50
+ let acc = 0;
51
+ for (let i = 0; i < cardIndex; i++) {
52
+ acc += deck.cards[i].durationSec || 4;
53
+ }
54
+ const here = deck.cards[cardIndex].durationSec || 4;
55
+ return Math.floor((acc + here / 2) * FPS);
56
+ }
57
+
58
+ async function findFreePort(start) {
59
+ return new Promise((resolve) => {
60
+ const probe = http.createServer();
61
+ probe.unref();
62
+ probe.on('error', () => resolve(findFreePort(start + 1)));
63
+ probe.listen(start, () => {
64
+ const port = probe.address().port;
65
+ probe.close(() => resolve(port));
66
+ });
67
+ });
68
+ }
69
+
70
+ function escapeHtml(s) {
71
+ return String(s ?? '').replace(/[&<>"']/g, (c) => ({
72
+ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
73
+ }[c]));
74
+ }
75
+
76
+ function renderHtml(deck, port) {
77
+ const title = escapeHtml(deck.seriesTitle || deck.header || 'Preview');
78
+ const meta = `${deck.cards.length} cards · theme: ${escapeHtml(deck.theme)} · 1080×1920`;
79
+ const cards = deck.cards.map((c, i) => {
80
+ const label = c.kind === 'title'
81
+ ? (Array.isArray(c.title) ? c.title.join(' / ') : '')
82
+ : (c.caption || '');
83
+ return `<figure class="card">
84
+ <img src="/still/${i}.png" alt="Card ${i + 1}" loading="lazy">
85
+ <figcaption><span class="idx">${i + 1}</span> <span class="kind">${escapeHtml(c.kind)}</span> ${escapeHtml(label)}</figcaption>
86
+ </figure>`;
87
+ }).join('\n ');
88
+
89
+ return `<!DOCTYPE html>
90
+ <html lang="zh">
91
+ <head>
92
+ <meta charset="utf-8">
93
+ <title>VoxFlow Slice · ${title}</title>
94
+ <style>
95
+ :root { color-scheme: light; }
96
+ body { font-family: ui-sans-serif, system-ui, -apple-system, "PingFang SC", sans-serif; margin: 0; padding: 40px 48px 64px; background: #faf6ee; color: #1b1815; }
97
+ h1 { font-size: 28px; font-weight: 500; letter-spacing: -0.01em; margin: 0 0 6px; }
98
+ .meta { color: #7a6f5f; font-size: 13px; margin-bottom: 28px; }
99
+ .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(216px, 1fr)); gap: 20px 16px; }
100
+ .card { margin: 0; display: flex; flex-direction: column; gap: 8px; }
101
+ .card img { width: 100%; aspect-ratio: 9/16; background: #000; border-radius: 6px; box-shadow: 0 1px 2px rgba(0,0,0,0.08), 0 4px 14px rgba(60,40,20,0.07); display: block; }
102
+ .card figcaption { font-size: 12px; line-height: 1.45; color: #4d4339; display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; }
103
+ .idx { font-variant-numeric: tabular-nums; color: #b8a994; font-weight: 600; }
104
+ .kind { display: inline-block; padding: 1px 6px; border-radius: 3px; background: #efe7d6; color: #6e5f49; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
105
+ .cmd { font-family: ui-monospace, "SF Mono", monospace; background: #2b2520; color: #faf6ee; padding: 14px 18px; border-radius: 6px; font-size: 13px; margin-top: 32px; user-select: all; }
106
+ .cmd .hint { color: #9c8e75; font-size: 11px; display: block; margin-bottom: 6px; }
107
+ </style>
108
+ </head>
109
+ <body>
110
+ <h1>${title}</h1>
111
+ <div class="meta">${meta}</div>
112
+ <div class="grid">
113
+ ${cards}
114
+ </div>
115
+ <div class="cmd">
116
+ <span class="hint">Satisfied? Render to mp4:</span>
117
+ voxflow slice render &lt;your-deck.json&gt; --output out.mp4
118
+ </div>
119
+ </body>
120
+ </html>`;
121
+ }
122
+
123
+ async function preview(opts) {
124
+ const deckPath = path.resolve(opts.deckPath);
125
+ if (!fs.existsSync(deckPath)) {
126
+ throw new Error(`Deck file not found: ${deckPath}`);
127
+ }
128
+ const raw = fs.readFileSync(deckPath, 'utf8');
129
+ let deck;
130
+ try {
131
+ deck = JSON.parse(raw);
132
+ } catch (err) {
133
+ throw new Error(`Could not parse deck JSON: ${err.message}`);
134
+ }
135
+
136
+ if (isV2LayoutTreeDeck(deck)) {
137
+ throw new Error(
138
+ 'V2 LayoutTree decks are not yet supported by offline preview. ' +
139
+ 'Use voxflow.studio/apps/slice for V2 decks or downgrade to V1 shape.'
140
+ );
141
+ }
142
+ validatePaperSlideDeck(deck);
143
+
144
+ const theme = deck.theme || DEFAULT_THEME;
145
+ const compId = THEME_TO_DECK_ID[theme];
146
+ if (!compId) {
147
+ throw new Error(
148
+ `Unknown theme "${theme}". Valid: ${Object.keys(THEME_TO_DECK_ID).join(', ')}`
149
+ );
150
+ }
151
+
152
+ const serveUrl = resolveServeUrl();
153
+ const inputProps = buildInputProps(deck);
154
+ const cacheDir = path.join(os.tmpdir(), `voxflow-preview-${deckHash(deck)}`);
155
+ fs.mkdirSync(cacheDir, { recursive: true });
156
+
157
+ // Lazy require — mirrors slice-render.js — keeps `voxflow login` etc. fast
158
+ // for users who never trigger Remotion.
159
+ const { selectComposition, renderStill } = require('@remotion/renderer');
160
+
161
+ const coldStart = !chromeBinaryExists();
162
+ if (coldStart) {
163
+ console.log(' First-run: Remotion will download chrome-headless-shell (~90 MB).');
164
+ }
165
+
166
+ console.log(`[slice preview] theme: ${theme}`);
167
+ console.log(`[slice preview] composition: ${compId}`);
168
+ console.log(`[slice preview] cards: ${deck.cards.length}`);
169
+
170
+ const composition = await selectComposition({
171
+ serveUrl,
172
+ id: compId,
173
+ inputProps,
174
+ });
175
+
176
+ const t0 = Date.now();
177
+ const cachedCount = deck.cards.filter((_, i) =>
178
+ fs.existsSync(path.join(cacheDir, `card-${i}.png`))
179
+ ).length;
180
+ if (cachedCount === deck.cards.length) {
181
+ console.log(`[slice preview] all ${cachedCount} stills cached — skipping render`);
182
+ } else {
183
+ for (let i = 0; i < deck.cards.length; i++) {
184
+ const outPath = path.join(cacheDir, `card-${i}.png`);
185
+ if (fs.existsSync(outPath)) continue;
186
+ const frame = cardMidpointFrame(deck, i);
187
+ process.stdout.write(`\r[slice preview] rendering still ${i + 1}/${deck.cards.length}...`);
188
+ await renderStill({
189
+ composition,
190
+ serveUrl,
191
+ inputProps,
192
+ frame,
193
+ output: outPath,
194
+ imageFormat: 'png',
195
+ scale: PREVIEW_WIDTH / 1080, // halve the resolution for snappier renders
196
+ });
197
+ }
198
+ const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
199
+ process.stdout.write(`\r[slice preview] ${deck.cards.length} stills rendered in ${elapsed}s\n`);
200
+ }
201
+
202
+ const port = await findFreePort(DEFAULT_PORT);
203
+ const html = renderHtml(deck, port);
204
+
205
+ const server = http.createServer((req, res) => {
206
+ const urlPath = (req.url || '/').split('?')[0];
207
+ if (urlPath === '/' || urlPath === '/index.html') {
208
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
209
+ res.end(html);
210
+ return;
211
+ }
212
+ const m = /^\/still\/(\d+)\.png$/.exec(urlPath);
213
+ if (m) {
214
+ const idx = Number(m[1]);
215
+ const file = path.join(cacheDir, `card-${idx}.png`);
216
+ if (!fs.existsSync(file)) {
217
+ res.writeHead(404);
218
+ res.end('still not found');
219
+ return;
220
+ }
221
+ res.writeHead(200, { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=600' });
222
+ fs.createReadStream(file).pipe(res);
223
+ return;
224
+ }
225
+ res.writeHead(404);
226
+ res.end('Not found');
227
+ });
228
+
229
+ await new Promise((resolve) => server.listen(port, '127.0.0.1', resolve));
230
+ const url = `http://localhost:${port}`;
231
+ console.log(`[slice preview] http://localhost:${port} (Ctrl+C to stop)`);
232
+
233
+ try {
234
+ await open(url);
235
+ } catch {
236
+ // open failed — user can paste the URL manually
237
+ }
238
+
239
+ process.stdin.resume();
240
+ process.on('SIGINT', () => {
241
+ console.log('\n[slice preview] stopped');
242
+ server.close(() => process.exit(0));
243
+ });
244
+ }
245
+
246
+ async function handle(args) {
247
+ const { parseFlag } = require('../core/args');
248
+ const positional = args.find((a) => !a.startsWith('-'));
249
+ parseFlag(args, '--output', '-o'); // parsed-but-ignored for symmetry with `slice render`
250
+ if (!positional) {
251
+ console.error('Usage: voxflow slice preview <deck.json>');
252
+ process.exit(1);
253
+ }
254
+ try {
255
+ await preview({ deckPath: positional });
256
+ } catch (err) {
257
+ console.error(`\nslice preview failed: ${err.message}`);
258
+ if (process.env.VOXFLOW_DEBUG) console.error(err.stack);
259
+ process.exit(1);
260
+ }
261
+ }
262
+
263
+ module.exports = {
264
+ preview,
265
+ handle,
266
+ };
@@ -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
+ };