@kenkaiiii/gg-editor 0.7.0 → 0.7.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.
- package/dist/cli.js +64 -1
- package/dist/cli.js.map +1 -1
- package/dist/core/audio-mix.d.ts.map +1 -1
- package/dist/core/audio-mix.js +8 -1
- package/dist/core/audio-mix.js.map +1 -1
- package/dist/core/audio-mix.test.js +1 -1
- package/dist/core/audio-mix.test.js.map +1 -1
- package/dist/core/auth/api-keys.d.ts +1 -1
- package/dist/core/auth/api-keys.d.ts.map +1 -1
- package/dist/core/auth/api-keys.js +2 -1
- package/dist/core/auth/api-keys.js.map +1 -1
- package/dist/core/auth/login.d.ts.map +1 -1
- package/dist/core/auth/login.js.map +1 -1
- package/dist/core/beats.d.ts +59 -0
- package/dist/core/beats.d.ts.map +1 -0
- package/dist/core/beats.js +122 -0
- package/dist/core/beats.js.map +1 -0
- package/dist/core/beats.test.d.ts +2 -0
- package/dist/core/beats.test.d.ts.map +1 -0
- package/dist/core/beats.test.js +86 -0
- package/dist/core/beats.test.js.map +1 -0
- package/dist/core/brand-kit.d.ts +80 -0
- package/dist/core/brand-kit.d.ts.map +1 -0
- package/dist/core/brand-kit.js +96 -0
- package/dist/core/brand-kit.js.map +1 -0
- package/dist/core/brand-kit.test.d.ts +2 -0
- package/dist/core/brand-kit.test.d.ts.map +1 -0
- package/dist/core/brand-kit.test.js +76 -0
- package/dist/core/brand-kit.test.js.map +1 -0
- package/dist/core/bundled-sfx.d.ts +64 -0
- package/dist/core/bundled-sfx.d.ts.map +1 -0
- package/dist/core/bundled-sfx.js +218 -0
- package/dist/core/bundled-sfx.js.map +1 -0
- package/dist/core/bundled-sfx.test.d.ts +2 -0
- package/dist/core/bundled-sfx.test.d.ts.map +1 -0
- package/dist/core/bundled-sfx.test.js +81 -0
- package/dist/core/bundled-sfx.test.js.map +1 -0
- package/dist/core/child-abort.d.ts +57 -0
- package/dist/core/child-abort.d.ts.map +1 -0
- package/dist/core/child-abort.js +95 -0
- package/dist/core/child-abort.js.map +1 -0
- package/dist/core/child-abort.test.d.ts +2 -0
- package/dist/core/child-abort.test.d.ts.map +1 -0
- package/dist/core/child-abort.test.js +88 -0
- package/dist/core/child-abort.test.js.map +1 -0
- package/dist/core/clip-scoring.d.ts +44 -0
- package/dist/core/clip-scoring.d.ts.map +1 -0
- package/dist/core/clip-scoring.js +165 -0
- package/dist/core/clip-scoring.js.map +1 -0
- package/dist/core/clip-scoring.test.d.ts +2 -0
- package/dist/core/clip-scoring.test.d.ts.map +1 -0
- package/dist/core/clip-scoring.test.js +113 -0
- package/dist/core/clip-scoring.test.js.map +1 -0
- package/dist/core/emoji-captions.d.ts +45 -0
- package/dist/core/emoji-captions.d.ts.map +1 -0
- package/dist/core/emoji-captions.js +121 -0
- package/dist/core/emoji-captions.js.map +1 -0
- package/dist/core/face-reframe.d.ts +91 -0
- package/dist/core/face-reframe.d.ts.map +1 -0
- package/dist/core/face-reframe.js +141 -0
- package/dist/core/face-reframe.js.map +1 -0
- package/dist/core/face-reframe.test.d.ts +2 -0
- package/dist/core/face-reframe.test.d.ts.map +1 -0
- package/dist/core/face-reframe.test.js +171 -0
- package/dist/core/face-reframe.test.js.map +1 -0
- package/dist/core/filler-words.d.ts +57 -9
- package/dist/core/filler-words.d.ts.map +1 -1
- package/dist/core/filler-words.js +61 -9
- package/dist/core/filler-words.js.map +1 -1
- package/dist/core/filler-words.test.js +91 -17
- package/dist/core/filler-words.test.js.map +1 -1
- package/dist/core/hook-rewrite.d.ts +48 -0
- package/dist/core/hook-rewrite.d.ts.map +1 -0
- package/dist/core/hook-rewrite.js +151 -0
- package/dist/core/hook-rewrite.js.map +1 -0
- package/dist/core/hook-rewrite.test.d.ts +2 -0
- package/dist/core/hook-rewrite.test.d.ts.map +1 -0
- package/dist/core/hook-rewrite.test.js +58 -0
- package/dist/core/hook-rewrite.test.js.map +1 -0
- package/dist/core/hosts/lazy.d.ts.map +1 -1
- package/dist/core/hosts/lazy.js +2 -0
- package/dist/core/hosts/lazy.js.map +1 -1
- package/dist/core/hosts/premiere/adapter.d.ts +1 -0
- package/dist/core/hosts/premiere/adapter.d.ts.map +1 -1
- package/dist/core/hosts/premiere/adapter.js.map +1 -1
- package/dist/core/hosts/premiere/bridge-source.d.ts.map +1 -1
- package/dist/core/hosts/premiere/bridge-source.js +6 -3
- package/dist/core/hosts/premiere/bridge-source.js.map +1 -1
- package/dist/core/hosts/resolve/adapter.d.ts +1 -0
- package/dist/core/hosts/resolve/adapter.d.ts.map +1 -1
- package/dist/core/hosts/resolve/adapter.js.map +1 -1
- package/dist/core/hosts/resolve/bridge-source.d.ts.map +1 -1
- package/dist/core/hosts/resolve/bridge-source.js +31 -4
- package/dist/core/hosts/resolve/bridge-source.js.map +1 -1
- package/dist/core/hosts/resolve/bridge.d.ts +2 -19
- package/dist/core/hosts/resolve/bridge.d.ts.map +1 -1
- package/dist/core/hosts/resolve/bridge.js +70 -41
- package/dist/core/hosts/resolve/bridge.js.map +1 -1
- package/dist/core/hosts/resolve/bridge.test.js +130 -0
- package/dist/core/hosts/resolve/bridge.test.js.map +1 -1
- package/dist/core/hosts/types.d.ts +6 -0
- package/dist/core/hosts/types.d.ts.map +1 -1
- package/dist/core/hosts/types.js.map +1 -1
- package/dist/core/logger.d.ts +32 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +188 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/loop-match.d.ts +57 -0
- package/dist/core/loop-match.d.ts.map +1 -0
- package/dist/core/loop-match.js +91 -0
- package/dist/core/loop-match.js.map +1 -0
- package/dist/core/media/ffmpeg.d.ts.map +1 -1
- package/dist/core/media/ffmpeg.js +14 -3
- package/dist/core/media/ffmpeg.js.map +1 -1
- package/dist/core/multi-format.d.ts +67 -0
- package/dist/core/multi-format.d.ts.map +1 -0
- package/dist/core/multi-format.js +127 -0
- package/dist/core/multi-format.js.map +1 -0
- package/dist/core/multi-format.test.d.ts +2 -0
- package/dist/core/multi-format.test.d.ts.map +1 -0
- package/dist/core/multi-format.test.js +151 -0
- package/dist/core/multi-format.test.js.map +1 -0
- package/dist/core/python/beats.py +61 -0
- package/dist/core/python/face_reframe.py +163 -0
- package/dist/core/python/sidecar-path.d.ts +13 -0
- package/dist/core/python/sidecar-path.d.ts.map +1 -0
- package/dist/core/python/sidecar-path.js +24 -0
- package/dist/core/python/sidecar-path.js.map +1 -0
- package/dist/core/python.d.ts +57 -0
- package/dist/core/python.d.ts.map +1 -0
- package/dist/core/python.js +107 -0
- package/dist/core/python.js.map +1 -0
- package/dist/core/python.test.d.ts +2 -0
- package/dist/core/python.test.d.ts.map +1 -0
- package/dist/core/python.test.js +129 -0
- package/dist/core/python.test.js.map +1 -0
- package/dist/core/retention-structure.d.ts +81 -0
- package/dist/core/retention-structure.d.ts.map +1 -0
- package/dist/core/retention-structure.js +206 -0
- package/dist/core/retention-structure.js.map +1 -0
- package/dist/core/retention-structure.test.d.ts +2 -0
- package/dist/core/retention-structure.test.d.ts.map +1 -0
- package/dist/core/retention-structure.test.js +88 -0
- package/dist/core/retention-structure.test.js.map +1 -0
- package/dist/core/review.d.ts +17 -0
- package/dist/core/review.d.ts.map +1 -1
- package/dist/core/review.js +20 -24
- package/dist/core/review.js.map +1 -1
- package/dist/core/safe-paths.d.ts +11 -0
- package/dist/core/safe-paths.d.ts.map +1 -1
- package/dist/core/safe-paths.js +26 -10
- package/dist/core/safe-paths.js.map +1 -1
- package/dist/core/safe-paths.test.js +16 -0
- package/dist/core/safe-paths.test.js.map +1 -1
- package/dist/core/skills-loader.d.ts +48 -2
- package/dist/core/skills-loader.d.ts.map +1 -1
- package/dist/core/skills-loader.js +97 -19
- package/dist/core/skills-loader.js.map +1 -1
- package/dist/core/skills-loader.test.js +63 -1
- package/dist/core/skills-loader.test.js.map +1 -1
- package/dist/core/srt.d.ts +42 -7
- package/dist/core/srt.d.ts.map +1 -1
- package/dist/core/srt.js +101 -32
- package/dist/core/srt.js.map +1 -1
- package/dist/core/srt.test.js +54 -1
- package/dist/core/srt.test.js.map +1 -1
- package/dist/core/thumbnail-compose.d.ts +58 -0
- package/dist/core/thumbnail-compose.d.ts.map +1 -0
- package/dist/core/thumbnail-compose.js +101 -0
- package/dist/core/thumbnail-compose.js.map +1 -0
- package/dist/core/thumbnail-promise.d.ts +46 -0
- package/dist/core/thumbnail-promise.d.ts.map +1 -0
- package/dist/core/thumbnail-promise.js +133 -0
- package/dist/core/thumbnail-promise.js.map +1 -0
- package/dist/core/thumbnail-promise.test.d.ts +2 -0
- package/dist/core/thumbnail-promise.test.d.ts.map +1 -0
- package/dist/core/thumbnail-promise.test.js +52 -0
- package/dist/core/thumbnail-promise.test.js.map +1 -0
- package/dist/core/viral-moments.d.ts +70 -0
- package/dist/core/viral-moments.d.ts.map +1 -0
- package/dist/core/viral-moments.js +192 -0
- package/dist/core/viral-moments.js.map +1 -0
- package/dist/core/viral-moments.test.d.ts +2 -0
- package/dist/core/viral-moments.test.d.ts.map +1 -0
- package/dist/core/viral-moments.test.js +153 -0
- package/dist/core/viral-moments.test.js.map +1 -0
- package/dist/core/whisper.d.ts +16 -0
- package/dist/core/whisper.d.ts.map +1 -1
- package/dist/core/whisper.js +72 -5
- package/dist/core/whisper.js.map +1 -1
- package/dist/core/whisper.test.js +111 -1
- package/dist/core/whisper.test.js.map +1 -1
- package/dist/core/youtube-metadata.d.ts +44 -0
- package/dist/core/youtube-metadata.d.ts.map +1 -0
- package/dist/core/youtube-metadata.js +168 -0
- package/dist/core/youtube-metadata.js.map +1 -0
- package/dist/core/youtube-metadata.test.d.ts +2 -0
- package/dist/core/youtube-metadata.test.d.ts.map +1 -0
- package/dist/core/youtube-metadata.test.js +132 -0
- package/dist/core/youtube-metadata.test.js.map +1 -0
- package/dist/prompt-commands.d.ts +24 -0
- package/dist/prompt-commands.d.ts.map +1 -0
- package/dist/prompt-commands.js +243 -0
- package/dist/prompt-commands.js.map +1 -0
- package/dist/prompt-commands.test.d.ts +2 -0
- package/dist/prompt-commands.test.d.ts.map +1 -0
- package/dist/prompt-commands.test.js +46 -0
- package/dist/prompt-commands.test.js.map +1 -0
- package/dist/skills.d.ts +6 -6
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +1426 -445
- package/dist/skills.js.map +1 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +108 -0
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/add-fades.d.ts.map +1 -1
- package/dist/tools/add-fades.js +2 -1
- package/dist/tools/add-fades.js.map +1 -1
- package/dist/tools/add-sfx-at-cuts.d.ts.map +1 -1
- package/dist/tools/add-sfx-at-cuts.js +36 -11
- package/dist/tools/add-sfx-at-cuts.js.map +1 -1
- package/dist/tools/add-sfx-to-timeline.d.ts +34 -0
- package/dist/tools/add-sfx-to-timeline.d.ts.map +1 -0
- package/dist/tools/add-sfx-to-timeline.js +169 -0
- package/dist/tools/add-sfx-to-timeline.js.map +1 -0
- package/dist/tools/add-sfx-to-timeline.test.d.ts +2 -0
- package/dist/tools/add-sfx-to-timeline.test.d.ts.map +1 -0
- package/dist/tools/add-sfx-to-timeline.test.js +181 -0
- package/dist/tools/add-sfx-to-timeline.test.js.map +1 -0
- package/dist/tools/audit-first-frame.d.ts +36 -0
- package/dist/tools/audit-first-frame.d.ts.map +1 -0
- package/dist/tools/audit-first-frame.js +181 -0
- package/dist/tools/audit-first-frame.js.map +1 -0
- package/dist/tools/audit-retention-structure.d.ts +20 -0
- package/dist/tools/audit-retention-structure.d.ts.map +1 -0
- package/dist/tools/audit-retention-structure.js +95 -0
- package/dist/tools/audit-retention-structure.js.map +1 -0
- package/dist/tools/audit-retention-structure.test.d.ts +2 -0
- package/dist/tools/audit-retention-structure.test.d.ts.map +1 -0
- package/dist/tools/audit-retention-structure.test.js +93 -0
- package/dist/tools/audit-retention-structure.test.js.map +1 -0
- package/dist/tools/bleep-words.d.ts +59 -0
- package/dist/tools/bleep-words.d.ts.map +1 -0
- package/dist/tools/bleep-words.js +211 -0
- package/dist/tools/bleep-words.js.map +1 -0
- package/dist/tools/bleep-words.test.d.ts +2 -0
- package/dist/tools/bleep-words.test.d.ts.map +1 -0
- package/dist/tools/bleep-words.test.js +96 -0
- package/dist/tools/bleep-words.test.js.map +1 -0
- package/dist/tools/burn-subtitles.d.ts.map +1 -1
- package/dist/tools/burn-subtitles.js +10 -5
- package/dist/tools/burn-subtitles.js.map +1 -1
- package/dist/tools/clean-audio.d.ts.map +1 -1
- package/dist/tools/clean-audio.js +2 -1
- package/dist/tools/clean-audio.js.map +1 -1
- package/dist/tools/cluster-takes.js +2 -1
- package/dist/tools/cluster-takes.js.map +1 -1
- package/dist/tools/compose-thumbnail-variants.d.ts +70 -0
- package/dist/tools/compose-thumbnail-variants.d.ts.map +1 -0
- package/dist/tools/compose-thumbnail-variants.js +274 -0
- package/dist/tools/compose-thumbnail-variants.js.map +1 -0
- package/dist/tools/compose-thumbnail.d.ts +6 -13
- package/dist/tools/compose-thumbnail.d.ts.map +1 -1
- package/dist/tools/compose-thumbnail.js +44 -81
- package/dist/tools/compose-thumbnail.js.map +1 -1
- package/dist/tools/concat-videos.d.ts.map +1 -1
- package/dist/tools/concat-videos.js +12 -5
- package/dist/tools/concat-videos.js.map +1 -1
- package/dist/tools/concat-videos.test.d.ts +2 -0
- package/dist/tools/concat-videos.test.d.ts.map +1 -0
- package/dist/tools/concat-videos.test.js +103 -0
- package/dist/tools/concat-videos.test.js.map +1 -0
- package/dist/tools/crossfade-videos.d.ts.map +1 -1
- package/dist/tools/crossfade-videos.js +2 -1
- package/dist/tools/crossfade-videos.js.map +1 -1
- package/dist/tools/cut-filler-words.d.ts.map +1 -1
- package/dist/tools/cut-filler-words.js +24 -8
- package/dist/tools/cut-filler-words.js.map +1 -1
- package/dist/tools/detect-speaker-changes.js +2 -1
- package/dist/tools/detect-speaker-changes.js.map +1 -1
- package/dist/tools/extract-audio.d.ts.map +1 -1
- package/dist/tools/extract-audio.js +13 -7
- package/dist/tools/extract-audio.js.map +1 -1
- package/dist/tools/face-reframe.d.ts +30 -0
- package/dist/tools/face-reframe.d.ts.map +1 -0
- package/dist/tools/face-reframe.js +143 -0
- package/dist/tools/face-reframe.js.map +1 -0
- package/dist/tools/face-reframe.test.d.ts +2 -0
- package/dist/tools/face-reframe.test.d.ts.map +1 -0
- package/dist/tools/face-reframe.test.js +139 -0
- package/dist/tools/face-reframe.test.js.map +1 -0
- package/dist/tools/find-viral-moments.d.ts +23 -0
- package/dist/tools/find-viral-moments.d.ts.map +1 -0
- package/dist/tools/find-viral-moments.js +176 -0
- package/dist/tools/find-viral-moments.js.map +1 -0
- package/dist/tools/find-viral-moments.test.d.ts +2 -0
- package/dist/tools/find-viral-moments.test.d.ts.map +1 -0
- package/dist/tools/find-viral-moments.test.js +144 -0
- package/dist/tools/find-viral-moments.test.js.map +1 -0
- package/dist/tools/generate-gif.d.ts.map +1 -1
- package/dist/tools/generate-gif.js +47 -40
- package/dist/tools/generate-gif.js.map +1 -1
- package/dist/tools/generate-gif.test.d.ts +2 -0
- package/dist/tools/generate-gif.test.d.ts.map +1 -0
- package/dist/tools/generate-gif.test.js +115 -0
- package/dist/tools/generate-gif.test.js.map +1 -0
- package/dist/tools/generate-outro.d.ts +18 -0
- package/dist/tools/generate-outro.d.ts.map +1 -0
- package/dist/tools/generate-outro.js +175 -0
- package/dist/tools/generate-outro.js.map +1 -0
- package/dist/tools/generate-youtube-metadata.d.ts +23 -0
- package/dist/tools/generate-youtube-metadata.d.ts.map +1 -0
- package/dist/tools/generate-youtube-metadata.js +103 -0
- package/dist/tools/generate-youtube-metadata.js.map +1 -0
- package/dist/tools/generate-youtube-metadata.test.d.ts +2 -0
- package/dist/tools/generate-youtube-metadata.test.d.ts.map +1 -0
- package/dist/tools/generate-youtube-metadata.test.js +118 -0
- package/dist/tools/generate-youtube-metadata.test.js.map +1 -0
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +130 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/index.test.js +27 -1
- package/dist/tools/index.test.js.map +1 -1
- package/dist/tools/ken-burns.d.ts.map +1 -1
- package/dist/tools/ken-burns.js +2 -1
- package/dist/tools/ken-burns.js.map +1 -1
- package/dist/tools/loop-match-short.d.ts +22 -0
- package/dist/tools/loop-match-short.d.ts.map +1 -0
- package/dist/tools/loop-match-short.js +107 -0
- package/dist/tools/loop-match-short.js.map +1 -0
- package/dist/tools/mix-audio.d.ts.map +1 -1
- package/dist/tools/mix-audio.js +2 -1
- package/dist/tools/mix-audio.js.map +1 -1
- package/dist/tools/normalize-loudness.d.ts.map +1 -1
- package/dist/tools/normalize-loudness.js +2 -1
- package/dist/tools/normalize-loudness.js.map +1 -1
- package/dist/tools/path-traversal.test.d.ts +15 -0
- package/dist/tools/path-traversal.test.d.ts.map +1 -0
- package/dist/tools/path-traversal.test.js +223 -0
- package/dist/tools/path-traversal.test.js.map +1 -0
- package/dist/tools/pick-best-takes.js +2 -1
- package/dist/tools/pick-best-takes.js.map +1 -1
- package/dist/tools/punch-in.d.ts.map +1 -1
- package/dist/tools/punch-in.js +2 -1
- package/dist/tools/punch-in.js.map +1 -1
- package/dist/tools/read-transcript.js +2 -1
- package/dist/tools/read-transcript.js.map +1 -1
- package/dist/tools/render-multi-format.d.ts +35 -0
- package/dist/tools/render-multi-format.d.ts.map +1 -0
- package/dist/tools/render-multi-format.js +206 -0
- package/dist/tools/render-multi-format.js.map +1 -0
- package/dist/tools/render-multi-format.test.d.ts +2 -0
- package/dist/tools/render-multi-format.test.d.ts.map +1 -0
- package/dist/tools/render-multi-format.test.js +312 -0
- package/dist/tools/render-multi-format.test.js.map +1 -0
- package/dist/tools/render.d.ts.map +1 -1
- package/dist/tools/render.js +2 -2
- package/dist/tools/render.js.map +1 -1
- package/dist/tools/rewrite-hook.d.ts +32 -0
- package/dist/tools/rewrite-hook.d.ts.map +1 -0
- package/dist/tools/rewrite-hook.js +65 -0
- package/dist/tools/rewrite-hook.js.map +1 -0
- package/dist/tools/score-clip.d.ts +30 -0
- package/dist/tools/score-clip.d.ts.map +1 -0
- package/dist/tools/score-clip.js +109 -0
- package/dist/tools/score-clip.js.map +1 -0
- package/dist/tools/score-clip.test.d.ts +2 -0
- package/dist/tools/score-clip.test.d.ts.map +1 -0
- package/dist/tools/score-clip.test.js +110 -0
- package/dist/tools/score-clip.test.js.map +1 -0
- package/dist/tools/search-tools.d.ts +34 -0
- package/dist/tools/search-tools.d.ts.map +1 -0
- package/dist/tools/search-tools.js +86 -0
- package/dist/tools/search-tools.js.map +1 -0
- package/dist/tools/search-tools.test.d.ts +2 -0
- package/dist/tools/search-tools.test.d.ts.map +1 -0
- package/dist/tools/search-tools.test.js +60 -0
- package/dist/tools/search-tools.test.js.map +1 -0
- package/dist/tools/snap-cuts-to-beats.d.ts +18 -0
- package/dist/tools/snap-cuts-to-beats.d.ts.map +1 -0
- package/dist/tools/snap-cuts-to-beats.js +110 -0
- package/dist/tools/snap-cuts-to-beats.js.map +1 -0
- package/dist/tools/snap-cuts-to-beats.test.d.ts +2 -0
- package/dist/tools/snap-cuts-to-beats.test.d.ts.map +1 -0
- package/dist/tools/snap-cuts-to-beats.test.js +99 -0
- package/dist/tools/snap-cuts-to-beats.test.js.map +1 -0
- package/dist/tools/speed-ramp.d.ts.map +1 -1
- package/dist/tools/speed-ramp.js +2 -1
- package/dist/tools/speed-ramp.js.map +1 -1
- package/dist/tools/stabilize-video.d.ts.map +1 -1
- package/dist/tools/stabilize-video.js +2 -1
- package/dist/tools/stabilize-video.js.map +1 -1
- package/dist/tools/suggest-broll.d.ts +34 -0
- package/dist/tools/suggest-broll.d.ts.map +1 -0
- package/dist/tools/suggest-broll.js +367 -0
- package/dist/tools/suggest-broll.js.map +1 -0
- package/dist/tools/suggest-broll.test.d.ts +2 -0
- package/dist/tools/suggest-broll.test.d.ts.map +1 -0
- package/dist/tools/suggest-broll.test.js +217 -0
- package/dist/tools/suggest-broll.test.js.map +1 -0
- package/dist/tools/text-based-cut.d.ts +33 -0
- package/dist/tools/text-based-cut.d.ts.map +1 -0
- package/dist/tools/text-based-cut.js +172 -0
- package/dist/tools/text-based-cut.js.map +1 -0
- package/dist/tools/text-based-cut.test.d.ts +2 -0
- package/dist/tools/text-based-cut.test.d.ts.map +1 -0
- package/dist/tools/text-based-cut.test.js +32 -0
- package/dist/tools/text-based-cut.test.js.map +1 -0
- package/dist/tools/transcribe.d.ts +1 -1
- package/dist/tools/transition-videos.d.ts +1 -1
- package/dist/tools/transition-videos.d.ts.map +1 -1
- package/dist/tools/transition-videos.js +2 -1
- package/dist/tools/transition-videos.js.map +1 -1
- package/dist/tools/trim-dead-air.d.ts +59 -0
- package/dist/tools/trim-dead-air.d.ts.map +1 -0
- package/dist/tools/trim-dead-air.js +215 -0
- package/dist/tools/trim-dead-air.js.map +1 -0
- package/dist/tools/trim-dead-air.test.d.ts +2 -0
- package/dist/tools/trim-dead-air.test.d.ts.map +1 -0
- package/dist/tools/trim-dead-air.test.js +75 -0
- package/dist/tools/trim-dead-air.test.js.map +1 -0
- package/dist/tools/verify-thumbnail-promise.d.ts +33 -0
- package/dist/tools/verify-thumbnail-promise.d.ts.map +1 -0
- package/dist/tools/verify-thumbnail-promise.js +112 -0
- package/dist/tools/verify-thumbnail-promise.js.map +1 -0
- package/dist/tools/verify-thumbnail-promise.test.d.ts +2 -0
- package/dist/tools/verify-thumbnail-promise.test.d.ts.map +1 -0
- package/dist/tools/verify-thumbnail-promise.test.js +38 -0
- package/dist/tools/verify-thumbnail-promise.test.js.map +1 -0
- package/dist/tools/write-keyword-captions.d.ts +7 -0
- package/dist/tools/write-keyword-captions.d.ts.map +1 -1
- package/dist/tools/write-keyword-captions.js +35 -4
- package/dist/tools/write-keyword-captions.js.map +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +75 -11
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/tool-formatters.d.ts +30 -0
- package/dist/ui/tool-formatters.d.ts.map +1 -0
- package/dist/ui/tool-formatters.js +461 -0
- package/dist/ui/tool-formatters.js.map +1 -0
- package/dist/ui/tool-formatters.test.d.ts +2 -0
- package/dist/ui/tool-formatters.test.d.ts.map +1 -0
- package/dist/ui/tool-formatters.test.js +143 -0
- package/dist/ui/tool-formatters.test.js.map +1 -0
- package/package.json +10 -9
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand-kit loader.
|
|
3
|
+
*
|
|
4
|
+
* A "brand kit" is a small JSON file at `<cwd>/.gg/brand.json` that holds the
|
|
5
|
+
* channel-level constants every render-time tool wants: logo, intro/outro/
|
|
6
|
+
* watermark video paths, default fonts, colour palette, channel name, CTA
|
|
7
|
+
* text. The loader is opt-in — if the file isn't present, every consumer
|
|
8
|
+
* falls back to its existing defaults.
|
|
9
|
+
*
|
|
10
|
+
* Why JSON, not YAML: zero new deps; brand kits are short enough that the
|
|
11
|
+
* JSON line noise doesn't hurt readability.
|
|
12
|
+
*
|
|
13
|
+
* Why separate from styles (`<cwd>/.gg/editor-styles/*.md`): styles modulate
|
|
14
|
+
* the LLM's behaviour (its writing voice, tone, defaults). The brand kit
|
|
15
|
+
* modulates raw render output (which logo to overlay, which font to draw
|
|
16
|
+
* with). They serve different layers and we keep them separate so a user
|
|
17
|
+
* can change one without touching the other.
|
|
18
|
+
*/
|
|
19
|
+
export interface BrandKitFonts {
|
|
20
|
+
/** Display font for headlines / titles. */
|
|
21
|
+
heading?: string;
|
|
22
|
+
/** Body font for captions / lower-thirds. */
|
|
23
|
+
body?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface BrandKitColors {
|
|
26
|
+
/** Hex (RRGGBB, no leading #). */
|
|
27
|
+
primary?: string;
|
|
28
|
+
secondary?: string;
|
|
29
|
+
accent?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface BrandKit {
|
|
32
|
+
/** Channel display name; surfaced in outros, lower-thirds, end-screens. */
|
|
33
|
+
channelName?: string;
|
|
34
|
+
/** Path (relative to cwd) to a logo PNG with alpha — used by overlay_watermark and generate_outro. */
|
|
35
|
+
logo?: string;
|
|
36
|
+
/** Default watermark path (logo, but a more polished pre-positioned variant if you have one). */
|
|
37
|
+
watermark?: string;
|
|
38
|
+
/** Pre-rendered intro video. concat_videos uses this. */
|
|
39
|
+
intro?: string;
|
|
40
|
+
/** Pre-rendered outro video. concat_videos / generate_outro use this. */
|
|
41
|
+
outro?: string;
|
|
42
|
+
/** Per-font-role family (or path to .ttf for ffmpeg drawtext). */
|
|
43
|
+
fonts?: BrandKitFonts;
|
|
44
|
+
/** Brand palette (hex without leading #). */
|
|
45
|
+
colors?: BrandKitColors;
|
|
46
|
+
/** Default CTA text — used by generate_outro and overlay banners. */
|
|
47
|
+
ctaText?: string;
|
|
48
|
+
/** Default subscribe handle / URL surfaced in description templates. */
|
|
49
|
+
subscribeUrl?: string;
|
|
50
|
+
}
|
|
51
|
+
/** Path of the brand-kit file relative to cwd. Stable so docs can quote it. */
|
|
52
|
+
export declare const BRAND_KIT_PATH = ".gg/brand.json";
|
|
53
|
+
/**
|
|
54
|
+
* Load `<cwd>/.gg/brand.json` if present. Returns `null` when the file is
|
|
55
|
+
* missing OR malformed — callers should treat absence as "no brand kit
|
|
56
|
+
* configured" and fall back to their normal defaults. Malformed JSON also
|
|
57
|
+
* returns null (we don't want a syntax slip in a config file to break a
|
|
58
|
+
* render); the agent surfaces the issue separately via `validateBrandKit`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function loadBrandKit(cwd: string): BrandKit | null;
|
|
61
|
+
export interface BrandKitValidation {
|
|
62
|
+
ok: boolean;
|
|
63
|
+
/** Empty when ok=true. */
|
|
64
|
+
errors: string[];
|
|
65
|
+
/** Warnings don't block; they surface "you set logo but the file doesn't exist" cases. */
|
|
66
|
+
warnings: string[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Validate a brand kit. Returns structured findings the agent can surface.
|
|
70
|
+
* Doesn't throw — caller decides whether to gate.
|
|
71
|
+
*
|
|
72
|
+
* Strict checks (errors):
|
|
73
|
+
* - any path field that isn't a string
|
|
74
|
+
* - any color that isn't 6-hex chars
|
|
75
|
+
*
|
|
76
|
+
* Soft checks (warnings):
|
|
77
|
+
* - referenced files that don't exist on disk
|
|
78
|
+
*/
|
|
79
|
+
export declare function validateBrandKit(kit: BrandKit, cwd: string, fileExists?: (absPath: string) => boolean): BrandKitValidation;
|
|
80
|
+
//# sourceMappingURL=brand-kit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-kit.d.ts","sourceRoot":"","sources":["../../src/core/brand-kit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sGAAsG;IACtG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iGAAiG;IACjG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,+EAA+E;AAC/E,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAezD;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,0BAA0B;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0FAA0F;IAC1F,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,QAAQ,EACb,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,GACxC,kBAAkB,CAsCpB"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand-kit loader.
|
|
3
|
+
*
|
|
4
|
+
* A "brand kit" is a small JSON file at `<cwd>/.gg/brand.json` that holds the
|
|
5
|
+
* channel-level constants every render-time tool wants: logo, intro/outro/
|
|
6
|
+
* watermark video paths, default fonts, colour palette, channel name, CTA
|
|
7
|
+
* text. The loader is opt-in — if the file isn't present, every consumer
|
|
8
|
+
* falls back to its existing defaults.
|
|
9
|
+
*
|
|
10
|
+
* Why JSON, not YAML: zero new deps; brand kits are short enough that the
|
|
11
|
+
* JSON line noise doesn't hurt readability.
|
|
12
|
+
*
|
|
13
|
+
* Why separate from styles (`<cwd>/.gg/editor-styles/*.md`): styles modulate
|
|
14
|
+
* the LLM's behaviour (its writing voice, tone, defaults). The brand kit
|
|
15
|
+
* modulates raw render output (which logo to overlay, which font to draw
|
|
16
|
+
* with). They serve different layers and we keep them separate so a user
|
|
17
|
+
* can change one without touching the other.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
20
|
+
import { resolve as resolvePath } from "node:path";
|
|
21
|
+
/** Path of the brand-kit file relative to cwd. Stable so docs can quote it. */
|
|
22
|
+
export const BRAND_KIT_PATH = ".gg/brand.json";
|
|
23
|
+
/**
|
|
24
|
+
* Load `<cwd>/.gg/brand.json` if present. Returns `null` when the file is
|
|
25
|
+
* missing OR malformed — callers should treat absence as "no brand kit
|
|
26
|
+
* configured" and fall back to their normal defaults. Malformed JSON also
|
|
27
|
+
* returns null (we don't want a syntax slip in a config file to break a
|
|
28
|
+
* render); the agent surfaces the issue separately via `validateBrandKit`.
|
|
29
|
+
*/
|
|
30
|
+
export function loadBrandKit(cwd) {
|
|
31
|
+
const path = resolvePath(cwd, BRAND_KIT_PATH);
|
|
32
|
+
let raw;
|
|
33
|
+
try {
|
|
34
|
+
raw = readFileSync(path, "utf8");
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const parsed = JSON.parse(raw);
|
|
41
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
42
|
+
return null;
|
|
43
|
+
return parsed;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validate a brand kit. Returns structured findings the agent can surface.
|
|
51
|
+
* Doesn't throw — caller decides whether to gate.
|
|
52
|
+
*
|
|
53
|
+
* Strict checks (errors):
|
|
54
|
+
* - any path field that isn't a string
|
|
55
|
+
* - any color that isn't 6-hex chars
|
|
56
|
+
*
|
|
57
|
+
* Soft checks (warnings):
|
|
58
|
+
* - referenced files that don't exist on disk
|
|
59
|
+
*/
|
|
60
|
+
export function validateBrandKit(kit, cwd, fileExists) {
|
|
61
|
+
const errors = [];
|
|
62
|
+
const warnings = [];
|
|
63
|
+
const exists = fileExists ?? ((p) => existsSync(p));
|
|
64
|
+
const pathFields = ["logo", "watermark", "intro", "outro"];
|
|
65
|
+
for (const f of pathFields) {
|
|
66
|
+
const v = kit[f];
|
|
67
|
+
if (v === undefined)
|
|
68
|
+
continue;
|
|
69
|
+
if (typeof v !== "string" || v.trim().length === 0) {
|
|
70
|
+
errors.push(`${f}: must be a non-empty string`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const abs = resolvePath(cwd, v);
|
|
74
|
+
if (!exists(abs))
|
|
75
|
+
warnings.push(`${f}: file not found at ${abs}`);
|
|
76
|
+
}
|
|
77
|
+
if (kit.colors) {
|
|
78
|
+
for (const [k, v] of Object.entries(kit.colors)) {
|
|
79
|
+
if (v !== undefined && !/^[0-9a-fA-F]{6}$/.test(v)) {
|
|
80
|
+
errors.push(`colors.${k}: '${v}' is not 6-char hex (RRGGBB, no #)`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (kit.fonts) {
|
|
85
|
+
for (const [k, v] of Object.entries(kit.fonts)) {
|
|
86
|
+
if (v !== undefined && typeof v !== "string") {
|
|
87
|
+
errors.push(`fonts.${k}: must be a string (font name or path to .ttf)`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (kit.channelName !== undefined && typeof kit.channelName !== "string") {
|
|
92
|
+
errors.push("channelName: must be a string");
|
|
93
|
+
}
|
|
94
|
+
return { ok: errors.length === 0, errors, warnings };
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=brand-kit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-kit.js","sourceRoot":"","sources":["../../src/core/brand-kit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-kit.test.d.ts","sourceRoot":"","sources":["../../src/core/brand-kit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { BRAND_KIT_PATH, loadBrandKit, validateBrandKit } from "./brand-kit.js";
|
|
6
|
+
function makeCwd(brand) {
|
|
7
|
+
const cwd = mkdtempSync(join(tmpdir(), "gg-brandkit-test-"));
|
|
8
|
+
if (brand !== undefined) {
|
|
9
|
+
mkdirSync(join(cwd, ".gg"), { recursive: true });
|
|
10
|
+
writeFileSync(join(cwd, BRAND_KIT_PATH), brand, "utf8");
|
|
11
|
+
}
|
|
12
|
+
return cwd;
|
|
13
|
+
}
|
|
14
|
+
describe("loadBrandKit", () => {
|
|
15
|
+
it("returns null when no brand.json exists", () => {
|
|
16
|
+
const cwd = makeCwd();
|
|
17
|
+
expect(loadBrandKit(cwd)).toBeNull();
|
|
18
|
+
});
|
|
19
|
+
it("returns null on malformed JSON (no throw)", () => {
|
|
20
|
+
const cwd = makeCwd("{ not valid json");
|
|
21
|
+
expect(loadBrandKit(cwd)).toBeNull();
|
|
22
|
+
});
|
|
23
|
+
it("returns null on non-object root (array, primitive)", () => {
|
|
24
|
+
expect(loadBrandKit(makeCwd('["a", "b"]'))).toBeNull();
|
|
25
|
+
expect(loadBrandKit(makeCwd('"string"'))).toBeNull();
|
|
26
|
+
expect(loadBrandKit(makeCwd("42"))).toBeNull();
|
|
27
|
+
});
|
|
28
|
+
it("parses a valid brand kit", () => {
|
|
29
|
+
const cwd = makeCwd(JSON.stringify({
|
|
30
|
+
channelName: "Ken Kai",
|
|
31
|
+
logo: "assets/logo.png",
|
|
32
|
+
fonts: { heading: "Bebas Neue", body: "Inter" },
|
|
33
|
+
colors: { primary: "FF6B35", secondary: "004E89" },
|
|
34
|
+
ctaText: "Subscribe for more",
|
|
35
|
+
}));
|
|
36
|
+
const kit = loadBrandKit(cwd);
|
|
37
|
+
expect(kit?.channelName).toBe("Ken Kai");
|
|
38
|
+
expect(kit?.fonts?.heading).toBe("Bebas Neue");
|
|
39
|
+
expect(kit?.colors?.primary).toBe("FF6B35");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe("validateBrandKit", () => {
|
|
43
|
+
const noExist = () => false;
|
|
44
|
+
it("ok=true on empty kit", () => {
|
|
45
|
+
const v = validateBrandKit({}, "/tmp", noExist);
|
|
46
|
+
expect(v.ok).toBe(true);
|
|
47
|
+
expect(v.errors).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
it("rejects malformed colors", () => {
|
|
50
|
+
const kit = { colors: { primary: "#FF6B35", secondary: "no" } };
|
|
51
|
+
const v = validateBrandKit(kit, "/tmp", noExist);
|
|
52
|
+
expect(v.ok).toBe(false);
|
|
53
|
+
expect(v.errors.find((e) => e.includes("colors.primary"))).toBeTruthy();
|
|
54
|
+
expect(v.errors.find((e) => e.includes("colors.secondary"))).toBeTruthy();
|
|
55
|
+
});
|
|
56
|
+
it("rejects empty path strings", () => {
|
|
57
|
+
const v = validateBrandKit({ logo: "" }, "/tmp", noExist);
|
|
58
|
+
expect(v.ok).toBe(false);
|
|
59
|
+
expect(v.errors.find((e) => e.includes("logo"))).toBeTruthy();
|
|
60
|
+
});
|
|
61
|
+
it("warns on missing referenced files", () => {
|
|
62
|
+
const v = validateBrandKit({ logo: "missing.png" }, "/tmp", noExist);
|
|
63
|
+
expect(v.ok).toBe(true);
|
|
64
|
+
expect(v.warnings.find((w) => w.includes("logo"))).toBeTruthy();
|
|
65
|
+
});
|
|
66
|
+
it("no warning when fileExists returns true", () => {
|
|
67
|
+
const v = validateBrandKit({ logo: "logo.png" }, "/tmp", () => true);
|
|
68
|
+
expect(v.ok).toBe(true);
|
|
69
|
+
expect(v.warnings).toEqual([]);
|
|
70
|
+
});
|
|
71
|
+
it("accepts six-char hex colors (no leading #)", () => {
|
|
72
|
+
const v = validateBrandKit({ colors: { primary: "FFFFFF", accent: "00aaff" } }, "/tmp", noExist);
|
|
73
|
+
expect(v.ok).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=brand-kit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-kit.test.js","sourceRoot":"","sources":["../../src/core/brand-kit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled sound-effect library.
|
|
3
|
+
*
|
|
4
|
+
* gg-editor's `add_sfx_at_cuts` needs a WAV/MP3 file per hit. Without this
|
|
5
|
+
* module the agent had to either ask the user to supply one or invent a path
|
|
6
|
+
* — both broke the canonical retention pipeline. Now the agent calls
|
|
7
|
+
* `add_sfx_at_cuts(sfx="whoosh", …)` with a bundled name and the resolver
|
|
8
|
+
* either returns a cached WAV or synthesises one via ffmpeg into
|
|
9
|
+
* `~/.gg/sfx-cache/<name>.wav` (~50 ms one-time per name; cached forever).
|
|
10
|
+
*
|
|
11
|
+
* Why synthesised, not shipped: no third-party files = no IP risk + no npm
|
|
12
|
+
* weight. Every recipe is a pure ffmpeg-lavfi expression so the output is
|
|
13
|
+
* deterministic and reproducible. Quality is "creator-vlog acceptable" —
|
|
14
|
+
* not Skywalker Sound, but indistinguishable from typical CapCut presets in
|
|
15
|
+
* a sub-1-second cut.
|
|
16
|
+
*
|
|
17
|
+
* Design rules for recipes:
|
|
18
|
+
* - Stereo (`-ac 2`) — every consumer mixes into stereo audio.
|
|
19
|
+
* - 200–500 ms long. Anything longer fights the next cut.
|
|
20
|
+
* - Peak ≤ -3 dBFS (the recipes all keep amplitude ≤ 0.7) so
|
|
21
|
+
* `add_sfx_at_cuts`'s default -8 dB gain leaves headroom for voice.
|
|
22
|
+
* - Linear fade in (≥5 ms) + fade out — no clicks at the envelope edges.
|
|
23
|
+
*/
|
|
24
|
+
export interface SfxRecipe {
|
|
25
|
+
/** One-line creator description shown in the system prompt + tool description. */
|
|
26
|
+
description: string;
|
|
27
|
+
/**
|
|
28
|
+
* ffmpeg args producing the WAV. The cached output path is appended at
|
|
29
|
+
* synthesis time; recipes should NOT include it themselves.
|
|
30
|
+
*/
|
|
31
|
+
args: string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The bundled set. Keep this list short and creator-relevant —
|
|
35
|
+
* 8–10 names covers the entire short-form vocabulary.
|
|
36
|
+
*/
|
|
37
|
+
export declare const BUNDLED_SFX: Record<string, SfxRecipe>;
|
|
38
|
+
/** Names of every bundled SFX (sorted for stable display). */
|
|
39
|
+
export declare function listBundledSfxNames(): string[];
|
|
40
|
+
/** A short markdown-style list of bundled SFX for tool descriptions / system prompts. */
|
|
41
|
+
export declare function bundledSfxDescriptionList(): string;
|
|
42
|
+
/** Cache dir for synthesised WAVs. Stable across sessions, gitignored by convention. */
|
|
43
|
+
export declare function getSfxCacheDir(): string;
|
|
44
|
+
/**
|
|
45
|
+
* Resolve an SFX argument:
|
|
46
|
+
*
|
|
47
|
+
* - Bundled name (e.g. `"whoosh"`) → cache hit at `~/.gg/sfx-cache/whoosh.wav`,
|
|
48
|
+
* synthesising via ffmpeg on first request.
|
|
49
|
+
* - File-like string (contains `/`, `\`, `.`, `~`) → resolve against `cwd`
|
|
50
|
+
* and return as-is. The caller still has to verify the file exists.
|
|
51
|
+
*
|
|
52
|
+
* Throws when the name is neither bundled nor file-like.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveSfx(nameOrPath: string, cwd: string, signal?: AbortSignal): Promise<{
|
|
55
|
+
path: string;
|
|
56
|
+
bundled: boolean;
|
|
57
|
+
name?: string;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* Ensure a bundled SFX is present on disk, synthesising it via ffmpeg if not.
|
|
61
|
+
* Returns the absolute cache path.
|
|
62
|
+
*/
|
|
63
|
+
export declare function ensureBundledSfx(name: string, signal?: AbortSignal): Promise<string>;
|
|
64
|
+
//# sourceMappingURL=bundled-sfx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled-sfx.d.ts","sourceRoot":"","sources":["../../src/core/bundled-sfx.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAOH,MAAM,WAAW,SAAS;IACxB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CA4HjD,CAAC;AAEF,8DAA8D;AAC9D,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C;AAED,yFAAyF;AACzF,wBAAgB,yBAAyB,IAAI,MAAM,CAIlD;AAED,wFAAwF;AACxF,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB5D;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB1F"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled sound-effect library.
|
|
3
|
+
*
|
|
4
|
+
* gg-editor's `add_sfx_at_cuts` needs a WAV/MP3 file per hit. Without this
|
|
5
|
+
* module the agent had to either ask the user to supply one or invent a path
|
|
6
|
+
* — both broke the canonical retention pipeline. Now the agent calls
|
|
7
|
+
* `add_sfx_at_cuts(sfx="whoosh", …)` with a bundled name and the resolver
|
|
8
|
+
* either returns a cached WAV or synthesises one via ffmpeg into
|
|
9
|
+
* `~/.gg/sfx-cache/<name>.wav` (~50 ms one-time per name; cached forever).
|
|
10
|
+
*
|
|
11
|
+
* Why synthesised, not shipped: no third-party files = no IP risk + no npm
|
|
12
|
+
* weight. Every recipe is a pure ffmpeg-lavfi expression so the output is
|
|
13
|
+
* deterministic and reproducible. Quality is "creator-vlog acceptable" —
|
|
14
|
+
* not Skywalker Sound, but indistinguishable from typical CapCut presets in
|
|
15
|
+
* a sub-1-second cut.
|
|
16
|
+
*
|
|
17
|
+
* Design rules for recipes:
|
|
18
|
+
* - Stereo (`-ac 2`) — every consumer mixes into stereo audio.
|
|
19
|
+
* - 200–500 ms long. Anything longer fights the next cut.
|
|
20
|
+
* - Peak ≤ -3 dBFS (the recipes all keep amplitude ≤ 0.7) so
|
|
21
|
+
* `add_sfx_at_cuts`'s default -8 dB gain leaves headroom for voice.
|
|
22
|
+
* - Linear fade in (≥5 ms) + fade out — no clicks at the envelope edges.
|
|
23
|
+
*/
|
|
24
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
25
|
+
import { homedir } from "node:os";
|
|
26
|
+
import { join, resolve as resolvePath } from "node:path";
|
|
27
|
+
import { runFfmpeg } from "./media/ffmpeg.js";
|
|
28
|
+
/**
|
|
29
|
+
* The bundled set. Keep this list short and creator-relevant —
|
|
30
|
+
* 8–10 names covers the entire short-form vocabulary.
|
|
31
|
+
*/
|
|
32
|
+
export const BUNDLED_SFX = {
|
|
33
|
+
pop: {
|
|
34
|
+
description: "Short upward blip — punctuates a cut without dominating",
|
|
35
|
+
args: [
|
|
36
|
+
"-f",
|
|
37
|
+
"lavfi",
|
|
38
|
+
"-i",
|
|
39
|
+
"sine=frequency=880:duration=0.08",
|
|
40
|
+
"-af",
|
|
41
|
+
"afade=t=in:d=0.005,afade=t=out:d=0.05:st=0.03,volume=0.5",
|
|
42
|
+
"-ac",
|
|
43
|
+
"2",
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
click: {
|
|
47
|
+
description: "Very short tick — for fast-cut emphasis where pop would be too musical",
|
|
48
|
+
args: [
|
|
49
|
+
"-f",
|
|
50
|
+
"lavfi",
|
|
51
|
+
"-i",
|
|
52
|
+
"anoisesrc=color=white:duration=0.03:amplitude=0.6",
|
|
53
|
+
"-af",
|
|
54
|
+
"bandpass=frequency=4000:width_type=h:w=3000,afade=t=in:d=0.002,afade=t=out:d=0.025:st=0.005,volume=2",
|
|
55
|
+
"-ac",
|
|
56
|
+
"2",
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
whoosh: {
|
|
60
|
+
description: "Downward frequency sweep — classic vlog cut effect",
|
|
61
|
+
args: [
|
|
62
|
+
"-f",
|
|
63
|
+
"lavfi",
|
|
64
|
+
// Linear sweep 3000 Hz → ~200 Hz over 0.3 s (phase = 2π·(f₀·t + ½·k·t²))
|
|
65
|
+
"-i",
|
|
66
|
+
"aevalsrc='0.4*sin(2*PI*(3000*t-4667*t*t))':d=0.3",
|
|
67
|
+
"-af",
|
|
68
|
+
"afade=t=in:d=0.05,afade=t=out:d=0.1:st=0.2",
|
|
69
|
+
"-ac",
|
|
70
|
+
"2",
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
"whoosh-up": {
|
|
74
|
+
description: "Upward frequency sweep — for reveals / 'lift' moments",
|
|
75
|
+
args: [
|
|
76
|
+
"-f",
|
|
77
|
+
"lavfi",
|
|
78
|
+
// Sweep 200 Hz → ~3000 Hz over 0.3 s
|
|
79
|
+
"-i",
|
|
80
|
+
"aevalsrc='0.4*sin(2*PI*(200*t+4667*t*t))':d=0.3",
|
|
81
|
+
"-af",
|
|
82
|
+
"afade=t=in:d=0.05,afade=t=out:d=0.1:st=0.2",
|
|
83
|
+
"-ac",
|
|
84
|
+
"2",
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
swoosh: {
|
|
88
|
+
description: "Filtered noise sweep — softer/airier than whoosh, good for transitions",
|
|
89
|
+
args: [
|
|
90
|
+
"-f",
|
|
91
|
+
"lavfi",
|
|
92
|
+
"-i",
|
|
93
|
+
"anoisesrc=color=brown:duration=0.4:amplitude=0.5",
|
|
94
|
+
"-af",
|
|
95
|
+
"bandpass=frequency=1500:width_type=h:w=2500,afade=t=in:d=0.05,afade=t=out:d=0.15:st=0.25,volume=2.5",
|
|
96
|
+
"-ac",
|
|
97
|
+
"2",
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
riser: {
|
|
101
|
+
description: "600 ms sine sweep up — builds tension before a reveal",
|
|
102
|
+
args: [
|
|
103
|
+
"-f",
|
|
104
|
+
"lavfi",
|
|
105
|
+
// Sweep 200 → ~2200 Hz over 0.6 s with rising amplitude
|
|
106
|
+
"-i",
|
|
107
|
+
"aevalsrc='(0.2+0.4*t)*sin(2*PI*(200*t+1667*t*t))':d=0.6",
|
|
108
|
+
"-af",
|
|
109
|
+
"afade=t=in:d=0.1,afade=t=out:d=0.05:st=0.55",
|
|
110
|
+
"-ac",
|
|
111
|
+
"2",
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
"bass-drop": {
|
|
115
|
+
description: "Sub bass with downward sweep — for big-reveal cuts (use sparingly)",
|
|
116
|
+
args: [
|
|
117
|
+
"-f",
|
|
118
|
+
"lavfi",
|
|
119
|
+
// Sweep 120 Hz → ~40 Hz over 0.45 s
|
|
120
|
+
"-i",
|
|
121
|
+
"aevalsrc='0.6*sin(2*PI*(120*t-89*t*t))':d=0.45",
|
|
122
|
+
"-af",
|
|
123
|
+
"afade=t=in:d=0.02,afade=t=out:d=0.15:st=0.3",
|
|
124
|
+
"-ac",
|
|
125
|
+
"2",
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
ding: {
|
|
129
|
+
description: "Soft bell — for highlighting an on-screen item / list bullet",
|
|
130
|
+
args: [
|
|
131
|
+
"-f",
|
|
132
|
+
"lavfi",
|
|
133
|
+
"-i",
|
|
134
|
+
// Two-harmonic bell: fundamental + perfect fifth, exponential decay
|
|
135
|
+
"aevalsrc='exp(-4*t)*(0.5*sin(2*PI*1318*t)+0.25*sin(2*PI*1976*t))':d=0.5",
|
|
136
|
+
"-af",
|
|
137
|
+
"afade=t=in:d=0.005",
|
|
138
|
+
"-ac",
|
|
139
|
+
"2",
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
thump: {
|
|
143
|
+
description: "Low percussive hit — landing impact, soft punch",
|
|
144
|
+
args: [
|
|
145
|
+
"-f",
|
|
146
|
+
"lavfi",
|
|
147
|
+
"-i",
|
|
148
|
+
// Damped sine ~80 Hz with fast decay
|
|
149
|
+
"aevalsrc='exp(-12*t)*0.7*sin(2*PI*80*t)':d=0.25",
|
|
150
|
+
"-af",
|
|
151
|
+
"afade=t=in:d=0.005",
|
|
152
|
+
"-ac",
|
|
153
|
+
"2",
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
/** Names of every bundled SFX (sorted for stable display). */
|
|
158
|
+
export function listBundledSfxNames() {
|
|
159
|
+
return Object.keys(BUNDLED_SFX).sort();
|
|
160
|
+
}
|
|
161
|
+
/** A short markdown-style list of bundled SFX for tool descriptions / system prompts. */
|
|
162
|
+
export function bundledSfxDescriptionList() {
|
|
163
|
+
return listBundledSfxNames()
|
|
164
|
+
.map((n) => `${n} (${BUNDLED_SFX[n].description})`)
|
|
165
|
+
.join(", ");
|
|
166
|
+
}
|
|
167
|
+
/** Cache dir for synthesised WAVs. Stable across sessions, gitignored by convention. */
|
|
168
|
+
export function getSfxCacheDir() {
|
|
169
|
+
return resolvePath(homedir(), ".gg", "sfx-cache");
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Resolve an SFX argument:
|
|
173
|
+
*
|
|
174
|
+
* - Bundled name (e.g. `"whoosh"`) → cache hit at `~/.gg/sfx-cache/whoosh.wav`,
|
|
175
|
+
* synthesising via ffmpeg on first request.
|
|
176
|
+
* - File-like string (contains `/`, `\`, `.`, `~`) → resolve against `cwd`
|
|
177
|
+
* and return as-is. The caller still has to verify the file exists.
|
|
178
|
+
*
|
|
179
|
+
* Throws when the name is neither bundled nor file-like.
|
|
180
|
+
*/
|
|
181
|
+
export async function resolveSfx(nameOrPath, cwd, signal) {
|
|
182
|
+
// Bundled names are bare alphanumeric+hyphen tokens with no path / extension.
|
|
183
|
+
// Any string with `/`, `\`, `.`, or `~` is a file path.
|
|
184
|
+
const looksLikePath = /[/\\.~]/.test(nameOrPath);
|
|
185
|
+
if (!looksLikePath) {
|
|
186
|
+
const recipe = BUNDLED_SFX[nameOrPath];
|
|
187
|
+
if (!recipe) {
|
|
188
|
+
throw new Error(`unknown SFX name: '${nameOrPath}'. Bundled: ${listBundledSfxNames().join(", ")}`);
|
|
189
|
+
}
|
|
190
|
+
const path = await ensureBundledSfx(nameOrPath, signal);
|
|
191
|
+
return { path, bundled: true, name: nameOrPath };
|
|
192
|
+
}
|
|
193
|
+
// Treat as a file path.
|
|
194
|
+
return { path: resolvePath(cwd, nameOrPath), bundled: false };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Ensure a bundled SFX is present on disk, synthesising it via ffmpeg if not.
|
|
198
|
+
* Returns the absolute cache path.
|
|
199
|
+
*/
|
|
200
|
+
export async function ensureBundledSfx(name, signal) {
|
|
201
|
+
const recipe = BUNDLED_SFX[name];
|
|
202
|
+
if (!recipe) {
|
|
203
|
+
throw new Error(`unknown bundled SFX: '${name}'`);
|
|
204
|
+
}
|
|
205
|
+
const dir = getSfxCacheDir();
|
|
206
|
+
mkdirSync(dir, { recursive: true });
|
|
207
|
+
const out = join(dir, `${name}.wav`);
|
|
208
|
+
if (existsSync(out))
|
|
209
|
+
return out;
|
|
210
|
+
const ffArgs = [...recipe.args, out];
|
|
211
|
+
const r = await runFfmpeg(ffArgs, { signal });
|
|
212
|
+
if (r.code !== 0) {
|
|
213
|
+
const tail = r.stderr.split("\n").filter(Boolean).slice(-3).join(" | ").slice(-300);
|
|
214
|
+
throw new Error(`ffmpeg failed synthesising '${name}' SFX: ${tail}`);
|
|
215
|
+
}
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=bundled-sfx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled-sfx.js","sourceRoot":"","sources":["../../src/core/bundled-sfx.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled-sfx.test.d.ts","sourceRoot":"","sources":["../../src/core/bundled-sfx.test.ts"],"names":[],"mappings":""}
|