kasaframe 0.1.1 → 0.1.4
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/NOTICE +10 -8
- package/README.md +16 -1
- package/app/server/engine.ts +133 -15
- package/app/server/main.ts +7 -3
- package/app/web/main.ts +218 -36
- package/app/web/style.css +37 -4
- package/bin/hooks.mjs +14 -1
- package/bin/kf.mjs +20 -2
- package/docs/AGENTS.md +64 -16
- package/music/README.md +31 -7
- package/package.json +2 -2
- package/packages/adapters/src/project.ts +5 -1
- package/packages/adapters/src/texture.ts +94 -0
- package/packages/check/src/check.ts +10 -3
- package/packages/check/src/rules/assets.ts +4 -1
- package/packages/check/src/rules/frames.ts +2 -1
- package/packages/check/src/rules/layout.ts +30 -6
- package/packages/cli/package.json +2 -1
- package/packages/cli/src/blur.ts +15 -3
- package/packages/cli/src/cells.ts +227 -0
- package/packages/cli/src/commands/bench.ts +123 -0
- package/packages/cli/src/commands/blender.ts +72 -49
- package/packages/cli/src/commands/direct.ts +210 -0
- package/packages/cli/src/commands/doctor.ts +9 -1
- package/packages/cli/src/commands/look.ts +9 -2
- package/packages/cli/src/commands/lyrics.ts +47 -1
- package/packages/cli/src/commands/make.ts +797 -150
- package/packages/cli/src/commands/music.ts +78 -0
- package/packages/cli/src/commands/narrate.ts +238 -0
- package/packages/cli/src/commands/new.ts +15 -6
- package/packages/cli/src/commands/render.ts +94 -60
- package/packages/cli/src/commands/series.ts +110 -0
- package/packages/cli/src/commands/setup.ts +42 -3
- package/packages/cli/src/commands/theme.ts +222 -4
- package/packages/cli/src/deps.ts +6 -6
- package/packages/cli/src/kf.ts +14 -3
- package/packages/cli/src/lock.ts +8 -6
- package/packages/cli/src/mix.ts +131 -0
- package/packages/cli/src/pick.ts +110 -0
- package/packages/cli/src/pool.ts +34 -0
- package/packages/cli/src/segments.ts +402 -0
- package/packages/cli/src/vary.ts +124 -0
- package/packages/core/src/index.ts +1 -1
- package/packages/core/src/project.ts +26 -0
- package/packages/director/package.json +23 -0
- package/packages/director/src/assemble.ts +364 -0
- package/packages/director/src/brief.ts +122 -0
- package/packages/director/src/catalog.ts +49 -0
- package/packages/director/src/content.ts +343 -0
- package/packages/director/src/fallback.ts +58 -0
- package/packages/director/src/index.ts +13 -0
- package/packages/director/src/pattern.ts +90 -0
- package/packages/director/src/patterns/fill/cells.ts +115 -0
- package/packages/director/src/patterns/formula/stack.ts +93 -0
- package/packages/director/src/patterns/util.ts +58 -0
- package/packages/director/src/scene.ts +62 -0
- package/packages/director/src/select.ts +163 -0
- package/packages/director/src/transitions.ts +115 -0
- package/packages/gen/PROVIDERS.md +39 -4
- package/packages/gen/src/ask.ts +132 -0
- package/packages/gen/src/audio.ts +91 -0
- package/packages/gen/src/blender.ts +54 -0
- package/packages/gen/src/capabilities.ts +151 -0
- package/packages/gen/src/generate.ts +4 -2
- package/packages/gen/src/image/codex.ts +117 -0
- package/packages/gen/src/index.ts +10 -2
- package/packages/gen/src/keys.ts +26 -0
- package/packages/gen/src/music/elevenlabs.ts +41 -0
- package/packages/gen/src/music/index.ts +120 -0
- package/packages/gen/src/music/library.ts +73 -0
- package/packages/gen/src/music/lyria.ts +52 -0
- package/packages/gen/src/music/types.ts +28 -0
- package/packages/gen/src/registry.ts +6 -3
- package/packages/gen/src/tts/dashscope.ts +64 -0
- package/packages/gen/src/tts/elevenlabs.ts +84 -0
- package/packages/gen/src/tts/gemini.ts +73 -0
- package/packages/gen/src/tts/index.ts +241 -0
- package/packages/gen/src/tts/local.ts +42 -0
- package/packages/gen/src/tts/types.ts +44 -0
- package/packages/gen/src/types.ts +4 -0
- package/packages/gen/src/video/h3.ts +5 -0
- package/packages/gen/src/video/http.ts +1 -1
- package/packages/jizura/src/index.ts +2 -2
- package/packages/jizura/src/install.ts +198 -155
- package/packages/jizura/src/layer.ts +75 -32
- package/packages/jizura/src/pin.ts +4 -1
- package/packages/jizura/src/vendor.ts +140 -0
- package/packages/jizura/src/vite.ts +34 -7
- package/packages/jizura/vendor/JIZURA_LICENSE.txt +21 -0
- package/packages/jizura/vendor/NOTICE +43 -0
- package/packages/jizura/vendor/engine.json +47 -0
- package/packages/jizura/vendor/fonts/BlackHanSans-Regular.woff2 +0 -0
- package/packages/jizura/vendor/fonts/DotGothic16-Regular.woff2 +0 -0
- package/packages/jizura/vendor/fonts/GowunBatang-Bold.woff2 +0 -0
- package/packages/jizura/vendor/fonts/IBMPlexMono-Medium.ttf +0 -0
- package/packages/jizura/vendor/fonts/IBMPlexMono-SemiBold.ttf +0 -0
- package/packages/jizura/vendor/fonts/NotoSansKR-VF.woff2 +0 -0
- package/packages/jizura/vendor/fonts/NotoSerifKR-VF.woff2 +0 -0
- package/packages/jizura/vendor/fonts/OFL-blackhansans.txt +93 -0
- package/packages/jizura/vendor/fonts/OFL-dotgothic16.txt +93 -0
- package/packages/jizura/vendor/fonts/OFL-gowunbatang.txt +94 -0
- package/packages/jizura/vendor/fonts/OFL-ibmplexmono.txt +93 -0
- package/packages/jizura/vendor/fonts/OFL-notosanskr.txt +93 -0
- package/packages/jizura/vendor/fonts/OFL-notoserifkr.txt +94 -0
- package/packages/jizura/vendor/fonts/fonts.json +52 -0
- package/packages/jizura/vendor/jizura_engine.js +35024 -0
- package/packages/motion/README.md +11 -0
- package/packages/motion/package.json +1 -0
- package/packages/motion/src/captions.ts +32 -24
- package/packages/motion/src/moves/brushWipe.ts +14 -1
- package/packages/motion/src/moves/dress.ts +191 -0
- package/packages/motion/src/moves/echoStack.ts +3 -3
- package/packages/motion/src/moves/fit.ts +36 -0
- package/packages/motion/src/moves/ghostStack.ts +14 -5
- package/packages/motion/src/moves/imageSwarm.ts +8 -4
- package/packages/motion/src/moves/index.ts +23 -4
- package/packages/motion/src/moves/karaokeLine.ts +17 -3
- package/packages/motion/src/moves/menu.ts +139 -0
- package/packages/motion/src/moves/neonSign.ts +2 -1
- package/packages/motion/src/moves/slamWords.ts +14 -7
- package/packages/motion/src/moves/sparkTrail.ts +16 -0
- package/packages/motion/src/moves/splitFlap.ts +4 -2
- package/packages/motion/src/moves/stepWords.ts +19 -8
- package/packages/motion/src/moves/tickerRows.ts +13 -9
- package/packages/motion/src/moves/tokenPicker.ts +4 -3
- package/packages/motion/src/moves/wordWall.ts +23 -21
- package/packages/motion/src/moves/worldRoll.ts +2 -1
- package/packages/motion/src/transition3d/index.ts +10 -3
- package/packages/motion/src/transition3d/rig.ts +107 -9
- package/packages/motion/src/transition3d/shatter.ts +5 -2
- package/packages/motion/src/transition3d/space.ts +173 -0
- package/packages/motion/src/transition3d/whip.ts +19 -6
- package/packages/motion/src/transition3d/world.ts +54 -0
- package/packages/motion/src/transitions2d/catalog.ts +1 -1
- package/packages/patterns/package.json +15 -0
- package/packages/patterns/src/bars/rank.ts +119 -0
- package/packages/patterns/src/before-after/shrink.ts +127 -0
- package/packages/patterns/src/before-after/strike.ts +125 -0
- package/packages/patterns/src/code/terminal.ts +133 -0
- package/packages/patterns/src/compare/bars.ts +142 -0
- package/packages/patterns/src/compare/duel.ts +103 -0
- package/packages/patterns/src/end/lockup.ts +85 -0
- package/packages/patterns/src/end/slam.ts +91 -0
- package/packages/patterns/src/flow/graph.ts +250 -0
- package/packages/patterns/src/hook-number/counter.ts +101 -0
- package/packages/patterns/src/hook-number/roll.ts +179 -0
- package/packages/patterns/src/hook-number/slam.ts +91 -0
- package/packages/patterns/src/index.ts +38 -0
- package/packages/patterns/src/kit.ts +450 -0
- package/packages/patterns/src/limit/gauge.ts +117 -0
- package/packages/patterns/src/list/cards.ts +131 -0
- package/packages/patterns/src/list/cycle.ts +22 -0
- package/packages/patterns/src/media/panels.ts +100 -0
- package/packages/patterns/src/objects.ts +234 -0
- package/packages/patterns/src/quote/karaoke.ts +126 -0
- package/packages/patterns/src/quote/slam.ts +22 -0
- package/packages/patterns/src/screen/focus.ts +85 -0
- package/packages/patterns/src/steps/path.ts +137 -0
- package/packages/patterns/src/steps/stack.ts +111 -0
- package/packages/patterns/src/term-reveal/chips.ts +92 -0
- package/packages/patterns/src/term-reveal/expand.ts +127 -0
- package/packages/patterns/src/timeline/axis.ts +144 -0
- package/packages/patterns/src/title/mask.ts +85 -0
- package/packages/patterns/src/title/slam.ts +23 -0
- package/packages/patterns/src/toggle-sim/switch.ts +92 -0
- package/packages/patterns/src/trend/columns.ts +108 -0
- package/packages/patterns/src/trend/draw.ts +140 -0
- package/packages/patterns/src/types.ts +67 -0
- package/packages/patterns/src/words/moves.ts +42 -0
- package/packages/patterns/src/words/slam.ts +4 -0
- package/packages/plan/src/asks.ts +265 -0
- package/packages/plan/src/brief.ts +136 -7
- package/packages/plan/src/captions.ts +5 -2
- package/packages/plan/src/index.ts +7 -3
- package/packages/plan/src/llm.ts +64 -2
- package/packages/plan/src/look.ts +3 -1
- package/packages/plan/src/plan.ts +70 -14
- package/packages/plan/src/series.ts +76 -0
- package/packages/plan/src/themes.ts +65 -9
- package/packages/plan/src/types.ts +56 -2
- package/packages/themes/kit/THEME-MAKE.md +1 -0
- package/packages/themes/kit/fonts.json +743 -0
- package/packages/themes/package.json +10 -2
- package/packages/themes/src/color.ts +152 -0
- package/packages/themes/src/fx.ts +4 -2
- package/packages/themes/src/index.ts +74 -1
- package/packages/themes/src/look.ts +223 -0
- package/packages/themes/src/name.ts +9 -0
- package/packages/themes/src/palette.ts +194 -0
- package/packages/themes/src/slotkit.ts +92 -0
- package/packages/themes/src/slots.ts +61 -0
- package/packages/themes/src/stage3d.ts +20 -0
- package/packages/themes/src/types.ts +84 -0
- package/packages/themes/src/vary.ts +290 -0
- package/series/og-shorts.json +14 -0
- package/skills/kasaframe/SKILL.md +4 -3
- package/templates/_shared/project/lib/stage.ts +70 -50
- package/templates/_shared/scenes/default.ts +10 -7
- package/templates/_shared/scenes/drop.ts +11 -8
- package/templates/_shared/scenes/end.ts +9 -6
- package/templates/_shared/scenes/hook.ts +8 -6
- package/templates/_shared/scenes/step.ts +13 -4
- package/templates/ad/template.json +20 -1
- package/templates/demo/template.json +11 -0
- package/templates/explainer/template.json +54 -3
- package/templates/launch/template.json +34 -1
- package/templates/mv/scenes/default.ts +11 -8
- package/templates/mv/template.json +4 -2
- package/templates/promo/template.json +42 -1
- package/templates/shorts/scenes/hook.ts +8 -5
- package/templates/shorts/template.json +72 -3
- package/templates/square/template.json +21 -1
- package/themes/blueprint/THEME.md +64 -0
- package/themes/blueprint/effects.ts +570 -0
- package/themes/blueprint/sample/assets/og/logo.svg +12 -0
- package/themes/blueprint/sample/beats.json +1005 -0
- package/themes/blueprint/sample/index.html +11 -0
- package/themes/blueprint/sample/kf.json +59 -0
- package/themes/blueprint/sample/lib/facts.ts +29 -0
- package/themes/blueprint/sample/lib/sheet.ts +55 -0
- package/themes/blueprint/sample/main.ts +9 -0
- package/themes/blueprint/sample/scenes/end.ts +91 -0
- package/themes/blueprint/sample/scenes/gather.ts +75 -0
- package/themes/blueprint/sample/scenes/models.ts +117 -0
- package/themes/blueprint/sample/scenes/price.ts +94 -0
- package/themes/blueprint/sample/style.json +114 -0
- package/themes/blueprint/slots.ts +88 -0
- package/themes/blueprint/still.png +0 -0
- package/themes/blueprint/style.json +114 -0
- package/themes/blueprint/theme.json +137 -0
- package/themes/blueprint/variants/deep-rose-2.json +90 -0
- package/themes/blueprint/variants/deep-rose-suit.json +117 -0
- package/themes/blueprint/variants/deep-rose.json +117 -0
- package/themes/blueprint/variants/navy-coral-2.json +118 -0
- package/themes/blueprint/variants/navy-coral-suit.json +118 -0
- package/themes/blueprint/variants/navy-coral.json +128 -0
- package/themes/blueprint/variants/navy-orange-2.json +118 -0
- package/themes/blueprint/variants/navy-orange-3.json +91 -0
- package/themes/blueprint/variants/navy-orange-4.json +128 -0
- package/themes/blueprint/variants/navy-orange-mono.json +128 -0
- package/themes/blueprint/variants/navy-orange-plex.json +128 -0
- package/themes/blueprint/variants/navy-orange-suit.json +118 -0
- package/themes/blueprint/variants/navy-orange.json +118 -0
- package/themes/blueprint/variants/navy-rose-plex.json +127 -0
- package/themes/blueprint/variants/navy-rose.json +127 -0
- package/themes/cartography/THEME.md +61 -0
- package/themes/cartography/effects.ts +845 -0
- package/themes/cartography/sample/assets/og/logo.svg +12 -0
- package/themes/cartography/sample/beats.json +1005 -0
- package/themes/cartography/sample/index.html +11 -0
- package/themes/cartography/sample/kf.json +59 -0
- package/themes/cartography/sample/lib/facts.ts +29 -0
- package/themes/cartography/sample/lib/map.ts +39 -0
- package/themes/cartography/sample/main.ts +9 -0
- package/themes/cartography/sample/scenes/end.ts +56 -0
- package/themes/cartography/sample/scenes/gather.ts +46 -0
- package/themes/cartography/sample/scenes/models.ts +43 -0
- package/themes/cartography/sample/scenes/price.ts +73 -0
- package/themes/cartography/sample/style.json +103 -0
- package/themes/cartography/slots.ts +87 -0
- package/themes/cartography/still.png +0 -0
- package/themes/cartography/style.json +103 -0
- package/themes/cartography/theme.json +160 -0
- package/themes/cartography/variants/blush-amber.json +128 -0
- package/themes/cartography/variants/blush-indigo.json +128 -0
- package/themes/cartography/variants/blush-pink.json +136 -0
- package/themes/cartography/variants/chalk-plum.json +137 -0
- package/themes/cartography/variants/chalk-sky.json +100 -0
- package/themes/cartography/variants/cocoa-amber.json +128 -0
- package/themes/cartography/variants/deep-plum.json +125 -0
- package/themes/cartography/variants/dusk-coral.json +128 -0
- package/themes/cartography/variants/forest-lime.json +131 -0
- package/themes/cartography/variants/frost-teal.json +128 -0
- package/themes/cartography/variants/navy-green.json +131 -0
- package/themes/cartography/variants/navy-indigo.json +138 -0
- package/themes/cartography/variants/navy-sky.json +100 -0
- package/themes/cartography/variants/sage-green.json +127 -0
- package/themes/cartography/variants/wine-teal.json +128 -0
- package/themes/circuit-board/THEME.md +61 -0
- package/themes/circuit-board/effects.ts +576 -0
- package/themes/circuit-board/pcb.ts +103 -0
- package/themes/circuit-board/sample/assets/og/logo.svg +12 -0
- package/themes/circuit-board/sample/beats.json +1005 -0
- package/themes/circuit-board/sample/index.html +11 -0
- package/themes/circuit-board/sample/kf.json +59 -0
- package/themes/circuit-board/sample/lib/facts.ts +29 -0
- package/themes/circuit-board/sample/lib/kit.ts +45 -0
- package/themes/circuit-board/sample/main.ts +9 -0
- package/themes/circuit-board/sample/scenes/end.ts +102 -0
- package/themes/circuit-board/sample/scenes/gather.ts +76 -0
- package/themes/circuit-board/sample/scenes/models.ts +101 -0
- package/themes/circuit-board/sample/scenes/price.ts +109 -0
- package/themes/circuit-board/sample/style.json +104 -0
- package/themes/circuit-board/slots.ts +108 -0
- package/themes/circuit-board/still.png +0 -0
- package/themes/circuit-board/style.json +104 -0
- package/themes/circuit-board/theme.json +149 -0
- package/themes/circuit-board/variants/deep-gold-plex.json +133 -0
- package/themes/circuit-board/variants/deep-gold.json +123 -0
- package/themes/circuit-board/variants/dusk-gold-grain.json +138 -0
- package/themes/circuit-board/variants/dusk-gold-pretendard.json +118 -0
- package/themes/circuit-board/variants/dusk-gold.json +97 -0
- package/themes/circuit-board/variants/forest-gold-2.json +96 -0
- package/themes/circuit-board/variants/forest-gold-grain.json +129 -0
- package/themes/circuit-board/variants/forest-gold-mono.json +124 -0
- package/themes/circuit-board/variants/forest-gold.json +128 -0
- package/themes/circuit-board/variants/navy-gold-grain.json +124 -0
- package/themes/circuit-board/variants/navy-gold.json +101 -0
- package/themes/circuit-board/variants/wine-gold-grain.json +128 -0
- package/themes/circuit-board/variants/wine-gold-plex.json +133 -0
- package/themes/circuit-board/variants/wine-gold-pretendard.json +118 -0
- package/themes/circuit-board/variants/wine-gold.json +128 -0
- package/themes/clay-toy/THEME.md +73 -0
- package/themes/clay-toy/effects.ts +643 -0
- package/themes/clay-toy/glyphs.ts +32 -0
- package/themes/clay-toy/sample/assets/og/logo.svg +12 -0
- package/themes/clay-toy/sample/beats.json +1005 -0
- package/themes/clay-toy/sample/index.html +11 -0
- package/themes/clay-toy/sample/kf.json +59 -0
- package/themes/clay-toy/sample/lib/clay.ts +20 -0
- package/themes/clay-toy/sample/lib/facts.ts +29 -0
- package/themes/clay-toy/sample/main.ts +9 -0
- package/themes/clay-toy/sample/scenes/end.ts +81 -0
- package/themes/clay-toy/sample/scenes/gather.ts +53 -0
- package/themes/clay-toy/sample/scenes/models.ts +77 -0
- package/themes/clay-toy/sample/scenes/price.ts +91 -0
- package/themes/clay-toy/sample/style.json +101 -0
- package/themes/clay-toy/slots.ts +132 -0
- package/themes/clay-toy/still.png +0 -0
- package/themes/clay-toy/style.json +101 -0
- package/themes/clay-toy/theme.json +146 -0
- package/themes/clay-toy/variants/blush-coral-grain.json +126 -0
- package/themes/clay-toy/variants/blush-coral.json +123 -0
- package/themes/clay-toy/variants/chalk-indigo.json +97 -0
- package/themes/clay-toy/variants/chalk-jade.json +125 -0
- package/themes/clay-toy/variants/chalk-teal.json +120 -0
- package/themes/clay-toy/variants/frost-orange.json +125 -0
- package/themes/clay-toy/variants/frost-sky-grain.json +126 -0
- package/themes/clay-toy/variants/frost-sky.json +121 -0
- package/themes/clay-toy/variants/frost-teal.json +120 -0
- package/themes/clay-toy/variants/lilac-green.json +120 -0
- package/themes/clay-toy/variants/lilac-plum-round.json +120 -0
- package/themes/clay-toy/variants/lilac-plum.json +124 -0
- package/themes/clay-toy/variants/sage-indigo.json +125 -0
- package/themes/clay-toy/variants/sage-magenta.json +119 -0
- package/themes/clay-toy/variants/sand-amber.json +125 -0
- package/themes/conveyor-factory/THEME.md +71 -0
- package/themes/conveyor-factory/effects.ts +744 -0
- package/themes/conveyor-factory/sample/assets/og/logo.svg +12 -0
- package/themes/conveyor-factory/sample/beats.json +1005 -0
- package/themes/conveyor-factory/sample/index.html +11 -0
- package/themes/conveyor-factory/sample/kf.json +59 -0
- package/themes/conveyor-factory/sample/lib/factory.ts +48 -0
- package/themes/conveyor-factory/sample/lib/facts.ts +29 -0
- package/themes/conveyor-factory/sample/main.ts +9 -0
- package/themes/conveyor-factory/sample/scenes/end.ts +44 -0
- package/themes/conveyor-factory/sample/scenes/gather.ts +47 -0
- package/themes/conveyor-factory/sample/scenes/models.ts +91 -0
- package/themes/conveyor-factory/sample/scenes/price.ts +77 -0
- package/themes/conveyor-factory/sample/style.json +107 -0
- package/themes/conveyor-factory/slots.ts +121 -0
- package/themes/conveyor-factory/still.png +0 -0
- package/themes/conveyor-factory/style.json +107 -0
- package/themes/conveyor-factory/theme.json +142 -0
- package/themes/conveyor-factory/variants/cocoa-gold.json +125 -0
- package/themes/conveyor-factory/variants/dusk-gold.json +135 -0
- package/themes/conveyor-factory/variants/forest-gold.json +135 -0
- package/themes/conveyor-factory/variants/navy-amber.json +93 -0
- package/themes/conveyor-factory/variants/navy-gold.json +120 -0
- package/themes/conveyor-factory/variants/slate-amber-2.json +120 -0
- package/themes/conveyor-factory/variants/slate-amber-grain.json +125 -0
- package/themes/conveyor-factory/variants/slate-amber-suit.json +120 -0
- package/themes/conveyor-factory/variants/slate-amber.json +125 -0
- package/themes/conveyor-factory/variants/slate-gold-bold.json +120 -0
- package/themes/conveyor-factory/variants/slate-gold.json +120 -0
- package/themes/data-journal/THEME.md +74 -0
- package/themes/data-journal/effects.ts +570 -0
- package/themes/data-journal/sample/assets/og/logo.svg +12 -0
- package/themes/data-journal/sample/beats.json +1005 -0
- package/themes/data-journal/sample/index.html +11 -0
- package/themes/data-journal/sample/kf.json +59 -0
- package/themes/data-journal/sample/lib/facts.ts +29 -0
- package/themes/data-journal/sample/lib/page.ts +25 -0
- package/themes/data-journal/sample/main.ts +9 -0
- package/themes/data-journal/sample/scenes/end.ts +69 -0
- package/themes/data-journal/sample/scenes/gather.ts +58 -0
- package/themes/data-journal/sample/scenes/models.ts +51 -0
- package/themes/data-journal/sample/scenes/price.ts +73 -0
- package/themes/data-journal/sample/style.json +114 -0
- package/themes/data-journal/slots.ts +119 -0
- package/themes/data-journal/still.png +0 -0
- package/themes/data-journal/style.json +114 -0
- package/themes/data-journal/theme.json +140 -0
- package/themes/data-journal/variants/blush-green.json +129 -0
- package/themes/data-journal/variants/chalk-magenta.json +138 -0
- package/themes/data-journal/variants/cocoa-amber.json +140 -0
- package/themes/data-journal/variants/cocoa-sky.json +130 -0
- package/themes/data-journal/variants/deep-coral.json +135 -0
- package/themes/data-journal/variants/dusk-plum.json +132 -0
- package/themes/data-journal/variants/forest-green.json +138 -0
- package/themes/data-journal/variants/frost-indigo.json +141 -0
- package/themes/data-journal/variants/lilac-amber.json +141 -0
- package/themes/data-journal/variants/navy-indigo.json +131 -0
- package/themes/data-journal/variants/navy-lime.json +128 -0
- package/themes/data-journal/variants/snow-sky.json +125 -0
- package/themes/data-journal/variants/wine-indigo.json +129 -0
- package/themes/data-journal/variants/wine-magenta.json +138 -0
- package/themes/data-journal/variants/wine-teal.json +97 -0
- package/themes/debug-hud/slots.ts +99 -0
- package/themes/debug-hud/theme.json +56 -0
- package/themes/debug-hud/variants/cocoa-gold.json +113 -0
- package/themes/debug-hud/variants/cocoa-teal.json +119 -0
- package/themes/debug-hud/variants/deep-cyan.json +85 -0
- package/themes/debug-hud/variants/deep-teal-scan.json +118 -0
- package/themes/debug-hud/variants/deep-teal.json +112 -0
- package/themes/debug-hud/variants/dusk-plum.json +116 -0
- package/themes/debug-hud/variants/dusk-violet.json +112 -0
- package/themes/debug-hud/variants/forest-amber.json +86 -0
- package/themes/debug-hud/variants/forest-lime.json +117 -0
- package/themes/debug-hud/variants/forest-magenta.json +111 -0
- package/themes/debug-hud/variants/navy-jade.json +79 -0
- package/themes/debug-hud/variants/slate-green.json +112 -0
- package/themes/debug-hud/variants/wine-gold.json +119 -0
- package/themes/debug-hud/variants/wine-jade.json +83 -0
- package/themes/debug-hud/variants/wine-sky.json +112 -0
- package/themes/depth-flight/THEME.md +54 -0
- package/themes/depth-flight/effects.ts +396 -0
- package/themes/depth-flight/sample/assets/og/logo.svg +12 -0
- package/themes/depth-flight/sample/beats.json +1005 -0
- package/themes/depth-flight/sample/index.html +11 -0
- package/themes/depth-flight/sample/kf.json +59 -0
- package/themes/depth-flight/sample/lib/facts.ts +29 -0
- package/themes/depth-flight/sample/lib/fly.ts +15 -0
- package/themes/depth-flight/sample/main.ts +9 -0
- package/themes/depth-flight/sample/scenes/end.ts +51 -0
- package/themes/depth-flight/sample/scenes/gather.ts +40 -0
- package/themes/depth-flight/sample/scenes/models.ts +75 -0
- package/themes/depth-flight/sample/scenes/price.ts +50 -0
- package/themes/depth-flight/sample/style.json +94 -0
- package/themes/depth-flight/slots.ts +196 -0
- package/themes/depth-flight/still.png +0 -0
- package/themes/depth-flight/style.json +94 -0
- package/themes/depth-flight/theme.json +119 -0
- package/themes/depth-flight/variants/cocoa-cyan.json +121 -0
- package/themes/depth-flight/variants/cocoa-indigo.json +121 -0
- package/themes/depth-flight/variants/cocoa-violet.json +122 -0
- package/themes/depth-flight/variants/deep-sky.json +126 -0
- package/themes/depth-flight/variants/dusk-violet-bold.json +117 -0
- package/themes/depth-flight/variants/dusk-violet.json +132 -0
- package/themes/depth-flight/variants/forest-blue.json +116 -0
- package/themes/depth-flight/variants/forest-cyan.json +122 -0
- package/themes/depth-flight/variants/navy-sky-grain.json +122 -0
- package/themes/depth-flight/variants/navy-sky.json +116 -0
- package/themes/depth-flight/variants/night-teal.json +116 -0
- package/themes/depth-flight/variants/slate-cyan.json +127 -0
- package/themes/depth-flight/variants/slate-indigo.json +116 -0
- package/themes/depth-flight/variants/slate-sky.json +94 -0
- package/themes/depth-flight/variants/slate-violet.json +121 -0
- package/themes/film-reel/THEME.md +60 -0
- package/themes/film-reel/effects.ts +477 -0
- package/themes/film-reel/sample/assets/og/logo.svg +12 -0
- package/themes/film-reel/sample/beats.json +1005 -0
- package/themes/film-reel/sample/index.html +11 -0
- package/themes/film-reel/sample/kf.json +59 -0
- package/themes/film-reel/sample/lib/facts.ts +29 -0
- package/themes/film-reel/sample/lib/reel.ts +9 -0
- package/themes/film-reel/sample/main.ts +9 -0
- package/themes/film-reel/sample/scenes/end.ts +31 -0
- package/themes/film-reel/sample/scenes/gather.ts +40 -0
- package/themes/film-reel/sample/scenes/models.ts +37 -0
- package/themes/film-reel/sample/scenes/price.ts +63 -0
- package/themes/film-reel/sample/style.json +103 -0
- package/themes/film-reel/slots.ts +75 -0
- package/themes/film-reel/still.png +0 -0
- package/themes/film-reel/style.json +103 -0
- package/themes/film-reel/theme.json +142 -0
- package/themes/film-reel/variants/chalk-amber.json +124 -0
- package/themes/film-reel/variants/chalk-pink.json +123 -0
- package/themes/film-reel/variants/cream-violet.json +121 -0
- package/themes/film-reel/variants/frost-blue.json +126 -0
- package/themes/film-reel/variants/frost-green.json +123 -0
- package/themes/film-reel/variants/frost-indigo.json +121 -0
- package/themes/film-reel/variants/frost-jade.json +120 -0
- package/themes/film-reel/variants/frost-plum.json +120 -0
- package/themes/film-reel/variants/frost-teal.json +93 -0
- package/themes/film-reel/variants/lilac-gold.json +120 -0
- package/themes/film-reel/variants/lilac-jade.json +123 -0
- package/themes/film-reel/variants/lilac-magenta.json +125 -0
- package/themes/film-reel/variants/lilac-violet.json +122 -0
- package/themes/film-reel/variants/sand-cyan.json +124 -0
- package/themes/film-reel/variants/sand-lime.json +96 -0
- package/themes/glass-light/THEME.md +55 -0
- package/themes/glass-light/effects.ts +433 -0
- package/themes/glass-light/sample/assets/og/logo.svg +12 -0
- package/themes/glass-light/sample/beats.json +1005 -0
- package/themes/glass-light/sample/index.html +11 -0
- package/themes/glass-light/sample/kf.json +59 -0
- package/themes/glass-light/sample/lib/facts.ts +29 -0
- package/themes/glass-light/sample/lib/glass.ts +81 -0
- package/themes/glass-light/sample/main.ts +9 -0
- package/themes/glass-light/sample/scenes/end.ts +43 -0
- package/themes/glass-light/sample/scenes/gather.ts +38 -0
- package/themes/glass-light/sample/scenes/models.ts +46 -0
- package/themes/glass-light/sample/scenes/price.ts +46 -0
- package/themes/glass-light/sample/style.json +94 -0
- package/themes/glass-light/slots.ts +198 -0
- package/themes/glass-light/still.png +0 -0
- package/themes/glass-light/style.json +94 -0
- package/themes/glass-light/theme.json +119 -0
- package/themes/glass-light/variants/blush-coral.json +116 -0
- package/themes/glass-light/variants/blush-orange.json +115 -0
- package/themes/glass-light/variants/blush-pink.json +119 -0
- package/themes/glass-light/variants/blush-plum.json +115 -0
- package/themes/glass-light/variants/blush-teal.json +115 -0
- package/themes/glass-light/variants/cream-blue.json +125 -0
- package/themes/glass-light/variants/cream-pink.json +87 -0
- package/themes/glass-light/variants/cream-violet.json +119 -0
- package/themes/glass-light/variants/frost-green.json +115 -0
- package/themes/glass-light/variants/frost-sky-suit.json +115 -0
- package/themes/glass-light/variants/frost-sky.json +121 -0
- package/themes/glass-light/variants/lilac-gold.json +126 -0
- package/themes/glass-light/variants/lilac-plum.json +114 -0
- package/themes/glass-light/variants/sage-green.json +119 -0
- package/themes/glass-light/variants/sage-rose.json +114 -0
- package/themes/ink-wash/THEME.md +59 -0
- package/themes/ink-wash/effects.ts +574 -0
- package/themes/ink-wash/sample/assets/og/logo.svg +12 -0
- package/themes/ink-wash/sample/beats.json +1005 -0
- package/themes/ink-wash/sample/index.html +11 -0
- package/themes/ink-wash/sample/kf.json +59 -0
- package/themes/ink-wash/sample/lib/facts.ts +29 -0
- package/themes/ink-wash/sample/lib/ink.ts +35 -0
- package/themes/ink-wash/sample/main.ts +9 -0
- package/themes/ink-wash/sample/scenes/end.ts +72 -0
- package/themes/ink-wash/sample/scenes/gather.ts +53 -0
- package/themes/ink-wash/sample/scenes/models.ts +58 -0
- package/themes/ink-wash/sample/scenes/price.ts +85 -0
- package/themes/ink-wash/sample/style.json +108 -0
- package/themes/ink-wash/slots.ts +150 -0
- package/themes/ink-wash/still.png +0 -0
- package/themes/ink-wash/style.json +108 -0
- package/themes/ink-wash/theme.json +131 -0
- package/themes/ink-wash/variants/blush-orange.json +125 -0
- package/themes/ink-wash/variants/blush-rose.json +124 -0
- package/themes/ink-wash/variants/chalk-coral-grain.json +125 -0
- package/themes/ink-wash/variants/chalk-coral-serif.json +118 -0
- package/themes/ink-wash/variants/chalk-coral.json +120 -0
- package/themes/ink-wash/variants/chalk-orange-grain.json +97 -0
- package/themes/ink-wash/variants/chalk-orange-serif.json +120 -0
- package/themes/ink-wash/variants/chalk-orange.json +120 -0
- package/themes/ink-wash/variants/frost-coral.json +119 -0
- package/themes/ink-wash/variants/frost-rose.json +91 -0
- package/themes/ink-wash/variants/lilac-coral.json +96 -0
- package/themes/ink-wash/variants/sage-coral.json +119 -0
- package/themes/ink-wash/variants/sage-rose.json +119 -0
- package/themes/ink-wash/variants/sand-coral.json +125 -0
- package/themes/ink-wash/variants/sand-orange.json +120 -0
- package/themes/iso-diorama/THEME.md +54 -0
- package/themes/iso-diorama/effects.ts +374 -0
- package/themes/iso-diorama/sample/assets/og/logo.svg +12 -0
- package/themes/iso-diorama/sample/beats.json +1005 -0
- package/themes/iso-diorama/sample/index.html +11 -0
- package/themes/iso-diorama/sample/kf.json +59 -0
- package/themes/iso-diorama/sample/lib/facts.ts +29 -0
- package/themes/iso-diorama/sample/lib/iso.ts +22 -0
- package/themes/iso-diorama/sample/main.ts +9 -0
- package/themes/iso-diorama/sample/scenes/end.ts +41 -0
- package/themes/iso-diorama/sample/scenes/gather.ts +39 -0
- package/themes/iso-diorama/sample/scenes/models.ts +55 -0
- package/themes/iso-diorama/sample/scenes/price.ts +49 -0
- package/themes/iso-diorama/sample/style.json +98 -0
- package/themes/iso-diorama/slots.ts +222 -0
- package/themes/iso-diorama/still.png +0 -0
- package/themes/iso-diorama/style.json +98 -0
- package/themes/iso-diorama/theme.json +131 -0
- package/themes/iso-diorama/variants/chalk-plum.json +121 -0
- package/themes/iso-diorama/variants/cocoa-gold.json +93 -0
- package/themes/iso-diorama/variants/cocoa-indigo.json +122 -0
- package/themes/iso-diorama/variants/cocoa-rose.json +125 -0
- package/themes/iso-diorama/variants/deep-green.json +125 -0
- package/themes/iso-diorama/variants/deep-plum.json +120 -0
- package/themes/iso-diorama/variants/dusk-blue.json +126 -0
- package/themes/iso-diorama/variants/dusk-coral.json +122 -0
- package/themes/iso-diorama/variants/dusk-jade.json +121 -0
- package/themes/iso-diorama/variants/forest-cyan.json +127 -0
- package/themes/iso-diorama/variants/frost-blue.json +126 -0
- package/themes/iso-diorama/variants/frost-green.json +126 -0
- package/themes/iso-diorama/variants/frost-orange.json +125 -0
- package/themes/iso-diorama/variants/frost-teal.json +123 -0
- package/themes/iso-diorama/variants/navy-amber.json +125 -0
- package/themes/isotype/THEME.md +58 -0
- package/themes/isotype/effects.ts +297 -0
- package/themes/isotype/kit.ts +102 -0
- package/themes/isotype/sample/assets/og/logo.svg +12 -0
- package/themes/isotype/sample/beats.json +1005 -0
- package/themes/isotype/sample/index.html +11 -0
- package/themes/isotype/sample/kf.json +59 -0
- package/themes/isotype/sample/lib/chart.ts +47 -0
- package/themes/isotype/sample/lib/facts.ts +29 -0
- package/themes/isotype/sample/main.ts +9 -0
- package/themes/isotype/sample/scenes/end.ts +80 -0
- package/themes/isotype/sample/scenes/gather.ts +65 -0
- package/themes/isotype/sample/scenes/models.ts +75 -0
- package/themes/isotype/sample/scenes/price.ts +90 -0
- package/themes/isotype/sample/style.json +101 -0
- package/themes/isotype/slots.ts +74 -0
- package/themes/isotype/still.png +0 -0
- package/themes/isotype/style.json +101 -0
- package/themes/isotype/theme.json +140 -0
- package/themes/isotype/variants/blush-rose.json +127 -0
- package/themes/isotype/variants/chalk-amber.json +123 -0
- package/themes/isotype/variants/chalk-coral-bold.json +118 -0
- package/themes/isotype/variants/chalk-coral.json +117 -0
- package/themes/isotype/variants/chalk-orange.json +128 -0
- package/themes/isotype/variants/frost-amber-plex.json +128 -0
- package/themes/isotype/variants/frost-amber.json +123 -0
- package/themes/isotype/variants/frost-coral-grain.json +95 -0
- package/themes/isotype/variants/frost-coral.json +123 -0
- package/themes/isotype/variants/frost-orange.json +123 -0
- package/themes/isotype/variants/lilac-rose.json +127 -0
- package/themes/isotype/variants/sage-coral.json +90 -0
- package/themes/isotype/variants/sand-amber.json +118 -0
- package/themes/isotype/variants/sand-orange.json +128 -0
- package/themes/kawaii-sticker/THEME.md +44 -0
- package/themes/kawaii-sticker/effects.ts +412 -0
- package/themes/kawaii-sticker/sample/assets/stickers/bolt.png +0 -0
- package/themes/kawaii-sticker/sample/assets/stickers/bubble.png +0 -0
- package/themes/kawaii-sticker/sample/assets/stickers/clock.png +0 -0
- package/themes/kawaii-sticker/sample/assets/stickers/coin.png +0 -0
- package/themes/kawaii-sticker/sample/assets/stickers/moon.png +0 -0
- package/themes/kawaii-sticker/sample/assets/stickers/og-logo.svg +12 -0
- package/themes/kawaii-sticker/sample/assets/stickers/server.png +0 -0
- package/themes/kawaii-sticker/sample/beats.json +1 -0
- package/themes/kawaii-sticker/sample/index.html +11 -0
- package/themes/kawaii-sticker/sample/kf.json +66 -0
- package/themes/kawaii-sticker/sample/lib/og.ts +54 -0
- package/themes/kawaii-sticker/sample/main.ts +9 -0
- package/themes/kawaii-sticker/sample/scenes/end.ts +37 -0
- package/themes/kawaii-sticker/sample/scenes/gather.ts +50 -0
- package/themes/kawaii-sticker/sample/scenes/models.ts +43 -0
- package/themes/kawaii-sticker/sample/scenes/one.ts +45 -0
- package/themes/kawaii-sticker/sample/scenes/price.ts +47 -0
- package/themes/kawaii-sticker/sample/style.json +99 -0
- package/themes/kawaii-sticker/slots.ts +95 -0
- package/themes/kawaii-sticker/still.png +0 -0
- package/themes/kawaii-sticker/style.json +99 -0
- package/themes/kawaii-sticker/theme.json +145 -0
- package/themes/kawaii-sticker/variants/blush-coral.json +134 -0
- package/themes/kawaii-sticker/variants/blush-teal.json +129 -0
- package/themes/kawaii-sticker/variants/blush-violet.json +134 -0
- package/themes/kawaii-sticker/variants/cream-gold.json +135 -0
- package/themes/kawaii-sticker/variants/deep-jade.json +134 -0
- package/themes/kawaii-sticker/variants/dusk-green.json +134 -0
- package/themes/kawaii-sticker/variants/dusk-indigo.json +130 -0
- package/themes/kawaii-sticker/variants/dusk-sky.json +135 -0
- package/themes/kawaii-sticker/variants/dusk-teal.json +134 -0
- package/themes/kawaii-sticker/variants/frost-blue.json +135 -0
- package/themes/kawaii-sticker/variants/frost-plum.json +133 -0
- package/themes/kawaii-sticker/variants/lilac-violet.json +133 -0
- package/themes/kawaii-sticker/variants/navy-gold.json +134 -0
- package/themes/kawaii-sticker/variants/navy-plum.json +134 -0
- package/themes/kawaii-sticker/variants/sage-sky.json +107 -0
- package/themes/lab-notebook/THEME.md +56 -0
- package/themes/lab-notebook/effects.ts +423 -0
- package/themes/lab-notebook/kit.ts +84 -0
- package/themes/lab-notebook/sample/assets/og/logo.svg +12 -0
- package/themes/lab-notebook/sample/beats.json +1005 -0
- package/themes/lab-notebook/sample/index.html +11 -0
- package/themes/lab-notebook/sample/kf.json +59 -0
- package/themes/lab-notebook/sample/lib/facts.ts +29 -0
- package/themes/lab-notebook/sample/lib/note.ts +53 -0
- package/themes/lab-notebook/sample/main.ts +9 -0
- package/themes/lab-notebook/sample/scenes/end.ts +127 -0
- package/themes/lab-notebook/sample/scenes/gather.ts +83 -0
- package/themes/lab-notebook/sample/scenes/models.ts +73 -0
- package/themes/lab-notebook/sample/scenes/price.ts +89 -0
- package/themes/lab-notebook/sample/style.json +109 -0
- package/themes/lab-notebook/slots.ts +75 -0
- package/themes/lab-notebook/still.png +0 -0
- package/themes/lab-notebook/style.json +109 -0
- package/themes/lab-notebook/theme.json +141 -0
- package/themes/lab-notebook/variants/blush-orange.json +125 -0
- package/themes/lab-notebook/variants/chalk-coral.json +126 -0
- package/themes/lab-notebook/variants/chalk-orange.json +129 -0
- package/themes/lab-notebook/variants/chalk-rose-2.json +96 -0
- package/themes/lab-notebook/variants/chalk-rose.json +125 -0
- package/themes/lab-notebook/variants/frost-orange.json +101 -0
- package/themes/lab-notebook/variants/frost-rose-grain.json +101 -0
- package/themes/lab-notebook/variants/frost-rose.json +96 -0
- package/themes/lab-notebook/variants/lilac-rose-2.json +96 -0
- package/themes/lab-notebook/variants/lilac-rose.json +128 -0
- package/themes/lab-notebook/variants/sage-rose.json +124 -0
- package/themes/lab-notebook/variants/sand-orange-grain.json +101 -0
- package/themes/lab-notebook/variants/sand-orange.json +129 -0
- package/themes/lab-notebook/variants/snow-orange.json +129 -0
- package/themes/lab-notebook/variants/snow-rose.json +124 -0
- package/themes/manga-panel/THEME.md +60 -0
- package/themes/manga-panel/effects.ts +791 -0
- package/themes/manga-panel/sample/assets/og/logo.svg +12 -0
- package/themes/manga-panel/sample/beats.json +1005 -0
- package/themes/manga-panel/sample/index.html +11 -0
- package/themes/manga-panel/sample/kf.json +59 -0
- package/themes/manga-panel/sample/lib/facts.ts +29 -0
- package/themes/manga-panel/sample/lib/manga.ts +58 -0
- package/themes/manga-panel/sample/main.ts +9 -0
- package/themes/manga-panel/sample/scenes/end.ts +96 -0
- package/themes/manga-panel/sample/scenes/gather.ts +100 -0
- package/themes/manga-panel/sample/scenes/models.ts +96 -0
- package/themes/manga-panel/sample/scenes/price.ts +154 -0
- package/themes/manga-panel/sample/style.json +105 -0
- package/themes/manga-panel/slots.ts +127 -0
- package/themes/manga-panel/still.png +0 -0
- package/themes/manga-panel/style.json +105 -0
- package/themes/manga-panel/theme.json +148 -0
- package/themes/manga-panel/variants/chalk-cyan-dots.json +122 -0
- package/themes/manga-panel/variants/chalk-cyan.json +122 -0
- package/themes/manga-panel/variants/chalk-magenta.json +121 -0
- package/themes/manga-panel/variants/chalk-orange-bold.json +117 -0
- package/themes/manga-panel/variants/chalk-orange.json +122 -0
- package/themes/manga-panel/variants/chalk-rose-dots.json +121 -0
- package/themes/manga-panel/variants/chalk-rose.json +116 -0
- package/themes/manga-panel/variants/chalk-sky-bold.json +117 -0
- package/themes/manga-panel/variants/chalk-sky.json +117 -0
- package/themes/manga-panel/variants/snow-cyan.json +117 -0
- package/themes/manga-panel/variants/snow-magenta-dots.json +121 -0
- package/themes/manga-panel/variants/snow-magenta.json +116 -0
- package/themes/manga-panel/variants/snow-rose-dots.json +121 -0
- package/themes/manga-panel/variants/snow-rose.json +116 -0
- package/themes/manga-panel/variants/snow-sky.json +95 -0
- package/themes/marble-run/THEME.md +73 -0
- package/themes/marble-run/effects.ts +678 -0
- package/themes/marble-run/sample/assets/og/logo.svg +12 -0
- package/themes/marble-run/sample/beats.json +1005 -0
- package/themes/marble-run/sample/index.html +11 -0
- package/themes/marble-run/sample/kf.json +59 -0
- package/themes/marble-run/sample/lib/facts.ts +29 -0
- package/themes/marble-run/sample/lib/run.ts +30 -0
- package/themes/marble-run/sample/main.ts +9 -0
- package/themes/marble-run/sample/scenes/end.ts +58 -0
- package/themes/marble-run/sample/scenes/gather.ts +51 -0
- package/themes/marble-run/sample/scenes/models.ts +73 -0
- package/themes/marble-run/sample/scenes/price.ts +51 -0
- package/themes/marble-run/sample/style.json +109 -0
- package/themes/marble-run/slots.ts +116 -0
- package/themes/marble-run/still.png +0 -0
- package/themes/marble-run/style.json +109 -0
- package/themes/marble-run/theme.json +145 -0
- package/themes/marble-run/variants/cocoa-amber.json +128 -0
- package/themes/marble-run/variants/cocoa-orange.json +142 -0
- package/themes/marble-run/variants/dusk-orange.json +133 -0
- package/themes/marble-run/variants/forest-gold.json +128 -0
- package/themes/marble-run/variants/navy-gold.json +137 -0
- package/themes/marble-run/variants/night-gold-grain.json +105 -0
- package/themes/marble-run/variants/night-gold.json +137 -0
- package/themes/marble-run/variants/slate-gold-grain.json +133 -0
- package/themes/marble-run/variants/slate-gold.json +133 -0
- package/themes/marble-run/variants/slate-orange-grain.json +105 -0
- package/themes/marble-run/variants/slate-orange-mono.json +128 -0
- package/themes/marble-run/variants/slate-orange-serif.json +128 -0
- package/themes/marble-run/variants/slate-orange.json +131 -0
- package/themes/neon-sign/THEME.md +59 -0
- package/themes/neon-sign/effects.ts +424 -0
- package/themes/neon-sign/sample/assets/og/logo.svg +12 -0
- package/themes/neon-sign/sample/beats.json +1005 -0
- package/themes/neon-sign/sample/index.html +11 -0
- package/themes/neon-sign/sample/kf.json +59 -0
- package/themes/neon-sign/sample/lib/facts.ts +29 -0
- package/themes/neon-sign/sample/lib/neon.ts +50 -0
- package/themes/neon-sign/sample/main.ts +9 -0
- package/themes/neon-sign/sample/scenes/end.ts +62 -0
- package/themes/neon-sign/sample/scenes/gather.ts +59 -0
- package/themes/neon-sign/sample/scenes/models.ts +63 -0
- package/themes/neon-sign/sample/scenes/price.ts +74 -0
- package/themes/neon-sign/sample/style.json +104 -0
- package/themes/neon-sign/slots.ts +110 -0
- package/themes/neon-sign/still.png +0 -0
- package/themes/neon-sign/style.json +104 -0
- package/themes/neon-sign/theme.json +136 -0
- package/themes/neon-sign/variants/deep-plum.json +121 -0
- package/themes/neon-sign/variants/deep-violet.json +121 -0
- package/themes/neon-sign/variants/dusk-plum.json +121 -0
- package/themes/neon-sign/variants/forest-magenta.json +126 -0
- package/themes/neon-sign/variants/forest-plum.json +98 -0
- package/themes/neon-sign/variants/night-coral.json +122 -0
- package/themes/neon-sign/variants/night-magenta-round.json +121 -0
- package/themes/neon-sign/variants/night-magenta.json +126 -0
- package/themes/neon-sign/variants/night-pink.json +125 -0
- package/themes/neon-sign/variants/night-violet.json +126 -0
- package/themes/neon-sign/variants/slate-plum.json +120 -0
- package/themes/neon-sign/variants/slate-violet.json +127 -0
- package/themes/neon-sign/variants/wine-coral.json +127 -0
- package/themes/neon-sign/variants/wine-pink.json +125 -0
- package/themes/neon-sign/variants/wine-plum.json +126 -0
- package/themes/newspaper/THEME.md +67 -0
- package/themes/newspaper/effects.ts +347 -0
- package/themes/newspaper/sample/assets/og/logo.svg +12 -0
- package/themes/newspaper/sample/beats.json +1005 -0
- package/themes/newspaper/sample/index.html +11 -0
- package/themes/newspaper/sample/kf.json +59 -0
- package/themes/newspaper/sample/lib/facts.ts +29 -0
- package/themes/newspaper/sample/lib/page.ts +27 -0
- package/themes/newspaper/sample/main.ts +9 -0
- package/themes/newspaper/sample/scenes/end.ts +62 -0
- package/themes/newspaper/sample/scenes/gather.ts +66 -0
- package/themes/newspaper/sample/scenes/models.ts +61 -0
- package/themes/newspaper/sample/scenes/price.ts +103 -0
- package/themes/newspaper/sample/style.json +100 -0
- package/themes/newspaper/slots.ts +73 -0
- package/themes/newspaper/still.png +0 -0
- package/themes/newspaper/style.json +100 -0
- package/themes/newspaper/theme.json +140 -0
- package/themes/newspaper/variants/blush-amber.json +123 -0
- package/themes/newspaper/variants/blush-orange.json +125 -0
- package/themes/newspaper/variants/chalk-cyan.json +119 -0
- package/themes/newspaper/variants/chalk-indigo.json +124 -0
- package/themes/newspaper/variants/chalk-jade.json +95 -0
- package/themes/newspaper/variants/chalk-magenta.json +127 -0
- package/themes/newspaper/variants/frost-plum.json +123 -0
- package/themes/newspaper/variants/frost-sky.json +129 -0
- package/themes/newspaper/variants/frost-violet.json +96 -0
- package/themes/newspaper/variants/lilac-coral.json +124 -0
- package/themes/newspaper/variants/lilac-green.json +127 -0
- package/themes/newspaper/variants/sage-blue.json +124 -0
- package/themes/newspaper/variants/sage-lime.json +117 -0
- package/themes/newspaper/variants/sand-gold.json +124 -0
- package/themes/newspaper/variants/sand-jade.json +123 -0
- package/themes/og-measure/slots.ts +88 -0
- package/themes/og-measure/theme.json +62 -0
- package/themes/og-measure/variants/chalk-orange.json +102 -0
- package/themes/og-measure/variants/cocoa-amber.json +138 -0
- package/themes/og-measure/variants/cocoa-cyan.json +138 -0
- package/themes/og-measure/variants/cocoa-gold.json +129 -0
- package/themes/og-measure/variants/cream-lime.json +137 -0
- package/themes/og-measure/variants/deep-jade.json +100 -0
- package/themes/og-measure/variants/dusk-violet.json +133 -0
- package/themes/og-measure/variants/forest-cyan.json +123 -0
- package/themes/og-measure/variants/frost-amber.json +129 -0
- package/themes/og-measure/variants/frost-cyan.json +133 -0
- package/themes/og-measure/variants/lilac-plum.json +127 -0
- package/themes/og-measure/variants/lilac-violet.json +139 -0
- package/themes/og-measure/variants/sage-green.json +137 -0
- package/themes/og-measure/variants/wine-jade.json +127 -0
- package/themes/og-measure/variants/wine-plum.json +127 -0
- package/themes/orbit-system/THEME.md +77 -0
- package/themes/orbit-system/effects.ts +665 -0
- package/themes/orbit-system/sample/assets/og/logo.svg +12 -0
- package/themes/orbit-system/sample/beats.json +1005 -0
- package/themes/orbit-system/sample/index.html +11 -0
- package/themes/orbit-system/sample/kf.json +59 -0
- package/themes/orbit-system/sample/lib/facts.ts +29 -0
- package/themes/orbit-system/sample/lib/orbit.ts +84 -0
- package/themes/orbit-system/sample/main.ts +9 -0
- package/themes/orbit-system/sample/scenes/end.ts +50 -0
- package/themes/orbit-system/sample/scenes/gather.ts +35 -0
- package/themes/orbit-system/sample/scenes/models.ts +49 -0
- package/themes/orbit-system/sample/scenes/price.ts +57 -0
- package/themes/orbit-system/sample/style.json +108 -0
- package/themes/orbit-system/slots.ts +174 -0
- package/themes/orbit-system/still.png +0 -0
- package/themes/orbit-system/style.json +108 -0
- package/themes/orbit-system/theme.json +142 -0
- package/themes/orbit-system/variants/cocoa-amber.json +122 -0
- package/themes/orbit-system/variants/cocoa-coral.json +127 -0
- package/themes/orbit-system/variants/cocoa-cyan.json +127 -0
- package/themes/orbit-system/variants/deep-cyan.json +132 -0
- package/themes/orbit-system/variants/deep-sky.json +122 -0
- package/themes/orbit-system/variants/dusk-magenta.json +121 -0
- package/themes/orbit-system/variants/dusk-plum-pretendard.json +121 -0
- package/themes/orbit-system/variants/dusk-plum.json +127 -0
- package/themes/orbit-system/variants/dusk-violet.json +122 -0
- package/themes/orbit-system/variants/forest-coral.json +126 -0
- package/themes/orbit-system/variants/forest-plum.json +94 -0
- package/themes/orbit-system/variants/navy-amber.json +127 -0
- package/themes/orbit-system/variants/wine-amber.json +127 -0
- package/themes/orbit-system/variants/wine-coral.json +123 -0
- package/themes/orbit-system/variants/wine-magenta.json +126 -0
- package/themes/origami-fold/THEME.md +74 -0
- package/themes/origami-fold/effects.ts +535 -0
- package/themes/origami-fold/sample/assets/og/logo.svg +12 -0
- package/themes/origami-fold/sample/beats.json +1005 -0
- package/themes/origami-fold/sample/index.html +11 -0
- package/themes/origami-fold/sample/kf.json +59 -0
- package/themes/origami-fold/sample/lib/facts.ts +29 -0
- package/themes/origami-fold/sample/lib/fold.ts +53 -0
- package/themes/origami-fold/sample/main.ts +9 -0
- package/themes/origami-fold/sample/scenes/end.ts +51 -0
- package/themes/origami-fold/sample/scenes/gather.ts +37 -0
- package/themes/origami-fold/sample/scenes/models.ts +46 -0
- package/themes/origami-fold/sample/scenes/price.ts +54 -0
- package/themes/origami-fold/sample/style.json +108 -0
- package/themes/origami-fold/slots.ts +176 -0
- package/themes/origami-fold/still.png +0 -0
- package/themes/origami-fold/style.json +108 -0
- package/themes/origami-fold/theme.json +123 -0
- package/themes/origami-fold/variants/deep-amber-sunflower.json +122 -0
- package/themes/origami-fold/variants/deep-amber.json +122 -0
- package/themes/origami-fold/variants/deep-orange.json +128 -0
- package/themes/origami-fold/variants/forest-coral.json +127 -0
- package/themes/origami-fold/variants/forest-pink.json +123 -0
- package/themes/origami-fold/variants/forest-rose.json +100 -0
- package/themes/origami-fold/variants/frost-amber-grain.json +127 -0
- package/themes/origami-fold/variants/frost-amber-paper.json +128 -0
- package/themes/origami-fold/variants/frost-amber.json +95 -0
- package/themes/origami-fold/variants/sage-coral-grain.json +100 -0
- package/themes/origami-fold/variants/sage-coral-sunflower.json +122 -0
- package/themes/origami-fold/variants/sage-coral.json +128 -0
- package/themes/origami-fold/variants/sage-pink-grain.json +127 -0
- package/themes/origami-fold/variants/sage-pink-round.json +124 -0
- package/themes/origami-fold/variants/sage-pink.json +127 -0
- package/themes/paper-cutout/THEME.md +63 -0
- package/themes/paper-cutout/effects.ts +609 -0
- package/themes/paper-cutout/sample/assets/og/logo.svg +12 -0
- package/themes/paper-cutout/sample/beats.json +1005 -0
- package/themes/paper-cutout/sample/index.html +11 -0
- package/themes/paper-cutout/sample/kf.json +59 -0
- package/themes/paper-cutout/sample/lib/facts.ts +29 -0
- package/themes/paper-cutout/sample/lib/paper.ts +91 -0
- package/themes/paper-cutout/sample/main.ts +9 -0
- package/themes/paper-cutout/sample/scenes/end.ts +54 -0
- package/themes/paper-cutout/sample/scenes/gather.ts +63 -0
- package/themes/paper-cutout/sample/scenes/models.ts +93 -0
- package/themes/paper-cutout/sample/scenes/price.ts +100 -0
- package/themes/paper-cutout/sample/style.json +104 -0
- package/themes/paper-cutout/slots.ts +87 -0
- package/themes/paper-cutout/still.png +0 -0
- package/themes/paper-cutout/style.json +104 -0
- package/themes/paper-cutout/theme.json +144 -0
- package/themes/paper-cutout/variants/blush-coral.json +122 -0
- package/themes/paper-cutout/variants/blush-violet.json +122 -0
- package/themes/paper-cutout/variants/chalk-teal.json +122 -0
- package/themes/paper-cutout/variants/cream-gold.json +122 -0
- package/themes/paper-cutout/variants/frost-blue.json +123 -0
- package/themes/paper-cutout/variants/frost-lime.json +121 -0
- package/themes/paper-cutout/variants/frost-plum.json +122 -0
- package/themes/paper-cutout/variants/lilac-blue.json +123 -0
- package/themes/paper-cutout/variants/lilac-gold.json +122 -0
- package/themes/paper-cutout/variants/sage-cyan.json +122 -0
- package/themes/paper-cutout/variants/sage-green.json +122 -0
- package/themes/paper-cutout/variants/sage-pink.json +122 -0
- package/themes/paper-cutout/variants/sand-blue.json +123 -0
- package/themes/paper-cutout/variants/sand-green.json +121 -0
- package/themes/paper-cutout/variants/sand-plum.json +120 -0
- package/themes/pixel-quest/THEME.md +57 -0
- package/themes/pixel-quest/effects.ts +491 -0
- package/themes/pixel-quest/kit.ts +232 -0
- package/themes/pixel-quest/sample/assets/og/logo.svg +12 -0
- package/themes/pixel-quest/sample/beats.json +1005 -0
- package/themes/pixel-quest/sample/index.html +11 -0
- package/themes/pixel-quest/sample/kf.json +59 -0
- package/themes/pixel-quest/sample/lib/facts.ts +29 -0
- package/themes/pixel-quest/sample/lib/game.ts +71 -0
- package/themes/pixel-quest/sample/lib/logo.ts +51 -0
- package/themes/pixel-quest/sample/main.ts +9 -0
- package/themes/pixel-quest/sample/scenes/end.ts +75 -0
- package/themes/pixel-quest/sample/scenes/gather.ts +65 -0
- package/themes/pixel-quest/sample/scenes/models.ts +104 -0
- package/themes/pixel-quest/sample/scenes/price.ts +118 -0
- package/themes/pixel-quest/sample/style.json +115 -0
- package/themes/pixel-quest/slots.ts +94 -0
- package/themes/pixel-quest/still.png +0 -0
- package/themes/pixel-quest/style.json +115 -0
- package/themes/pixel-quest/theme.json +143 -0
- package/themes/pixel-quest/variants/navy-gold-2.json +98 -0
- package/themes/pixel-quest/variants/navy-gold-3.json +127 -0
- package/themes/pixel-quest/variants/navy-gold-4.json +95 -0
- package/themes/pixel-quest/variants/navy-gold-pixel.json +132 -0
- package/themes/pixel-quest/variants/navy-gold-scan.json +99 -0
- package/themes/pixel-quest/variants/navy-gold.json +131 -0
- package/themes/pixel-quest/variants/navy-lime-2.json +93 -0
- package/themes/pixel-quest/variants/navy-lime-3.json +124 -0
- package/themes/pixel-quest/variants/navy-lime-pixel.json +126 -0
- package/themes/pixel-quest/variants/navy-lime-scan.json +97 -0
- package/themes/pixel-quest/variants/navy-lime.json +130 -0
- package/themes/receipt/THEME.md +72 -0
- package/themes/receipt/effects.ts +257 -0
- package/themes/receipt/sample/assets/og/logo.svg +12 -0
- package/themes/receipt/sample/beats.json +1005 -0
- package/themes/receipt/sample/index.html +11 -0
- package/themes/receipt/sample/kf.json +59 -0
- package/themes/receipt/sample/lib/facts.ts +29 -0
- package/themes/receipt/sample/lib/sheet.ts +52 -0
- package/themes/receipt/sample/main.ts +9 -0
- package/themes/receipt/sample/scenes/end.ts +44 -0
- package/themes/receipt/sample/scenes/gather.ts +57 -0
- package/themes/receipt/sample/scenes/models.ts +48 -0
- package/themes/receipt/sample/scenes/price.ts +64 -0
- package/themes/receipt/sample/style.json +104 -0
- package/themes/receipt/slots.ts +89 -0
- package/themes/receipt/still.png +0 -0
- package/themes/receipt/style.json +104 -0
- package/themes/receipt/theme.json +133 -0
- package/themes/receipt/variants/blush-coral.json +89 -0
- package/themes/receipt/variants/sand-coral-2.json +89 -0
- package/themes/receipt/variants/sand-coral-3.json +89 -0
- package/themes/receipt/variants/sand-coral-4.json +90 -0
- package/themes/receipt/variants/sand-coral-5.json +89 -0
- package/themes/receipt/variants/sand-coral-6.json +88 -0
- package/themes/receipt/variants/sand-coral-7.json +88 -0
- package/themes/receipt/variants/sand-coral-8.json +89 -0
- package/themes/receipt/variants/sand-coral-9.json +89 -0
- package/themes/receipt/variants/sand-coral.json +88 -0
- package/themes/receipt/variants/sand-orange-2.json +89 -0
- package/themes/receipt/variants/sand-orange-3.json +90 -0
- package/themes/receipt/variants/sand-orange-4.json +89 -0
- package/themes/receipt/variants/sand-orange-5.json +89 -0
- package/themes/receipt/variants/sand-orange.json +89 -0
- package/themes/riso-zine/slots.ts +81 -0
- package/themes/riso-zine/theme.json +66 -0
- package/themes/riso-zine/variants/blush-coral.json +120 -0
- package/themes/riso-zine/variants/blush-gold.json +120 -0
- package/themes/riso-zine/variants/blush-pink.json +115 -0
- package/themes/riso-zine/variants/chalk-cyan.json +120 -0
- package/themes/riso-zine/variants/chalk-lime.json +119 -0
- package/themes/riso-zine/variants/cream-amber.json +89 -0
- package/themes/riso-zine/variants/frost-blue.json +119 -0
- package/themes/riso-zine/variants/frost-coral.json +118 -0
- package/themes/riso-zine/variants/frost-cyan.json +120 -0
- package/themes/riso-zine/variants/frost-green.json +120 -0
- package/themes/riso-zine/variants/lilac-plum.json +118 -0
- package/themes/riso-zine/variants/lilac-violet.json +123 -0
- package/themes/riso-zine/variants/sage-green.json +119 -0
- package/themes/riso-zine/variants/sand-jade.json +120 -0
- package/themes/riso-zine/variants/sand-plum.json +113 -0
- package/themes/server-hall/THEME.md +58 -0
- package/themes/server-hall/effects.ts +455 -0
- package/themes/server-hall/kit.ts +138 -0
- package/themes/server-hall/sample/assets/og/logo.svg +12 -0
- package/themes/server-hall/sample/beats.json +1005 -0
- package/themes/server-hall/sample/index.html +11 -0
- package/themes/server-hall/sample/kf.json +59 -0
- package/themes/server-hall/sample/lib/facts.ts +29 -0
- package/themes/server-hall/sample/lib/hall.ts +81 -0
- package/themes/server-hall/sample/main.ts +9 -0
- package/themes/server-hall/sample/scenes/end.ts +57 -0
- package/themes/server-hall/sample/scenes/gather.ts +38 -0
- package/themes/server-hall/sample/scenes/models.ts +49 -0
- package/themes/server-hall/sample/scenes/price.ts +65 -0
- package/themes/server-hall/sample/style.json +107 -0
- package/themes/server-hall/slots.ts +106 -0
- package/themes/server-hall/still.png +0 -0
- package/themes/server-hall/style.json +107 -0
- package/themes/server-hall/theme.json +143 -0
- package/themes/server-hall/variants/navy-indigo.json +140 -0
- package/themes/server-hall/variants/night-indigo.json +120 -0
- package/themes/server-hall/variants/wine-plum.json +130 -0
- package/themes/server-hall/variants/wine-violet.json +103 -0
- package/themes/subway-map/THEME.md +64 -0
- package/themes/subway-map/effects.ts +662 -0
- package/themes/subway-map/sample/assets/og/logo.svg +12 -0
- package/themes/subway-map/sample/beats.json +1005 -0
- package/themes/subway-map/sample/index.html +11 -0
- package/themes/subway-map/sample/kf.json +59 -0
- package/themes/subway-map/sample/lib/facts.ts +29 -0
- package/themes/subway-map/sample/lib/map.ts +58 -0
- package/themes/subway-map/sample/main.ts +9 -0
- package/themes/subway-map/sample/scenes/end.ts +94 -0
- package/themes/subway-map/sample/scenes/gather.ts +64 -0
- package/themes/subway-map/sample/scenes/models.ts +117 -0
- package/themes/subway-map/sample/scenes/price.ts +119 -0
- package/themes/subway-map/sample/style.json +108 -0
- package/themes/subway-map/slots.ts +66 -0
- package/themes/subway-map/still.png +0 -0
- package/themes/subway-map/style.json +108 -0
- package/themes/subway-map/theme.json +161 -0
- package/themes/subway-map/variants/blush-jade.json +131 -0
- package/themes/subway-map/variants/chalk-jade.json +141 -0
- package/themes/subway-map/variants/deep-green.json +131 -0
- package/themes/subway-map/variants/deep-teal.json +132 -0
- package/themes/subway-map/variants/dusk-teal-bold.json +132 -0
- package/themes/subway-map/variants/dusk-teal.json +105 -0
- package/themes/subway-map/variants/forest-green.json +141 -0
- package/themes/subway-map/variants/forest-jade.json +131 -0
- package/themes/subway-map/variants/frost-jade.json +131 -0
- package/themes/subway-map/variants/frost-teal.json +137 -0
- package/themes/subway-map/variants/sage-teal.json +132 -0
- package/themes/subway-map/variants/slate-green.json +136 -0
- package/themes/subway-map/variants/slate-jade.json +131 -0
- package/themes/subway-map/variants/snow-jade.json +136 -0
- package/themes/subway-map/variants/wine-green.json +104 -0
- package/themes/swiss-poster/THEME.md +59 -0
- package/themes/swiss-poster/effects.ts +339 -0
- package/themes/swiss-poster/grid.ts +35 -0
- package/themes/swiss-poster/sample/assets/og/logo.svg +12 -0
- package/themes/swiss-poster/sample/beats.json +1005 -0
- package/themes/swiss-poster/sample/index.html +11 -0
- package/themes/swiss-poster/sample/kf.json +59 -0
- package/themes/swiss-poster/sample/lib/facts.ts +29 -0
- package/themes/swiss-poster/sample/lib/poster.ts +36 -0
- package/themes/swiss-poster/sample/main.ts +9 -0
- package/themes/swiss-poster/sample/scenes/end.ts +70 -0
- package/themes/swiss-poster/sample/scenes/gather.ts +68 -0
- package/themes/swiss-poster/sample/scenes/models.ts +63 -0
- package/themes/swiss-poster/sample/scenes/price.ts +74 -0
- package/themes/swiss-poster/sample/style.json +100 -0
- package/themes/swiss-poster/slots.ts +69 -0
- package/themes/swiss-poster/still.png +0 -0
- package/themes/swiss-poster/style.json +100 -0
- package/themes/swiss-poster/theme.json +131 -0
- package/themes/swiss-poster/variants/blush-green.json +116 -0
- package/themes/swiss-poster/variants/chalk-plum.json +121 -0
- package/themes/swiss-poster/variants/chalk-sky.json +90 -0
- package/themes/swiss-poster/variants/cocoa-cyan.json +117 -0
- package/themes/swiss-poster/variants/dusk-coral.json +89 -0
- package/themes/swiss-poster/variants/dusk-magenta.json +117 -0
- package/themes/swiss-poster/variants/forest-amber.json +127 -0
- package/themes/swiss-poster/variants/forest-blue.json +118 -0
- package/themes/swiss-poster/variants/forest-jade.json +89 -0
- package/themes/swiss-poster/variants/frost-gold.json +128 -0
- package/themes/swiss-poster/variants/frost-indigo.json +117 -0
- package/themes/swiss-poster/variants/sand-magenta.json +126 -0
- package/themes/swiss-poster/variants/slate-orange.json +122 -0
- package/themes/swiss-poster/variants/slate-pink.json +126 -0
- package/themes/swiss-poster/variants/slate-violet.json +89 -0
- package/themes/water-works/THEME.md +61 -0
- package/themes/water-works/effects.ts +609 -0
- package/themes/water-works/sample/assets/og/logo.svg +12 -0
- package/themes/water-works/sample/beats.json +1005 -0
- package/themes/water-works/sample/index.html +11 -0
- package/themes/water-works/sample/kf.json +59 -0
- package/themes/water-works/sample/lib/facts.ts +29 -0
- package/themes/water-works/sample/lib/hall.ts +22 -0
- package/themes/water-works/sample/main.ts +9 -0
- package/themes/water-works/sample/scenes/end.ts +55 -0
- package/themes/water-works/sample/scenes/gather.ts +42 -0
- package/themes/water-works/sample/scenes/models.ts +51 -0
- package/themes/water-works/sample/scenes/price.ts +66 -0
- package/themes/water-works/sample/style.json +105 -0
- package/themes/water-works/slots.ts +140 -0
- package/themes/water-works/still.png +0 -0
- package/themes/water-works/style.json +105 -0
- package/themes/water-works/theme.json +138 -0
- package/themes/water-works/variants/blush-rose.json +123 -0
- package/themes/water-works/variants/chalk-blue.json +124 -0
- package/themes/water-works/variants/chalk-cyan-2.json +96 -0
- package/themes/water-works/variants/chalk-cyan.json +123 -0
- package/themes/water-works/variants/chalk-gold-mono.json +122 -0
- package/themes/water-works/variants/chalk-gold.json +123 -0
- package/themes/water-works/variants/chalk-green.json +123 -0
- package/themes/water-works/variants/frost-indigo.json +123 -0
- package/themes/water-works/variants/frost-rose.json +132 -0
- package/themes/water-works/variants/lilac-violet-mono.json +124 -0
- package/themes/water-works/variants/lilac-violet.json +133 -0
- package/themes/water-works/variants/sage-amber.json +133 -0
- package/themes/water-works/variants/sage-green.json +133 -0
- package/themes/water-works/variants/sand-amber.json +123 -0
- package/themes/water-works/variants/sand-plum.json +121 -0
- package/themes/whiteboard/THEME.md +78 -0
- package/themes/whiteboard/effects.ts +561 -0
- package/themes/whiteboard/sample/assets/og/logo.svg +12 -0
- package/themes/whiteboard/sample/beats.json +1005 -0
- package/themes/whiteboard/sample/index.html +11 -0
- package/themes/whiteboard/sample/kf.json +59 -0
- package/themes/whiteboard/sample/lib/board.ts +93 -0
- package/themes/whiteboard/sample/lib/facts.ts +29 -0
- package/themes/whiteboard/sample/main.ts +9 -0
- package/themes/whiteboard/sample/scenes/end.ts +59 -0
- package/themes/whiteboard/sample/scenes/gather.ts +50 -0
- package/themes/whiteboard/sample/scenes/models.ts +56 -0
- package/themes/whiteboard/sample/scenes/price.ts +79 -0
- package/themes/whiteboard/sample/style.json +106 -0
- package/themes/whiteboard/slots.ts +152 -0
- package/themes/whiteboard/still.png +0 -0
- package/themes/whiteboard/style.json +106 -0
- package/themes/whiteboard/theme.json +143 -0
- package/themes/whiteboard/variants/chalk-coral-do.json +126 -0
- package/themes/whiteboard/variants/chalk-coral.json +132 -0
- package/themes/whiteboard/variants/cream-lime.json +131 -0
- package/themes/whiteboard/variants/cream-plum.json +103 -0
- package/themes/whiteboard/variants/frost-teal-do.json +127 -0
- package/themes/whiteboard/variants/frost-teal.json +127 -0
- package/themes/whiteboard/variants/lilac-indigo.json +132 -0
- package/themes/whiteboard/variants/lilac-jade.json +103 -0
- package/themes/whiteboard/variants/sage-green.json +130 -0
- package/themes/whiteboard/variants/sage-plum.json +126 -0
- package/themes/whiteboard/variants/snow-amber.json +99 -0
- package/themes/whiteboard/variants/snow-cyan.json +132 -0
- package/themes/whiteboard/variants/snow-green.json +126 -0
- package/themes/whiteboard/variants/snow-magenta.json +126 -0
- package/themes/whiteboard/variants/snow-plum.json +130 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { basename, join, resolve } from "node:path";
|
|
3
|
+
import { addToLibrary, byMood, genMusic, listLibrary, MOOD_PROMPT, MOOD_TAGS, musicDir, musicProviders } from "@kasaframe/gen";
|
|
4
|
+
import { type Command, num, parseFlags } from "../command.ts";
|
|
5
|
+
|
|
6
|
+
const cmd: Command = {
|
|
7
|
+
summary: "곡 모음 — 분위기 태그로 보기·넣기, 음악 생성(Lyria·Eleven Music)",
|
|
8
|
+
usage: `kf music list [--mood deep]
|
|
9
|
+
kf music add <곡 파일> --mood deep,ambient --source <원본 주소> --license "<라이선스>" [--title 이름] [--genre 장르] [--bpm 70]
|
|
10
|
+
kf music gen --mood deep --dur 45 [--provider lyria-3.5|elevenlabs-music|lyria-3-clip-preview] [--out 폴더] [--add]
|
|
11
|
+
|
|
12
|
+
곡 모음은 ${"KF_MUSIC_DIR"}(없으면 레포 music/). 곡마다 분위기·원본·라이선스는 그 폴더 tags.json, 사람용 표는 README.md.
|
|
13
|
+
분위기: ${Object.keys(MOOD_PROMPT).join(" · ")} — 틀(template.json mood)이 고른다. 원본·라이선스를 모르는 곡은 넣지 않는다.
|
|
14
|
+
gen 은 분위기·길이 칸(30·45·60·90·120…초)이 같으면 캐시에서 같은 곡을 0원으로 꺼낸다. --add 면 곡 모음에 태그와 함께 넣는다.`,
|
|
15
|
+
async run(argv) {
|
|
16
|
+
const [sub, ...rest] = argv;
|
|
17
|
+
const { args, opts } = parseFlags(rest);
|
|
18
|
+
const str = (k: string) => (typeof opts[k] === "string" ? (opts[k] as string) : undefined);
|
|
19
|
+
|
|
20
|
+
if (sub === "list" || !sub) {
|
|
21
|
+
const tracks = str("mood") ? byMood(str("mood")!) : listLibrary();
|
|
22
|
+
console.log(`곡 모음 ${musicDir()}${str("mood") ? ` — 분위기 ${str("mood")}` : ""}`);
|
|
23
|
+
for (const t of tracks) console.log(` ${t.file.padEnd(34)} ${(t.mood.join("·") || "태그 없음").padEnd(32)} ${t.license ? t.license.slice(0, 60) : "라이선스 모름 — 고르는 데서 빠진다"}`);
|
|
24
|
+
if (!tracks.length) console.log(" (없음)");
|
|
25
|
+
console.log(`\n생성: ${musicProviders().map((p) => `${p.id}(${p.ready() ? "키 있음" : "키 없음"})`).join(" · ")}`);
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (sub === "add") {
|
|
30
|
+
const src = args[0] ? resolve(args[0]) : undefined;
|
|
31
|
+
if (!src || !existsSync(src) || !str("mood") || !str("source") || !str("license")) {
|
|
32
|
+
console.error("kf music add: <곡 파일> --mood --source --license 가 필요하다");
|
|
33
|
+
return 2;
|
|
34
|
+
}
|
|
35
|
+
const t = addToLibrary(src, {
|
|
36
|
+
title: str("title") ?? basename(src).replace(/\.[^.]+$/, ""),
|
|
37
|
+
mood: str("mood")!.split(",").map((m) => m.trim()).filter(Boolean),
|
|
38
|
+
...(str("genre") ? { genre: str("genre") } : {}),
|
|
39
|
+
...(str("bpm") ? { bpm: num(opts.bpm, 0) } : {}),
|
|
40
|
+
source: str("source")!,
|
|
41
|
+
license: str("license")!,
|
|
42
|
+
});
|
|
43
|
+
readmeRow(t.file, t.title, t.mood, t.source, t.license);
|
|
44
|
+
console.log(t.path);
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (sub === "gen") {
|
|
49
|
+
const mood = str("mood") ?? "deep";
|
|
50
|
+
const dur = num(opts.dur, 45);
|
|
51
|
+
const outDir = resolve(str("out") ?? ".");
|
|
52
|
+
const m = await genMusic(`${mood}-${dur}`, { mood, duration: dur }, { outDir, log: (l) => console.error(l) }, str("provider"));
|
|
53
|
+
console.error(`${m.provider} · ${m.seconds}초 · ${m.cached ? "캐시(0)" : `$${m.costUsd.toFixed(3)}`} · ${m.license}`);
|
|
54
|
+
if (opts.add) {
|
|
55
|
+
const file = `${mood}-${m.provider}-${Math.round(m.seconds)}s.${m.path.split(".").pop()}`;
|
|
56
|
+
const t = addToLibrary(m.path, { title: `${mood} ${Math.round(m.seconds)}초 (${m.provider} 생성)`, mood: [...new Set([mood, ...(MOOD_TAGS[mood] ?? []).slice(0, 3)])], source: `${m.provider} 생성(MOOD_PROMPT.${mood}, ${new Date().toISOString().slice(0, 10)})`, license: m.license }, musicDir(), file);
|
|
57
|
+
readmeRow(t.file, t.title, t.mood, t.source, t.license);
|
|
58
|
+
console.log(t.path);
|
|
59
|
+
} else console.log(m.path);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.error(this.usage);
|
|
64
|
+
return 2;
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
export default cmd;
|
|
68
|
+
|
|
69
|
+
// README 표에 한 줄 — 같은 파일 줄이 있으면 두지 않는다. 표 끝(마지막 표 줄 다음)에 끼운다
|
|
70
|
+
function readmeRow(file: string, title: string, mood: string[], source: string, license: string): void {
|
|
71
|
+
const readme = join(musicDir(), "README.md");
|
|
72
|
+
if (!existsSync(readme)) return;
|
|
73
|
+
const lines = readFileSync(readme, "utf8").split("\n");
|
|
74
|
+
if (lines.some((l) => l.startsWith(`| \`${file}\` |`))) return;
|
|
75
|
+
const last = lines.map((l, i) => (l.startsWith("| `") ? i : -1)).filter((i) => i >= 0).at(-1) ?? lines.length - 1;
|
|
76
|
+
lines.splice(last + 1, 0, `| \`${file}\` | ${title} | ${mood.join("·")} | ${source} · ${license} |`);
|
|
77
|
+
writeFileSync(readme, lines.join("\n"));
|
|
78
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { basename, join, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { cutVoice, DEFAULT_STYLE, genMusic, howToGet, layVoices, parseVoice, pickTts, speak, ttsProviders, type Speech } from "@kasaframe/gen";
|
|
6
|
+
import { loadTemplate, parseDuration, type Template } from "@kasaframe/plan";
|
|
7
|
+
import { type Command, parseFlags } from "../command.ts";
|
|
8
|
+
import { jobLimit, pool } from "../pool.ts";
|
|
9
|
+
|
|
10
|
+
const kfBin = fileURLToPath(new URL("../kf.ts", import.meta.url));
|
|
11
|
+
// 한 장면에 문장이 둘 넘으면 그 사이 숨
|
|
12
|
+
const GAP = 0.35;
|
|
13
|
+
const MIN_SCENE = 0.75;
|
|
14
|
+
// 박이 이보다 멀면 붙이지 않는다(느린 곡의 마디 사이에서 장면이 늘어지지 않게)
|
|
15
|
+
const SNAP_REACH = 0.8;
|
|
16
|
+
|
|
17
|
+
const NARRATION_DEFAULT: NonNullable<Template["narration"]> = { on: true, lang: "ko", style: DEFAULT_STYLE, voice: "elevenlabs:George", voices: {}, lead: 0.25, tail: 0.5 };
|
|
18
|
+
|
|
19
|
+
interface Line {
|
|
20
|
+
scene: string;
|
|
21
|
+
text: string;
|
|
22
|
+
from?: number;
|
|
23
|
+
to?: number;
|
|
24
|
+
words?: { text: string; from: number; to: number }[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const cmd: Command = {
|
|
28
|
+
summary: "내레이션 먼저 — 장면별 문장을 읽혀 장면 길이를 목소리에 맞추고(곡이 있으면 박에), 자막 시각을 목소리 시각으로",
|
|
29
|
+
usage: `kf narrate <폴더> [--voice <엔진>:<목소리>|none] [--music <곡 파일>|gen:<공급자>|none]
|
|
30
|
+
kf narrate --voices 키가 있는 목소리 엔진·목소리 목록
|
|
31
|
+
kf narrate --say "<글>" --voice gemini:Charon [--out x.wav] 한 문장만 읽혀 본다
|
|
32
|
+
|
|
33
|
+
captions.json([{"scene": "<장면 id>", "text": "그 장면에서 들릴 말"}])을 장면마다 읽힌다 → 장면 길이 = 앞 숨 + 말 + 뒤 숨(틀 narration.lead·tail).
|
|
34
|
+
곡을 주면(또는 plan.json 답) 곡을 깔고 kf beats 로 재서 장면 경계를 다음 박에 붙인다. gen:<공급자> 면 틀 분위기(mood)로 영상 길이에 맞춘 곡을 만든다.
|
|
35
|
+
요청 글에 길이(「45초」)가 있으면 목소리 빠르기를 0.9–1.1 안에서만 맞추고 남는 시간은 장면 뒤 숨으로 편다. 그 밖엔 빠르기를 건드리지 않는다.
|
|
36
|
+
결과: assets/voice/<장면>-<n>.wav · assets/voice/narration.wav(영상 길이 한 줄) · captions.json 의 낱말 시각 · kf.json 의 장면 시각·narration·audio(섞기 값).
|
|
37
|
+
kf render 가 narration·곡을 섞는다 — 곡은 말할 때 덕킹, 끝 페이드, 틀의 LUFS 로 정규화.
|
|
38
|
+
--voice·--music 을 안 주면 plan.json 의 voice·music(또는 answers)을, 그것도 없으면 틀이 엔진마다 박은 목소리를 쓴다.`,
|
|
39
|
+
async run(argv) {
|
|
40
|
+
const { args, opts } = parseFlags(argv);
|
|
41
|
+
const str = (k: string) => (typeof opts[k] === "string" ? (opts[k] as string) : undefined);
|
|
42
|
+
if (opts.voices) return voices();
|
|
43
|
+
if (str("say")) return say(str("say")!, str("voice") ?? "gemini", str("out"));
|
|
44
|
+
const dir = args[0] ? resolve(args[0]) : undefined;
|
|
45
|
+
if (!dir || !existsSync(join(dir, "kf.json"))) {
|
|
46
|
+
console.error("kf narrate: kf.json 이 있는 프로젝트 폴더가 필요하다 — kf narrate --help");
|
|
47
|
+
return 2;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
return await narrate(dir, { voice: str("voice"), music: str("music") });
|
|
51
|
+
} catch (e) {
|
|
52
|
+
console.error(`kf narrate: ${e instanceof Error ? e.message : e}`);
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
export default cmd;
|
|
58
|
+
|
|
59
|
+
function voices(): number {
|
|
60
|
+
for (const p of ttsProviders()) {
|
|
61
|
+
const state = p.ready() ? "쓸 수 있음" : "키 없음";
|
|
62
|
+
console.log(`${p.id.padEnd(11)} ${p.label} · ${p.model} · ${state}${p.verified ? "" : " · 실호출 미검증"}${p.timestamps ? " · 낱말 시각 정확" : ""}`);
|
|
63
|
+
for (const v of p.voices) console.log(` --voice ${`${p.id}:${v.id}`.padEnd(26)} ${v.label}`);
|
|
64
|
+
}
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function say(text: string, voice: string, out?: string): Promise<number> {
|
|
69
|
+
const v = parseVoice(voice);
|
|
70
|
+
if (!v) return 2;
|
|
71
|
+
const dst = resolve(out ?? "say.wav");
|
|
72
|
+
const s = await speak("say", { text, voice: v.voice, lang: "ko", style: DEFAULT_STYLE }, { outDir: join(dst, ".."), log: (l) => console.error(l) }, v.engine);
|
|
73
|
+
await cutVoice(s.path, dst, { from: s.voiced.from, to: s.voiced.to });
|
|
74
|
+
console.error(`${s.engine} ${s.voice} · ${(s.voiced.to - s.voiced.from).toFixed(2)}초 · ${s.cached ? "캐시(0)" : `$${s.costUsd.toFixed(4)}`}`);
|
|
75
|
+
console.log(dst);
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const readJson = (p: string) => JSON.parse(readFileSync(p, "utf8"));
|
|
80
|
+
const writeJson = (p: string, v: unknown) => writeFileSync(p, `${JSON.stringify(v, null, 2)}\n`);
|
|
81
|
+
|
|
82
|
+
// 답 고르는 순서: 명령 줄 > plan.json 의 voice·answers > 묻기의 권장과 같은 것(로컬 Qwen3 주소가 있으면 그 목소리, 없으면 틀이 못 박은 목소리).
|
|
83
|
+
// 다른 엔진·「자막만」으로 조용히 넘어가지 않는다
|
|
84
|
+
function pickVoice(given: string | undefined, plan: Record<string, any>, n: Template["narration"]): string {
|
|
85
|
+
const v = given ?? plan.voice ?? plan.answers?.voice;
|
|
86
|
+
if (v) return v;
|
|
87
|
+
if (!n?.on) return "none";
|
|
88
|
+
return pickTts("local").ready() ? `local:${n.voices.local ?? pickTts("local").defaultVoice}` : n.voice;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function narrate(dir: string, o: { voice?: string; music?: string } = {}): Promise<number> {
|
|
92
|
+
const kfPath = join(dir, "kf.json");
|
|
93
|
+
const planPath = join(dir, "plan.json");
|
|
94
|
+
const kf = readJson(kfPath);
|
|
95
|
+
const plan = existsSync(planPath) ? readJson(planPath) : {};
|
|
96
|
+
const tpl = loadTemplate(kf.template);
|
|
97
|
+
const n = { ...NARRATION_DEFAULT, ...tpl.narration };
|
|
98
|
+
const fps: number = kf.fps;
|
|
99
|
+
const f = (x: number) => Math.round(x * fps) / fps;
|
|
100
|
+
const log = (l: string) => console.error(l);
|
|
101
|
+
|
|
102
|
+
// 1. 목소리 — 장면마다 문장을 읽힌다
|
|
103
|
+
const voice = pickVoice(o.voice, plan, tpl.narration);
|
|
104
|
+
const v = parseVoice(voice);
|
|
105
|
+
if (v && !pickTts(v.engine).ready()) {
|
|
106
|
+
console.error(`kf narrate: 목소리 ${voice} 의 엔진 ${v.engine} 을 이 기계에서 못 쓴다 — 고르고 다시:\n 키 넣기 — ${howToGet(pickTts(v.engine).keyName)}\n 다른 엔진: ${ttsProviders().filter((p) => p.ready()).map((p) => `--voice ${p.id}:${n.voices[p.id] ?? p.defaultVoice}`).join(" · ") || "(쓸 수 있는 엔진 없음)"}\n 목소리 빼기: --voice none(자막만)`);
|
|
107
|
+
return 3;
|
|
108
|
+
}
|
|
109
|
+
const capPath = join(dir, "captions.json");
|
|
110
|
+
const lines: Line[] = existsSync(capPath) ? readJson(capPath) : [];
|
|
111
|
+
if (v && !lines.length) throw new Error("captions.json 에 장면별 문장이 없다 — [{\"scene\": \"hook\", \"text\": \"...\"}] 로 먼저 쓴다");
|
|
112
|
+
const spoken = new Map<string, { line: Line; s: Speech }[]>();
|
|
113
|
+
let costUsd = 0;
|
|
114
|
+
if (v) {
|
|
115
|
+
mkdirSync(join(dir, "assets", "voice", "raw"), { recursive: true });
|
|
116
|
+
const count: Record<string, number> = {};
|
|
117
|
+
const todo = lines.filter((line) => line.text?.trim() && kf.scenes.some((s: { id: string }) => s.id === line.scene)).map((line) => ({ line, k: (count[line.scene] = (count[line.scene] ?? 0) + 1) }));
|
|
118
|
+
// 줄마다 따로 읽힌다 — 동시에 부르고 장면 차례대로 모은다
|
|
119
|
+
const said = await pool(todo, jobLimit(4), ({ line, k }) => speak(`${line.scene}-${k}`, { text: line.text.trim(), voice: v.voice, lang: n.lang, style: n.style }, { outDir: join(dir, "assets", "voice", "raw"), log }, v.engine));
|
|
120
|
+
for (const [i, { line }] of todo.entries()) {
|
|
121
|
+
const s = said[i];
|
|
122
|
+
if (!s.cached) costUsd += s.costUsd;
|
|
123
|
+
spoken.set(line.scene, [...(spoken.get(line.scene) ?? []), { line, s }]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const talk = (id: string) => (spoken.get(id) ?? []).reduce((a, x, i) => a + (x.s.voiced.to - x.s.voiced.from) + (i ? GAP : 0), 0);
|
|
127
|
+
const leadOf = (s: { role: string }) => (s.role === "hook" ? Math.min(0.1, n.lead) : n.lead);
|
|
128
|
+
|
|
129
|
+
// 2. 빠르기 — 요청이 길이를 말했으면 그 길이에, 아니면 틀 상한을 넘을 때만. 늘 0.9–1.1 안
|
|
130
|
+
const voiceSec = kf.scenes.reduce((a: number, s: { id: string }) => a + talk(s.id), 0);
|
|
131
|
+
const restSec = kf.scenes.reduce((a: number, s: { id: string; role: string; dur: number }) => a + (spoken.has(s.id) ? leadOf(s) + n.tail : s.dur), 0);
|
|
132
|
+
const target = parseDuration(plan.request ?? "");
|
|
133
|
+
const clamp = (x: number) => Math.min(1.1, Math.max(0.9, x));
|
|
134
|
+
let tempo = 1;
|
|
135
|
+
if (voiceSec > 0) {
|
|
136
|
+
if (target !== undefined && target > restSec) tempo = clamp(voiceSec / (target - restSec));
|
|
137
|
+
else if (voiceSec + restSec > tpl.duration.max) tempo = clamp(voiceSec / Math.max(1, tpl.duration.max - restSec));
|
|
138
|
+
if (Math.abs(tempo - 1) < 0.02) tempo = 1;
|
|
139
|
+
}
|
|
140
|
+
const need = kf.scenes.map((s: { id: string; role: string; dur: number }) => (spoken.has(s.id) ? Math.max(MIN_SCENE, leadOf(s) + talk(s.id) / tempo + n.tail) : s.dur));
|
|
141
|
+
// 남는 시간은 말한 장면 뒤 숨으로 고르게(요청 길이가 있을 때만)
|
|
142
|
+
const short = target !== undefined ? target - need.reduce((a: number, b: number) => a + b, 0) : 0;
|
|
143
|
+
const voiced = kf.scenes.filter((s: { id: string }) => spoken.has(s.id)).length;
|
|
144
|
+
if (short > 0 && voiced) kf.scenes.forEach((s: { id: string }, i: number) => spoken.has(s.id) && (need[i] += short / voiced));
|
|
145
|
+
let total = need.reduce((a: number, b: number) => a + b, 0);
|
|
146
|
+
|
|
147
|
+
// 3. 곡 — 곡 모음 파일·생성·없음. 바뀌었으면 kf beats 로 다시 잰다
|
|
148
|
+
const music = o.music ?? plan.answers?.music ?? undefined;
|
|
149
|
+
if (music === "none") {
|
|
150
|
+
kf.music = null;
|
|
151
|
+
kf.beats = null;
|
|
152
|
+
} else if (music?.startsWith("gen:")) {
|
|
153
|
+
// 시리즈 묶음의 분위기가 틀 분위기를 덮는다
|
|
154
|
+
const m = await genMusic("score", { mood: plan.mood ?? tpl.mood ?? "deep", duration: total }, { outDir: join(dir, "assets", "music"), log }, music.slice(4));
|
|
155
|
+
if (!m.cached) costUsd += m.costUsd;
|
|
156
|
+
kf.music = `assets/music/${basename(m.path)}`;
|
|
157
|
+
log(`곡 ${m.provider} ${m.seconds}초 ${m.cached ? "캐시(0)" : `$${m.costUsd.toFixed(3)}`} — ${m.license}`);
|
|
158
|
+
} else if (music) {
|
|
159
|
+
const src = resolve(music);
|
|
160
|
+
if (!existsSync(src)) throw new Error(`곡이 없다: ${src}`);
|
|
161
|
+
mkdirSync(join(dir, "assets", "music"), { recursive: true });
|
|
162
|
+
const rel = `assets/music/${basename(src)}`;
|
|
163
|
+
if (resolve(dir, rel) !== src) copyFileSync(src, join(dir, rel));
|
|
164
|
+
kf.music = rel;
|
|
165
|
+
}
|
|
166
|
+
if (kf.music && (music !== undefined || !kf.beats || !existsSync(join(dir, kf.beats)))) {
|
|
167
|
+
const r = spawnSync(process.execPath, [kfBin, "beats", join(dir, kf.music), "--out", join(dir, "beats.json")], { stdio: ["ignore", "ignore", "inherit"] });
|
|
168
|
+
if (r.status !== 0) throw new Error("곡 분석(kf beats)이 실패했다");
|
|
169
|
+
kf.beats = "beats.json";
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 4. 경계 — 말이 끝난 뒤 다음 박에(곡이 있으면), 그다음 프레임에
|
|
173
|
+
const beats: number[] = kf.beats && existsSync(join(dir, kf.beats)) ? (readJson(join(dir, kf.beats)).beats ?? []) : [];
|
|
174
|
+
let t = 0;
|
|
175
|
+
for (const [i, s] of kf.scenes.entries()) {
|
|
176
|
+
s.from = +f(t).toFixed(4);
|
|
177
|
+
let end = t + need[i];
|
|
178
|
+
if (i < kf.scenes.length - 1) {
|
|
179
|
+
const next = beats.find((b) => b >= end - 0.02);
|
|
180
|
+
if (next !== undefined && next - end <= SNAP_REACH) end = next;
|
|
181
|
+
}
|
|
182
|
+
end = f(end);
|
|
183
|
+
s.dur = +(end - s.from).toFixed(4);
|
|
184
|
+
t = end;
|
|
185
|
+
}
|
|
186
|
+
total = f(t);
|
|
187
|
+
kf.duration = +total.toFixed(4);
|
|
188
|
+
|
|
189
|
+
// 5. 목소리 한 줄 — 조각을 장면 시각에 깔고, 자막에 낱말 시각을 영상 기준으로 적는다
|
|
190
|
+
const clips: { file: string; at: number }[] = [];
|
|
191
|
+
const outLines: Line[] = [];
|
|
192
|
+
for (const s of kf.scenes) {
|
|
193
|
+
let at = s.from + leadOf(s);
|
|
194
|
+
for (const [k, { line, s: sp }] of (spoken.get(s.id) ?? []).entries()) {
|
|
195
|
+
const file = join(dir, "assets", "voice", `${s.id}-${k + 1}.wav`);
|
|
196
|
+
await cutVoice(sp.path, file, { from: sp.voiced.from, to: sp.voiced.to, tempo });
|
|
197
|
+
clips.push({ file, at });
|
|
198
|
+
const words = sp.words.map((w) => ({ text: w.text, from: +(at + (w.from - sp.voiced.from) / tempo).toFixed(3), to: +(at + (w.to - sp.voiced.from) / tempo).toFixed(3) }));
|
|
199
|
+
const len = (sp.voiced.to - sp.voiced.from) / tempo;
|
|
200
|
+
outLines.push({ scene: s.id, text: line.text, from: +at.toFixed(3), to: +(at + len).toFixed(3), words });
|
|
201
|
+
at += len + GAP;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (v) {
|
|
205
|
+
await layVoices(clips, total, join(dir, "assets", "voice", "narration.wav"));
|
|
206
|
+
// 목소리를 안 읽힌 줄(장면 id 가 없던 줄)도 버리지 않는다
|
|
207
|
+
for (const l of lines) if (!outLines.some((x) => x.scene === l.scene && x.text === l.text)) outLines.push({ scene: l.scene, text: l.text });
|
|
208
|
+
writeJson(capPath, outLines);
|
|
209
|
+
writeJson(join(dir, "assets", "voice", "narration.json"), {
|
|
210
|
+
voice,
|
|
211
|
+
engine: v.engine,
|
|
212
|
+
model: pickTts(v.engine).model,
|
|
213
|
+
license: pickTts(v.engine).license,
|
|
214
|
+
tempo,
|
|
215
|
+
clips: clips.map((c, i) => ({ file: basename(c.file), at: +c.at.toFixed(3), text: outLines[i]?.text })),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
kf.narration = v ? "assets/voice/narration.wav" : null;
|
|
219
|
+
kf.voice = voice;
|
|
220
|
+
if (tpl.audio) kf.audio = tpl.audio;
|
|
221
|
+
writeJson(kfPath, kf);
|
|
222
|
+
if (existsSync(planPath)) {
|
|
223
|
+
plan.voice = voice;
|
|
224
|
+
plan.music = kf.music;
|
|
225
|
+
plan.duration = kf.duration;
|
|
226
|
+
for (const s of plan.scenes ?? []) {
|
|
227
|
+
const k = kf.scenes.find((x: { id: string }) => x.id === s.id);
|
|
228
|
+
if (k) Object.assign(s, { from: k.from, dur: k.dur });
|
|
229
|
+
}
|
|
230
|
+
writeJson(planPath, plan);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
console.log(`${dir} — 목소리 ${voice}${tempo !== 1 ? ` · 빠르기 ${tempo.toFixed(3)}` : ""} · 곡 ${kf.music ?? "없음"}${beats.length ? "(박에 붙임)" : ""} · ${kf.duration}초 · 이번에 쓴 돈 $${costUsd.toFixed(4)}`);
|
|
234
|
+
for (const s of kf.scenes) console.log(` ${s.from.toFixed(2).padStart(6)}–${(s.from + s.dur).toFixed(2).padEnd(6)} ${s.id.padEnd(9)} ${spoken.has(s.id) ? `말 ${(talk(s.id) / tempo).toFixed(2)}초` : "말 없음"}`);
|
|
235
|
+
if (target !== undefined && Math.abs(kf.duration - target) > 2) console.log(` · 요청 ${target}초와 ${(kf.duration - target).toFixed(1)}초 차이 — 빠르기는 0.9–1.1 까지만 바꾼다. 대본을 ${kf.duration > target ? "줄이면" : "늘리면"} 맞는다`);
|
|
236
|
+
else if (target === undefined && kf.duration > tpl.duration.max) console.log(` · 틀 상한 ${tpl.duration.max}초를 넘는다 — 대본을 줄인다`);
|
|
237
|
+
return 0;
|
|
238
|
+
}
|
|
@@ -2,7 +2,8 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, join, relative, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import {
|
|
5
|
+
import { bundleFor, dressSeed, NO_DRESS, pickDress } from "@kasaframe/motion/menu";
|
|
6
|
+
import { themeStyle, worldOf } from "@kasaframe/themes";
|
|
6
7
|
import { type Aspect, type BeatsLike, type Plan, type Purpose, listTemplates, loadTemplate, makePlan, parseTitle, PURPOSES, sceneSkeleton, templatesDir } from "@kasaframe/plan";
|
|
7
8
|
import { type Command, num, parseFlags } from "../command.ts";
|
|
8
9
|
|
|
@@ -16,7 +17,8 @@ kf new <폴더> --request "요청 글" [--music 곡.mp3] 용도를 요청 글
|
|
|
16
17
|
kf new <폴더> --plan plan.json kf make 가 세운 계획 그대로
|
|
17
18
|
|
|
18
19
|
용도: ${PURPOSES.join(" · ")}
|
|
19
|
-
--theme 테마 id(themes/<id>, approved·review)
|
|
20
|
+
--theme 테마 id(themes/<id>, approved·review) · auto(내용에 맞는 approved 후보 셋 중 첫째) · none(프리셋만) — 고르면 그 테마 style 을 쓴다
|
|
21
|
+
--series 시리즈 묶음 id · none. 안 주면 요청 글이 짚은 시리즈
|
|
20
22
|
--force 비어 있지 않은 폴더에도 쓴다(있는 장면 파일은 그대로 둔다)
|
|
21
23
|
--preview 다 만들고 kf preview 를 띄운다
|
|
22
24
|
|
|
@@ -82,6 +84,7 @@ kf new <폴더> --plan plan.json kf make 가 세운 계
|
|
|
82
84
|
style: str("style"),
|
|
83
85
|
title: str("title") ?? parseTitle(request ?? "") ?? basename(dir),
|
|
84
86
|
theme: str("theme"),
|
|
87
|
+
series: str("series"),
|
|
85
88
|
});
|
|
86
89
|
const purposeQ = plan.questions?.find((q) => q.id === "purpose");
|
|
87
90
|
if (purposeQ && !template) {
|
|
@@ -111,6 +114,10 @@ kf new <폴더> --plan plan.json kf make 가 세운 계
|
|
|
111
114
|
// 틀의 움직임 규칙이 프리셋을 이긴다 — 쇼츠에 박 펄스가 켜진 프리셋을 골라도 쇼츠 규칙대로
|
|
112
115
|
style.motion = { ...style.motion, ...tpl.motion };
|
|
113
116
|
|
|
117
|
+
// 장면마다 연출 수(바탕·글·경계 덮기) — 용도 틀·테마 묶음에서 요청 글을 씨앗으로 고른다. 같은 요청이면 같은 수가 걸린다
|
|
118
|
+
// 틀에 묶음이 없으면(뮤비 — 가사 층이 화면을 맡는다) 무대 기본 그대로
|
|
119
|
+
const dress = tpl.moves ? pickDress(dressSeed(plan.request || plan.title, plan.purpose, worldOf(plan.theme) ?? plan.style), plan.scenes, bundleFor(tpl.moves, plan.theme ?? undefined)) : plan.scenes.map(() => ({ ...NO_DRESS }));
|
|
120
|
+
|
|
114
121
|
const frame = { width: plan.width, height: plan.height };
|
|
115
122
|
const safeArea = plan.aspect === tpl.aspect ? tpl.safeArea : (tpl.variants?.[plan.aspect]?.safeArea ?? tpl.safeArea);
|
|
116
123
|
const kf = {
|
|
@@ -125,9 +132,10 @@ kf new <폴더> --plan plan.json kf make 가 세운 계
|
|
|
125
132
|
music,
|
|
126
133
|
beats: beats ? "beats.json" : null,
|
|
127
134
|
captions: plan.captions,
|
|
135
|
+
...(tpl.render ? { render: tpl.render } : {}),
|
|
128
136
|
safeArea,
|
|
129
137
|
text: tpl.text,
|
|
130
|
-
scenes: plan.scenes.map((s) => ({ id: s.id, from: s.from, dur: s.dur, file: `scenes/${s.id}.ts`, role: s.role, idea: s.idea, assets: s.assets })),
|
|
138
|
+
scenes: plan.scenes.map((s, i) => ({ id: s.id, from: s.from, dur: s.dur, file: `scenes/${s.id}.ts`, role: s.role, idea: s.idea, assets: s.assets, ...(s.role === "seam" || !tpl.moves ? {} : { dress: dress[i] }) })),
|
|
131
139
|
};
|
|
132
140
|
|
|
133
141
|
const write = (rel: string, body: string) => writeFileSync(join(dir, rel), body);
|
|
@@ -152,15 +160,16 @@ kf new <폴더> --plan plan.json kf make 가 세운 계
|
|
|
152
160
|
}
|
|
153
161
|
const step = plan.scenes.slice(0, i + 1).filter((x) => x.role === "step").length;
|
|
154
162
|
const word = s.role === "end" || s.role === "hook" || s.role === "brand" ? plan.title : s.role === "step" ? `${step}단계` : s.id.replace(/\d+$/, "").toUpperCase();
|
|
155
|
-
write(rel, fill(sceneSkeleton(plan.purpose, s.id, s.role), { word, title: plan.title, line: str("line") ?? "", move: MOVES[i % MOVES.length], transition: tpl.scenes.find((x) => x.id === s.id)?.transition ?? "dive" }));
|
|
163
|
+
write(rel, fill(sceneSkeleton(plan.purpose, s.id, s.role), { word, title: plan.title, line: str("line") ?? "", move: MOVES[i % MOVES.length], transition: tpl.scenes.find((x) => x.id === s.id)?.transition ?? "dive", ...dress[i] }));
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
const near = relative(process.cwd(), dir);
|
|
159
167
|
const rel = !near ? "." : near.startsWith("..") ? dir : near;
|
|
160
168
|
console.log(`${rel}/ — ${tpl.label}(${plan.purpose}) ${plan.aspect} ${plan.width}×${plan.height} · ${+plan.duration.toFixed(3)}초 · ${plan.theme ? `테마 ${plan.theme}` : `스타일 ${plan.style}`}${music ? ` · 곡 ${music}` : ""}`);
|
|
161
169
|
if (plan.themeCandidates?.length) console.log(` 테마 후보: ${plan.themeCandidates.map((c) => c.id).join(" · ")}`);
|
|
162
|
-
else if (str("theme") === "auto") console.log(" 맞는 approved 테마가 없어 프리셋으로 간다");
|
|
163
|
-
for (const s of kf.scenes) console.log(` ${s.from.toFixed(2).padStart(6)}–${(s.from + s.dur).toFixed(2).padEnd(6)} ${s.id.padEnd(9)} ${s.idea}`);
|
|
170
|
+
else if (str("theme") === "auto" && !plan.theme) console.log(" 맞는 approved 테마가 없어 프리셋으로 간다");
|
|
171
|
+
for (const s of kf.scenes) console.log(` ${s.from.toFixed(2).padStart(6)}–${(s.from + s.dur).toFixed(2).padEnd(6)} ${s.id.padEnd(9)} ${s.idea}${s.dress ? ` [${s.dress.bg}·${s.dress.text}${s.dress.wipeOut !== "none" ? `→${s.dress.wipeOut}` : ""}]` : ""}`);
|
|
172
|
+
if (tpl.render?.motionBlur) console.log(` 굽기: 모션 블러 ${tpl.render.motionBlur}장·셔터 ${tpl.render.shutter ?? 180}° (kf.json render — 끄려면 kf render --motion-blur 0)`);
|
|
164
173
|
for (const r of plan.reasons.filter((x) => !x.startsWith('"'))) console.log(` · ${r}`);
|
|
165
174
|
if (kept.length) console.log(` 있던 장면 파일은 그대로 뒀다: ${kept.join(" ")}`);
|
|
166
175
|
if (plan.gen?.length) console.log(` 만들 수 있는 자산(아직 안 만듦, 돈이 든다): ${plan.gen.map((g) => `gen:${g.kind}:${g.id}`).join(" ")} — 구체적인 프롬프트로 kf gen img --id <id> --project ${rel}`);
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { extname, join, resolve } from "node:path";
|
|
2
|
+
import { basename, extname, join, resolve } from "node:path";
|
|
3
3
|
import type { Command } from "../command.ts";
|
|
4
4
|
import { num, parseFlags } from "../command.ts";
|
|
5
5
|
import { openSession, type Tab } from "../browser.ts";
|
|
6
6
|
import { concat, encoder, type VideoFormat } from "../ffmpeg.ts";
|
|
7
7
|
import { acquireRenderLock } from "../lock.ts";
|
|
8
|
-
import { blurOpts, captureBlurred, rgbaToPng } from "../blur.ts";
|
|
8
|
+
import { blurOpts, captureBlurred, projectBlur, rgbaToPng } from "../blur.ts";
|
|
9
9
|
import { outPath } from "../out.ts";
|
|
10
|
+
import { cachedSegment, collectEvents, type KeyOpts, type MotionEvent, pruneSegments, renderSegments, sceneKeys, sceneSpans, type SegmentReport, type SegmentWork, variantOf } from "../segments.ts";
|
|
11
|
+
import { mixAudio, mixSettings } from "../mix.ts";
|
|
10
12
|
|
|
11
13
|
const MAX_TABS = 3;
|
|
12
14
|
|
|
@@ -50,29 +52,6 @@ function musicFrom(dir: string): number {
|
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
interface MotionEvent {
|
|
54
|
-
t: number;
|
|
55
|
-
kind: string;
|
|
56
|
-
source: string;
|
|
57
|
-
scene?: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// 움직임 부품이 쌓은 기록을 탭마다 모아 하나로 — step 모드는 pre-roll 구간이 겹쳐 같은 줄이 여러 탭에 있다.
|
|
61
|
-
async function collectEvents(tabs: Tab[]): Promise<MotionEvent[]> {
|
|
62
|
-
const seen = new Set<string>();
|
|
63
|
-
const out: MotionEvent[] = [];
|
|
64
|
-
for (const tab of tabs) {
|
|
65
|
-
const rows = await tab.page.evaluate(() => (window.__kf?.events ?? []) as unknown[]).catch(() => []);
|
|
66
|
-
for (const r of rows as MotionEvent[]) {
|
|
67
|
-
const key = `${r.t}|${r.kind}|${r.source}|${r.scene ?? ""}`;
|
|
68
|
-
if (seen.has(key)) continue;
|
|
69
|
-
seen.add(key);
|
|
70
|
-
out.push(r);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return out.sort((a, b) => a.t - b.t);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
55
|
// x.mp4 → x-자막.mp4 · frames → frames-자막
|
|
77
56
|
function withSuffix(p: string, suffix: string): string {
|
|
78
57
|
if (!suffix) return p;
|
|
@@ -82,14 +61,19 @@ function withSuffix(p: string, suffix: string): string {
|
|
|
82
61
|
|
|
83
62
|
const cmd: Command = {
|
|
84
63
|
summary: "MP4(또는 ProRes·PNG 묶음)로 굽기 — 탭 병렬, 곡 얹기, 잠금",
|
|
85
|
-
usage: `kf render <폴더> [--out x.mp4] [--from 초 --to 초] [--tabs 3] [--prores | --png] [--music 곡 | --no-music] [--captions on|off|fit [--suffix]]
|
|
64
|
+
usage: `kf render <폴더> [--out x.mp4] [--scenes a,b] [--from 초 --to 초] [--tabs 3] [--prores | --png] [--music 곡 | --no-music] [--captions on|off|fit [--suffix]]
|
|
86
65
|
--out 기본 <폴더>/out/render.mp4 (--prores 면 .mov, --png 면 폴더)
|
|
87
|
-
|
|
66
|
+
seek 모드는 장면 조각으로 굽는다 — 장면마다 out/segments/<장면>-<해시>.mp4(해시: 그 장면이 실제로 불러온 모듈·kf.json 그 장면 칸·
|
|
67
|
+
자산·자막 줄·굽기 설정, 이음매는 앞뒤 장면까지). 해시가 같은 조각은 다시 찍지 않고 이어 붙인 뒤 곡·목소리를 전체로 얹는다
|
|
68
|
+
--scenes a,b 그 장면 조각만 굽고 잇지 않는다(조각 경로를 찍는다). 다음 kf render 가 그 조각을 그대로 쓴다
|
|
69
|
+
--fresh 조각 캐시를 안 보고 다시 찍는다 · --no-segments 조각 없이 탭 몫으로 나눠 한 번에(예전 길)
|
|
70
|
+
--from/--to 구간만 굽는다. 곡도 그 구간만 잘라 얹는다(조각 없이)
|
|
88
71
|
--tabs 크롬 탭 수(최대 ${MAX_TABS}). seek 모드는 구간을 나눠 찍고, step 모드는 탭마다 구간 앞까지 빨리 돌린 뒤 찍는다
|
|
89
72
|
--music 기본은 페이지 meta.music(kf.json 의 music). 곡은 beats.json 의 from 초부터 얹는다
|
|
90
73
|
--music-from 곡에서 시작할 초를 직접 준다(--music 으로 다른 곡을 줄 때)
|
|
91
74
|
--crf H.264 화질(기본 16, 낮을수록 좋다)
|
|
92
|
-
--motion-blur N 한 프레임을 하위 프레임 N 장(예 8)으로 찍어 평균 — 빠른 것만 번진다. 3D 전환은 같은 평균으로 피사계 심도도. seek 모드만, N 배
|
|
75
|
+
--motion-blur N 한 프레임을 하위 프레임 N 장(예 8)으로 찍어 평균 — 빠른 것만 번진다. 3D 전환은 같은 평균으로 피사계 심도도. seek 모드만, N 배 느리다.
|
|
76
|
+
안 주면 kf.json 의 render.motionBlur(쇼츠·홍보 틀은 켜 둔다), 0 이면 끈다
|
|
93
77
|
--shutter 모션 블러 셔터 각도(기본 180 — 프레임 간격의 절반). 360 이면 다음 프레임까지 이어 번진다
|
|
94
78
|
--captions on|off|fit 화면 자막을 이 판만 켜거나 끈다(kf.json 의 captions 는 그대로). on 이면 captions.json 을 화면 아래 판에 얹고
|
|
95
79
|
안전 영역 아래쪽 자막 띠를 비운다(무대가 그 위로 배치) · fit 은 장면을 띠 위로 줄여 올린다(픽셀을 박아 둔 장면용)
|
|
@@ -127,15 +111,21 @@ const cmd: Command = {
|
|
|
127
111
|
const tabs = Math.max(1, Math.min(MAX_TABS, num(opts.tabs, MAX_TABS), Math.ceil(frames / fps)));
|
|
128
112
|
const chunks = split(startF, endF, tabs);
|
|
129
113
|
const music = format === "png" ? undefined : findMusic(dir, opts["no-music"] ? false : opts.music, meta.music);
|
|
130
|
-
let blur = blurOpts(opts);
|
|
114
|
+
let blur = blurOpts(opts, projectBlur(resolve(dir)));
|
|
131
115
|
if (blur && mode === "step") {
|
|
132
116
|
console.error("모션 블러는 seek 모드만 — step 모드는 뒤로 못 가서 끈다");
|
|
133
117
|
blur = null;
|
|
134
118
|
}
|
|
135
119
|
const songFrom = opts["music-from"] !== undefined ? num(opts["music-from"], 0) : opts.music ? 0 : musicFrom(resolve(dir));
|
|
136
120
|
|
|
121
|
+
// seek 모드 통짜 굽기는 장면 조각으로 — 바뀐 장면만 다시 찍고 잇는다. --no-segments 면 예전처럼 탭 몫으로 나눠 한 번에
|
|
122
|
+
const segmented = !opts["no-segments"] && mode === "seek" && format !== "png" && opts.from === undefined && opts.to === undefined;
|
|
123
|
+
const only = typeof opts.scenes === "string" ? opts.scenes.split(",").map((x) => x.trim()).filter(Boolean) : null;
|
|
124
|
+
if (only && !segmented) throw new Error("--scenes 는 seek 모드 영상 굽기에서만 — --from/--to·--png·--no-segments·step 모드와 같이 못 쓴다");
|
|
125
|
+
const crf = opts.crf ? num(opts.crf, 16) : undefined;
|
|
126
|
+
|
|
137
127
|
console.error(
|
|
138
|
-
`${meta.width}x${meta.height} ${fps}fps ${mode} 모드 · 프레임 ${startF}..${endF - 1} (${frames}장) · 탭 ${chunks.length}` +
|
|
128
|
+
`${meta.width}x${meta.height} ${fps}fps ${mode} 모드 · 프레임 ${startF}..${endF - 1} (${frames}장) · 탭 ${segmented ? tabs : chunks.length}` +
|
|
139
129
|
(music ? ` · 곡 ${music}${songFrom ? ` (${songFrom}초부터)` : ""}` : "") +
|
|
140
130
|
(blur ? ` · 모션 블러 ${blur.count}장·셔터 ${blur.shutter}°` : ""),
|
|
141
131
|
);
|
|
@@ -143,38 +133,75 @@ const cmd: Command = {
|
|
|
143
133
|
if (format === "png") mkdirSync(out, { recursive: true });
|
|
144
134
|
|
|
145
135
|
let done = 0;
|
|
136
|
+
let todo = frames;
|
|
146
137
|
const progress = setInterval(() => {
|
|
147
138
|
const sec = (Date.now() - started) / 1000;
|
|
148
|
-
console.error(` ${done}/${
|
|
139
|
+
console.error(` ${done}/${todo} (${((done / Math.max(1, todo)) * 100).toFixed(0)}%) ${(done / sec).toFixed(1)} 장/초`);
|
|
149
140
|
}, 5000);
|
|
150
141
|
|
|
151
|
-
|
|
152
|
-
|
|
142
|
+
let segFiles: string[] = [];
|
|
143
|
+
let events: MotionEvent[] = [];
|
|
144
|
+
let segments: { rendered: string[]; reused: string[] } | undefined;
|
|
153
145
|
try {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
if (enc) await enc.write(frame);
|
|
172
|
-
else writeFileSync(join(out, `${String(f).padStart(6, "0")}.png`), frame);
|
|
173
|
-
done++;
|
|
146
|
+
if (segmented) {
|
|
147
|
+
const root = s.project.dir;
|
|
148
|
+
const keyOpts: KeyOpts = { width: meta.width, height: meta.height, fps, format, crf, blur, captions, browser: s.browser.version() };
|
|
149
|
+
const keys = sceneKeys(s.project.server, root, keyOpts);
|
|
150
|
+
const spans = sceneSpans(JSON.parse(readFileSync(join(root, "kf.json"), "utf8")).scenes, fps, total);
|
|
151
|
+
const missing = only?.filter((id) => !spans.some((sp) => sp.id === id)) ?? [];
|
|
152
|
+
if (missing.length) throw new Error(`kf.json 에 없는 장면: ${missing.join(", ")}`);
|
|
153
|
+
const reused = new Map<string, SegmentReport>();
|
|
154
|
+
const work: SegmentWork[] = [];
|
|
155
|
+
for (const span of only ? spans.filter((sp) => only.includes(sp.id)) : spans) {
|
|
156
|
+
const { key, inputs } = keys.get(span.id)!;
|
|
157
|
+
const hit = opts.fresh ? null : cachedSegment(root, span.id, key, ext);
|
|
158
|
+
if (!hit) work.push({ span, key, inputs });
|
|
159
|
+
else {
|
|
160
|
+
reused.set(span.id, hit);
|
|
161
|
+
console.error(`장면 ${span.id}: 그대로 — ${basename(hit.file)}`);
|
|
174
162
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
163
|
+
}
|
|
164
|
+
todo = work.reduce((n, w) => n + w.span.end - w.span.start, 0);
|
|
165
|
+
console.error(`조각 ${work.length + reused.size} — 다시 찍기 ${work.length}(${todo}장) · 그대로 ${reused.size}`);
|
|
166
|
+
const made = work.length ? await renderSegments({ session: s, root, work, tabs, format, ext, crf, blur, tmp, onFrame: () => done++ }) : [];
|
|
167
|
+
const all = new Map<string, SegmentReport>([...reused, ...made.map((m) => [m.id, m] as const)]);
|
|
168
|
+
if (only) {
|
|
169
|
+
for (const id of only) console.log(all.get(id)!.file);
|
|
170
|
+
console.error(`조각만 ${((Date.now() - started) / 1000).toFixed(1)}초 — 잇기는 kf render ${dir}`);
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
segFiles = spans.map((sp) => all.get(sp.id)!.file);
|
|
174
|
+
events = spans.flatMap((sp) => all.get(sp.id)!.events);
|
|
175
|
+
segments = { rendered: made.map((m) => m.id), reused: [...reused.keys()] };
|
|
176
|
+
pruneSegments(root, variantOf(keyOpts), Object.fromEntries(spans.map((sp) => [sp.id, keys.get(sp.id)!.key])));
|
|
177
|
+
} else {
|
|
178
|
+
const usedTabs: Tab[] = [];
|
|
179
|
+
await Promise.all(
|
|
180
|
+
chunks.map(async (c) => {
|
|
181
|
+
const tab: Tab = c.index === 0 ? s.first : await s.tab();
|
|
182
|
+
usedTabs.push(tab);
|
|
183
|
+
const seg = join(tmp, `seg-${String(c.index).padStart(2, "0")}${ext}`);
|
|
184
|
+
segFiles[c.index] = seg;
|
|
185
|
+
const raw = blur ? { width: meta.width, height: meta.height } : undefined;
|
|
186
|
+
const enc = format === "png" ? null : encoder(seg, fps, format, crf, raw);
|
|
187
|
+
for (let f = c.start; f < c.end; f++) {
|
|
188
|
+
let frame: Buffer;
|
|
189
|
+
if (blur) {
|
|
190
|
+
const img = await captureBlurred(tab, s.info, f / fps, blur);
|
|
191
|
+
frame = enc ? img.data : rgbaToPng(img);
|
|
192
|
+
} else {
|
|
193
|
+
await tab.seek(f / fps);
|
|
194
|
+
frame = await tab.capture();
|
|
195
|
+
}
|
|
196
|
+
if (enc) await enc.write(frame);
|
|
197
|
+
else writeFileSync(join(out, `${String(f).padStart(6, "0")}.png`), frame);
|
|
198
|
+
done++;
|
|
199
|
+
}
|
|
200
|
+
await enc?.end();
|
|
201
|
+
}),
|
|
202
|
+
);
|
|
203
|
+
events = await collectEvents(usedTabs);
|
|
204
|
+
}
|
|
178
205
|
} finally {
|
|
179
206
|
clearInterval(progress);
|
|
180
207
|
}
|
|
@@ -182,7 +209,13 @@ const cmd: Command = {
|
|
|
182
209
|
if (format !== "png") {
|
|
183
210
|
const list = join(tmp, "list.txt");
|
|
184
211
|
writeFileSync(list, segFiles.map((f) => `file '${f.replace(/'/g, "'\\''")}'`).join("\n") + "\n");
|
|
185
|
-
|
|
212
|
+
// kf.json 에 audio(섞기 값)가 있으면 내레이션·곡을 섞은 소리를 얹는다(덕킹·끝 페이드·LUFS) — 없으면 곡만
|
|
213
|
+
const mix = opts["no-music"] ? null : mixSettings(resolve(dir));
|
|
214
|
+
const mixed = mix && (mix.voice || music)
|
|
215
|
+
? await mixAudio({ voice: mix.voice, music, musicFrom: songFrom }, { audio: mix.audio, from: startF / fps, dur: frames / fps, total: meta.duration, out: join(tmp, "mix.wav") })
|
|
216
|
+
: undefined;
|
|
217
|
+
await concat({ list, out, format, music: mixed ?? music, from: mixed ? 0 : songFrom + startF / fps, dur: frames / fps });
|
|
218
|
+
if (mixed) console.error(`소리: ${mix!.voice ? "내레이션" : ""}${mix!.voice && music ? " + " : ""}${music ? "곡" : ""} → ${mix!.audio.lufs} LUFS`);
|
|
186
219
|
}
|
|
187
220
|
const sec = (Date.now() - started) / 1000;
|
|
188
221
|
const report = {
|
|
@@ -192,18 +225,19 @@ const cmd: Command = {
|
|
|
192
225
|
from: startF / fps,
|
|
193
226
|
to: endF / fps,
|
|
194
227
|
frames,
|
|
195
|
-
tabs: chunks.length,
|
|
228
|
+
tabs: segmented ? tabs : chunks.length,
|
|
196
229
|
format,
|
|
197
230
|
motionBlur: blur,
|
|
198
231
|
captions: captions ?? null,
|
|
199
232
|
music: music ?? null,
|
|
200
233
|
seconds: Math.round(sec * 10) / 10,
|
|
234
|
+
...(segments ? { segments } : {}),
|
|
201
235
|
markers: s.info.markers,
|
|
202
|
-
events
|
|
236
|
+
events,
|
|
203
237
|
};
|
|
204
238
|
const reportFile = format === "png" ? join(out, "render.json") : out.replace(/\.[^.]+$/, ".json");
|
|
205
239
|
writeFileSync(reportFile, JSON.stringify(report, null, 1));
|
|
206
|
-
console.error(`${
|
|
240
|
+
console.error(`${todo}장 ${sec.toFixed(1)}초 (${(todo / sec).toFixed(1)} 장/초)${segments ? ` · 조각 다시 ${segments.rendered.length}·그대로 ${segments.reused.length}` : ""} · 보고서 ${reportFile}`);
|
|
207
241
|
console.log(out);
|
|
208
242
|
return 0;
|
|
209
243
|
} finally {
|