voxflow 1.15.0 → 1.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (453) hide show
  1. package/README.md +34 -0
  2. package/bin/voxflow.js +27 -0
  3. package/dist/index.js +1 -1
  4. package/dist/remotion-bundle/02a2fb2eb80bc7bf.woff2 +0 -0
  5. package/dist/remotion-bundle/052ca5351e5e06ba.woff2 +0 -0
  6. package/dist/remotion-bundle/05853dd28f4019cb.woff2 +0 -0
  7. package/dist/remotion-bundle/072ead3737f7c0d0.woff2 +0 -0
  8. package/dist/remotion-bundle/07d4248613c86a2e.woff2 +0 -0
  9. package/dist/remotion-bundle/0884a5c2d1d2d99b.woff2 +0 -0
  10. package/dist/remotion-bundle/0b0e185b2752095e.woff2 +0 -0
  11. package/dist/remotion-bundle/0e66c11bde067d91.woff2 +0 -0
  12. package/dist/remotion-bundle/0f7794cfba2c5d21.woff2 +0 -0
  13. package/dist/remotion-bundle/0fdbae5a4365783a.woff2 +0 -0
  14. package/dist/remotion-bundle/112.bundle.js +11 -0
  15. package/dist/remotion-bundle/112.bundle.js.map +1 -0
  16. package/dist/remotion-bundle/113.bundle.js +11 -0
  17. package/dist/remotion-bundle/113.bundle.js.map +1 -0
  18. package/dist/remotion-bundle/119cae0c4c16f7ed.woff2 +0 -0
  19. package/dist/remotion-bundle/14725f649fd1e78c.woff2 +0 -0
  20. package/dist/remotion-bundle/14abe9e3f95f7888.woff2 +0 -0
  21. package/dist/remotion-bundle/163.bundle.js +14678 -0
  22. package/dist/remotion-bundle/163.bundle.js.map +1 -0
  23. package/dist/remotion-bundle/1808c54072bf6d14.woff2 +0 -0
  24. package/dist/remotion-bundle/18948bec3e3012fe.woff2 +0 -0
  25. package/dist/remotion-bundle/1a661c60d0fc84fc.woff2 +0 -0
  26. package/dist/remotion-bundle/1af94941e1bc7e1e.woff2 +0 -0
  27. package/dist/remotion-bundle/1bee0219595f606c.woff2 +0 -0
  28. package/dist/remotion-bundle/1bfd5da7ce9d4ec4.woff2 +0 -0
  29. package/dist/remotion-bundle/1c158d56f1884f3f.woff2 +0 -0
  30. package/dist/remotion-bundle/1cf5e88e667610eb.woff2 +0 -0
  31. package/dist/remotion-bundle/1d431bd10f53c481.woff2 +0 -0
  32. package/dist/remotion-bundle/1d701a81a7670db2.woff2 +0 -0
  33. package/dist/remotion-bundle/1da0fecad4240f16.woff2 +0 -0
  34. package/dist/remotion-bundle/1ed14d3d0c5c63fe.woff2 +0 -0
  35. package/dist/remotion-bundle/1edfecf40e586f53.woff2 +0 -0
  36. package/dist/remotion-bundle/1f479711bc34b054.woff +0 -0
  37. package/dist/remotion-bundle/1f86e54a0ff5fcd1.woff2 +0 -0
  38. package/dist/remotion-bundle/2043ea87d9aabd11.woff2 +0 -0
  39. package/dist/remotion-bundle/20563c39ee8a0e40.woff2 +0 -0
  40. package/dist/remotion-bundle/20c231590fd12c44.woff2 +0 -0
  41. package/dist/remotion-bundle/20ce61713f754c07.woff2 +0 -0
  42. package/dist/remotion-bundle/21eb9306fce24bb1.woff2 +0 -0
  43. package/dist/remotion-bundle/244bf71c0cc851af.woff2 +0 -0
  44. package/dist/remotion-bundle/274d4cfc02bffbcb.woff2 +0 -0
  45. package/dist/remotion-bundle/275.bundle.js +21 -0
  46. package/dist/remotion-bundle/275.bundle.js.map +1 -0
  47. package/dist/remotion-bundle/2958f540b39513dc.woff2 +0 -0
  48. package/dist/remotion-bundle/2a168b98fd97722e.woff2 +0 -0
  49. package/dist/remotion-bundle/2d1f6373937ab55f.woff2 +0 -0
  50. package/dist/remotion-bundle/2d213ae47ff6daa9.woff2 +0 -0
  51. package/dist/remotion-bundle/2e4b1f04fcd05047.woff2 +0 -0
  52. package/dist/remotion-bundle/304170d98f4c4563.woff2 +0 -0
  53. package/dist/remotion-bundle/30d02e136e7a5642.woff2 +0 -0
  54. package/dist/remotion-bundle/3135562b52a714cd.woff2 +0 -0
  55. package/dist/remotion-bundle/313713af2c8144e9.woff2 +0 -0
  56. package/dist/remotion-bundle/325fa4108d2285b9.woff2 +0 -0
  57. package/dist/remotion-bundle/338e927ed3345e0c.woff2 +0 -0
  58. package/dist/remotion-bundle/35fc6b190365bc17.woff2 +0 -0
  59. package/dist/remotion-bundle/37a51f1122d4efc5.woff2 +0 -0
  60. package/dist/remotion-bundle/39a4d63e02736f5e.woff2 +0 -0
  61. package/dist/remotion-bundle/3a00e0d62dfc4171.woff2 +0 -0
  62. package/dist/remotion-bundle/3a6955e6561affe1.woff2 +0 -0
  63. package/dist/remotion-bundle/3c573945aef49b89.woff2 +0 -0
  64. package/dist/remotion-bundle/3cdbfbfa23b516a5.woff2 +0 -0
  65. package/dist/remotion-bundle/3e42f85a9e64ca8a.woff2 +0 -0
  66. package/dist/remotion-bundle/3e83eaf1ec859415.woff2 +0 -0
  67. package/dist/remotion-bundle/3f3c8c90de1250ee.woff2 +0 -0
  68. package/dist/remotion-bundle/434.bundle.js +205 -0
  69. package/dist/remotion-bundle/434.bundle.js.map +1 -0
  70. package/dist/remotion-bundle/44ffc6ca4d781692.woff2 +0 -0
  71. package/dist/remotion-bundle/4670d9c4580b09eb.woff2 +0 -0
  72. package/dist/remotion-bundle/479756881b302824.woff2 +0 -0
  73. package/dist/remotion-bundle/481b82134bfa9c82.woff2 +0 -0
  74. package/dist/remotion-bundle/48d27029626f4328.woff2 +0 -0
  75. package/dist/remotion-bundle/49b7b2a30329c511.woff2 +0 -0
  76. package/dist/remotion-bundle/4c8b25a1a9337045.woff2 +0 -0
  77. package/dist/remotion-bundle/4cba14788ca9259b.woff2 +0 -0
  78. package/dist/remotion-bundle/4cd6c589c004a6a7.woff2 +0 -0
  79. package/dist/remotion-bundle/4cd8d79c1021608d.woff2 +0 -0
  80. package/dist/remotion-bundle/4d8fa99b3f00f9f0.woff2 +0 -0
  81. package/dist/remotion-bundle/4e7805a643f86d53.woff2 +0 -0
  82. package/dist/remotion-bundle/4ff91be454542e3f.woff2 +0 -0
  83. package/dist/remotion-bundle/504cbcba1f63591b.woff2 +0 -0
  84. package/dist/remotion-bundle/5202d792e5791d6c.woff2 +0 -0
  85. package/dist/remotion-bundle/534db5ad4770cc1d.woff2 +0 -0
  86. package/dist/remotion-bundle/53b9568eb85f866b.woff2 +0 -0
  87. package/dist/remotion-bundle/543ad386ca171de9.woff2 +0 -0
  88. package/dist/remotion-bundle/54798e55bbf7976e.woff2 +0 -0
  89. package/dist/remotion-bundle/580.bundle.js +11 -0
  90. package/dist/remotion-bundle/580.bundle.js.map +1 -0
  91. package/dist/remotion-bundle/58d174d1193af6d1.woff2 +0 -0
  92. package/dist/remotion-bundle/591d29ff3ff53c80.woff2 +0 -0
  93. package/dist/remotion-bundle/5c28c4f4824383c6.woff2 +0 -0
  94. package/dist/remotion-bundle/5da9740d2ce894c8.woff2 +0 -0
  95. package/dist/remotion-bundle/6197735364642360.woff2 +0 -0
  96. package/dist/remotion-bundle/6265a4335724080f.woff2 +0 -0
  97. package/dist/remotion-bundle/633f5e4f6394daa7.woff2 +0 -0
  98. package/dist/remotion-bundle/637d95ace6a69c49.woff2 +0 -0
  99. package/dist/remotion-bundle/648e04a04dacff8f.woff2 +0 -0
  100. package/dist/remotion-bundle/64a6e83045a008b2.woff2 +0 -0
  101. package/dist/remotion-bundle/651.bundle.js +11 -0
  102. package/dist/remotion-bundle/651.bundle.js.map +1 -0
  103. package/dist/remotion-bundle/65e2a988c070facc.woff2 +0 -0
  104. package/dist/remotion-bundle/66a2f6ce5cc69105.woff2 +0 -0
  105. package/dist/remotion-bundle/690.bundle.js +3479 -0
  106. package/dist/remotion-bundle/690.bundle.js.map +1 -0
  107. package/dist/remotion-bundle/690ff55252ca715d.woff2 +0 -0
  108. package/dist/remotion-bundle/6a01a1cff49314fc.woff2 +0 -0
  109. package/dist/remotion-bundle/6cbc32670982986c.woff2 +0 -0
  110. package/dist/remotion-bundle/6d3cc42ae547f454.woff2 +0 -0
  111. package/dist/remotion-bundle/6d8f4cfa1ddc0830.woff2 +0 -0
  112. package/dist/remotion-bundle/6e4d7c6ae65e2dc3.woff2 +0 -0
  113. package/dist/remotion-bundle/6e86418bbcefb2e8.woff2 +0 -0
  114. package/dist/remotion-bundle/6ee02884b29cf7fb.woff2 +0 -0
  115. package/dist/remotion-bundle/6f436a74c9e3252c.woff2 +0 -0
  116. package/dist/remotion-bundle/78c8022f1657618b.woff2 +0 -0
  117. package/dist/remotion-bundle/7c5444169792bca4.woff2 +0 -0
  118. package/dist/remotion-bundle/7c86bddd9d997212.woff2 +0 -0
  119. package/dist/remotion-bundle/7e1284684767f584.woff2 +0 -0
  120. package/dist/remotion-bundle/7e81c17522d182b2.woff2 +0 -0
  121. package/dist/remotion-bundle/7eb87be198f7858c.woff2 +0 -0
  122. package/dist/remotion-bundle/8060c928f948aab5.woff2 +0 -0
  123. package/dist/remotion-bundle/80bc9dfbea2b35ae.woff2 +0 -0
  124. package/dist/remotion-bundle/811b83f69963bb48.woff2 +0 -0
  125. package/dist/remotion-bundle/813.bundle.js +117511 -0
  126. package/dist/remotion-bundle/813.bundle.js.map +1 -0
  127. package/dist/remotion-bundle/84df492e349f82e9.woff2 +0 -0
  128. package/dist/remotion-bundle/8501bfd73eb36f2b.woff2 +0 -0
  129. package/dist/remotion-bundle/854236a8376093fe.woff2 +0 -0
  130. package/dist/remotion-bundle/8571d74529082753.woff2 +0 -0
  131. package/dist/remotion-bundle/860bf44f8e6f4b5d.woff2 +0 -0
  132. package/dist/remotion-bundle/879.bundle.js +64 -0
  133. package/dist/remotion-bundle/879.bundle.js.map +1 -0
  134. package/dist/remotion-bundle/887dd482f848d56f.woff2 +0 -0
  135. package/dist/remotion-bundle/89b2132e85fbbb5a.woff2 +0 -0
  136. package/dist/remotion-bundle/8ba60d6c306010c2.woff2 +0 -0
  137. package/dist/remotion-bundle/8c7c4dadea897806.woff2 +0 -0
  138. package/dist/remotion-bundle/8c943f9999706f61.woff2 +0 -0
  139. package/dist/remotion-bundle/8f2a718c90575cc9.woff2 +0 -0
  140. package/dist/remotion-bundle/906b6edb3e1772c9.woff2 +0 -0
  141. package/dist/remotion-bundle/930ff9daccdf14eb.woff2 +0 -0
  142. package/dist/remotion-bundle/934db2f1c403c4d0.woff2 +0 -0
  143. package/dist/remotion-bundle/938.bundle.js +451 -0
  144. package/dist/remotion-bundle/938.bundle.js.map +1 -0
  145. package/dist/remotion-bundle/967.bundle.js +4462 -0
  146. package/dist/remotion-bundle/967.bundle.js.map +1 -0
  147. package/dist/remotion-bundle/9684a1093d3c02ce.woff2 +0 -0
  148. package/dist/remotion-bundle/973dcd0faa6116cc.woff2 +0 -0
  149. package/dist/remotion-bundle/9745400694e76cd8.woff2 +0 -0
  150. package/dist/remotion-bundle/999ef957bed3bdca.woff2 +0 -0
  151. package/dist/remotion-bundle/99a3d67c8b0f43e3.woff2 +0 -0
  152. package/dist/remotion-bundle/a0586c3e03127283.woff2 +0 -0
  153. package/dist/remotion-bundle/a0eb654fdae46269.woff2 +0 -0
  154. package/dist/remotion-bundle/a20e35d3b08f7994.woff2 +0 -0
  155. package/dist/remotion-bundle/a2dcaced7c8c25ab.woff2 +0 -0
  156. package/dist/remotion-bundle/a79255a972a2681a.woff2 +0 -0
  157. package/dist/remotion-bundle/a804b352cb9fec1a.woff2 +0 -0
  158. package/dist/remotion-bundle/aae7117164e1eabc.woff2 +0 -0
  159. package/dist/remotion-bundle/affd121385d0442d.woff2 +0 -0
  160. package/dist/remotion-bundle/b19a6083987ee0d7.woff2 +0 -0
  161. package/dist/remotion-bundle/b1b2bd04d8637981.woff2 +0 -0
  162. package/dist/remotion-bundle/b2c07f341486be87.woff2 +0 -0
  163. package/dist/remotion-bundle/b33d8f82e575c4ce.woff2 +0 -0
  164. package/dist/remotion-bundle/b366c0bed35ef491.woff2 +0 -0
  165. package/dist/remotion-bundle/b41e857ec1b85642.woff2 +0 -0
  166. package/dist/remotion-bundle/b420bb34ccf23e7f.woff2 +0 -0
  167. package/dist/remotion-bundle/b4f7bf4efb0c0ccf.woff2 +0 -0
  168. package/dist/remotion-bundle/b60fe5eca03cff93.woff2 +0 -0
  169. package/dist/remotion-bundle/b6bd31a336e64bce.woff2 +0 -0
  170. package/dist/remotion-bundle/b6d2befba3dfefeb.woff2 +0 -0
  171. package/dist/remotion-bundle/b75f39ab06c43bf4.woff2 +0 -0
  172. package/dist/remotion-bundle/b77880e8c413d4fd.woff2 +0 -0
  173. package/dist/remotion-bundle/b7e38ec441e4a77a.woff2 +0 -0
  174. package/dist/remotion-bundle/b83baa383ff0bf2b.woff2 +0 -0
  175. package/dist/remotion-bundle/b9ad7b6c0a11450a.woff2 +0 -0
  176. package/dist/remotion-bundle/baf84486e8ae3aaf.woff2 +0 -0
  177. package/dist/remotion-bundle/bc047b1f6869cffa.woff2 +0 -0
  178. package/dist/remotion-bundle/bf4f3ac6e93f33aa.woff2 +0 -0
  179. package/dist/remotion-bundle/bf6835ffec5897a2.woff2 +0 -0
  180. package/dist/remotion-bundle/bf8885f581eb1724.woff2 +0 -0
  181. package/dist/remotion-bundle/bundle.js +83376 -0
  182. package/dist/remotion-bundle/bundle.js.map +1 -0
  183. package/dist/remotion-bundle/c03f046bccd789d0.woff2 +0 -0
  184. package/dist/remotion-bundle/c0bb1f8962b73bc3.woff2 +0 -0
  185. package/dist/remotion-bundle/c1003f9a7db6e1cf.woff2 +0 -0
  186. package/dist/remotion-bundle/c15d83fb1e199515.woff2 +0 -0
  187. package/dist/remotion-bundle/c28e7e5d310f73ef.woff2 +0 -0
  188. package/dist/remotion-bundle/c2b840274db78aea.woff2 +0 -0
  189. package/dist/remotion-bundle/c3000e3299d4e45f.woff2 +0 -0
  190. package/dist/remotion-bundle/c83ce886e5288510.woff2 +0 -0
  191. package/dist/remotion-bundle/c87a5a64d4ac0918.woff2 +0 -0
  192. package/dist/remotion-bundle/c8a7e0d049e965fa.woff2 +0 -0
  193. package/dist/remotion-bundle/c949a35d3a3b1faf.woff2 +0 -0
  194. package/dist/remotion-bundle/c9618c9b9ac2bc78.woff2 +0 -0
  195. package/dist/remotion-bundle/ca3add3b84152d5b.woff2 +0 -0
  196. package/dist/remotion-bundle/cad9dd036408d707.woff2 +0 -0
  197. package/dist/remotion-bundle/cbb24916619df439.woff2 +0 -0
  198. package/dist/remotion-bundle/cc054f0b5514e177.woff2 +0 -0
  199. package/dist/remotion-bundle/ccc248ed9312bc71.woff2 +0 -0
  200. package/dist/remotion-bundle/cd9d623aa07af925.woff2 +0 -0
  201. package/dist/remotion-bundle/ce2ba7a321bd1247.woff2 +0 -0
  202. package/dist/remotion-bundle/cf72455f79a29b14.woff2 +0 -0
  203. package/dist/remotion-bundle/d267cbfefab452ac.woff2 +0 -0
  204. package/dist/remotion-bundle/d435cff46a64955f.woff +0 -0
  205. package/dist/remotion-bundle/d494d07f67e363f6.woff2 +0 -0
  206. package/dist/remotion-bundle/d7aa0cc1fa47bf38.woff2 +0 -0
  207. package/dist/remotion-bundle/d7c5ca93d885160a.woff2 +0 -0
  208. package/dist/remotion-bundle/d855d3e252db74e2.woff2 +0 -0
  209. package/dist/remotion-bundle/d8f13d47f02f82c2.woff2 +0 -0
  210. package/dist/remotion-bundle/d9567cce2ee11019.woff2 +0 -0
  211. package/dist/remotion-bundle/db8d4456fc75dd86.woff +0 -0
  212. package/dist/remotion-bundle/dc274628378c47ee.woff2 +0 -0
  213. package/dist/remotion-bundle/dc3e06947bb69903.woff2 +0 -0
  214. package/dist/remotion-bundle/dd67040ac3b6d523.woff2 +0 -0
  215. package/dist/remotion-bundle/e0b04bd488f953f4.woff2 +0 -0
  216. package/dist/remotion-bundle/e2a572ff95089370.woff2 +0 -0
  217. package/dist/remotion-bundle/e2e18a86b1c2b0cc.woff2 +0 -0
  218. package/dist/remotion-bundle/e3a78ee2fc9c6931.woff2 +0 -0
  219. package/dist/remotion-bundle/e654c9d547605a9f.woff2 +0 -0
  220. package/dist/remotion-bundle/e67a3a64c129927c.woff2 +0 -0
  221. package/dist/remotion-bundle/e6be28b4203cd6ce.woff2 +0 -0
  222. package/dist/remotion-bundle/e841907ad9b0a191.woff +0 -0
  223. package/dist/remotion-bundle/e889d1541c69fffa.woff2 +0 -0
  224. package/dist/remotion-bundle/e88ef8c76373a9e2.woff2 +0 -0
  225. package/dist/remotion-bundle/e9c72f4bc37defef.woff2 +0 -0
  226. package/dist/remotion-bundle/e9e35f863403a255.woff2 +0 -0
  227. package/dist/remotion-bundle/eb23b37b009375da.woff2 +0 -0
  228. package/dist/remotion-bundle/ee1342b741625721.woff2 +0 -0
  229. package/dist/remotion-bundle/f07da88543a57ec9.woff2 +0 -0
  230. package/dist/remotion-bundle/f522982115306f8a.woff2 +0 -0
  231. package/dist/remotion-bundle/f8449bd864e6d8bc.woff2 +0 -0
  232. package/dist/remotion-bundle/f906dd5bd95ff9ab.woff2 +0 -0
  233. package/dist/remotion-bundle/f9e9e9413e3c38bb.woff2 +0 -0
  234. package/dist/remotion-bundle/fa5a5b16280994a8.woff2 +0 -0
  235. package/dist/remotion-bundle/favicon.ico +0 -0
  236. package/dist/remotion-bundle/fb19c0517725599b.woff2 +0 -0
  237. package/dist/remotion-bundle/fcaf24232f684b9b.woff2 +0 -0
  238. package/dist/remotion-bundle/fe09e084a3eea8cf.woff2 +0 -0
  239. package/dist/remotion-bundle/ff38d5317df7345a.woff2 +0 -0
  240. package/dist/remotion-bundle/ffe7ea1ea08f455a.woff2 +0 -0
  241. package/dist/remotion-bundle/index.html +49 -0
  242. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/0.mp3 +0 -0
  243. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/1.mp3 +0 -0
  244. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/2.mp3 +0 -0
  245. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/3.mp3 +0 -0
  246. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/0.mp3 +0 -0
  247. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/1.mp3 +0 -0
  248. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/2.mp3 +0 -0
  249. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/3.mp3 +0 -0
  250. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/0.mp3 +0 -0
  251. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/1.mp3 +0 -0
  252. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/2.mp3 +0 -0
  253. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/3.mp3 +0 -0
  254. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/0.mp3 +0 -0
  255. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/1.mp3 +0 -0
  256. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/2.mp3 +0 -0
  257. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/3.mp3 +0 -0
  258. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/0.mp3 +0 -0
  259. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/1.mp3 +0 -0
  260. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/2.mp3 +0 -0
  261. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/3.mp3 +0 -0
  262. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/0.mp3 +0 -0
  263. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/1.mp3 +0 -0
  264. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/2.mp3 +0 -0
  265. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/3.mp3 +0 -0
  266. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/0.mp3 +0 -0
  267. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/1.mp3 +0 -0
  268. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/2.mp3 +0 -0
  269. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/3.mp3 +0 -0
  270. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/0.mp3 +0 -0
  271. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/1.mp3 +0 -0
  272. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/2.mp3 +0 -0
  273. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/3.mp3 +0 -0
  274. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/0.mp3 +0 -0
  275. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/1.mp3 +0 -0
  276. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/2.mp3 +0 -0
  277. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/3.mp3 +0 -0
  278. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/0.mp3 +0 -0
  279. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/1.mp3 +0 -0
  280. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/2.mp3 +0 -0
  281. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/3.mp3 +0 -0
  282. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/0.mp3 +0 -0
  283. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/1.mp3 +0 -0
  284. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/2.mp3 +0 -0
  285. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/3.mp3 +0 -0
  286. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/4.mp3 +0 -0
  287. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/0.mp3 +0 -0
  288. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/1.mp3 +0 -0
  289. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/2.mp3 +0 -0
  290. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/3.mp3 +0 -0
  291. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/0.mp3 +0 -0
  292. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/1.mp3 +0 -0
  293. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/2.mp3 +0 -0
  294. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/3.mp3 +0 -0
  295. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/4.mp3 +0 -0
  296. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/0.mp3 +0 -0
  297. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/1.mp3 +0 -0
  298. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/2.mp3 +0 -0
  299. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/3.mp3 +0 -0
  300. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/4.mp3 +0 -0
  301. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/0.mp3 +0 -0
  302. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/1.mp3 +0 -0
  303. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/2.mp3 +0 -0
  304. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/3.mp3 +0 -0
  305. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/4.mp3 +0 -0
  306. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/0.mp3 +0 -0
  307. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/1.mp3 +0 -0
  308. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/2.mp3 +0 -0
  309. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/3.mp3 +0 -0
  310. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/4.mp3 +0 -0
  311. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/0.mp3 +0 -0
  312. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/1.mp3 +0 -0
  313. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/2.mp3 +0 -0
  314. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/3.mp3 +0 -0
  315. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/0.mp3 +0 -0
  316. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/1.mp3 +0 -0
  317. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/2.mp3 +0 -0
  318. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/3.mp3 +0 -0
  319. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/0.mp3 +0 -0
  320. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/1.mp3 +0 -0
  321. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/2.mp3 +0 -0
  322. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/3.mp3 +0 -0
  323. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/0.mp3 +0 -0
  324. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/1.mp3 +0 -0
  325. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/2.mp3 +0 -0
  326. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/3.mp3 +0 -0
  327. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/4.mp3 +0 -0
  328. package/dist/remotion-bundle/public/voiceover/ai-life/card-0.mp3 +0 -0
  329. package/dist/remotion-bundle/public/voiceover/ai-life/card-1.mp3 +0 -0
  330. package/dist/remotion-bundle/public/voiceover/ai-life/card-2.mp3 +0 -0
  331. package/dist/remotion-bundle/public/voiceover/ai-life/card-3.mp3 +0 -0
  332. package/dist/remotion-bundle/public/voiceover/ai-life/card-4.mp3 +0 -0
  333. package/dist/remotion-bundle/public/voiceover/ai-life/card-5.mp3 +0 -0
  334. package/dist/remotion-bundle/public/voiceover/coffee-science/card-0.mp3 +0 -0
  335. package/dist/remotion-bundle/public/voiceover/coffee-science/card-1.mp3 +0 -0
  336. package/dist/remotion-bundle/public/voiceover/coffee-science/card-2.mp3 +0 -0
  337. package/dist/remotion-bundle/public/voiceover/coffee-science/card-3.mp3 +0 -0
  338. package/dist/remotion-bundle/public/voiceover/coffee-science/card-4.mp3 +0 -0
  339. package/dist/remotion-bundle/public/voiceover/coffee-science/card-5.mp3 +0 -0
  340. package/dist/remotion-bundle/public/voiceover/coffee-science/card-6.mp3 +0 -0
  341. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-0.mp3 +0 -0
  342. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-1.mp3 +0 -0
  343. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-2.mp3 +0 -0
  344. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-3.mp3 +0 -0
  345. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-4.mp3 +0 -0
  346. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-5.mp3 +0 -0
  347. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-6.mp3 +0 -0
  348. package/dist/remotion-bundle/public/voiceover/remote-work/card-0.mp3 +0 -0
  349. package/dist/remotion-bundle/public/voiceover/remote-work/card-1.mp3 +0 -0
  350. package/dist/remotion-bundle/public/voiceover/remote-work/card-2.mp3 +0 -0
  351. package/dist/remotion-bundle/public/voiceover/remote-work/card-3.mp3 +0 -0
  352. package/dist/remotion-bundle/public/voiceover/remote-work/card-4.mp3 +0 -0
  353. package/dist/remotion-bundle/public/voiceover/remote-work/card-5.mp3 +0 -0
  354. package/dist/remotion-bundle/source-map-helper.wasm +0 -0
  355. package/lib/cli.js +270 -0
  356. package/lib/commands/_registry.js +48 -0
  357. package/lib/commands/add.js +242 -0
  358. package/lib/commands/asr/azure-transcribe.js +336 -0
  359. package/lib/commands/asr/cloud-transcribe.js +384 -0
  360. package/lib/commands/asr/helpers.js +76 -0
  361. package/lib/commands/asr/index.js +236 -0
  362. package/lib/commands/asr/local-transcribe.js +125 -0
  363. package/lib/commands/asr-jobs.js +257 -0
  364. package/lib/commands/asr.js +11 -0
  365. package/lib/commands/auth-cmds.js +358 -0
  366. package/lib/commands/dub.js +542 -0
  367. package/lib/commands/explain.js +512 -0
  368. package/lib/commands/feedback.js +152 -0
  369. package/lib/commands/image.js +207 -0
  370. package/lib/commands/mcp-key.js +166 -0
  371. package/lib/commands/narrate.js +639 -0
  372. package/lib/commands/picstory-templates.js +276 -0
  373. package/lib/commands/picstory.js +547 -0
  374. package/lib/commands/podcast/dialogue.js +109 -0
  375. package/lib/commands/podcast/generate.js +127 -0
  376. package/lib/commands/podcast/index.js +561 -0
  377. package/lib/commands/podcast/synthesize.js +188 -0
  378. package/lib/commands/podcast.js +11 -0
  379. package/lib/commands/present.js +519 -0
  380. package/lib/commands/publish.js +415 -0
  381. package/lib/commands/skills.js +473 -0
  382. package/lib/commands/slice-render.js +282 -0
  383. package/lib/commands/slice-stage.js +264 -0
  384. package/lib/commands/slice.js +346 -0
  385. package/lib/commands/slides/constants.js +108 -0
  386. package/lib/commands/slides/html-renderer.js +338 -0
  387. package/lib/commands/slides/index.js +345 -0
  388. package/lib/commands/slides.js +11 -0
  389. package/lib/commands/story.js +302 -0
  390. package/lib/commands/summarize.js +532 -0
  391. package/lib/commands/synthesize.js +261 -0
  392. package/lib/commands/translate.js +593 -0
  393. package/lib/commands/upgrade.js +249 -0
  394. package/lib/commands/video-translate.js +577 -0
  395. package/lib/commands/voices.js +292 -0
  396. package/lib/core/agent-env.js +104 -0
  397. package/lib/core/args.js +107 -0
  398. package/lib/core/asr-client.js +448 -0
  399. package/lib/core/asr-jobs-client.js +126 -0
  400. package/lib/core/asr-jobs-store.js +105 -0
  401. package/lib/core/asr-r2-upload.js +181 -0
  402. package/lib/core/asr-upload.js +132 -0
  403. package/lib/core/audio-extract.js +150 -0
  404. package/lib/core/audio.js +219 -0
  405. package/lib/core/auth.js +880 -0
  406. package/lib/core/config.js +197 -0
  407. package/lib/core/feedback.js +64 -0
  408. package/lib/core/ffmpeg.js +476 -0
  409. package/lib/core/http.js +188 -0
  410. package/lib/core/image-client.js +55 -0
  411. package/lib/core/intent-params.js +11 -0
  412. package/lib/core/llm-client.js +76 -0
  413. package/lib/core/logger.js +208 -0
  414. package/lib/core/mic-recorder.js +182 -0
  415. package/lib/core/pause-markers.js +94 -0
  416. package/lib/core/podcast-pacing.js +118 -0
  417. package/lib/core/spinner.js +33 -0
  418. package/lib/core/srt.js +394 -0
  419. package/lib/core/telemetry.js +100 -0
  420. package/lib/core/timeline.js +92 -0
  421. package/lib/core/tts-synthesizer.js +70 -0
  422. package/lib/core/update-check.js +185 -0
  423. package/lib/core/url-download.js +148 -0
  424. package/lib/core/whisper-local.js +279 -0
  425. package/lib/internal/deck-validator.js +488 -0
  426. package/lib/internal/slice-themes.json +370 -0
  427. package/lib/stage-core/cloud-render.js +170 -0
  428. package/lib/stage-core/deck-format.js +133 -0
  429. package/lib/stage-core/edit-prompt.js +104 -0
  430. package/lib/stage-core/event-bus.js +31 -0
  431. package/lib/stage-core/port.js +46 -0
  432. package/lib/stage-core/server.js +352 -0
  433. package/lib/stage-core/snapshot-store.js +198 -0
  434. package/lib/stage-core/watcher.js +106 -0
  435. package/lib/stage-ui/slice/template.js +1672 -0
  436. package/package.json +9 -4
  437. package/skills/.claude-plugin/marketplace.json +22 -0
  438. package/skills/.claude-plugin/plugin.json +25 -0
  439. package/skills/LICENSE +21 -0
  440. package/skills/README.md +120 -0
  441. package/skills/hub/SKILL.md +317 -0
  442. package/skills/podcast/SKILL.md +146 -0
  443. package/skills/slice/SKILL.md +205 -0
  444. package/skills/slice/agents/openai.yaml +4 -0
  445. package/skills/slice/references/deck-schema.md +183 -0
  446. package/skills/slice/references/example-decks.md +108 -0
  447. package/skills/slice/references/themes.md +172 -0
  448. package/skills/transcribe/SKILL.md +473 -0
  449. package/skills/video/SKILL.md +261 -0
  450. package/skills/voxflow-slice/SKILL.md +271 -0
  451. package/skills/voxflow-slice/examples/article.md +13 -0
  452. package/skills/voxflow-slice/examples/expected-deck.json +39 -0
  453. package/skills/voxflow-slice/examples/validate.mjs +46 -0
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Podcast pacing helpers — per-segment silence + pace presets.
3
+ *
4
+ * Mirrors `backend/config/intent-tts-params.js`. The backend stamps each
5
+ * dialogue turn's `pauseAfterMs` (base) into the script JSON; here we apply
6
+ * the user's pace preset and a speaker-change boost when the JSON is missing
7
+ * those fields (older payloads, hand-edited scripts).
8
+ *
9
+ * Keep these constants in sync with the backend file.
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const INTENT_PAUSE_AFTER_MS = Object.freeze({
15
+ opening: 600,
16
+ explain: 450,
17
+ question: 750,
18
+ react: 350,
19
+ debate: 400,
20
+ summary: 600,
21
+ summarize: 600,
22
+ transition: 1000,
23
+ joke: 500,
24
+ closing: 1500,
25
+ });
26
+
27
+ const DEFAULT_PAUSE_AFTER_MS = 500;
28
+ const SPEAKER_CHANGE_BOOST_MS = 100;
29
+
30
+ // Silence multiplier for inter-turn gaps (client-side splice).
31
+ const PACE_MULTIPLIERS = Object.freeze({ tight: 0.7, natural: 1.0, relaxed: 1.3 });
32
+
33
+ // Speed multiplier for the TRTC `Speed` API param. Reciprocals of
34
+ // PACE_MULTIPLIERS so within-turn pauses (TRTC scales them by Speed) and
35
+ // inter-turn silences (client-side, scaled by PACE_MULTIPLIERS) move
36
+ // together. Final Speed is clamped to [0.5, 2.0] (TRTC's API range).
37
+ const PACE_SPEED_MULTIPLIERS = Object.freeze({ tight: 1.43, natural: 1.0, relaxed: 0.77 });
38
+ const TRTC_SPEED_MIN = 0.5;
39
+ const TRTC_SPEED_MAX = 2.0;
40
+
41
+ const VALID_PACE = Object.freeze(['tight', 'natural', 'relaxed']);
42
+
43
+ function resolvePaceMultiplier(pace) {
44
+ if (typeof pace !== 'string') return PACE_MULTIPLIERS.natural;
45
+ return PACE_MULTIPLIERS[pace] ?? PACE_MULTIPLIERS.natural;
46
+ }
47
+
48
+ function resolvePaceSpeedMultiplier(pace) {
49
+ if (typeof pace !== 'string') return PACE_SPEED_MULTIPLIERS.natural;
50
+ return PACE_SPEED_MULTIPLIERS[pace] ?? PACE_SPEED_MULTIPLIERS.natural;
51
+ }
52
+
53
+ function composeTtsSpeed({ voiceSpeed = 1.0, intentSpeed = 1.0, pace = 'natural' } = {}) {
54
+ const paceMult = resolvePaceSpeedMultiplier(pace);
55
+ const raw = voiceSpeed * intentSpeed * paceMult;
56
+ return Math.min(TRTC_SPEED_MAX, Math.max(TRTC_SPEED_MIN, raw));
57
+ }
58
+
59
+ function intentBaseMs(intent) {
60
+ if (!intent || typeof intent !== 'string') return DEFAULT_PAUSE_AFTER_MS;
61
+ return INTENT_PAUSE_AFTER_MS[intent] ?? DEFAULT_PAUSE_AFTER_MS;
62
+ }
63
+
64
+ /**
65
+ * Resolve the per-segment silence array (in ms) for `buildWav({ gapsMs })`.
66
+ *
67
+ * - If a segment has a numeric `pauseAfterMs` (stamped by backend), use it
68
+ * - Otherwise compute from `intent` + speaker-change boost
69
+ * - Apply pace multiplier last
70
+ * - Last segment's gap is ignored by buildWav (no trailing silence)
71
+ *
72
+ * @param {{intent?:string|null,speaker?:string|null,pauseAfterMs?:number|null}[]} segments
73
+ * @param {object} [options]
74
+ * @param {string} [options.pace='natural']
75
+ * @param {number} [options.fallbackSec] — when neither pauseAfterMs nor intent
76
+ * yields a value AND a uniform legacy override is in play, use this seconds value
77
+ * @returns {number[]} gapsMs aligned to segments[]
78
+ */
79
+ function computeGapsMs(segments, options = {}) {
80
+ const multiplier = resolvePaceMultiplier(options.pace);
81
+ const fallbackMs = Number.isFinite(options.fallbackSec) ? options.fallbackSec * 1000 : null;
82
+
83
+ return segments.map((seg, i) => {
84
+ const next = segments[i + 1] || null;
85
+ if (!next) return 0;
86
+
87
+ let base;
88
+ if (Number.isFinite(seg?.pauseAfterMs)) {
89
+ base = seg.pauseAfterMs;
90
+ } else if (seg?.intent) {
91
+ base = intentBaseMs(seg.intent);
92
+ if ((seg?.speaker || null) !== (next?.speaker || null)) base += SPEAKER_CHANGE_BOOST_MS;
93
+ } else if (fallbackMs !== null) {
94
+ base = fallbackMs;
95
+ } else {
96
+ base = DEFAULT_PAUSE_AFTER_MS;
97
+ if ((seg?.speaker || null) !== (next?.speaker || null)) base += SPEAKER_CHANGE_BOOST_MS;
98
+ }
99
+
100
+ return Math.round(base * multiplier);
101
+ });
102
+ }
103
+
104
+ module.exports = {
105
+ INTENT_PAUSE_AFTER_MS,
106
+ DEFAULT_PAUSE_AFTER_MS,
107
+ SPEAKER_CHANGE_BOOST_MS,
108
+ PACE_MULTIPLIERS,
109
+ PACE_SPEED_MULTIPLIERS,
110
+ TRTC_SPEED_MIN,
111
+ TRTC_SPEED_MAX,
112
+ VALID_PACE,
113
+ resolvePaceMultiplier,
114
+ resolvePaceSpeedMultiplier,
115
+ composeTtsSpeed,
116
+ intentBaseMs,
117
+ computeGapsMs,
118
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * VoxFlow CLI — Terminal spinner utility
3
+ */
4
+
5
+ /**
6
+ * Start a simple CLI spinner with a message.
7
+ * @param {string} message - Text to display before the spinner
8
+ * @returns {{ stop: (suffix: string) => void, update: (newMessage: string) => void }}
9
+ */
10
+ function startSpinner(message) {
11
+ const frames = ['|', '/', '-', '\\'];
12
+ let i = 0;
13
+ let currentMessage = message;
14
+ process.stdout.write(message + ' ' + frames[0]);
15
+ const id = setInterval(() => {
16
+ i = (i + 1) % frames.length;
17
+ process.stdout.write('\b' + frames[i]);
18
+ }, 120);
19
+ return {
20
+ stop(suffix) {
21
+ clearInterval(id);
22
+ process.stdout.write('\b' + suffix + '\n');
23
+ },
24
+ update(newMessage) {
25
+ // Clear the current line and rewrite with new message + spinner frame
26
+ process.stdout.write('\r' + ' '.repeat(currentMessage.length + 4) + '\r');
27
+ currentMessage = newMessage;
28
+ process.stdout.write(newMessage + ' ' + frames[i]);
29
+ },
30
+ };
31
+ }
32
+
33
+ module.exports = { startSpinner };
@@ -0,0 +1,394 @@
1
+ /**
2
+ * VoxFlow CLI — SRT subtitle parser and formatter
3
+ *
4
+ * Parses standard SRT files into Caption objects with millisecond timestamps.
5
+ * Supports extended [Speaker: name] tags for multi-speaker dubbing.
6
+ */
7
+
8
+ /**
9
+ * Parse an SRT timestamp string into milliseconds.
10
+ * @param {string} str - Timestamp in format "HH:MM:SS,mmm"
11
+ * @returns {number} Time in milliseconds
12
+ */
13
+ function parseTimestamp(str) {
14
+ const match = str.trim().match(/^(\d{1,2}):(\d{2}):(\d{2})[,.](\d{3})$/);
15
+ if (!match) {
16
+ throw new Error(`Invalid SRT timestamp: "${str}"`);
17
+ }
18
+ const [, h, m, s, ms] = match;
19
+ return (
20
+ parseInt(h, 10) * 3600000 +
21
+ parseInt(m, 10) * 60000 +
22
+ parseInt(s, 10) * 1000 +
23
+ parseInt(ms, 10)
24
+ );
25
+ }
26
+
27
+ /**
28
+ * Format milliseconds into an SRT timestamp string.
29
+ * @param {number} ms - Time in milliseconds
30
+ * @returns {string} Timestamp in format "HH:MM:SS,mmm"
31
+ */
32
+ function formatTimestamp(ms) {
33
+ if (ms < 0) ms = 0;
34
+ const h = Math.floor(ms / 3600000);
35
+ ms %= 3600000;
36
+ const m = Math.floor(ms / 60000);
37
+ ms %= 60000;
38
+ const s = Math.floor(ms / 1000);
39
+ const remainder = ms % 1000;
40
+ return (
41
+ String(h).padStart(2, '0') + ':' +
42
+ String(m).padStart(2, '0') + ':' +
43
+ String(s).padStart(2, '0') + ',' +
44
+ String(remainder).padStart(3, '0')
45
+ );
46
+ }
47
+
48
+ /**
49
+ * @typedef {object} Caption
50
+ * @property {number} id - Sequence number (1-based)
51
+ * @property {number} startMs - Start time in milliseconds
52
+ * @property {number} endMs - End time in milliseconds
53
+ * @property {string} text - Subtitle text (may span multiple lines)
54
+ * @property {string} [speakerId] - Optional speaker name from [Speaker: xxx] tag
55
+ */
56
+
57
+ /**
58
+ * Parse SRT content into an array of Caption objects.
59
+ *
60
+ * Supports:
61
+ * - Standard SRT format (index, timestamp line, text, blank separator)
62
+ * - Extended [Speaker: name] tags in text lines
63
+ * - Both comma and dot as ms separator (00:00:01,000 or 00:00:01.000)
64
+ *
65
+ * @param {string} content - Raw SRT file content
66
+ * @returns {Caption[]} Parsed captions sorted by startMs
67
+ */
68
+ function parseSrt(content) {
69
+ if (!content || content.trim().length === 0) {
70
+ return [];
71
+ }
72
+
73
+ const captions = [];
74
+ // Normalize line endings and split into blocks by blank lines
75
+ const normalized = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
76
+ const blocks = normalized.split(/\n\s*\n/).filter((b) => b.trim().length > 0);
77
+
78
+ for (const block of blocks) {
79
+ const lines = block.trim().split('\n');
80
+ if (lines.length < 2) continue;
81
+
82
+ // Line 1: sequence number (may be missing or non-numeric in some SRT files)
83
+ let lineIdx = 0;
84
+ let id;
85
+ const firstLine = lines[0].trim();
86
+
87
+ // Check if first line is a number (sequence ID)
88
+ if (/^\d+$/.test(firstLine)) {
89
+ id = parseInt(firstLine, 10);
90
+ lineIdx = 1;
91
+ } else {
92
+ // First line might be the timestamp directly
93
+ id = captions.length + 1;
94
+ }
95
+
96
+ // Line 2 (or 1): timestamp line "HH:MM:SS,mmm --> HH:MM:SS,mmm"
97
+ if (lineIdx >= lines.length) continue;
98
+ const tsLine = lines[lineIdx].trim();
99
+ const tsMatch = tsLine.match(
100
+ /^(\d{1,2}:\d{2}:\d{2}[,.]\d{3})\s*-->\s*(\d{1,2}:\d{2}:\d{2}[,.]\d{3})/
101
+ );
102
+ if (!tsMatch) continue; // skip blocks without valid timestamp
103
+
104
+ const startMs = parseTimestamp(tsMatch[1]);
105
+ const endMs = parseTimestamp(tsMatch[2]);
106
+ lineIdx++;
107
+
108
+ // Remaining lines: subtitle text
109
+ const textLines = lines.slice(lineIdx).filter((l) => l.trim().length > 0);
110
+ if (textLines.length === 0) continue;
111
+
112
+ const rawText = textLines.join('\n');
113
+
114
+ // Extract optional [Speaker: xxx] tag
115
+ let speakerId;
116
+ let text = rawText;
117
+ const speakerMatch = rawText.match(/^\[Speaker:\s*([^\]]+)\]\s*/i);
118
+ if (speakerMatch) {
119
+ speakerId = speakerMatch[1].trim();
120
+ text = rawText.slice(speakerMatch[0].length);
121
+ }
122
+
123
+ if (text.trim().length === 0) continue;
124
+
125
+ captions.push({
126
+ id,
127
+ startMs,
128
+ endMs,
129
+ text: text.trim(),
130
+ ...(speakerId ? { speakerId } : {}),
131
+ });
132
+ }
133
+
134
+ // Sort by startMs for safety
135
+ captions.sort((a, b) => a.startMs - b.startMs);
136
+
137
+ return captions;
138
+ }
139
+
140
+ /**
141
+ * Format an array of Caption objects back into SRT string.
142
+ *
143
+ * @param {Caption[]} captions - Array of caption objects
144
+ * @returns {string} Formatted SRT content
145
+ */
146
+ function formatSrt(captions) {
147
+ return captions
148
+ .map((cap, i) => {
149
+ const id = cap.id || i + 1;
150
+ const start = formatTimestamp(cap.startMs);
151
+ const end = formatTimestamp(cap.endMs);
152
+ const speakerPrefix = cap.speakerId ? `[Speaker: ${cap.speakerId}] ` : '';
153
+ return `${id}\n${start} --> ${end}\n${speakerPrefix}${cap.text}`;
154
+ })
155
+ .join('\n\n') + '\n';
156
+ }
157
+
158
+ // ─── ASR result → Caption[] converters ──────────────────────────────────────
159
+
160
+ /**
161
+ * Convert Flash ASR result into Caption objects.
162
+ *
163
+ * Flash API returns an array of channel results, each containing a
164
+ * `sentence_list` with per-sentence start/end times and optional speaker_id.
165
+ *
166
+ * @param {Array} flashResult - flash_result array from /api/asr/flash
167
+ * @returns {Caption[]}
168
+ */
169
+ function buildCaptionsFromFlash(flashResult) {
170
+ const captions = [];
171
+ let idx = 1;
172
+
173
+ for (const channel of flashResult) {
174
+ const sentences = channel.sentence_list || [];
175
+ for (const s of sentences) {
176
+ const cap = {
177
+ id: idx++,
178
+ startMs: s.start_time || 0,
179
+ endMs: s.end_time || 0,
180
+ text: (s.text || '').trim(),
181
+ };
182
+ if (s.speaker_id !== undefined && s.speaker_id !== null) {
183
+ cap.speakerId = `Speaker${s.speaker_id}`;
184
+ }
185
+ if (cap.text.length > 0) {
186
+ captions.push(cap);
187
+ }
188
+ }
189
+ }
190
+
191
+ return captions;
192
+ }
193
+
194
+ /**
195
+ * Convert Sentence ASR result into a single Caption.
196
+ *
197
+ * Sentence mode returns one text blob and optionally a wordList with
198
+ * per-word timestamps. If wordList is available, we can create more
199
+ * granular captions (one per sentence/segment), but for simplicity
200
+ * we produce a single caption spanning the full audio.
201
+ *
202
+ * @param {string} result - Recognition text
203
+ * @param {number} audioTime - Audio duration in seconds
204
+ * @param {Array} [wordList] - Optional word-level timestamps
205
+ * @returns {Caption[]}
206
+ */
207
+ function buildCaptionsFromSentence(result, audioTime, wordList) {
208
+ if (!result || result.trim().length === 0) return [];
209
+
210
+ // If we have word-level data, try to split by natural pauses (>500ms gap)
211
+ if (wordList && wordList.length > 0) {
212
+ return buildCaptionsFromWordList(wordList, result);
213
+ }
214
+
215
+ // Fallback: single caption for the full audio
216
+ return [
217
+ {
218
+ id: 1,
219
+ startMs: 0,
220
+ endMs: Math.round(audioTime * 1000),
221
+ text: result.trim(),
222
+ },
223
+ ];
224
+ }
225
+
226
+ /**
227
+ * Convert word-level timestamps into sentence-level captions.
228
+ *
229
+ * Splitting strategy (in priority order):
230
+ * 1. Natural pauses > 500ms between words
231
+ * 2. Sentence-ending punctuation (. ! ? etc.) when caption exceeds 5 seconds
232
+ * 3. Hard split at MAX_CAPTION_DURATION_MS (15s) on word boundaries
233
+ *
234
+ * Automatically detects whether to join words with spaces (alphabetic languages)
235
+ * or without (CJK languages).
236
+ *
237
+ * @param {Array} wordList - Array of {word, startTime, endTime} or {Word, StartTime, EndTime} (ms)
238
+ * @param {string} fullText - Full recognition text (fallback)
239
+ * @returns {Caption[]}
240
+ */
241
+ function buildCaptionsFromWordList(wordList, fullText) {
242
+ if (!wordList || wordList.length === 0) {
243
+ return fullText
244
+ ? [{ id: 1, startMs: 0, endMs: 0, text: fullText }]
245
+ : [];
246
+ }
247
+
248
+ const PAUSE_THRESHOLD_MS = 500;
249
+ const MIN_DURATION_FOR_PUNCT_SPLIT_MS = 5000;
250
+ const MAX_CAPTION_DURATION_MS = 15000;
251
+ const SENTENCE_ENDERS = /[.!?。!?…]+$/;
252
+
253
+ // Support both casing: API returns {Word, StartTime, EndTime}, normalize here
254
+ const getWord = (w) => w.word || w.Word || '';
255
+ const getStart = (w) => w.startTime ?? w.StartTime ?? 0;
256
+ const getEnd = (w) => w.endTime ?? w.EndTime ?? 0;
257
+
258
+ // Detect if words need space separators (alphabetic vs CJK)
259
+ const sampleWords = wordList.slice(0, 10).map(getWord).join('');
260
+ const cjkChars = (sampleWords.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/g) || []).length;
261
+ const needsSpaces = cjkChars < sampleWords.length * 0.3;
262
+ const joiner = needsSpaces ? ' ' : '';
263
+
264
+ const captions = [];
265
+ let currentWords = [];
266
+ let currentStart = getStart(wordList[0]);
267
+ let prevEnd = currentStart;
268
+
269
+ function flushCaption() {
270
+ if (currentWords.length === 0) return;
271
+ const text = currentWords.join(joiner).trim();
272
+ if (text.length > 0) {
273
+ captions.push({
274
+ id: captions.length + 1,
275
+ startMs: currentStart,
276
+ endMs: prevEnd,
277
+ text,
278
+ });
279
+ }
280
+ currentWords = [];
281
+ }
282
+
283
+ for (let i = 0; i < wordList.length; i++) {
284
+ const w = wordList[i];
285
+ const wordStart = getStart(w);
286
+ const wordEnd = getEnd(w);
287
+ const gap = wordStart - prevEnd;
288
+ const captionDuration = wordStart - currentStart;
289
+
290
+ // Split on natural pauses > 500ms
291
+ if (gap > PAUSE_THRESHOLD_MS && currentWords.length > 0) {
292
+ flushCaption();
293
+ currentStart = wordStart;
294
+ }
295
+ // Split on sentence-ending punctuation if caption is already > 5 seconds
296
+ else if (
297
+ currentWords.length > 0 &&
298
+ captionDuration > MIN_DURATION_FOR_PUNCT_SPLIT_MS &&
299
+ SENTENCE_ENDERS.test(currentWords[currentWords.length - 1])
300
+ ) {
301
+ flushCaption();
302
+ currentStart = wordStart;
303
+ }
304
+ // Hard split if caption exceeds max duration
305
+ else if (
306
+ currentWords.length > 0 &&
307
+ captionDuration > MAX_CAPTION_DURATION_MS
308
+ ) {
309
+ flushCaption();
310
+ currentStart = wordStart;
311
+ }
312
+
313
+ currentWords.push(getWord(w));
314
+ prevEnd = wordEnd || prevEnd;
315
+ }
316
+
317
+ // Flush remaining
318
+ flushCaption();
319
+
320
+ return captions;
321
+ }
322
+
323
+ /**
324
+ * Convert File ASR (async) result into captions.
325
+ *
326
+ * The file mode Result field is a plain text string. If ResTextFormat was
327
+ * set to SRT (1), it may already be SRT formatted. Otherwise it is plain
328
+ * text that we wrap as a single caption.
329
+ *
330
+ * @param {string} result - Recognition result text
331
+ * @param {number} audioTime - Audio duration in seconds
332
+ * @returns {Caption[]}
333
+ */
334
+ function buildCaptionsFromFile(result, audioTime) {
335
+ if (!result || result.trim().length === 0) return [];
336
+
337
+ // Check if the result is already SRT formatted
338
+ if (/^\d+\s*\n\d{2}:\d{2}:\d{2}[,.]\d{3}\s*-->/.test(result.trim())) {
339
+ return parseSrt(result);
340
+ }
341
+
342
+ // Plain text — wrap as a single caption
343
+ return [
344
+ {
345
+ id: 1,
346
+ startMs: 0,
347
+ endMs: Math.round(audioTime * 1000),
348
+ text: result.trim(),
349
+ },
350
+ ];
351
+ }
352
+
353
+ /**
354
+ * Format captions as plain text (no timestamps).
355
+ *
356
+ * @param {Caption[]} captions
357
+ * @param {object} [opts]
358
+ * @param {boolean} [opts.includeSpeakers] - Prefix each line with speaker name
359
+ * @returns {string}
360
+ */
361
+ function formatPlainText(captions, opts = {}) {
362
+ return captions
363
+ .map((cap) => {
364
+ const prefix =
365
+ opts.includeSpeakers && cap.speakerId
366
+ ? `[${cap.speakerId}] `
367
+ : '';
368
+ return `${prefix}${cap.text}`;
369
+ })
370
+ .join('\n') + '\n';
371
+ }
372
+
373
+ /**
374
+ * Format captions as JSON (with full metadata).
375
+ *
376
+ * @param {Caption[]} captions
377
+ * @returns {string}
378
+ */
379
+ function formatJson(captions) {
380
+ return JSON.stringify(captions, null, 2) + '\n';
381
+ }
382
+
383
+ module.exports = {
384
+ parseSrt,
385
+ formatSrt,
386
+ parseTimestamp,
387
+ formatTimestamp,
388
+ buildCaptionsFromFlash,
389
+ buildCaptionsFromSentence,
390
+ buildCaptionsFromWordList,
391
+ buildCaptionsFromFile,
392
+ formatPlainText,
393
+ formatJson,
394
+ };
@@ -0,0 +1,100 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Anonymous, fire-and-forget event sink for the `voxflow` CLI.
5
+ *
6
+ * Used to answer one product question at a time without spinning up an
7
+ * analytics SDK. First event: `cli.slice.stage.launched` — feeds into the
8
+ * "should we build a Stage MCP?" decision (PR #3317 follow-up).
9
+ *
10
+ * Design rules — keep them, expanding scope is a separate review:
11
+ * • Anonymous. No JWT, no email, no userId. Random per-process session id
12
+ * so we can dedupe accidental duplicate events from a single launch.
13
+ * • Opt-out: VOXFLOW_TELEMETRY=0 OR DO_NOT_TRACK=1 disables. CI envs
14
+ * (CI=1, GITHUB_ACTIONS=true) auto-disable so test runs don't pollute.
15
+ * • Fail-silent: any network error, timeout, or thrown handler is
16
+ * swallowed. Telemetry MUST NEVER block a CLI command.
17
+ * • Allowlisted prop keys only — backend rejects unknown event names.
18
+ */
19
+
20
+ const http = require('http');
21
+ const https = require('https');
22
+ const crypto = require('crypto');
23
+
24
+ const { API_BASE } = require('./config');
25
+ const { logger } = require('./logger');
26
+
27
+ let sessionId = null;
28
+ function getSessionId() {
29
+ if (!sessionId) sessionId = crypto.randomUUID();
30
+ return sessionId;
31
+ }
32
+
33
+ function isDisabled() {
34
+ if (process.env.VOXFLOW_TELEMETRY === '0' || process.env.VOXFLOW_TELEMETRY === 'false') return true;
35
+ if (process.env.DO_NOT_TRACK === '1' || process.env.DO_NOT_TRACK === 'true') return true;
36
+ if (process.env.CI || process.env.GITHUB_ACTIONS || process.env.NODE_ENV === 'test') return true;
37
+ return false;
38
+ }
39
+
40
+ function readCliVersion() {
41
+ try {
42
+ return require('../../package.json').version || 'unknown';
43
+ } catch {
44
+ return 'unknown';
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Emit a telemetry event. Never throws; never returns a meaningful value
50
+ * to callers — it's intentionally fire-and-forget.
51
+ *
52
+ * @param {string} event Allowlisted event name (see backend/routes/cli-telemetry.js)
53
+ * @param {object} [props] Plain object; only theme / cli_version / node_version /
54
+ * platform / session_id keys are forwarded.
55
+ * @param {object} [opts]
56
+ * @param {string} [opts.apiBase] Override API base (used by tests)
57
+ */
58
+ function emit(event, props = {}, opts = {}) {
59
+ if (isDisabled()) return;
60
+
61
+ const apiBase = opts.apiBase || API_BASE;
62
+ let url;
63
+ try {
64
+ url = new URL('/api/cli-telemetry', apiBase);
65
+ } catch {
66
+ return;
67
+ }
68
+
69
+ const payload = JSON.stringify({
70
+ event,
71
+ props: {
72
+ cli_version: readCliVersion(),
73
+ node_version: process.version,
74
+ platform: process.platform,
75
+ session_id: getSessionId(),
76
+ ...props
77
+ }
78
+ });
79
+
80
+ const mod = url.protocol === 'https:' ? https : http;
81
+ let req;
82
+ try {
83
+ req = mod.request(url, {
84
+ method: 'POST',
85
+ headers: {
86
+ 'Content-Type': 'application/json',
87
+ 'Content-Length': Buffer.byteLength(payload)
88
+ }
89
+ }, (res) => { res.resume(); });
90
+ } catch (err) {
91
+ logger.debug({ err: err.message }, '[telemetry] request build failed');
92
+ return;
93
+ }
94
+ req.on('error', (err) => logger.debug({ err: err.message }, '[telemetry] network error'));
95
+ req.setTimeout(1500, () => req.destroy());
96
+ req.write(payload);
97
+ req.end();
98
+ }
99
+
100
+ module.exports = { emit, _isDisabled: isDisabled, _getSessionId: getSessionId };