@kenkaiiii/gg-editor 0.7.0 → 0.7.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 (446) hide show
  1. package/dist/cli.js +64 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/core/audio-mix.d.ts.map +1 -1
  4. package/dist/core/audio-mix.js +8 -1
  5. package/dist/core/audio-mix.js.map +1 -1
  6. package/dist/core/audio-mix.test.js +1 -1
  7. package/dist/core/audio-mix.test.js.map +1 -1
  8. package/dist/core/auth/api-keys.d.ts +1 -1
  9. package/dist/core/auth/api-keys.d.ts.map +1 -1
  10. package/dist/core/auth/api-keys.js +2 -1
  11. package/dist/core/auth/api-keys.js.map +1 -1
  12. package/dist/core/auth/login.d.ts.map +1 -1
  13. package/dist/core/auth/login.js.map +1 -1
  14. package/dist/core/beats.d.ts +59 -0
  15. package/dist/core/beats.d.ts.map +1 -0
  16. package/dist/core/beats.js +122 -0
  17. package/dist/core/beats.js.map +1 -0
  18. package/dist/core/beats.test.d.ts +2 -0
  19. package/dist/core/beats.test.d.ts.map +1 -0
  20. package/dist/core/beats.test.js +86 -0
  21. package/dist/core/beats.test.js.map +1 -0
  22. package/dist/core/brand-kit.d.ts +80 -0
  23. package/dist/core/brand-kit.d.ts.map +1 -0
  24. package/dist/core/brand-kit.js +96 -0
  25. package/dist/core/brand-kit.js.map +1 -0
  26. package/dist/core/brand-kit.test.d.ts +2 -0
  27. package/dist/core/brand-kit.test.d.ts.map +1 -0
  28. package/dist/core/brand-kit.test.js +76 -0
  29. package/dist/core/brand-kit.test.js.map +1 -0
  30. package/dist/core/bundled-sfx.d.ts +64 -0
  31. package/dist/core/bundled-sfx.d.ts.map +1 -0
  32. package/dist/core/bundled-sfx.js +218 -0
  33. package/dist/core/bundled-sfx.js.map +1 -0
  34. package/dist/core/bundled-sfx.test.d.ts +2 -0
  35. package/dist/core/bundled-sfx.test.d.ts.map +1 -0
  36. package/dist/core/bundled-sfx.test.js +81 -0
  37. package/dist/core/bundled-sfx.test.js.map +1 -0
  38. package/dist/core/child-abort.d.ts +57 -0
  39. package/dist/core/child-abort.d.ts.map +1 -0
  40. package/dist/core/child-abort.js +95 -0
  41. package/dist/core/child-abort.js.map +1 -0
  42. package/dist/core/child-abort.test.d.ts +2 -0
  43. package/dist/core/child-abort.test.d.ts.map +1 -0
  44. package/dist/core/child-abort.test.js +88 -0
  45. package/dist/core/child-abort.test.js.map +1 -0
  46. package/dist/core/clip-scoring.d.ts +44 -0
  47. package/dist/core/clip-scoring.d.ts.map +1 -0
  48. package/dist/core/clip-scoring.js +165 -0
  49. package/dist/core/clip-scoring.js.map +1 -0
  50. package/dist/core/clip-scoring.test.d.ts +2 -0
  51. package/dist/core/clip-scoring.test.d.ts.map +1 -0
  52. package/dist/core/clip-scoring.test.js +113 -0
  53. package/dist/core/clip-scoring.test.js.map +1 -0
  54. package/dist/core/emoji-captions.d.ts +45 -0
  55. package/dist/core/emoji-captions.d.ts.map +1 -0
  56. package/dist/core/emoji-captions.js +121 -0
  57. package/dist/core/emoji-captions.js.map +1 -0
  58. package/dist/core/face-reframe.d.ts +91 -0
  59. package/dist/core/face-reframe.d.ts.map +1 -0
  60. package/dist/core/face-reframe.js +141 -0
  61. package/dist/core/face-reframe.js.map +1 -0
  62. package/dist/core/face-reframe.test.d.ts +2 -0
  63. package/dist/core/face-reframe.test.d.ts.map +1 -0
  64. package/dist/core/face-reframe.test.js +171 -0
  65. package/dist/core/face-reframe.test.js.map +1 -0
  66. package/dist/core/filler-words.d.ts +57 -9
  67. package/dist/core/filler-words.d.ts.map +1 -1
  68. package/dist/core/filler-words.js +61 -9
  69. package/dist/core/filler-words.js.map +1 -1
  70. package/dist/core/filler-words.test.js +91 -17
  71. package/dist/core/filler-words.test.js.map +1 -1
  72. package/dist/core/hook-rewrite.d.ts +48 -0
  73. package/dist/core/hook-rewrite.d.ts.map +1 -0
  74. package/dist/core/hook-rewrite.js +151 -0
  75. package/dist/core/hook-rewrite.js.map +1 -0
  76. package/dist/core/hook-rewrite.test.d.ts +2 -0
  77. package/dist/core/hook-rewrite.test.d.ts.map +1 -0
  78. package/dist/core/hook-rewrite.test.js +58 -0
  79. package/dist/core/hook-rewrite.test.js.map +1 -0
  80. package/dist/core/hosts/lazy.d.ts.map +1 -1
  81. package/dist/core/hosts/lazy.js +2 -0
  82. package/dist/core/hosts/lazy.js.map +1 -1
  83. package/dist/core/hosts/premiere/adapter.d.ts +1 -0
  84. package/dist/core/hosts/premiere/adapter.d.ts.map +1 -1
  85. package/dist/core/hosts/premiere/adapter.js.map +1 -1
  86. package/dist/core/hosts/premiere/bridge-source.d.ts.map +1 -1
  87. package/dist/core/hosts/premiere/bridge-source.js +6 -3
  88. package/dist/core/hosts/premiere/bridge-source.js.map +1 -1
  89. package/dist/core/hosts/resolve/adapter.d.ts +1 -0
  90. package/dist/core/hosts/resolve/adapter.d.ts.map +1 -1
  91. package/dist/core/hosts/resolve/adapter.js.map +1 -1
  92. package/dist/core/hosts/resolve/bridge-source.d.ts.map +1 -1
  93. package/dist/core/hosts/resolve/bridge-source.js +31 -4
  94. package/dist/core/hosts/resolve/bridge-source.js.map +1 -1
  95. package/dist/core/hosts/resolve/bridge.d.ts +2 -19
  96. package/dist/core/hosts/resolve/bridge.d.ts.map +1 -1
  97. package/dist/core/hosts/resolve/bridge.js +70 -41
  98. package/dist/core/hosts/resolve/bridge.js.map +1 -1
  99. package/dist/core/hosts/resolve/bridge.test.js +130 -0
  100. package/dist/core/hosts/resolve/bridge.test.js.map +1 -1
  101. package/dist/core/hosts/types.d.ts +6 -0
  102. package/dist/core/hosts/types.d.ts.map +1 -1
  103. package/dist/core/hosts/types.js.map +1 -1
  104. package/dist/core/logger.d.ts +32 -0
  105. package/dist/core/logger.d.ts.map +1 -0
  106. package/dist/core/logger.js +188 -0
  107. package/dist/core/logger.js.map +1 -0
  108. package/dist/core/loop-match.d.ts +57 -0
  109. package/dist/core/loop-match.d.ts.map +1 -0
  110. package/dist/core/loop-match.js +91 -0
  111. package/dist/core/loop-match.js.map +1 -0
  112. package/dist/core/media/ffmpeg.d.ts.map +1 -1
  113. package/dist/core/media/ffmpeg.js +14 -3
  114. package/dist/core/media/ffmpeg.js.map +1 -1
  115. package/dist/core/multi-format.d.ts +67 -0
  116. package/dist/core/multi-format.d.ts.map +1 -0
  117. package/dist/core/multi-format.js +127 -0
  118. package/dist/core/multi-format.js.map +1 -0
  119. package/dist/core/multi-format.test.d.ts +2 -0
  120. package/dist/core/multi-format.test.d.ts.map +1 -0
  121. package/dist/core/multi-format.test.js +151 -0
  122. package/dist/core/multi-format.test.js.map +1 -0
  123. package/dist/core/python/beats.py +61 -0
  124. package/dist/core/python/face_reframe.py +163 -0
  125. package/dist/core/python/sidecar-path.d.ts +13 -0
  126. package/dist/core/python/sidecar-path.d.ts.map +1 -0
  127. package/dist/core/python/sidecar-path.js +24 -0
  128. package/dist/core/python/sidecar-path.js.map +1 -0
  129. package/dist/core/python.d.ts +57 -0
  130. package/dist/core/python.d.ts.map +1 -0
  131. package/dist/core/python.js +107 -0
  132. package/dist/core/python.js.map +1 -0
  133. package/dist/core/python.test.d.ts +2 -0
  134. package/dist/core/python.test.d.ts.map +1 -0
  135. package/dist/core/python.test.js +129 -0
  136. package/dist/core/python.test.js.map +1 -0
  137. package/dist/core/retention-structure.d.ts +81 -0
  138. package/dist/core/retention-structure.d.ts.map +1 -0
  139. package/dist/core/retention-structure.js +206 -0
  140. package/dist/core/retention-structure.js.map +1 -0
  141. package/dist/core/retention-structure.test.d.ts +2 -0
  142. package/dist/core/retention-structure.test.d.ts.map +1 -0
  143. package/dist/core/retention-structure.test.js +88 -0
  144. package/dist/core/retention-structure.test.js.map +1 -0
  145. package/dist/core/review.d.ts +17 -0
  146. package/dist/core/review.d.ts.map +1 -1
  147. package/dist/core/review.js +20 -24
  148. package/dist/core/review.js.map +1 -1
  149. package/dist/core/safe-paths.d.ts +11 -0
  150. package/dist/core/safe-paths.d.ts.map +1 -1
  151. package/dist/core/safe-paths.js +26 -10
  152. package/dist/core/safe-paths.js.map +1 -1
  153. package/dist/core/safe-paths.test.js +16 -0
  154. package/dist/core/safe-paths.test.js.map +1 -1
  155. package/dist/core/skills-loader.d.ts +48 -2
  156. package/dist/core/skills-loader.d.ts.map +1 -1
  157. package/dist/core/skills-loader.js +97 -19
  158. package/dist/core/skills-loader.js.map +1 -1
  159. package/dist/core/skills-loader.test.js +63 -1
  160. package/dist/core/skills-loader.test.js.map +1 -1
  161. package/dist/core/srt.d.ts +42 -7
  162. package/dist/core/srt.d.ts.map +1 -1
  163. package/dist/core/srt.js +101 -32
  164. package/dist/core/srt.js.map +1 -1
  165. package/dist/core/srt.test.js +54 -1
  166. package/dist/core/srt.test.js.map +1 -1
  167. package/dist/core/thumbnail-compose.d.ts +58 -0
  168. package/dist/core/thumbnail-compose.d.ts.map +1 -0
  169. package/dist/core/thumbnail-compose.js +101 -0
  170. package/dist/core/thumbnail-compose.js.map +1 -0
  171. package/dist/core/thumbnail-promise.d.ts +46 -0
  172. package/dist/core/thumbnail-promise.d.ts.map +1 -0
  173. package/dist/core/thumbnail-promise.js +133 -0
  174. package/dist/core/thumbnail-promise.js.map +1 -0
  175. package/dist/core/thumbnail-promise.test.d.ts +2 -0
  176. package/dist/core/thumbnail-promise.test.d.ts.map +1 -0
  177. package/dist/core/thumbnail-promise.test.js +52 -0
  178. package/dist/core/thumbnail-promise.test.js.map +1 -0
  179. package/dist/core/viral-moments.d.ts +70 -0
  180. package/dist/core/viral-moments.d.ts.map +1 -0
  181. package/dist/core/viral-moments.js +192 -0
  182. package/dist/core/viral-moments.js.map +1 -0
  183. package/dist/core/viral-moments.test.d.ts +2 -0
  184. package/dist/core/viral-moments.test.d.ts.map +1 -0
  185. package/dist/core/viral-moments.test.js +153 -0
  186. package/dist/core/viral-moments.test.js.map +1 -0
  187. package/dist/core/whisper.d.ts +16 -0
  188. package/dist/core/whisper.d.ts.map +1 -1
  189. package/dist/core/whisper.js +72 -5
  190. package/dist/core/whisper.js.map +1 -1
  191. package/dist/core/whisper.test.js +111 -1
  192. package/dist/core/whisper.test.js.map +1 -1
  193. package/dist/core/youtube-metadata.d.ts +44 -0
  194. package/dist/core/youtube-metadata.d.ts.map +1 -0
  195. package/dist/core/youtube-metadata.js +168 -0
  196. package/dist/core/youtube-metadata.js.map +1 -0
  197. package/dist/core/youtube-metadata.test.d.ts +2 -0
  198. package/dist/core/youtube-metadata.test.d.ts.map +1 -0
  199. package/dist/core/youtube-metadata.test.js +132 -0
  200. package/dist/core/youtube-metadata.test.js.map +1 -0
  201. package/dist/prompt-commands.d.ts +24 -0
  202. package/dist/prompt-commands.d.ts.map +1 -0
  203. package/dist/prompt-commands.js +243 -0
  204. package/dist/prompt-commands.js.map +1 -0
  205. package/dist/prompt-commands.test.d.ts +2 -0
  206. package/dist/prompt-commands.test.d.ts.map +1 -0
  207. package/dist/prompt-commands.test.js +46 -0
  208. package/dist/prompt-commands.test.js.map +1 -0
  209. package/dist/skills.d.ts +6 -6
  210. package/dist/skills.d.ts.map +1 -1
  211. package/dist/skills.js +1426 -445
  212. package/dist/skills.js.map +1 -1
  213. package/dist/system-prompt.d.ts.map +1 -1
  214. package/dist/system-prompt.js +108 -0
  215. package/dist/system-prompt.js.map +1 -1
  216. package/dist/tools/add-fades.d.ts.map +1 -1
  217. package/dist/tools/add-fades.js +2 -1
  218. package/dist/tools/add-fades.js.map +1 -1
  219. package/dist/tools/add-sfx-at-cuts.d.ts.map +1 -1
  220. package/dist/tools/add-sfx-at-cuts.js +36 -11
  221. package/dist/tools/add-sfx-at-cuts.js.map +1 -1
  222. package/dist/tools/add-sfx-to-timeline.d.ts +34 -0
  223. package/dist/tools/add-sfx-to-timeline.d.ts.map +1 -0
  224. package/dist/tools/add-sfx-to-timeline.js +169 -0
  225. package/dist/tools/add-sfx-to-timeline.js.map +1 -0
  226. package/dist/tools/add-sfx-to-timeline.test.d.ts +2 -0
  227. package/dist/tools/add-sfx-to-timeline.test.d.ts.map +1 -0
  228. package/dist/tools/add-sfx-to-timeline.test.js +181 -0
  229. package/dist/tools/add-sfx-to-timeline.test.js.map +1 -0
  230. package/dist/tools/audit-first-frame.d.ts +36 -0
  231. package/dist/tools/audit-first-frame.d.ts.map +1 -0
  232. package/dist/tools/audit-first-frame.js +181 -0
  233. package/dist/tools/audit-first-frame.js.map +1 -0
  234. package/dist/tools/audit-retention-structure.d.ts +20 -0
  235. package/dist/tools/audit-retention-structure.d.ts.map +1 -0
  236. package/dist/tools/audit-retention-structure.js +95 -0
  237. package/dist/tools/audit-retention-structure.js.map +1 -0
  238. package/dist/tools/audit-retention-structure.test.d.ts +2 -0
  239. package/dist/tools/audit-retention-structure.test.d.ts.map +1 -0
  240. package/dist/tools/audit-retention-structure.test.js +93 -0
  241. package/dist/tools/audit-retention-structure.test.js.map +1 -0
  242. package/dist/tools/bleep-words.d.ts +59 -0
  243. package/dist/tools/bleep-words.d.ts.map +1 -0
  244. package/dist/tools/bleep-words.js +211 -0
  245. package/dist/tools/bleep-words.js.map +1 -0
  246. package/dist/tools/bleep-words.test.d.ts +2 -0
  247. package/dist/tools/bleep-words.test.d.ts.map +1 -0
  248. package/dist/tools/bleep-words.test.js +96 -0
  249. package/dist/tools/bleep-words.test.js.map +1 -0
  250. package/dist/tools/burn-subtitles.d.ts.map +1 -1
  251. package/dist/tools/burn-subtitles.js +10 -5
  252. package/dist/tools/burn-subtitles.js.map +1 -1
  253. package/dist/tools/clean-audio.d.ts.map +1 -1
  254. package/dist/tools/clean-audio.js +2 -1
  255. package/dist/tools/clean-audio.js.map +1 -1
  256. package/dist/tools/cluster-takes.js +2 -1
  257. package/dist/tools/cluster-takes.js.map +1 -1
  258. package/dist/tools/compose-thumbnail-variants.d.ts +70 -0
  259. package/dist/tools/compose-thumbnail-variants.d.ts.map +1 -0
  260. package/dist/tools/compose-thumbnail-variants.js +274 -0
  261. package/dist/tools/compose-thumbnail-variants.js.map +1 -0
  262. package/dist/tools/compose-thumbnail.d.ts +6 -13
  263. package/dist/tools/compose-thumbnail.d.ts.map +1 -1
  264. package/dist/tools/compose-thumbnail.js +44 -81
  265. package/dist/tools/compose-thumbnail.js.map +1 -1
  266. package/dist/tools/concat-videos.d.ts.map +1 -1
  267. package/dist/tools/concat-videos.js +12 -5
  268. package/dist/tools/concat-videos.js.map +1 -1
  269. package/dist/tools/concat-videos.test.d.ts +2 -0
  270. package/dist/tools/concat-videos.test.d.ts.map +1 -0
  271. package/dist/tools/concat-videos.test.js +103 -0
  272. package/dist/tools/concat-videos.test.js.map +1 -0
  273. package/dist/tools/crossfade-videos.d.ts.map +1 -1
  274. package/dist/tools/crossfade-videos.js +2 -1
  275. package/dist/tools/crossfade-videos.js.map +1 -1
  276. package/dist/tools/cut-filler-words.d.ts.map +1 -1
  277. package/dist/tools/cut-filler-words.js +24 -8
  278. package/dist/tools/cut-filler-words.js.map +1 -1
  279. package/dist/tools/detect-speaker-changes.js +2 -1
  280. package/dist/tools/detect-speaker-changes.js.map +1 -1
  281. package/dist/tools/extract-audio.d.ts.map +1 -1
  282. package/dist/tools/extract-audio.js +13 -7
  283. package/dist/tools/extract-audio.js.map +1 -1
  284. package/dist/tools/face-reframe.d.ts +30 -0
  285. package/dist/tools/face-reframe.d.ts.map +1 -0
  286. package/dist/tools/face-reframe.js +143 -0
  287. package/dist/tools/face-reframe.js.map +1 -0
  288. package/dist/tools/face-reframe.test.d.ts +2 -0
  289. package/dist/tools/face-reframe.test.d.ts.map +1 -0
  290. package/dist/tools/face-reframe.test.js +139 -0
  291. package/dist/tools/face-reframe.test.js.map +1 -0
  292. package/dist/tools/find-viral-moments.d.ts +23 -0
  293. package/dist/tools/find-viral-moments.d.ts.map +1 -0
  294. package/dist/tools/find-viral-moments.js +176 -0
  295. package/dist/tools/find-viral-moments.js.map +1 -0
  296. package/dist/tools/find-viral-moments.test.d.ts +2 -0
  297. package/dist/tools/find-viral-moments.test.d.ts.map +1 -0
  298. package/dist/tools/find-viral-moments.test.js +144 -0
  299. package/dist/tools/find-viral-moments.test.js.map +1 -0
  300. package/dist/tools/generate-gif.d.ts.map +1 -1
  301. package/dist/tools/generate-gif.js +47 -40
  302. package/dist/tools/generate-gif.js.map +1 -1
  303. package/dist/tools/generate-gif.test.d.ts +2 -0
  304. package/dist/tools/generate-gif.test.d.ts.map +1 -0
  305. package/dist/tools/generate-gif.test.js +115 -0
  306. package/dist/tools/generate-gif.test.js.map +1 -0
  307. package/dist/tools/generate-outro.d.ts +18 -0
  308. package/dist/tools/generate-outro.d.ts.map +1 -0
  309. package/dist/tools/generate-outro.js +175 -0
  310. package/dist/tools/generate-outro.js.map +1 -0
  311. package/dist/tools/generate-youtube-metadata.d.ts +23 -0
  312. package/dist/tools/generate-youtube-metadata.d.ts.map +1 -0
  313. package/dist/tools/generate-youtube-metadata.js +103 -0
  314. package/dist/tools/generate-youtube-metadata.js.map +1 -0
  315. package/dist/tools/generate-youtube-metadata.test.d.ts +2 -0
  316. package/dist/tools/generate-youtube-metadata.test.d.ts.map +1 -0
  317. package/dist/tools/generate-youtube-metadata.test.js +118 -0
  318. package/dist/tools/generate-youtube-metadata.test.js.map +1 -0
  319. package/dist/tools/index.d.ts +14 -0
  320. package/dist/tools/index.d.ts.map +1 -1
  321. package/dist/tools/index.js +130 -1
  322. package/dist/tools/index.js.map +1 -1
  323. package/dist/tools/index.test.js +27 -1
  324. package/dist/tools/index.test.js.map +1 -1
  325. package/dist/tools/ken-burns.d.ts.map +1 -1
  326. package/dist/tools/ken-burns.js +2 -1
  327. package/dist/tools/ken-burns.js.map +1 -1
  328. package/dist/tools/loop-match-short.d.ts +22 -0
  329. package/dist/tools/loop-match-short.d.ts.map +1 -0
  330. package/dist/tools/loop-match-short.js +107 -0
  331. package/dist/tools/loop-match-short.js.map +1 -0
  332. package/dist/tools/mix-audio.d.ts.map +1 -1
  333. package/dist/tools/mix-audio.js +2 -1
  334. package/dist/tools/mix-audio.js.map +1 -1
  335. package/dist/tools/normalize-loudness.d.ts.map +1 -1
  336. package/dist/tools/normalize-loudness.js +2 -1
  337. package/dist/tools/normalize-loudness.js.map +1 -1
  338. package/dist/tools/path-traversal.test.d.ts +15 -0
  339. package/dist/tools/path-traversal.test.d.ts.map +1 -0
  340. package/dist/tools/path-traversal.test.js +223 -0
  341. package/dist/tools/path-traversal.test.js.map +1 -0
  342. package/dist/tools/pick-best-takes.js +2 -1
  343. package/dist/tools/pick-best-takes.js.map +1 -1
  344. package/dist/tools/punch-in.d.ts.map +1 -1
  345. package/dist/tools/punch-in.js +2 -1
  346. package/dist/tools/punch-in.js.map +1 -1
  347. package/dist/tools/read-transcript.js +2 -1
  348. package/dist/tools/read-transcript.js.map +1 -1
  349. package/dist/tools/render-multi-format.d.ts +35 -0
  350. package/dist/tools/render-multi-format.d.ts.map +1 -0
  351. package/dist/tools/render-multi-format.js +206 -0
  352. package/dist/tools/render-multi-format.js.map +1 -0
  353. package/dist/tools/render-multi-format.test.d.ts +2 -0
  354. package/dist/tools/render-multi-format.test.d.ts.map +1 -0
  355. package/dist/tools/render-multi-format.test.js +312 -0
  356. package/dist/tools/render-multi-format.test.js.map +1 -0
  357. package/dist/tools/render.d.ts.map +1 -1
  358. package/dist/tools/render.js +2 -2
  359. package/dist/tools/render.js.map +1 -1
  360. package/dist/tools/rewrite-hook.d.ts +32 -0
  361. package/dist/tools/rewrite-hook.d.ts.map +1 -0
  362. package/dist/tools/rewrite-hook.js +65 -0
  363. package/dist/tools/rewrite-hook.js.map +1 -0
  364. package/dist/tools/score-clip.d.ts +30 -0
  365. package/dist/tools/score-clip.d.ts.map +1 -0
  366. package/dist/tools/score-clip.js +109 -0
  367. package/dist/tools/score-clip.js.map +1 -0
  368. package/dist/tools/score-clip.test.d.ts +2 -0
  369. package/dist/tools/score-clip.test.d.ts.map +1 -0
  370. package/dist/tools/score-clip.test.js +110 -0
  371. package/dist/tools/score-clip.test.js.map +1 -0
  372. package/dist/tools/search-tools.d.ts +34 -0
  373. package/dist/tools/search-tools.d.ts.map +1 -0
  374. package/dist/tools/search-tools.js +86 -0
  375. package/dist/tools/search-tools.js.map +1 -0
  376. package/dist/tools/search-tools.test.d.ts +2 -0
  377. package/dist/tools/search-tools.test.d.ts.map +1 -0
  378. package/dist/tools/search-tools.test.js +60 -0
  379. package/dist/tools/search-tools.test.js.map +1 -0
  380. package/dist/tools/snap-cuts-to-beats.d.ts +18 -0
  381. package/dist/tools/snap-cuts-to-beats.d.ts.map +1 -0
  382. package/dist/tools/snap-cuts-to-beats.js +110 -0
  383. package/dist/tools/snap-cuts-to-beats.js.map +1 -0
  384. package/dist/tools/snap-cuts-to-beats.test.d.ts +2 -0
  385. package/dist/tools/snap-cuts-to-beats.test.d.ts.map +1 -0
  386. package/dist/tools/snap-cuts-to-beats.test.js +99 -0
  387. package/dist/tools/snap-cuts-to-beats.test.js.map +1 -0
  388. package/dist/tools/speed-ramp.d.ts.map +1 -1
  389. package/dist/tools/speed-ramp.js +2 -1
  390. package/dist/tools/speed-ramp.js.map +1 -1
  391. package/dist/tools/stabilize-video.d.ts.map +1 -1
  392. package/dist/tools/stabilize-video.js +2 -1
  393. package/dist/tools/stabilize-video.js.map +1 -1
  394. package/dist/tools/suggest-broll.d.ts +34 -0
  395. package/dist/tools/suggest-broll.d.ts.map +1 -0
  396. package/dist/tools/suggest-broll.js +367 -0
  397. package/dist/tools/suggest-broll.js.map +1 -0
  398. package/dist/tools/suggest-broll.test.d.ts +2 -0
  399. package/dist/tools/suggest-broll.test.d.ts.map +1 -0
  400. package/dist/tools/suggest-broll.test.js +217 -0
  401. package/dist/tools/suggest-broll.test.js.map +1 -0
  402. package/dist/tools/text-based-cut.d.ts +33 -0
  403. package/dist/tools/text-based-cut.d.ts.map +1 -0
  404. package/dist/tools/text-based-cut.js +172 -0
  405. package/dist/tools/text-based-cut.js.map +1 -0
  406. package/dist/tools/text-based-cut.test.d.ts +2 -0
  407. package/dist/tools/text-based-cut.test.d.ts.map +1 -0
  408. package/dist/tools/text-based-cut.test.js +32 -0
  409. package/dist/tools/text-based-cut.test.js.map +1 -0
  410. package/dist/tools/transcribe.d.ts +1 -1
  411. package/dist/tools/transition-videos.d.ts +1 -1
  412. package/dist/tools/transition-videos.d.ts.map +1 -1
  413. package/dist/tools/transition-videos.js +2 -1
  414. package/dist/tools/transition-videos.js.map +1 -1
  415. package/dist/tools/trim-dead-air.d.ts +59 -0
  416. package/dist/tools/trim-dead-air.d.ts.map +1 -0
  417. package/dist/tools/trim-dead-air.js +215 -0
  418. package/dist/tools/trim-dead-air.js.map +1 -0
  419. package/dist/tools/trim-dead-air.test.d.ts +2 -0
  420. package/dist/tools/trim-dead-air.test.d.ts.map +1 -0
  421. package/dist/tools/trim-dead-air.test.js +75 -0
  422. package/dist/tools/trim-dead-air.test.js.map +1 -0
  423. package/dist/tools/verify-thumbnail-promise.d.ts +33 -0
  424. package/dist/tools/verify-thumbnail-promise.d.ts.map +1 -0
  425. package/dist/tools/verify-thumbnail-promise.js +112 -0
  426. package/dist/tools/verify-thumbnail-promise.js.map +1 -0
  427. package/dist/tools/verify-thumbnail-promise.test.d.ts +2 -0
  428. package/dist/tools/verify-thumbnail-promise.test.d.ts.map +1 -0
  429. package/dist/tools/verify-thumbnail-promise.test.js +38 -0
  430. package/dist/tools/verify-thumbnail-promise.test.js.map +1 -0
  431. package/dist/tools/write-keyword-captions.d.ts +7 -0
  432. package/dist/tools/write-keyword-captions.d.ts.map +1 -1
  433. package/dist/tools/write-keyword-captions.js +35 -4
  434. package/dist/tools/write-keyword-captions.js.map +1 -1
  435. package/dist/ui/App.d.ts.map +1 -1
  436. package/dist/ui/App.js +75 -11
  437. package/dist/ui/App.js.map +1 -1
  438. package/dist/ui/tool-formatters.d.ts +30 -0
  439. package/dist/ui/tool-formatters.d.ts.map +1 -0
  440. package/dist/ui/tool-formatters.js +461 -0
  441. package/dist/ui/tool-formatters.js.map +1 -0
  442. package/dist/ui/tool-formatters.test.d.ts +2 -0
  443. package/dist/ui/tool-formatters.test.d.ts.map +1 -0
  444. package/dist/ui/tool-formatters.test.js +143 -0
  445. package/dist/ui/tool-formatters.test.js.map +1 -0
  446. package/package.json +10 -9
