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,512 @@
1
+ /**
2
+ * VoxFlow CLI — Explain command
3
+ *
4
+ * Generates an AI explainer video: LLM script → TTS narration → Remotion render → MP4
5
+ * Phase 1: demo mode with hardcoded script, audio-only fallback.
6
+ * Phase 2: full LLM + TTS pipeline.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { EXPLAIN_DEFAULTS } = require('../core/config');
12
+ const { request, throwApiError, throwNetworkError } = require('../core/http');
13
+ const { buildWav } = require('../core/audio');
14
+ const { BYTES_PER_MS } = require('../core/timeline');
15
+ const { mergeAudioVideo } = require('../core/ffmpeg');
16
+ const { startSpinner } = require('../core/spinner');
17
+
18
+ // ─── Demo Script ────────────────────────────────────────────────────────────
19
+
20
+ const DEMO_SCRIPT = {
21
+ title: 'What is React?',
22
+ language: 'en',
23
+ style: 'modern',
24
+ scenes: [
25
+ {
26
+ type: 'title',
27
+ title: 'What is React?',
28
+ subtitle: 'A JavaScript library for building user interfaces',
29
+ narration: 'Welcome to this quick explainer on React, one of the most popular frontend libraries in the world.',
30
+ },
31
+ {
32
+ type: 'bullets',
33
+ heading: 'Core Concepts',
34
+ bullets: [
35
+ 'Component-based architecture',
36
+ 'Virtual DOM for efficient updates',
37
+ 'Declarative UI with JSX',
38
+ 'Unidirectional data flow',
39
+ ],
40
+ narration: 'React is built around several core concepts. First, everything is a component. Second, it uses a virtual DOM for efficient rendering. Third, you write declarative UI with JSX syntax. And fourth, data flows in one direction, from parent to child.',
41
+ },
42
+ {
43
+ type: 'bullets',
44
+ heading: 'Why Use React?',
45
+ bullets: [
46
+ 'Massive ecosystem and community',
47
+ 'Reusable component library',
48
+ 'Excellent developer tools',
49
+ ],
50
+ narration: 'So why choose React? It has a massive ecosystem with thousands of libraries. You can build reusable component libraries. And the developer tools are some of the best in the industry.',
51
+ },
52
+ {
53
+ type: 'summary',
54
+ heading: 'Key Takeaways',
55
+ points: [
56
+ 'React makes UI development predictable and efficient',
57
+ 'Components are the building blocks of React apps',
58
+ 'The virtual DOM optimizes rendering performance',
59
+ ],
60
+ narration: 'To summarize: React makes UI development predictable and efficient. Components are the fundamental building blocks. And the virtual DOM ensures your app stays fast, even as it grows. Thanks for watching!',
61
+ },
62
+ ],
63
+ };
64
+
65
+ // ─── TTS Synthesis ──────────────────────────────────────────────────────────
66
+
67
+ async function synthesizeScene(apiBase, token, text, voiceId, speed, index, total) {
68
+ process.stdout.write(` TTS scene [${index + 1}/${total}]...`);
69
+
70
+ let status, data;
71
+ try {
72
+ ({ status, data } = await request(`${apiBase}/api/tts/synthesize`, {
73
+ method: 'POST',
74
+ headers: {
75
+ 'Content-Type': 'application/json',
76
+ 'Authorization': `Bearer ${token}`,
77
+ },
78
+ }, {
79
+ text,
80
+ voiceId,
81
+ speed,
82
+ volume: 1.0,
83
+ }));
84
+ } catch (err) {
85
+ console.log(' FAIL');
86
+ throwNetworkError(err, apiBase);
87
+ }
88
+
89
+ if (status !== 200 || data.code !== 'success') {
90
+ console.log(' FAIL');
91
+ throwApiError(status, data, `TTS scene ${index + 1}`);
92
+ }
93
+
94
+ const pcmBuffer = Buffer.from(data.audio, 'base64');
95
+ const durationMs = Math.round(pcmBuffer.length / BYTES_PER_MS);
96
+ console.log(` OK (${(pcmBuffer.length / 1024).toFixed(0)} KB, ${(durationMs / 1000).toFixed(1)}s)`);
97
+ return { pcm: pcmBuffer, durationMs, quota: data.quota };
98
+ }
99
+
100
+ // ─── Audio Helpers ──────────────────────────────────────────────────────────
101
+
102
+ function buildNarrationWav(pcmBuffers, silenceSec) {
103
+ const result = buildWav(pcmBuffers, silenceSec);
104
+ return result.wav;
105
+ }
106
+
107
+ // ─── LLM Script Generation ─────────────────────────────────────────────────
108
+
109
+ /**
110
+ * Generate an ExplainScript by calling the LLM API.
111
+ * Falls back to a demo-based script on failure.
112
+ */
113
+ async function generateScript(apiBase, token, topic, { language = 'en', sceneCount = 5, style = 'modern' } = {}) {
114
+ let status, data;
115
+ try {
116
+ ({ status, data } = await request(`${apiBase}/api/llm/generate-explain-script`, {
117
+ method: 'POST',
118
+ headers: {
119
+ 'Content-Type': 'application/json',
120
+ 'Authorization': `Bearer ${token}`,
121
+ },
122
+ timeoutMs: 180_000,
123
+ }, {
124
+ topic,
125
+ language,
126
+ sceneCount,
127
+ style,
128
+ }));
129
+ } catch (err) {
130
+ console.log(` ⚠ LLM request failed: ${err.message}`);
131
+ console.log(' Falling back to demo script template.');
132
+ return buildFallbackScript(topic, style);
133
+ }
134
+
135
+ if (status !== 200 || data.code !== 'success' || !data.script) {
136
+ const msg = data?.message || `HTTP ${status}`;
137
+ console.log(` ⚠ LLM generation failed: ${msg}`);
138
+ console.log(' Falling back to demo script template.');
139
+ return buildFallbackScript(topic, style);
140
+ }
141
+
142
+ console.log(` ✓ Script generated: "${data.script.title}" (${data.script.scenes.length} scenes)`);
143
+ return data.script;
144
+ }
145
+
146
+ /**
147
+ * Build a fallback script based on DEMO_SCRIPT when LLM is unavailable.
148
+ */
149
+ function buildFallbackScript(topic, style) {
150
+ const script = { ...DEMO_SCRIPT, title: topic, style };
151
+ script.scenes = [
152
+ { ...DEMO_SCRIPT.scenes[0], title: topic, narration: `Welcome to this explainer on ${topic}.` },
153
+ ...DEMO_SCRIPT.scenes.slice(1),
154
+ ];
155
+ return script;
156
+ }
157
+
158
+ // ─── Remotion Render ────────────────────────────────────────────────────────
159
+
160
+ function findRemotionDir() {
161
+ // Walk up from CLI dir to find remotion/ at repo root
162
+ let dir = __dirname;
163
+ for (let i = 0; i < 5; i++) {
164
+ const candidate = path.join(dir, 'remotion');
165
+ if (fs.existsSync(path.join(candidate, 'package.json'))) {
166
+ return candidate;
167
+ }
168
+ dir = path.dirname(dir);
169
+ }
170
+ return null;
171
+ }
172
+
173
+ function isRemotionAvailable() {
174
+ const remotionDir = findRemotionDir();
175
+ if (!remotionDir) return false;
176
+ return fs.existsSync(path.join(remotionDir, 'node_modules', 'remotion'));
177
+ }
178
+
179
+ async function renderVideo(script, sceneTimings, outputPath, onProgress) {
180
+ const remotionDir = findRemotionDir();
181
+ if (!remotionDir) {
182
+ throw new Error('Remotion directory not found. Ensure remotion/ exists at the repo root with dependencies installed.');
183
+ }
184
+
185
+ // Write props to temp file
186
+ const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'voxflow-explain-'));
187
+ const propsPath = path.join(tmpDir, 'props.json');
188
+
189
+ const props = {
190
+ fps: 30,
191
+ script,
192
+ scenes: sceneTimings,
193
+ };
194
+ fs.writeFileSync(propsPath, JSON.stringify(props, null, 2));
195
+
196
+ // Spawn render.ts as a subprocess
197
+ const renderScript = path.join(remotionDir, 'render.ts');
198
+ const { execFile } = require('child_process');
199
+ const tsNode = path.join(remotionDir, 'node_modules', '.bin', 'ts-node');
200
+
201
+ return new Promise((resolve, reject) => {
202
+ const child = execFile(
203
+ tsNode,
204
+ [renderScript, '--props', propsPath, '--output', outputPath],
205
+ { cwd: remotionDir, maxBuffer: 50 * 1024 * 1024, timeout: 600_000 },
206
+ (err, stdout, stderr) => {
207
+ // Clean up temp
208
+ try { fs.unlinkSync(propsPath); fs.rmdirSync(tmpDir); } catch { /* ignore */ }
209
+
210
+ if (err) {
211
+ reject(new Error(`Remotion render failed: ${err.message}\n${stderr}`));
212
+ return;
213
+ }
214
+
215
+ // Parse stdout for result
216
+ try {
217
+ const lines = stdout.trim().split('\n');
218
+ const last = lines[lines.length - 1];
219
+ const result = JSON.parse(last);
220
+ resolve(result);
221
+ } catch {
222
+ resolve({ output: outputPath });
223
+ }
224
+ }
225
+ );
226
+
227
+ // Parse stderr for progress
228
+ if (child.stderr && onProgress) {
229
+ let buffer = '';
230
+ child.stderr.on('data', (chunk) => {
231
+ buffer += chunk.toString();
232
+ const lines = buffer.split('\n');
233
+ buffer = lines.pop() || '';
234
+ for (const line of lines) {
235
+ try {
236
+ const msg = JSON.parse(line);
237
+ if (msg.type === 'progress' && onProgress) {
238
+ onProgress(msg.percent);
239
+ }
240
+ } catch {
241
+ // Not JSON, just log output
242
+ }
243
+ }
244
+ });
245
+ }
246
+ });
247
+ }
248
+
249
+ // ─── Public API ─────────────────────────────────────────────────────────────
250
+
251
+ /**
252
+ * Generate an AI explainer video.
253
+ *
254
+ * @param {object} opts
255
+ * @param {string} opts.token - JWT token
256
+ * @param {string} opts.api - API base URL
257
+ * @param {string} [opts.topic] - Video topic (or 'demo' for hardcoded demo)
258
+ * @param {string} [opts.voice] - TTS voice ID
259
+ * @param {string} [opts.style] - Visual style: modern, playful, corporate, chalkboard
260
+ * @param {string} [opts.language] - Script language (en, zh, ja, etc.)
261
+ * @param {string} [opts.output] - Output file path
262
+ * @param {number} [opts.speed] - TTS speed
263
+ * @param {number} [opts.scenes] - Number of scenes (for LLM generation)
264
+ * @param {boolean} [opts.audioOnly] - Skip video render, output WAV only
265
+ * @param {boolean} [opts.cloud] - Render on cloud instead of local
266
+ * @returns {Promise<{outputPath: string, scriptPath: string, duration: number}>}
267
+ */
268
+ async function explain(opts) {
269
+ const {
270
+ token,
271
+ api: apiBase,
272
+ topic = 'demo',
273
+ voice = EXPLAIN_DEFAULTS.voice,
274
+ style = EXPLAIN_DEFAULTS.style,
275
+ language = EXPLAIN_DEFAULTS.language,
276
+ speed = EXPLAIN_DEFAULTS.speed,
277
+ scenes: sceneCount = EXPLAIN_DEFAULTS.sceneCount,
278
+ audioOnly = false,
279
+ cloud = false,
280
+ } = opts;
281
+
282
+ // Reject .mp3 output — we write raw WAV bytes, not MP3-encoded data
283
+ if (opts.output && opts.output.endsWith('.mp3')) {
284
+ throw new Error('MP3 output is not supported for explain. Use .wav or .mp4');
285
+ }
286
+
287
+ // SIGINT handler for graceful cancellation
288
+ const sigintHandler = () => {
289
+ console.log('\n\nGeneration cancelled.');
290
+ process.exit(130);
291
+ };
292
+ process.on('SIGINT', sigintHandler);
293
+
294
+ try {
295
+ // ── Step 1: Get or generate script ────────────────────────────────────
296
+ let script;
297
+ const isDemo = topic === 'demo' || topic === 'Demo';
298
+
299
+ if (isDemo) {
300
+ console.log('\n[1/4] Using demo script (hardcoded)...');
301
+ script = { ...DEMO_SCRIPT, style };
302
+ } else {
303
+ // Phase 2: LLM script generation
304
+ console.log('\n[1/4] Generating script via LLM...');
305
+ console.log(` Topic: "${topic}" (${language}, ${sceneCount} scenes)`);
306
+ script = await generateScript(apiBase, token, topic, { language, sceneCount, style });
307
+ }
308
+
309
+ console.log(` Script: ${script.scenes.length} scenes, style: ${script.style}`);
310
+
311
+ // ── Step 2: Synthesize TTS narration ──────────────────────────────────
312
+ console.log(`\n[2/4] Synthesizing narration (${script.scenes.length} scenes)...`);
313
+
314
+ const pcmBuffers = [];
315
+ const sceneTimings = [];
316
+ let firstQuota = null;
317
+ let lastQuota = null;
318
+
319
+ for (let i = 0; i < script.scenes.length; i++) {
320
+ const scene = script.scenes[i];
321
+ const result = await synthesizeScene(
322
+ apiBase, token, scene.narration, voice, speed, i, script.scenes.length
323
+ );
324
+ pcmBuffers.push(result.pcm);
325
+ if (!firstQuota && result.quota) firstQuota = result.quota;
326
+ lastQuota = result.quota;
327
+
328
+ // Build timing data for Remotion (audio merged via ffmpeg after render)
329
+ sceneTimings.push({
330
+ scene,
331
+ durationMs: result.durationMs,
332
+ audioSrc: '',
333
+ });
334
+ }
335
+
336
+ // Build narration WAV from all scene PCM buffers (used for audio-only or muxing)
337
+ const narrationWavBuffer = buildNarrationWav(pcmBuffers, EXPLAIN_DEFAULTS.silence);
338
+
339
+ // ── Step 3: Render video or audio ─────────────────────────────────────
340
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
341
+ let outputPath;
342
+
343
+ if (audioOnly || !isRemotionAvailable()) {
344
+ if (!audioOnly && !isRemotionAvailable()) {
345
+ console.log('\n[3/4] Remotion not available. Falling back to audio-only output.');
346
+ console.log(' To enable video rendering, install Remotion:');
347
+ console.log(' cd remotion && npm install');
348
+ } else {
349
+ console.log('\n[3/4] Building audio-only output...');
350
+ }
351
+
352
+ outputPath = opts.output || `explain-${timestamp}.wav`;
353
+ fs.writeFileSync(outputPath, narrationWavBuffer);
354
+ } else {
355
+ if (cloud) {
356
+ console.log('\n[3/4] Cloud rendering coming in Phase 2. Using local render.');
357
+ }
358
+ outputPath = opts.output || `explain-${timestamp}.mp4`;
359
+ const spinner = startSpinner(cloud ? ' Rendering video...' : '\n[3/4] Rendering video...');
360
+ try {
361
+ // Remotion renders video-only (no audio); we merge narration via ffmpeg
362
+ const silentVideoPath = outputPath + '.silent.mp4';
363
+ await renderVideo(script, sceneTimings, silentVideoPath, (percent) => {
364
+ spinner.update(` Rendering video... ${percent}%`);
365
+ });
366
+ spinner.stop('OK');
367
+
368
+ // Merge narration audio into the rendered video
369
+ console.log(' Merging narration audio...');
370
+ const narrationWavPath = outputPath + '.narration.wav';
371
+ fs.writeFileSync(narrationWavPath, narrationWavBuffer);
372
+ await mergeAudioVideo(silentVideoPath, narrationWavPath, outputPath);
373
+
374
+ // Clean up temp files
375
+ try { fs.unlinkSync(silentVideoPath); } catch { /* ignore */ }
376
+ try { fs.unlinkSync(narrationWavPath); } catch { /* ignore */ }
377
+ console.log(' Audio merged OK');
378
+ } catch (err) {
379
+ spinner.stop('FAIL');
380
+ console.log(` Video render failed: ${err.message}`);
381
+ console.log(' Falling back to audio-only...');
382
+ outputPath = outputPath.replace(/\.mp4$/, '.wav');
383
+ fs.writeFileSync(outputPath, narrationWavBuffer);
384
+ }
385
+ }
386
+
387
+ // ── Step 4: Save script & summary ─────────────────────────────────────
388
+ const scriptPath = outputPath.replace(/\.(mp4|wav)$/, '.json');
389
+ fs.writeFileSync(scriptPath, JSON.stringify(script, null, 2));
390
+
391
+ const totalDurationMs = sceneTimings.reduce((sum, s) => sum + s.durationMs, 0);
392
+ const fileStat = fs.statSync(outputPath);
393
+ const fileSizeMb = (fileStat.size / (1024 * 1024)).toFixed(1);
394
+ const durationStr = formatDuration(totalDurationMs);
395
+
396
+ console.log('\n[4/4] Done!');
397
+ console.log(`\n=== Output ===`);
398
+ console.log(` File: ${outputPath} (${fileSizeMb} MB, ${durationStr})`);
399
+ console.log(` Script: ${scriptPath}`);
400
+ console.log(` Scenes: ${script.scenes.length}`);
401
+ console.log(` Style: ${script.style}`);
402
+ if (lastQuota) {
403
+ const quotaUsed = firstQuota && lastQuota
404
+ ? (firstQuota.remaining - lastQuota.remaining)
405
+ : script.scenes.length * 100;
406
+ console.log(` Quota: ${quotaUsed} used, ${lastQuota.remaining ?? '?'} remaining`);
407
+ }
408
+
409
+ return { outputPath, scriptPath, duration: totalDurationMs };
410
+ } finally {
411
+ process.removeListener('SIGINT', sigintHandler);
412
+ }
413
+ }
414
+
415
+ function formatDuration(ms) {
416
+ const totalSec = Math.round(ms / 1000);
417
+ const min = Math.floor(totalSec / 60);
418
+ const sec = totalSec % 60;
419
+ if (min === 0) return `${sec}s`;
420
+ return `${min}m${sec.toString().padStart(2, '0')}s`;
421
+ }
422
+
423
+ // ─── CLI Handler ────────────────────────────────────────────────────────────
424
+
425
+ async function handle(args) {
426
+ const { parseFlag, parseIntFlag, parseFloatFlag, parseBoolFlag, validateSpeed, runWithRetry } = require('../core/args');
427
+ const { getToken, getTokenInfo } = require('../core/auth');
428
+ const { API_BASE } = require('../core/config');
429
+
430
+ const api = parseFlag(args, '--api') || API_BASE;
431
+ const explicitToken = parseFlag(args, '--token');
432
+
433
+ // Validate flags BEFORE auth (to avoid browser login for invalid args)
434
+ const speed = parseFloatFlag(args, '--speed');
435
+ const output = parseFlag(args, '--output', '-o');
436
+ const scenes = parseIntFlag(args, '--scenes');
437
+
438
+ validateSpeed(args, speed);
439
+
440
+ if (output) {
441
+ const validExts = ['.wav', '.mp3', '.mp4'];
442
+ const hasValidExt = validExts.some(ext => output.toLowerCase().endsWith(ext));
443
+ if (!hasValidExt) {
444
+ console.error('Error: --output path must end with .wav, .mp3, or .mp4');
445
+ process.exit(1);
446
+ }
447
+ }
448
+
449
+ const style = parseFlag(args, '--style');
450
+ if (style && !['modern', 'playful', 'corporate', 'chalkboard'].includes(style)) {
451
+ console.error(`Error: --style must be one of: modern, playful, corporate, chalkboard (got: "${style}")`);
452
+ process.exit(1);
453
+ }
454
+
455
+ if (scenes !== undefined) {
456
+ if (isNaN(scenes) || scenes < 3 || scenes > 12) {
457
+ console.error(`Error: --scenes must be between 3 and 12 (got: "${parseFlag(args, '--scenes')}")`);
458
+ process.exit(1);
459
+ }
460
+ }
461
+
462
+ // Auth after validation
463
+ let token;
464
+ if (explicitToken) {
465
+ token = explicitToken;
466
+ } else {
467
+ token = await getToken({ api });
468
+ const info = getTokenInfo();
469
+ if (info) {
470
+ console.log(`\x1b[32mLogged in as ${info.email}\x1b[0m`);
471
+ }
472
+ }
473
+
474
+ const opts = {
475
+ token, api,
476
+ topic: parseFlag(args, '--topic') || undefined,
477
+ voice: parseFlag(args, '--voice') || undefined,
478
+ style: style || undefined,
479
+ language: parseFlag(args, '--language') || undefined,
480
+ output, speed, scenes,
481
+ audioOnly: parseBoolFlag(args, '--audio-only'),
482
+ cloud: parseBoolFlag(args, '--cloud'),
483
+ };
484
+
485
+ await runWithRetry(explain, opts, api, explicitToken);
486
+ }
487
+
488
+ const meta = {
489
+ explain: {
490
+ usage: '[opts]',
491
+ description: 'Generate an AI explainer video from a topic',
492
+ options: [
493
+ `--topic <text> Topic to explain (use "demo" for built-in demo)`,
494
+ `--style <style> Visual style: modern (default), playful, corporate, chalkboard`,
495
+ `--language <code> Script language: en (default), zh, ja, ko, etc.`,
496
+ `--voice <id> TTS voice ID (default: ${EXPLAIN_DEFAULTS.voice})`,
497
+ `--speed <n> TTS speed 0.5-2.0 (default: ${EXPLAIN_DEFAULTS.speed})`,
498
+ `--scenes <n> Number of scenes, 3-12 (default: ${EXPLAIN_DEFAULTS.sceneCount})`,
499
+ `--audio-only Skip video render, output WAV narration only`,
500
+ `--cloud Render on cloud instead of local Remotion`,
501
+ `--output <path> Output file path (default: ./explain-<timestamp>.mp4)`,
502
+ ],
503
+ examples: [
504
+ 'voxflow explain --topic "What is React?"',
505
+ 'voxflow explain --topic demo --output demo.mp4',
506
+ 'voxflow explain --topic "区块链入门" --style chalkboard --voice v-male-Bk7vD3xP',
507
+ 'voxflow explain --topic "Machine Learning" --audio-only',
508
+ ],
509
+ },
510
+ };
511
+
512
+ module.exports = { explain, handle, meta };
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+
3
+ const os = require('os');
4
+ const { execFile } = require('child_process');
5
+ const readline = require('readline');
6
+ const pkg = require('../../package.json');
7
+
8
+ const REPO = 'VoxFlowStudio/FlowStudio';
9
+ const REPO_URL = `https://github.com/${REPO}`;
10
+
11
+ const TYPE_MAP = {
12
+ bug: { prefix: '[CLI Bug] ', labels: 'type/bug,area/cli', template: 'cli-bug.md' },
13
+ feature: { prefix: '[CLI Feature] ', labels: 'type/feat,area/cli', template: 'cli-feature.md' },
14
+ general: { prefix: '[CLI] ', labels: 'area/cli', template: null },
15
+ };
16
+
17
+ function systemInfo() {
18
+ return [
19
+ `**CLI version:** ${pkg.version}`,
20
+ `**OS:** ${process.platform} ${os.arch()} (${os.release()})`,
21
+ `**Node:** ${process.version}`,
22
+ ].join('\n');
23
+ }
24
+
25
+ function buildIssueUrl({ type, title, body }) {
26
+ const tmpl = TYPE_MAP[type] || TYPE_MAP.general;
27
+ const params = new URLSearchParams();
28
+ params.set('title', tmpl.prefix + title);
29
+ params.set('body', (body ? body + '\n\n' : '') + '---\n\n' + systemInfo());
30
+ params.set('labels', tmpl.labels);
31
+ if (tmpl.template) params.set('template', tmpl.template);
32
+ return `${REPO_URL}/issues/new?${params.toString()}`;
33
+ }
34
+
35
+ // Try `gh issue create`. Returns issue URL on success, null if gh unavailable/fails.
36
+ function ghSubmit({ type, title, body }) {
37
+ return new Promise((resolve) => {
38
+ const tmpl = TYPE_MAP[type] || TYPE_MAP.general;
39
+ const fullTitle = tmpl.prefix + title;
40
+ const fullBody = (body ? body + '\n\n' : '') + '---\n\n' + systemInfo();
41
+ const ghArgs = [
42
+ 'issue', 'create',
43
+ '--repo', REPO,
44
+ '--title', fullTitle,
45
+ '--body', fullBody,
46
+ '--label', tmpl.labels,
47
+ ];
48
+ execFile('gh', ghArgs, { timeout: 15000 }, (err, stdout) => {
49
+ if (err) { resolve(null); return; }
50
+ resolve((stdout || '').trim() || null);
51
+ });
52
+ });
53
+ }
54
+
55
+ async function collectInfo(presetType) {
56
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
57
+ const ask = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim())));
58
+ try {
59
+ process.stderr.write('\n VoxFlow CLI Feedback\n\n');
60
+ let type = presetType;
61
+ if (!type) {
62
+ process.stderr.write(' Type:\n 1) Bug report\n 2) Feature request\n 3) General feedback\n\n');
63
+ const choice = await ask(' Choice [1/2/3]: ');
64
+ type = ({ '1': 'bug', '2': 'feature', '3': 'general' })[choice] || 'general';
65
+ }
66
+ const title = await ask('\n Title: ');
67
+ if (!title) { process.stderr.write(' Title is required.\n\n'); return null; }
68
+ process.stderr.write('\n Description (empty line to finish):\n');
69
+ const lines = [];
70
+ let line;
71
+ do {
72
+ line = await ask(' > ');
73
+ if (line) lines.push(line);
74
+ } while (line !== '');
75
+ return { type, title, body: lines.join('\n') };
76
+ } finally {
77
+ rl.close();
78
+ }
79
+ }
80
+
81
+ async function submitOrFallback({ type, title, body }, printUrl) {
82
+ // Try gh first (direct submit, no browser)
83
+ const issueUrl = await ghSubmit({ type, title, body });
84
+ if (issueUrl) {
85
+ process.stderr.write(`\n ✓ Issue created: ${issueUrl}\n\n`);
86
+ process.stdout.write(issueUrl + '\n');
87
+ return;
88
+ }
89
+ // gh unavailable → URL fallback
90
+ const url = buildIssueUrl({ type, title, body });
91
+ if (printUrl) {
92
+ process.stdout.write(url + '\n');
93
+ } else {
94
+ process.stderr.write('\n Opening browser...\n\n');
95
+ const open = (await import('open')).default;
96
+ await open(url);
97
+ }
98
+ }
99
+
100
+ async function handle(args) {
101
+ const { parseBoolFlag, parseFlag } = require('../core/args');
102
+
103
+ let presetType = null;
104
+ if (parseBoolFlag(args, '--bug')) presetType = 'bug';
105
+ else if (parseBoolFlag(args, '--feature')) presetType = 'feature';
106
+ else if (parseBoolFlag(args, '--general')) presetType = 'general';
107
+
108
+ const titleArg = parseFlag(args, '--title');
109
+ const bodyArg = parseFlag(args, '--body');
110
+ const printUrl = parseBoolFlag(args, '--print-url');
111
+
112
+ // Non-interactive mode: --title provided (AI / scripted use)
113
+ if (titleArg) {
114
+ await submitOrFallback({ type: presetType || 'general', title: titleArg, body: bodyArg || '' }, printUrl);
115
+ return;
116
+ }
117
+
118
+ // Interactive mode: requires TTY
119
+ if (!process.stdin.isTTY) {
120
+ process.stderr.write(
121
+ 'voxflow feedback requires an interactive terminal.\n' +
122
+ 'For non-interactive use: voxflow feedback --bug --title "..." --body "..."\n'
123
+ );
124
+ process.exit(1);
125
+ }
126
+
127
+ const info = await collectInfo(presetType);
128
+ if (!info) return;
129
+ await submitOrFallback(info, printUrl);
130
+ }
131
+
132
+ const meta = {
133
+ feedback: {
134
+ usage: '[--bug | --feature | --general] [--title "..."] [--body "..."] [--print-url]',
135
+ description: 'Submit a bug report or feature request to GitHub',
136
+ options: [
137
+ '--bug Pre-fill as bug report',
138
+ '--feature Pre-fill as feature request',
139
+ '--general Pre-fill as general feedback',
140
+ '--title <text> Title (enables non-interactive / AI mode)',
141
+ '--body <text> Description body',
142
+ '--print-url Print URL instead of opening browser (fallback only)',
143
+ ],
144
+ examples: [
145
+ 'voxflow feedback',
146
+ 'voxflow feedback --bug',
147
+ 'voxflow feedback --bug --title "asr crashes on wav files" --body "Steps: ..."',
148
+ ],
149
+ },
150
+ };
151
+
152
+ module.exports = { buildIssueUrl, systemInfo, ghSubmit, handle, meta };