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,264 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * `voxflow slice stage <deck.json>` — boot a local Stage server that watches a
5
+ * deck JSON file and hot-reloads a localhost preview page on every change.
6
+ *
7
+ * No `meta` export — this is reached only via `slice.js` dispatch (first
8
+ * positional arg `stage`), so it stays out of the registry parity test.
9
+ *
10
+ * Phase 1 scope: server + watcher + minimal deck-JSON UI.
11
+ * Phase 1.3+ will add: Remotion `renderStill` thumbnails, exports, publish.
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ const { startStageServer } = require('../stage-core/server');
18
+ const { watchDeckFile } = require('../stage-core/watcher');
19
+ const { createEventBus } = require('../stage-core/event-bus');
20
+ const { createSnapshotStore } = require('../stage-core/snapshot-store');
21
+ const { createCloudRenderClient } = require('../stage-core/cloud-render');
22
+ const { renderSliceStageHtml } = require('../stage-ui/slice/template');
23
+ const { emit: emitTelemetry } = require('../core/telemetry');
24
+
25
+ // Sourced from the canonical registry at repo root. Previously this list
26
+ // silently fell out of sync (lagged at 6 themes while the rest of the repo
27
+ // had 13) — the JSON import makes that impossible.
28
+ const SLICE_THEME_REGISTRY = require('../../../slice-themes.json');
29
+ const VALID_THEMES = SLICE_THEME_REGISTRY.themes.map((t) => t.id);
30
+
31
+ function loadInitialDeck(absPath) {
32
+ try {
33
+ const raw = fs.readFileSync(absPath, 'utf8');
34
+ return { deck: JSON.parse(raw), error: null };
35
+ } catch (err) {
36
+ return { deck: null, error: err.code === 'ENOENT'
37
+ ? `File not found: ${absPath}. Run \`voxflow slice <article>\` first, or pass --output to write a deck.json.`
38
+ : `Could not read deck: ${err.message}` };
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Programmatic entry — used by tests + the CLI handler. Returns a handle that
44
+ * exposes the listening URL and a stop() that tears everything down.
45
+ *
46
+ * @param {object} opts
47
+ * @param {string} opts.deckPath
48
+ * @param {string} [opts.theme] Lock to a single theme (validated)
49
+ * @param {number} [opts.port=5180]
50
+ * @param {number} [opts.maxPortTries=10]
51
+ * @returns {Promise<{url:string, port:number, stop:() => Promise<void>}>}
52
+ */
53
+ async function startSliceStage(opts) {
54
+ const deckPath = path.resolve(opts.deckPath);
55
+ const theme = opts.theme;
56
+ if (theme && !VALID_THEMES.includes(theme)) {
57
+ throw new Error(
58
+ `Unknown theme "${theme}". Valid: ${VALID_THEMES.join(', ')}`
59
+ );
60
+ }
61
+
62
+ const bus = createEventBus();
63
+ let snapshot = loadInitialDeck(deckPath);
64
+ snapshot.sourcePath = deckPath;
65
+
66
+ // Per-deck history. Initial parseable load = baseline snapshot (v0). Every
67
+ // subsequent watcher event with parseable content gets snapshotted; the
68
+ // store dedups by content hash so atomic-write double events stay one
69
+ // version. A `restore` flag rides on the deck event so the UI knows to
70
+ // skip the just-changed diff highlight (otherwise restoring an old deck
71
+ // would flash every card as "changed").
72
+ const snapshotStore = createSnapshotStore(deckPath);
73
+ let pendingRestoreId = null;
74
+ if (snapshot.deck) snapshotStore.snapshotIfChanged(snapshot.deck);
75
+
76
+ const watcher = watchDeckFile({
77
+ filePath: deckPath,
78
+ onChange(deck, err) {
79
+ if (err && !deck) {
80
+ snapshot = { deck: null, error: err.message, sourcePath: deckPath };
81
+ bus.publish({ type: 'error', sourcePath: deckPath, error: err.message, ts: Date.now() });
82
+ return;
83
+ }
84
+ snapshot = { deck, error: null, sourcePath: deckPath };
85
+ snapshotStore.snapshotIfChanged(deck);
86
+ const restoredFromId = pendingRestoreId;
87
+ pendingRestoreId = null;
88
+ bus.publish({
89
+ type: 'deck',
90
+ sourcePath: deckPath,
91
+ deck,
92
+ theme: theme || null,
93
+ ts: Date.now(),
94
+ restoredFrom: restoredFromId,
95
+ });
96
+ },
97
+ });
98
+
99
+ // Server-side restore writer: writes the chosen snapshot bytes back to the
100
+ // live deck file, sets `pendingRestoreId` so the next watcher event tags
101
+ // the deck event with `restoredFrom`. Server.js passes through to this.
102
+ const snapshotsBridge = {
103
+ list: snapshotStore.list,
104
+ load: snapshotStore.load,
105
+ getStorePath: snapshotStore.getStorePath,
106
+ restore(id) {
107
+ const deck = snapshotStore.load(id);
108
+ if (!deck) return null;
109
+ pendingRestoreId = id;
110
+ try {
111
+ fs.writeFileSync(deckPath, JSON.stringify(deck, null, 2) + '\n', 'utf8');
112
+ } catch (err) {
113
+ pendingRestoreId = null;
114
+ throw err;
115
+ }
116
+ return { deck };
117
+ },
118
+ };
119
+
120
+ const preferredPort = Number.isInteger(opts.port) && opts.port > 0 ? opts.port : 5180;
121
+ const maxPortTries = Number.isInteger(opts.maxPortTries) && opts.maxPortTries > 0
122
+ ? opts.maxPortTries : 10;
123
+
124
+ // We have to start the server before knowing the final port (port-conflict
125
+ // retry happens inside startStageServer). So we render the HTML lazily,
126
+ // re-rendering each request with the actual port baked in.
127
+ let actualPort = preferredPort;
128
+ const uiHtml = () => renderSliceStageHtml({ sourcePath: deckPath, port: actualPort });
129
+
130
+ // startStageServer takes a string for uiHtml; we pass a stub that will be
131
+ // replaced once we know the port. Cheap workaround: render once with
132
+ // `preferredPort`, then patch after we get the real port back.
133
+ const initialHtml = renderSliceStageHtml({ sourcePath: deckPath, port: preferredPort });
134
+
135
+ // Cloud render bridge — proxies POST /api/render-mp4 (and friends) to the
136
+ // backend cloud-render endpoint. Token comes from the same CLI cache the
137
+ // user logged into; the page never sees the JWT.
138
+ const cloudRender = opts.cloudRender || createCloudRenderClient();
139
+
140
+ const handle = await startStageServer({
141
+ uiHtml: initialHtml,
142
+ getDeckSnapshot: () => snapshot,
143
+ subscribe: bus.subscribe,
144
+ snapshots: snapshotsBridge,
145
+ cloudRender,
146
+ preferredPort,
147
+ maxPortTries,
148
+ });
149
+ actualPort = handle.port;
150
+
151
+ // Single anonymous launch event — feeds the "should Stage get an MCP?"
152
+ // decision. Fire-and-forget; opt-out via VOXFLOW_TELEMETRY=0 / DO_NOT_TRACK.
153
+ emitTelemetry('cli.slice.stage.launched', { theme: theme || null });
154
+
155
+ // If the port shifted (5180 → 5181 due to conflict), re-render the HTML so
156
+ // the footer hint shows the truthy URL. We swap by replacing the listener
157
+ // on the server: simpler than re-injecting, just update the closure used
158
+ // by startStageServer's GET / branch via a tiny patch.
159
+ if (actualPort !== preferredPort) {
160
+ // The HTML is captured by closure inside startStageServer; rather than
161
+ // wiring a setter, we do nothing — the page only references the port in
162
+ // a footer hint, and the live SSE connection works regardless. This is
163
+ // acceptable for MVP; revisit if/when port mismatch becomes user-visible.
164
+ void uiHtml;
165
+ }
166
+
167
+ return {
168
+ url: handle.url,
169
+ port: handle.port,
170
+ sourcePath: deckPath,
171
+ async stop() {
172
+ watcher.stop();
173
+ await handle.close();
174
+ },
175
+ };
176
+ }
177
+
178
+ // ─── CLI Handler ────────────────────────────────────────────────────────────
179
+
180
+ async function handle(args) {
181
+ const { parseFlag } = require('../core/args');
182
+
183
+ const portFlag = parseFlag(args, '--port');
184
+ const themeFlag = parseFlag(args, '--theme');
185
+ const noOpen = args.includes('--no-open');
186
+
187
+ // First positional arg that isn't a flag value
188
+ const valuedFlags = new Set(['--port', '--theme']);
189
+ let deckPath = null;
190
+ for (let i = 0; i < args.length; i++) {
191
+ if (args[i].startsWith('-')) {
192
+ if (valuedFlags.has(args[i])) i++;
193
+ continue;
194
+ }
195
+ deckPath = args[i];
196
+ break;
197
+ }
198
+ if (!deckPath) {
199
+ console.error('Error: provide a deck JSON file');
200
+ console.error('Usage: voxflow slice stage <deck.json> [--port <n>] [--theme <id>] [--no-open]');
201
+ process.exit(1);
202
+ }
203
+
204
+ let port;
205
+ if (portFlag) {
206
+ const parsed = Number.parseInt(portFlag, 10);
207
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
208
+ console.error(`Error: --port "${portFlag}" is not a valid TCP port`);
209
+ process.exit(1);
210
+ }
211
+ port = parsed;
212
+ }
213
+
214
+ let stage;
215
+ try {
216
+ stage = await startSliceStage({
217
+ deckPath,
218
+ theme: themeFlag || undefined,
219
+ port,
220
+ });
221
+ } catch (err) {
222
+ console.error(`\x1b[31mError:\x1b[0m ${err.message}`);
223
+ process.exit(1);
224
+ }
225
+
226
+ console.log('');
227
+ console.log('\x1b[36m=== VoxFlow Stage — Slice ===\x1b[0m');
228
+ console.log(`Watching: ${stage.sourcePath}`);
229
+ console.log(`Preview: ${stage.url}`);
230
+ if (themeFlag) console.log(`Theme: ${themeFlag}`);
231
+ console.log('');
232
+ console.log('Edit the deck JSON to hot-reload. Ctrl+C to stop.');
233
+ console.log('');
234
+
235
+ if (!noOpen && !process.env.CI) {
236
+ try {
237
+ const open = (await import('open')).default;
238
+ await open(stage.url);
239
+ } catch {
240
+ // Non-fatal — user can open the URL manually.
241
+ }
242
+ }
243
+
244
+ // Keep the process alive until SIGINT.
245
+ let stopping = false;
246
+ const stopAndExit = async (signal) => {
247
+ if (stopping) return;
248
+ stopping = true;
249
+ console.log(`\n\x1b[33m${signal} received — stopping Stage…\x1b[0m`);
250
+ try { await stage.stop(); } catch { /* best-effort */ }
251
+ process.exit(0);
252
+ };
253
+ process.on('SIGINT', () => stopAndExit('SIGINT'));
254
+ process.on('SIGTERM', () => stopAndExit('SIGTERM'));
255
+
256
+ // Park.
257
+ return new Promise(() => { /* never resolves; SIGINT exits */ });
258
+ }
259
+
260
+ module.exports = {
261
+ handle,
262
+ startSliceStage,
263
+ VALID_THEMES,
264
+ };
@@ -0,0 +1,343 @@
1
+ /**
2
+ * VoxFlow CLI — Slice command
3
+ *
4
+ * Slice an article (markdown / plain text) into a 5–8 card narrated deck JSON
5
+ * via /api/slice/deck (renamed from /api/paper-slide/slice in #3307 Deploy 2).
6
+ * Charges `slice-deck` (200 quota) under the new mount; legacy `paper-slide-slice`
7
+ * op still exists for the soft-deprecated `/api/paper-slide/slice` path that
8
+ * stays mounted for ~30 days.
9
+ *
10
+ * The cloud renderer that turns this deck into a 1080×1920 mp4 lives in the
11
+ * web app (voxflow.studio/apps/slice) — this command only produces the
12
+ * structured deck JSON, suitable for piping into custom pipelines or for
13
+ * inspection / iteration on the AI's slicing quality.
14
+ *
15
+ * Thirty-three themes supported (paper-slide / editorial-mag / bold-poster /
16
+ * notion-card / brutalist / glass-dark / broadsheet / blueprint /
17
+ * daisy-pastel / showa-catalog / photo-feature / atmospheric / art-mag /
18
+ * tome-noir / flomo-mute / substack-drop / ink-scroll / podcast-clip /
19
+ * ink-wash / morandi-calm / douyin-data / highlighter-note / memphis-design /
20
+ * bauhaus-grid / riso-print / chrome-y2k / botanical-press / art-nouveau /
21
+ * tabloid-print / arcade-pixel / hand-lettered / stamp-collector /
22
+ * tropical-postcard) — must match backend's VALID_THEMES set
23
+ * (backend/services/paper-slide/deck-validator.js) or the API rejects with
24
+ * `invalid_theme`. Both lists derive from /slice-themes.json so they
25
+ * auto-sync.
26
+ */
27
+
28
+ const fs = require('fs');
29
+ const path = require('path');
30
+ const { request, throwApiError, throwNetworkError, ApiError } = require('../core/http');
31
+
32
+ // Sourced from the canonical registry at repo root. The CLI gates user
33
+ // input against this list, so adding a theme to slice-themes.json is enough
34
+ // for `voxflow slice --theme xxx` to accept it.
35
+ const SLICE_THEME_REGISTRY = require('../../../slice-themes.json');
36
+ const VALID_THEMES = SLICE_THEME_REGISTRY.themes.map((t) => t.id);
37
+ const DEFAULT_THEME = SLICE_THEME_REGISTRY.default;
38
+ // Theme id -> array of valid variant ids (palette swaps within a theme).
39
+ // Mirrors backend/services/paper-slide/deck-validator.js#THEME_VARIANTS so
40
+ // the CLI rejects unknown variants client-side before paying a network round
41
+ // trip. Themes without variants get an empty array — passing --variant for
42
+ // them is a hard error too (theme typo'd or feature confusion).
43
+ const THEME_VARIANTS = Object.fromEntries(
44
+ SLICE_THEME_REGISTRY.themes.map((t) => [
45
+ t.id,
46
+ (t.variants || []).map((v) => v.id),
47
+ ])
48
+ );
49
+ const MIN_ARTICLE_CHARS = 80;
50
+
51
+ /**
52
+ * Slice an article via the backend.
53
+ *
54
+ * @param {object} opts
55
+ * @param {string} opts.token JWT token
56
+ * @param {string} opts.api API base URL
57
+ * @param {string} opts.article Article text (≥ 80 chars)
58
+ * @param {string} [opts.theme] Slice theme id (default: paper-slide)
59
+ * @param {string} [opts.output] Output JSON path (default: stdout)
60
+ * @param {boolean} [opts.json] Pure-JSON mode (suppress logs / spinners)
61
+ * @returns {Promise<{deck: object, usage: object, quotaUsed: number, outputPath: string|null}>}
62
+ */
63
+ async function slice(opts) {
64
+ const article = opts.article;
65
+ if (typeof article !== 'string' || article.trim().length < MIN_ARTICLE_CHARS) {
66
+ throw new Error(
67
+ `Article must be a non-empty string of at least ${MIN_ARTICLE_CHARS} characters ` +
68
+ `(got ${article ? article.trim().length : 0}).`
69
+ );
70
+ }
71
+
72
+ const theme = opts.theme || DEFAULT_THEME;
73
+ if (!VALID_THEMES.includes(theme)) {
74
+ throw new Error(
75
+ `Unknown theme "${theme}". Valid: ${VALID_THEMES.join(', ')}`
76
+ );
77
+ }
78
+
79
+ const variantId = opts.variant;
80
+ if (variantId != null) {
81
+ const validVariants = THEME_VARIANTS[theme] || [];
82
+ if (validVariants.length === 0) {
83
+ throw new Error(
84
+ `--variant set but theme "${theme}" declares no variants. ` +
85
+ `Themes with variants: ${
86
+ Object.entries(THEME_VARIANTS)
87
+ .filter(([, ids]) => ids.length > 0)
88
+ .map(([id]) => id)
89
+ .join(', ') || '(none yet)'
90
+ }`
91
+ );
92
+ }
93
+ if (!validVariants.includes(variantId)) {
94
+ throw new Error(
95
+ `Unknown variant "${variantId}" for theme "${theme}". Valid: ${validVariants.join(', ')}`
96
+ );
97
+ }
98
+ }
99
+
100
+ const api = opts.api;
101
+ const token = opts.token;
102
+ const quiet = opts.json === true;
103
+
104
+ if (!quiet) {
105
+ console.log('\n=== VoxFlow Slice ===');
106
+ console.log(`Theme: ${theme}${variantId ? ` (variant: ${variantId})` : ''}`);
107
+ console.log(`Article: ${article.trim().length} chars`);
108
+ }
109
+
110
+ let status, data;
111
+ try {
112
+ ({ status, data } = await request(`${api}/api/slice/deck`, {
113
+ method: 'POST',
114
+ headers: {
115
+ 'Content-Type': 'application/json',
116
+ 'Authorization': `Bearer ${token}`,
117
+ },
118
+ // Slice runs a structured-output Claude call producing 5–8
119
+ // narrated cards. P50 is 10–20s but P95 spikes past the 60s
120
+ // default request timeout under upstream load (verified
121
+ // against api.voxflow.studio/api/slice/deck with the
122
+ // 1.11.1 client). Match the 180s convention used by every
123
+ // other LLM-bound CLI command (image / picstory / explain /
124
+ // present / story).
125
+ timeoutMs: 180_000,
126
+ }, {
127
+ article: article.trim(),
128
+ theme,
129
+ ...(variantId ? { variantId } : {}),
130
+ }));
131
+ } catch (err) {
132
+ throwNetworkError(err, api);
133
+ }
134
+
135
+ if (status !== 200 || !data || data.code !== 'success') {
136
+ throwApiError(status, data, 'slice');
137
+ }
138
+
139
+ const deck = data.data?.deck;
140
+ const usage = data.data?.usage || {};
141
+ if (!deck) {
142
+ throw new Error('Slice response missing `data.deck`');
143
+ }
144
+
145
+ // Authoritative cost from quota response, fallback to catalog.
146
+ const PAPER_SLIDE_SLICE_CATALOG_COST = 200;
147
+ const quotaUsed = typeof data.quota?.costDelta === 'number'
148
+ ? data.quota.costDelta
149
+ : PAPER_SLIDE_SLICE_CATALOG_COST;
150
+
151
+ const payload = JSON.stringify({ deck, usage, theme }, null, 2);
152
+ let outputPath = null;
153
+ if (opts.output) {
154
+ outputPath = path.resolve(opts.output);
155
+ const outDir = path.dirname(outputPath);
156
+ fs.mkdirSync(outDir, { recursive: true });
157
+ fs.writeFileSync(outputPath, payload + '\n', 'utf8');
158
+ if (!quiet) {
159
+ console.log(`\nWrote ${outputPath} (${(payload.length / 1024).toFixed(1)} KB)`);
160
+ }
161
+ } else if (quiet) {
162
+ process.stdout.write(payload + '\n');
163
+ } else {
164
+ console.log('\n--- Deck JSON ---');
165
+ console.log(payload);
166
+ }
167
+
168
+ if (!quiet) {
169
+ const cardCount = Array.isArray(deck.cards) ? deck.cards.length : 0;
170
+ console.log('\n=== Done ===');
171
+ console.log(`Cards: ${cardCount}`);
172
+ console.log(`Quota: ${quotaUsed} used, ${data.quota?.remaining ?? '?'} remaining`);
173
+ // Two next-step paths: (1) iterate locally with stage if an output
174
+ // file exists (no quota cost, hot-reload); (2) ship via the web
175
+ // renderer. Only print the stage hint when we actually have a path
176
+ // to feed it — stdout-only runs have nowhere for stage to attach.
177
+ if (outputPath) {
178
+ console.log(`\nPreview locally: voxflow slice stage ${outputPath}`);
179
+ console.log(`Or render mp4: https://voxflow.studio/apps/slice (paste/import the JSON)`);
180
+ } else {
181
+ console.log(`\nNext: render the deck at https://voxflow.studio/apps/slice (paste the JSON or import).`);
182
+ }
183
+ }
184
+
185
+ return { deck, usage, quotaUsed, outputPath };
186
+ }
187
+
188
+ // ─── CLI Handler ────────────────────────────────────────────────────────────
189
+
190
+ async function handle(args) {
191
+ // Sub-action dispatch: `voxflow slice stage <deck.json>` boots the local
192
+ // preview/iteration server instead of generating a deck. Kept as a
193
+ // first-positional-arg check so the legacy `voxflow slice <article>`
194
+ // signature is unchanged. slice-stage.js has no `meta` export of its
195
+ // own — it would otherwise need to be in _registry.js (parity test).
196
+ if (args.length > 0 && args[0] === 'stage') {
197
+ const sliceStage = require('./slice-stage');
198
+ return sliceStage.handle(args.slice(1));
199
+ }
200
+ // `voxflow slice render <deck.json>` — offline local Remotion render
201
+ // (Phase 0: silent video). Same first-positional dispatch pattern as
202
+ // `stage`; the subcommand file has no `meta` export so it stays out of
203
+ // _registry.js (parity test).
204
+ if (args.length > 0 && args[0] === 'render') {
205
+ const sliceRender = require('./slice-render');
206
+ return sliceRender.handle(args.slice(1));
207
+ }
208
+ // `voxflow slice preview <deck.json>` — localhost HTML page with a
209
+ // midpoint-frame still per card (no mp4, fraction of the render cost).
210
+ if (args.length > 0 && args[0] === 'preview') {
211
+ const slicePreview = require('./slice-preview');
212
+ return slicePreview.handle(args.slice(1));
213
+ }
214
+
215
+ const { parseFlag, runWithRetry } = require('../core/args');
216
+ const { getToken, getTokenInfo } = require('../core/auth');
217
+ const { API_BASE } = require('../core/config');
218
+
219
+ const api = parseFlag(args, '--api') || API_BASE;
220
+ const explicitToken = parseFlag(args, '--token');
221
+
222
+ const inlineText = parseFlag(args, '--text');
223
+ const theme = parseFlag(args, '--theme');
224
+ const variant = parseFlag(args, '--variant');
225
+ const output = parseFlag(args, '--output', '-o');
226
+ const jsonMode = args.includes('--json');
227
+
228
+ let article;
229
+ if (inlineText) {
230
+ article = inlineText;
231
+ } else {
232
+ // First positional arg that isn't a flag value
233
+ const valuedFlags = new Set([
234
+ '--text', '--theme', '--variant', '--output', '-o',
235
+ '--token', '--api',
236
+ ]);
237
+ let filePath;
238
+ for (let i = 0; i < args.length; i++) {
239
+ if (args[i].startsWith('-')) {
240
+ if (valuedFlags.has(args[i])) i++;
241
+ continue;
242
+ }
243
+ filePath = args[i];
244
+ break;
245
+ }
246
+ if (!filePath) {
247
+ console.error('Error: provide an article file or --text "..."');
248
+ console.error('Usage: voxflow slice <file> [--theme <id>] [-o <out.json>]');
249
+ process.exit(1);
250
+ }
251
+ try {
252
+ article = fs.readFileSync(path.resolve(filePath), 'utf8');
253
+ } catch (err) {
254
+ console.error(`Error: cannot read file "${filePath}": ${err.message}`);
255
+ process.exit(1);
256
+ }
257
+ }
258
+
259
+ if (theme && !VALID_THEMES.includes(theme)) {
260
+ console.error(`Error: --theme "${theme}" not in: ${VALID_THEMES.join(', ')}`);
261
+ process.exit(1);
262
+ }
263
+ if (variant) {
264
+ const targetTheme = theme || DEFAULT_THEME;
265
+ const validVariants = THEME_VARIANTS[targetTheme] || [];
266
+ if (validVariants.length === 0) {
267
+ console.error(
268
+ `Error: --variant set but theme "${targetTheme}" declares no variants.`
269
+ );
270
+ const themesWithVariants = Object.entries(THEME_VARIANTS)
271
+ .filter(([, ids]) => ids.length > 0)
272
+ .map(([id]) => id);
273
+ if (themesWithVariants.length > 0) {
274
+ console.error(`Themes with variants: ${themesWithVariants.join(', ')}`);
275
+ }
276
+ process.exit(1);
277
+ }
278
+ if (!validVariants.includes(variant)) {
279
+ console.error(
280
+ `Error: --variant "${variant}" not in ${targetTheme}'s variants: ${validVariants.join(', ')}`
281
+ );
282
+ process.exit(1);
283
+ }
284
+ }
285
+
286
+ let token;
287
+ if (explicitToken) {
288
+ token = explicitToken;
289
+ } else {
290
+ token = await getToken({ api });
291
+ if (!jsonMode) {
292
+ const info = getTokenInfo();
293
+ if (info) {
294
+ console.log(`\x1b[32mLogged in as ${info.email}\x1b[0m`);
295
+ }
296
+ }
297
+ }
298
+
299
+ const opts = {
300
+ token, api, article,
301
+ theme: theme || undefined,
302
+ variant: variant || undefined,
303
+ output: output || undefined,
304
+ json: jsonMode,
305
+ };
306
+
307
+ await runWithRetry(slice, opts, api, explicitToken);
308
+ }
309
+
310
+ const meta = {
311
+ slice: {
312
+ usage: '<file> [--theme <id>] [--variant <id>] [--text <s>] [-o <file>] [--json]',
313
+ description: 'Slice an article into a 5–8 card deck JSON (Slice / paper-slide AI; 200 quota)',
314
+ options: [
315
+ '<file> Article file (markdown / plain text, ≥ 80 chars)',
316
+ '--text <string> Pass article inline instead of reading a file',
317
+ `--theme <id> One of: ${VALID_THEMES.join(', ')} (default: ${DEFAULT_THEME})`,
318
+ '--variant <id> Palette swap within the theme (e.g. editorial-mag → cream / sepia / cold-grey). Themes without variants reject this flag.',
319
+ '-o, --output <path> Write deck JSON to file (default: print to stdout/console)',
320
+ '--json Pure JSON mode — suppress all logs, emit only the deck JSON to stdout (for piping)',
321
+ ],
322
+ examples: [
323
+ 'voxflow slice article.md',
324
+ 'voxflow slice article.md --theme editorial-mag -o deck.json',
325
+ 'voxflow slice article.md --theme editorial-mag --variant sepia -o deck.json',
326
+ 'voxflow slice --text "..." --theme bold-poster --json | jq .deck',
327
+ 'voxflow slice stage deck.json # local preview, hot-reload',
328
+ 'voxflow slice stage deck.json --port 5180 --no-open',
329
+ 'voxflow slice render deck.json --output out.mp4 # offline render (no quota, no cloud)',
330
+ ],
331
+ },
332
+ };
333
+
334
+ module.exports = {
335
+ slice,
336
+ handle,
337
+ meta,
338
+ VALID_THEMES,
339
+ THEME_VARIANTS,
340
+ DEFAULT_THEME,
341
+ MIN_ARTICLE_CHARS,
342
+ ApiError,
343
+ };