@@ -0,0 +1,188 @@
1
+ /**
2
+ * gg-editor debug logger — optimized for LLM consumption.
3
+ *
4
+ * Writes to `~/.gg/ggeditor.log`. Append mode across sessions, rotated at
5
+ * MAX_BYTES, one generation kept (`ggeditor.log.1`).
6
+ *
7
+ * Format choices (every byte fed to an LLM matters):
8
+ * - Per-session HEADER carries the rarely-changing fields (date, sid,
9
+ * version, provider, model, pid, platform). Lines below reference them
10
+ * implicitly so we don't repeat ~80 bytes on every line.
11
+ * - Each line: `HH:MM:SS.mmm L cat msg k=v k=v`
12
+ * L — single-letter level (I/W/E). Saves 3-4 bytes/line vs. INFO/WARN/ERROR.
13
+ * cat — short stable category (tool, bridge, bridge.call, host, …).
14
+ * msg — short imperative phrase, no decorative arrows.
15
+ * - Field values JSON-encoded only when they need quoting; bare otherwise.
16
+ * - Args/results truncated at MAX_FIELD_CHARS with `…(+N)` overflow marker
17
+ * so the LLM sees the prefix + how much was elided.
18
+ *
19
+ * Categories:
20
+ * startup CLI launch line + capability probe result
21
+ * bridge Resolve Python bridge spawn/handshake/exit
22
+ * bridge.call Per-method round trips (one line per call: name + dur + ok/err)
23
+ * tool Agent tool calls (one line per call: name + dur + args + result)
24
+ * host Lazy host detection transitions
25
+ * shutdown Clean exit
26
+ * fatal Unhandled errors
27
+ *
28
+ * Filter a single run: `grep " sid=<id>" ~/.gg/ggeditor.log` once you've
29
+ * read the header. Multi-run greps key off the category column.
30
+ */
31
+ import fs from "node:fs";
32
+ import { mkdirSync } from "node:fs";
33
+ import { homedir } from "node:os";
34
+ import { dirname, join } from "node:path";
35
+ import { randomBytes } from "node:crypto";
36
+ const MAX_BYTES = 10 * 1024 * 1024; // 10 MB
37
+ const MAX_FIELD_CHARS = 200; // Per k=v value cap. Args/results truncated above this.
38
+ let fd = null;
39
+ let sessionId = "";
40
+ /** Default path: `~/.gg/ggeditor.log`. */
41
+ export function defaultLogPath() {
42
+ return join(homedir(), ".gg", "ggeditor.log");
43
+ }
44
+ function rotateIfNeeded(filePath) {
45
+ try {
46
+ const st = fs.statSync(filePath);
47
+ if (st.size < MAX_BYTES)
48
+ return;
49
+ const rotated = `${filePath}.1`;
50
+ try {
51
+ fs.unlinkSync(rotated);
52
+ }
53
+ catch {
54
+ // No prior rotation
55
+ }
56
+ fs.renameSync(filePath, rotated);
57
+ }
58
+ catch {
59
+ // Log file doesn't exist yet
60
+ }
61
+ }
62
+ /** Pad to 2/3 chars with leading zeros for stable column widths. */
63
+ function pad(n, w) {
64
+ return String(n).padStart(w, "0");
65
+ }
66
+ function timestamp() {
67
+ const d = new Date();
68
+ return `${pad(d.getHours(), 2)}:${pad(d.getMinutes(), 2)}:${pad(d.getSeconds(), 2)}.${pad(d.getMilliseconds(), 3)}`;
69
+ }
70
+ /**
71
+ * Initialize logger. Idempotent. Writes a session HEADER line with the
72
+ * fields that don't change across the run, so per-line overhead stays low.
73
+ */
74
+ export function initLogger(filePath = defaultLogPath(), meta) {
75
+ if (fd !== null)
76
+ return;
77
+ try {
78
+ mkdirSync(dirname(filePath), { recursive: true });
79
+ }
80
+ catch {
81
+ // openSync below will surface real failures
82
+ }
83
+ rotateIfNeeded(filePath);
84
+ try {
85
+ fd = fs.openSync(filePath, "a");
86
+ }
87
+ catch {
88
+ return;
89
+ }
90
+ sessionId = randomBytes(4).toString("hex");
91
+ // Session header — all per-run constants in one line. Bash/awk friendly,
92
+ // human-readable, LLM-parseable. Subsequent lines don't repeat any of this.
93
+ const headerParts = [
94
+ `=== ${new Date().toISOString()}`,
95
+ `sid=${sessionId}`,
96
+ `ggeditor=${meta?.version ?? "?"}`,
97
+ ];
98
+ if (meta?.provider)
99
+ headerParts.push(`provider=${meta.provider}`);
100
+ if (meta?.model)
101
+ headerParts.push(`model=${meta.model}`);
102
+ if (meta?.host)
103
+ headerParts.push(`host=${meta.host}`);
104
+ headerParts.push(`pid=${process.pid}`, `platform=${process.platform}`, "===");
105
+ try {
106
+ fs.writeSync(fd, "\n" + headerParts.join(" ") + "\n");
107
+ }
108
+ catch {
109
+ // Header write failed — proceed; subsequent lines may still land
110
+ }
111
+ }
112
+ /** Session ID — useful for filtering one run out of an interleaved log. */
113
+ export function getSessionId() {
114
+ return sessionId;
115
+ }
116
+ /** Hot-path skip flag for callers that want to avoid building log payloads. */
117
+ export function isLoggerActive() {
118
+ return fd !== null;
119
+ }
120
+ /**
121
+ * Write a log line. Format: `HH:MM:SS.mmm L cat msg k=v k=v`.
122
+ *
123
+ * Values that contain spaces or `=` are JSON-quoted; bare otherwise. Long
124
+ * fields are truncated at MAX_FIELD_CHARS with `…(+N)` so the LLM sees the
125
+ * prefix plus a count of elided chars (rather than a silent cliff).
126
+ */
127
+ export function log(level, category, message, data) {
128
+ if (fd === null)
129
+ return;
130
+ let line = `${timestamp()} ${level} ${category} ${message}`;
131
+ if (data) {
132
+ for (const [k, v] of Object.entries(data)) {
133
+ if (v === undefined || v === null)
134
+ continue;
135
+ line += ` ${k}=${formatValue(v)}`;
136
+ }
137
+ }
138
+ line += "\n";
139
+ try {
140
+ fs.writeSync(fd, line);
141
+ }
142
+ catch {
143
+ // Don't crash the CLI on a write failure
144
+ }
145
+ }
146
+ function formatValue(v) {
147
+ let s;
148
+ if (typeof v === "string") {
149
+ s = v;
150
+ }
151
+ else if (typeof v === "number" || typeof v === "boolean") {
152
+ return String(v); // numerics/booleans never need quoting and never overflow
153
+ }
154
+ else {
155
+ try {
156
+ s = JSON.stringify(v);
157
+ }
158
+ catch {
159
+ s = String(v);
160
+ }
161
+ }
162
+ if (s.length > MAX_FIELD_CHARS) {
163
+ s = s.slice(0, MAX_FIELD_CHARS) + `…(+${s.length - MAX_FIELD_CHARS})`;
164
+ }
165
+ // Quote when the value has whitespace, `=`, or quotes — so grep/awk on
166
+ // ` k=` boundaries works reliably. Bare otherwise (saves 2 bytes).
167
+ if (/[\s="]/.test(s)) {
168
+ return JSON.stringify(s);
169
+ }
170
+ return s;
171
+ }
172
+ export const logInfo = (cat, msg, data) => log("I", cat, msg, data);
173
+ export const logWarn = (cat, msg, data) => log("W", cat, msg, data);
174
+ export const logError = (cat, msg, data) => log("E", cat, msg, data);
175
+ /** Close the log fd. Safe to call multiple times. */
176
+ export function closeLogger() {
177
+ if (fd === null)
178
+ return;
179
+ log("I", "shutdown", "exit");
180
+ try {
181
+ fs.closeSync(fd);
182
+ }
183
+ catch {
184
+ // Ignore
185
+ }
186
+ fd = null;
187
+ }
188
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Loop-match filter builder for Shorts.
3
+ *
4
+ * YouTube Shorts auto-replays from the start when the clip ends. A
5
+ * crossfade between the last N ms and the first N ms makes the
6
+ * re-loop feel seamless. Loop rate is a confirmed Shorts ranking
7
+ * signal — see the `youtube-algorithm-primer` skill.
8
+ *
9
+ * This module produces the `-filter_complex` string and the matching
10
+ * `-map` directives. The wrapper tool then runs ffmpeg with libx264 +
11
+ * aac at standard delivery settings.
12
+ *
13
+ * Two modes:
14
+ * - "crossfade" (default): xfade fade between the tail and a copy of
15
+ * the head. Audio: acrossfade.
16
+ * - "jumpcut": no crossfade — simply hard-cut the last cf seconds off
17
+ * so the loop boundary lands right before the head's first frame.
18
+ * Cheaper; safe for very short clips where any crossfade would eat
19
+ * too much of the visible content.
20
+ */
21
+ export type LoopMatchMethod = "crossfade" | "jumpcut";
22
+ export interface LoopMatchPlan {
23
+ /** Effective crossfade duration after clamping. */
24
+ crossfadeSec: number;
25
+ /** Final output duration after the filter applies. */
26
+ outDurationSec: number;
27
+ method: LoopMatchMethod;
28
+ /** ffmpeg -filter_complex value. */
29
+ filter: string;
30
+ /** -map directives in order. */
31
+ maps: string[];
32
+ }
33
+ /**
34
+ * Build the filter graph for a loop-match pass.
35
+ *
36
+ * crossfade mode plan:
37
+ * [0:v] split=2 [main][tail_src];
38
+ * [main] trim=0:T-cf, setpts=PTS-STARTPTS [vmain];
39
+ * [tail_src] trim=T-cf:T, setpts=PTS-STARTPTS [tail];
40
+ * [0:v] trim=0:cf, setpts=PTS-STARTPTS [head];
41
+ * [tail][head] xfade=transition=fade:duration=cf:offset=0 [xfade];
42
+ * [vmain][xfade] concat=n=2:v=1:a=0 [vout]
43
+ * ── audio ──
44
+ * [0:a] asplit=2 [amain][atail_src];
45
+ * [amain] atrim=0:T-cf, asetpts=PTS-STARTPTS [aout1];
46
+ * [atail_src] atrim=T-cf:T, asetpts=PTS-STARTPTS [atail];
47
+ * [0:a] atrim=0:cf, asetpts=PTS-STARTPTS [ahead];
48
+ * [atail][ahead] acrossfade=d=cf [axfade];
49
+ * [aout1][axfade] concat=n=2:v=0:a=1 [aout]
50
+ *
51
+ * Final length = (T - cf) + cf = T. Duration unchanged; only the last
52
+ * cf seconds become a crossfade into the head.
53
+ *
54
+ * Pure — easy to unit-test the filter string. Throws on invalid input.
55
+ */
56
+ export declare function buildLoopMatchFilter(durationSec: number, crossfadeSec: number, method?: LoopMatchMethod): LoopMatchPlan;
57
+ //# sourceMappingURL=loop-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-match.d.ts","sourceRoot":"","sources":["../../src/core/loop-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,SAAS,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;IACxB,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,MAAM,GAAE,eAA6B,GACpC,aAAa,CAqDf"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Loop-match filter builder for Shorts.
3
+ *
4
+ * YouTube Shorts auto-replays from the start when the clip ends. A
5
+ * crossfade between the last N ms and the first N ms makes the
6
+ * re-loop feel seamless. Loop rate is a confirmed Shorts ranking
7
+ * signal — see the `youtube-algorithm-primer` skill.
8
+ *
9
+ * This module produces the `-filter_complex` string and the matching
10
+ * `-map` directives. The wrapper tool then runs ffmpeg with libx264 +
11
+ * aac at standard delivery settings.
12
+ *
13
+ * Two modes:
14
+ * - "crossfade" (default): xfade fade between the tail and a copy of
15
+ * the head. Audio: acrossfade.
16
+ * - "jumpcut": no crossfade — simply hard-cut the last cf seconds off
17
+ * so the loop boundary lands right before the head's first frame.
18
+ * Cheaper; safe for very short clips where any crossfade would eat
19
+ * too much of the visible content.
20
+ */
21
+ /**
22
+ * Build the filter graph for a loop-match pass.
23
+ *
24
+ * crossfade mode plan:
25
+ * [0:v] split=2 [main][tail_src];
26
+ * [main] trim=0:T-cf, setpts=PTS-STARTPTS [vmain];
27
+ * [tail_src] trim=T-cf:T, setpts=PTS-STARTPTS [tail];
28
+ * [0:v] trim=0:cf, setpts=PTS-STARTPTS [head];
29
+ * [tail][head] xfade=transition=fade:duration=cf:offset=0 [xfade];
30
+ * [vmain][xfade] concat=n=2:v=1:a=0 [vout]
31
+ * ── audio ──
32
+ * [0:a] asplit=2 [amain][atail_src];
33
+ * [amain] atrim=0:T-cf, asetpts=PTS-STARTPTS [aout1];
34
+ * [atail_src] atrim=T-cf:T, asetpts=PTS-STARTPTS [atail];
35
+ * [0:a] atrim=0:cf, asetpts=PTS-STARTPTS [ahead];
36
+ * [atail][ahead] acrossfade=d=cf [axfade];
37
+ * [aout1][axfade] concat=n=2:v=0:a=1 [aout]
38
+ *
39
+ * Final length = (T - cf) + cf = T. Duration unchanged; only the last
40
+ * cf seconds become a crossfade into the head.
41
+ *
42
+ * Pure — easy to unit-test the filter string. Throws on invalid input.
43
+ */
44
+ export function buildLoopMatchFilter(durationSec, crossfadeSec, method = "crossfade") {
45
+ if (!Number.isFinite(durationSec) || durationSec <= 0) {
46
+ throw new Error("durationSec must be > 0");
47
+ }
48
+ if (!Number.isFinite(crossfadeSec) || crossfadeSec <= 0) {
49
+ throw new Error("crossfadeSec must be > 0");
50
+ }
51
+ // Crossfade can't be ≥ half the clip — there'd be no "main" body left.
52
+ const maxCf = durationSec / 2;
53
+ const cf = Math.min(crossfadeSec, +(maxCf - 0.05).toFixed(3));
54
+ if (cf <= 0) {
55
+ throw new Error(`clip too short (${durationSec.toFixed(2)}s) for any crossfade; pass crossfadeSec ≤ ${maxCf.toFixed(2)}`);
56
+ }
57
+ const T = +durationSec.toFixed(3);
58
+ const tailStart = +(T - cf).toFixed(3);
59
+ if (method === "jumpcut") {
60
+ // Just trim the last cf seconds. No xfade. Output is shorter.
61
+ const filter = `[0:v]trim=0:${tailStart},setpts=PTS-STARTPTS[vout];` +
62
+ `[0:a]atrim=0:${tailStart},asetpts=PTS-STARTPTS[aout]`;
63
+ return {
64
+ crossfadeSec: cf,
65
+ outDurationSec: tailStart,
66
+ method,
67
+ filter,
68
+ maps: ["[vout]", "[aout]"],
69
+ };
70
+ }
71
+ const filter = `[0:v]split=2[main][tail_src];` +
72
+ `[main]trim=0:${tailStart},setpts=PTS-STARTPTS[vmain];` +
73
+ `[tail_src]trim=${tailStart}:${T},setpts=PTS-STARTPTS[tail];` +
74
+ `[0:v]trim=0:${cf},setpts=PTS-STARTPTS[head];` +
75
+ `[tail][head]xfade=transition=fade:duration=${cf}:offset=0[xf];` +
76
+ `[vmain][xf]concat=n=2:v=1:a=0[vout];` +
77
+ `[0:a]asplit=2[amain][atail_src];` +
78
+ `[amain]atrim=0:${tailStart},asetpts=PTS-STARTPTS[aout1];` +
79
+ `[atail_src]atrim=${tailStart}:${T},asetpts=PTS-STARTPTS[atail];` +
80
+ `[0:a]atrim=0:${cf},asetpts=PTS-STARTPTS[ahead];` +
81
+ `[atail][ahead]acrossfade=d=${cf}[axf];` +
82
+ `[aout1][axf]concat=n=2:v=0:a=1[aout]`;
83
+ return {
84
+ crossfadeSec: cf,
85
+ outDurationSec: T,
86
+ method,
87
+ filter,
88
+ maps: ["[vout]", "[aout]"],
89
+ };
90
+ }
91
+ //# sourceMappingURL=loop-match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-match.js","sourceRoot":"","sources":["../../src/core/loop-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAgBH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,oBAAoB,CAClC,WAAmB,EACnB,YAAoB,EACpB,SAA0B,WAAW;IAErC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;IACD,uEAAuE;IACvE,MAAM,KAAK,GAAG,WAAW,GAAG,CAAC,CAAC;IAC9B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,mBAAmB,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,6CAA6C,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CACzG,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAEvC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,8DAA8D;QAC9D,MAAM,MAAM,GACV,eAAe,SAAS,6BAA6B;YACrD,gBAAgB,SAAS,6BAA6B,CAAC;QACzD,OAAO;YACL,YAAY,EAAE,EAAE;YAChB,cAAc,EAAE,SAAS;YACzB,MAAM;YACN,MAAM;YACN,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;SAC3B,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GACV,+BAA+B;QAC/B,gBAAgB,SAAS,8BAA8B;QACvD,kBAAkB,SAAS,IAAI,CAAC,6BAA6B;QAC7D,eAAe,EAAE,6BAA6B;QAC9C,8CAA8C,EAAE,gBAAgB;QAChE,sCAAsC;QACtC,kCAAkC;QAClC,kBAAkB,SAAS,+BAA+B;QAC1D,oBAAoB,SAAS,IAAI,CAAC,+BAA+B;QACjE,gBAAgB,EAAE,+BAA+B;QACjD,8BAA8B,EAAE,QAAQ;QACxC,sCAAsC,CAAC;IAEzC,OAAO;QACL,YAAY,EAAE,EAAE;QAChB,cAAc,EAAE,CAAC;QACjB,MAAM;QACN,MAAM;QACN,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;KAC3B,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ffmpeg.d.ts","sourceRoot":"","sources":["../../../src/core/media/ffmpeg.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,WAAW,IAAI,OAAO,CAGrC;AAED,wBAAgB,YAAY,IAAI,OAAO,CAGtC;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GAClC,OAAO,CAAC,YAAY,CAAC,CAavB;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA4C9D"}
1
+ {"version":3,"file":"ffmpeg.d.ts","sourceRoot":"","sources":["../../../src/core/media/ffmpeg.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,WAAW,IAAI,OAAO,CAGrC;AAED,wBAAgB,YAAY,IAAI,OAAO,CAGtC;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GAClC,OAAO,CAAC,YAAY,CAAC,CAuBvB;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA4C9D"}
@@ -1,4 +1,5 @@
1
1
  import { spawn, spawnSync } from "node:child_process";
2
+ import { wireChildAbort } from "../child-abort.js";
2
3
  export function checkFfmpeg() {
3
4
  const r = spawnSync("ffmpeg", ["-version"], { encoding: "utf8" });
4
5
  return r.status === 0;
@@ -16,9 +17,19 @@ export function runFfmpeg(args, opts = {}) {
16
17
  let stderr = "";
17
18
  child.stdout.on("data", (d) => (stdout += d.toString()));
18
19
  child.stderr.on("data", (d) => (stderr += d.toString()));
19
- opts.signal?.addEventListener("abort", () => child.kill("SIGTERM"));
20
- child.on("error", reject);
21
- child.on("close", (code) => resolve({ code: code ?? 1, stdout, stderr }));
20
+ // Robust abort: SIGTERM SIGKILL after 1.5s. ffmpeg respects SIGTERM
21
+ // quickly on its read loop; the SIGKILL backstop covers wedge cases.
22
+ // We resolve with a non-zero code on abort instead of rejecting because
23
+ // ffmpeg callers check `r.code !== 0` and surface a tailored error.
24
+ const cleanup = wireChildAbort(opts.signal, child);
25
+ child.on("error", (e) => {
26
+ cleanup();
27
+ reject(e);
28
+ });
29
+ child.on("close", (code) => {
30
+ cleanup();
31
+ resolve({ code: code ?? 1, stdout, stderr });
32
+ });
22
33
  });
23
34
  }
24
35
  export function probeMedia(filePath) {
@@ -1 +1 @@
1
- {"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../../src/core/media/ffmpeg.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../../src/core/media/ffmpeg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Multi-format render presets — the "render every platform at once" matrix.
3
+ *
4
+ * Pure dispatcher for `render_multi_format`: given a source resolution and a
5
+ * preset name, produces the ffmpeg `-vf` filter graph plus the target W×H.
6
+ * Spawning ffmpeg lives in the tool layer so this module stays trivially
7
+ * unit-testable.
8
+ *
9
+ * Two transform modes:
10
+ *
11
+ * - "scale-pad" — letterbox/pillarbox the source into the target frame
12
+ * without cropping. Used for 16:9-into-16:9 (different
13
+ * resolution) and any time we want to preserve every
14
+ * pixel of the source.
15
+ *
16
+ * - "centre-crop" — crop the source to the target aspect, then scale to
17
+ * target resolution. The dumb / mechanical version of
18
+ * content-aware reframing — for face-tracked re-cropping
19
+ * the agent should pipe the source through `face_reframe`
20
+ * or Resolve `smart_reframe` BEFORE calling this tool.
21
+ */
22
+ export type MultiFormat = "youtube-1080p" | "shorts-9x16" | "reels-9x16" | "tiktok-9x16" | "square-1x1" | "instagram-4x5" | "twitter-16x9";
23
+ export declare const MULTI_FORMATS: readonly MultiFormat[];
24
+ export type MultiFormatTransform = "scale-pad" | "centre-crop";
25
+ interface PresetSpec {
26
+ width: number;
27
+ height: number;
28
+ transform: MultiFormatTransform;
29
+ }
30
+ export declare function multiFormatSpec(format: MultiFormat): PresetSpec;
31
+ export interface BuildRenderFilterResult {
32
+ targetW: number;
33
+ targetH: number;
34
+ vf: string;
35
+ transform: MultiFormatTransform;
36
+ }
37
+ export interface BuildRenderFilterOptions {
38
+ /**
39
+ * If true, treat the source as already cropped to the target aspect (e.g.
40
+ * via face_reframe / smart_reframe upstream). Forces a non-cropping
41
+ * scale-pad regardless of the preset's default transform.
42
+ */
43
+ faceTracked?: boolean;
44
+ }
45
+ /**
46
+ * Build the ffmpeg `-vf` filter for a single (source, preset) pair.
47
+ *
48
+ * Pure function — no side effects, no I/O. Used by `render_multi_format`
49
+ * to compose ffmpeg args, and by the unit tests to lock the filter math.
50
+ *
51
+ * Math notes for centre-crop:
52
+ *
53
+ * srcAR = srcW/srcH, tgtAR = Wt/Ht. The crop keeps the larger axis full.
54
+ *
55
+ * - srcAR > tgtAR ⇒ source is wider than target. Crop width.
56
+ * cropW = ih * Wt/Ht ; cropH = ih ; x = (iw - cropW)/2 ; y = 0
57
+ * - srcAR < tgtAR ⇒ source is taller than target. Crop height.
58
+ * cropW = iw ; cropH = iw * Ht/Wt ; x = 0 ; y = (ih - cropH)/2
59
+ * - srcAR == tgtAR ⇒ no crop needed; emit `scale=Wt:Ht` only.
60
+ *
61
+ * Float crop dimensions are passed through to ffmpeg (which accepts them
62
+ * and rounds internally); the trailing `scale=Wt:Ht` enforces the final
63
+ * even-dimension target so x264 stays happy.
64
+ */
65
+ export declare function buildRenderFilter(srcW: number, srcH: number, format: MultiFormat, opts?: BuildRenderFilterOptions): BuildRenderFilterResult;
66
+ export {};
67
+ //# sourceMappingURL=multi-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-format.d.ts","sourceRoot":"","sources":["../../src/core/multi-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,aAAa,GACb,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,eAAe,GACf,cAAc,CAAC;AAEnB,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAQtC,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAAG,aAAa,CAAC;AAE/D,UAAU,UAAU;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAmBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU,CAQ/D;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAED,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EACnB,IAAI,GAAE,wBAA6B,GAClC,uBAAuB,CA0CzB"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Multi-format render presets — the "render every platform at once" matrix.
3
+ *
4
+ * Pure dispatcher for `render_multi_format`: given a source resolution and a
5
+ * preset name, produces the ffmpeg `-vf` filter graph plus the target W×H.
6
+ * Spawning ffmpeg lives in the tool layer so this module stays trivially
7
+ * unit-testable.
8
+ *
9
+ * Two transform modes:
10
+ *
11
+ * - "scale-pad" — letterbox/pillarbox the source into the target frame
12
+ * without cropping. Used for 16:9-into-16:9 (different
13
+ * resolution) and any time we want to preserve every
14
+ * pixel of the source.
15
+ *
16
+ * - "centre-crop" — crop the source to the target aspect, then scale to
17
+ * target resolution. The dumb / mechanical version of
18
+ * content-aware reframing — for face-tracked re-cropping
19
+ * the agent should pipe the source through `face_reframe`
20
+ * or Resolve `smart_reframe` BEFORE calling this tool.
21
+ */
22
+ export const MULTI_FORMATS = [
23
+ "youtube-1080p",
24
+ "shorts-9x16",
25
+ "reels-9x16",
26
+ "tiktok-9x16",
27
+ "square-1x1",
28
+ "instagram-4x5",
29
+ "twitter-16x9",
30
+ ];
31
+ /**
32
+ * Per-preset target resolution + default transform.
33
+ *
34
+ * Resolutions are platform-canonical: 1080p horizontal, 1080×1920 vertical,
35
+ * 1080-square IG, 1080×1350 IG-portrait, 720p Twitter (their feed downsamples
36
+ * anything taller anyway).
37
+ */
38
+ const PRESETS = {
39
+ "youtube-1080p": { width: 1920, height: 1080, transform: "scale-pad" },
40
+ "shorts-9x16": { width: 1080, height: 1920, transform: "centre-crop" },
41
+ "reels-9x16": { width: 1080, height: 1920, transform: "centre-crop" },
42
+ "tiktok-9x16": { width: 1080, height: 1920, transform: "centre-crop" },
43
+ "square-1x1": { width: 1080, height: 1080, transform: "centre-crop" },
44
+ "instagram-4x5": { width: 1080, height: 1350, transform: "centre-crop" },
45
+ "twitter-16x9": { width: 1280, height: 720, transform: "scale-pad" },
46
+ };
47
+ export function multiFormatSpec(format) {
48
+ const spec = PRESETS[format];
49
+ if (!spec) {
50
+ throw new Error(`unknown multi-format preset: ${format}; valid: ${Object.keys(PRESETS).join(", ")}`);
51
+ }
52
+ return spec;
53
+ }
54
+ /**
55
+ * Build the ffmpeg `-vf` filter for a single (source, preset) pair.
56
+ *
57
+ * Pure function — no side effects, no I/O. Used by `render_multi_format`
58
+ * to compose ffmpeg args, and by the unit tests to lock the filter math.
59
+ *
60
+ * Math notes for centre-crop:
61
+ *
62
+ * srcAR = srcW/srcH, tgtAR = Wt/Ht. The crop keeps the larger axis full.
63
+ *
64
+ * - srcAR > tgtAR ⇒ source is wider than target. Crop width.
65
+ * cropW = ih * Wt/Ht ; cropH = ih ; x = (iw - cropW)/2 ; y = 0
66
+ * - srcAR < tgtAR ⇒ source is taller than target. Crop height.
67
+ * cropW = iw ; cropH = iw * Ht/Wt ; x = 0 ; y = (ih - cropH)/2
68
+ * - srcAR == tgtAR ⇒ no crop needed; emit `scale=Wt:Ht` only.
69
+ *
70
+ * Float crop dimensions are passed through to ffmpeg (which accepts them
71
+ * and rounds internally); the trailing `scale=Wt:Ht` enforces the final
72
+ * even-dimension target so x264 stays happy.
73
+ */
74
+ export function buildRenderFilter(srcW, srcH, format, opts = {}) {
75
+ if (!Number.isFinite(srcW) || srcW <= 0 || !Number.isFinite(srcH) || srcH <= 0) {
76
+ throw new Error(`invalid source dimensions: ${srcW}x${srcH}`);
77
+ }
78
+ const spec = multiFormatSpec(format);
79
+ const targetW = spec.width;
80
+ const targetH = spec.height;
81
+ const transform = opts.faceTracked ? "scale-pad" : spec.transform;
82
+ if (transform === "scale-pad") {
83
+ const vf = `scale=${targetW}:${targetH}:force_original_aspect_ratio=decrease,` +
84
+ `pad=${targetW}:${targetH}:(ow-iw)/2:(oh-ih)/2:color=black`;
85
+ return { targetW, targetH, vf, transform };
86
+ }
87
+ // centre-crop
88
+ const srcAR = srcW / srcH;
89
+ const tgtAR = targetW / targetH;
90
+ // Use a tiny tolerance so 1920x1080 vs perfectly-equal 16:9 sources round
91
+ // to the no-crop branch; prevents emitting `crop=iw:ih:0:0`.
92
+ const eps = 1e-6;
93
+ if (Math.abs(srcAR - tgtAR) < eps) {
94
+ return {
95
+ targetW,
96
+ targetH,
97
+ vf: `scale=${targetW}:${targetH}`,
98
+ transform,
99
+ };
100
+ }
101
+ if (srcAR > tgtAR) {
102
+ // crop width
103
+ const cropW = formatNum((srcH * targetW) / targetH);
104
+ const x = formatNum((srcW - (srcH * targetW) / targetH) / 2);
105
+ const vf = `crop=${cropW}:${srcH}:${x}:0,scale=${targetW}:${targetH}`;
106
+ return { targetW, targetH, vf, transform };
107
+ }
108
+ // srcAR < tgtAR — crop height
109
+ const cropH = formatNum((srcW * targetH) / targetW);
110
+ const y = formatNum((srcH - (srcW * targetH) / targetW) / 2);
111
+ const vf = `crop=${srcW}:${cropH}:0:${y},scale=${targetW}:${targetH}`;
112
+ return { targetW, targetH, vf, transform };
113
+ }
114
+ /**
115
+ * Format a number for inclusion in an ffmpeg filter expression. Trims any
116
+ * `.0` tail (e.g. 1080.0 → "1080") so integer paths remain integer-looking
117
+ * in the filter graph; otherwise emits up to 2 decimals.
118
+ */
119
+ function formatNum(n) {
120
+ if (Number.isInteger(n))
121
+ return String(n);
122
+ // Two decimals is enough for any practical (W, H) from canonical sources;
123
+ // the trailing `scale` rounds to the final integer target.
124
+ const rounded = Math.round(n * 100) / 100;
125
+ return Number.isInteger(rounded) ? String(rounded) : String(rounded);
126
+ }
127
+ //# sourceMappingURL=multi-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-format.js","sourceRoot":"","sources":["../../src/core/multi-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAWH,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,eAAe;IACf,aAAa;IACb,YAAY;IACZ,aAAa;IACb,YAAY;IACZ,eAAe;IACf,cAAc;CACN,CAAC;AAUX;;;;;;GAMG;AACH,MAAM,OAAO,GAAoC;IAC/C,eAAe,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE;IACtE,aAAa,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE;IACtE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE;IACrE,aAAa,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE;IACtE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE;IACrE,eAAe,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE;IACxE,cAAc,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE;CACrE,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,MAAmB;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,gCAAgC,MAAM,YAAY,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACpF,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAkBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,IAAY,EACZ,MAAmB,EACnB,OAAiC,EAAE;IAEnC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QAC/E,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC;IAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,MAAM,SAAS,GAAyB,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;IAExF,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;QAC9B,MAAM,EAAE,GACN,SAAS,OAAO,IAAI,OAAO,wCAAwC;YACnE,OAAO,OAAO,IAAI,OAAO,kCAAkC,CAAC;QAC9D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC;IAC7C,CAAC;IAED,cAAc;IACd,MAAM,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,MAAM,KAAK,GAAG,OAAO,GAAG,OAAO,CAAC;IAChC,0EAA0E;IAC1E,6DAA6D;IAC7D,MAAM,GAAG,GAAG,IAAI,CAAC;IACjB,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC;QAClC,OAAO;YACL,OAAO;YACP,OAAO;YACP,EAAE,EAAE,SAAS,OAAO,IAAI,OAAO,EAAE;YACjC,SAAS;SACV,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,GAAG,KAAK,EAAE,CAAC;QAClB,aAAa;QACb,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7D,MAAM,EAAE,GAAG,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,YAAY,OAAO,IAAI,OAAO,EAAE,CAAC;QACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC;IAC7C,CAAC;IACD,8BAA8B;IAC9B,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,MAAM,EAAE,GAAG,QAAQ,IAAI,IAAI,KAAK,MAAM,CAAC,UAAU,OAAO,IAAI,OAAO,EAAE,CAAC;IACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,CAAS;IAC1B,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1C,0EAA0E;IAC1E,2DAA2D;IAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAC1C,OAAO,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=multi-format.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-format.test.d.ts","sourceRoot":"","sources":["../../src/core/multi-format.test.ts"],"names":[],"mappings":""}