kasaframe 0.0.0-stage → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +148 -0
  3. package/README.md +92 -2
  4. package/app/package.json +12 -0
  5. package/app/server/engine.ts +533 -0
  6. package/app/server/main.ts +209 -0
  7. package/app/web/env.d.ts +2 -0
  8. package/app/web/icons.ts +31 -0
  9. package/app/web/index.html +21 -0
  10. package/app/web/main.ts +1195 -0
  11. package/app/web/style.css +463 -0
  12. package/bin/hooks.mjs +64 -0
  13. package/bin/kf.mjs +32 -0
  14. package/docs/AGENTS.md +286 -0
  15. package/music/README.md +37 -0
  16. package/package.json +44 -4
  17. package/packages/adapters/package.json +36 -0
  18. package/packages/adapters/src/css.ts +11 -0
  19. package/packages/adapters/src/gsap.ts +26 -0
  20. package/packages/adapters/src/index.ts +9 -0
  21. package/packages/adapters/src/lottie.ts +22 -0
  22. package/packages/adapters/src/media.ts +59 -0
  23. package/packages/adapters/src/page.ts +229 -0
  24. package/packages/adapters/src/project.ts +214 -0
  25. package/packages/adapters/src/react.ts +36 -0
  26. package/packages/adapters/src/svelte.ts +22 -0
  27. package/packages/adapters/src/three.ts +18 -0
  28. package/packages/adapters/src/vue.ts +27 -0
  29. package/packages/check/package.json +19 -0
  30. package/packages/check/src/check.ts +280 -0
  31. package/packages/check/src/cli.ts +70 -0
  32. package/packages/check/src/image.ts +146 -0
  33. package/packages/check/src/index.ts +9 -0
  34. package/packages/check/src/ocr.swift +37 -0
  35. package/packages/check/src/probe.ts +328 -0
  36. package/packages/check/src/report.ts +44 -0
  37. package/packages/check/src/rules/assets.ts +45 -0
  38. package/packages/check/src/rules/beats.ts +212 -0
  39. package/packages/check/src/rules/camera.ts +217 -0
  40. package/packages/check/src/rules/captions.ts +49 -0
  41. package/packages/check/src/rules/flash.ts +235 -0
  42. package/packages/check/src/rules/frames.ts +136 -0
  43. package/packages/check/src/rules/layout.ts +281 -0
  44. package/packages/check/src/rules/page.ts +87 -0
  45. package/packages/check/src/rules/runs.ts +20 -0
  46. package/packages/check/src/rules/text.ts +326 -0
  47. package/packages/check/src/scan.ts +240 -0
  48. package/packages/check/src/serve.ts +67 -0
  49. package/packages/check/src/types.ts +80 -0
  50. package/packages/check/src/video.ts +137 -0
  51. package/packages/cli/package.json +30 -0
  52. package/packages/cli/src/blur.ts +80 -0
  53. package/packages/cli/src/browser.ts +203 -0
  54. package/packages/cli/src/command.ts +44 -0
  55. package/packages/cli/src/commands/app.ts +25 -0
  56. package/packages/cli/src/commands/beats.ts +36 -0
  57. package/packages/cli/src/commands/bench.ts +123 -0
  58. package/packages/cli/src/commands/blender.ts +293 -0
  59. package/packages/cli/src/commands/check.ts +25 -0
  60. package/packages/cli/src/commands/direct.ts +210 -0
  61. package/packages/cli/src/commands/doctor.ts +30 -0
  62. package/packages/cli/src/commands/gen.ts +173 -0
  63. package/packages/cli/src/commands/look.ts +46 -0
  64. package/packages/cli/src/commands/lyrics.ts +157 -0
  65. package/packages/cli/src/commands/make.ts +1121 -0
  66. package/packages/cli/src/commands/music.ts +78 -0
  67. package/packages/cli/src/commands/narrate.ts +238 -0
  68. package/packages/cli/src/commands/new.ts +185 -0
  69. package/packages/cli/src/commands/preview.ts +43 -0
  70. package/packages/cli/src/commands/render.ts +250 -0
  71. package/packages/cli/src/commands/series.ts +110 -0
  72. package/packages/cli/src/commands/setup.ts +102 -0
  73. package/packages/cli/src/commands/sheet.ts +114 -0
  74. package/packages/cli/src/commands/skill.ts +43 -0
  75. package/packages/cli/src/commands/still.ts +40 -0
  76. package/packages/cli/src/commands/theme.ts +172 -0
  77. package/packages/cli/src/commands/transitions.ts +40 -0
  78. package/packages/cli/src/deps.ts +122 -0
  79. package/packages/cli/src/ffmpeg.ts +81 -0
  80. package/packages/cli/src/index.ts +8 -0
  81. package/packages/cli/src/kf.ts +64 -0
  82. package/packages/cli/src/lock.ts +72 -0
  83. package/packages/cli/src/mix.ts +131 -0
  84. package/packages/cli/src/out.ts +13 -0
  85. package/packages/cli/src/pool.ts +34 -0
  86. package/packages/cli/src/segments.ts +402 -0
  87. package/packages/cli/src/serve.ts +124 -0
  88. package/packages/clock/package.json +14 -0
  89. package/packages/clock/src/index.ts +25 -0
  90. package/packages/clock/src/install.ts +351 -0
  91. package/packages/clock/src/types.ts +45 -0
  92. package/packages/core/package.json +9 -0
  93. package/packages/core/src/beats.ts +283 -0
  94. package/packages/core/src/ease.ts +87 -0
  95. package/packages/core/src/frame.ts +99 -0
  96. package/packages/core/src/index.ts +27 -0
  97. package/packages/core/src/project.ts +157 -0
  98. package/packages/core/src/rand.ts +56 -0
  99. package/packages/core/src/spring.ts +52 -0
  100. package/packages/core/src/style.ts +100 -0
  101. package/packages/core/src/time.ts +88 -0
  102. package/packages/director/package.json +23 -0
  103. package/packages/director/src/assemble.ts +361 -0
  104. package/packages/director/src/brief.ts +122 -0
  105. package/packages/director/src/catalog.ts +49 -0
  106. package/packages/director/src/content.ts +343 -0
  107. package/packages/director/src/fallback.ts +58 -0
  108. package/packages/director/src/index.ts +13 -0
  109. package/packages/director/src/pattern.ts +90 -0
  110. package/packages/director/src/patterns/fill/cells.ts +115 -0
  111. package/packages/director/src/patterns/formula/stack.ts +93 -0
  112. package/packages/director/src/patterns/util.ts +58 -0
  113. package/packages/director/src/scene.ts +62 -0
  114. package/packages/director/src/select.ts +163 -0
  115. package/packages/director/src/transitions.ts +115 -0
  116. package/packages/gen/PROVIDERS.md +90 -0
  117. package/packages/gen/package.json +9 -0
  118. package/packages/gen/src/ask.ts +132 -0
  119. package/packages/gen/src/audio.ts +91 -0
  120. package/packages/gen/src/blender.ts +54 -0
  121. package/packages/gen/src/capabilities.ts +151 -0
  122. package/packages/gen/src/generate.ts +206 -0
  123. package/packages/gen/src/image/codex.ts +117 -0
  124. package/packages/gen/src/image/og.ts +133 -0
  125. package/packages/gen/src/index.ts +26 -0
  126. package/packages/gen/src/keys.ts +86 -0
  127. package/packages/gen/src/media.ts +150 -0
  128. package/packages/gen/src/music/elevenlabs.ts +41 -0
  129. package/packages/gen/src/music/index.ts +120 -0
  130. package/packages/gen/src/music/library.ts +73 -0
  131. package/packages/gen/src/music/lyria.ts +52 -0
  132. package/packages/gen/src/music/types.ts +28 -0
  133. package/packages/gen/src/plan.ts +106 -0
  134. package/packages/gen/src/registry.ts +54 -0
  135. package/packages/gen/src/seam.ts +103 -0
  136. package/packages/gen/src/tts/dashscope.ts +64 -0
  137. package/packages/gen/src/tts/elevenlabs.ts +84 -0
  138. package/packages/gen/src/tts/gemini.ts +73 -0
  139. package/packages/gen/src/tts/index.ts +241 -0
  140. package/packages/gen/src/tts/local.ts +42 -0
  141. package/packages/gen/src/tts/types.ts +44 -0
  142. package/packages/gen/src/types.ts +90 -0
  143. package/packages/gen/src/video/fal.ts +140 -0
  144. package/packages/gen/src/video/h3.ts +112 -0
  145. package/packages/gen/src/video/http.ts +44 -0
  146. package/packages/gen/src/video/ltx.ts +60 -0
  147. package/packages/gen/src/video/minimax.ts +55 -0
  148. package/packages/gen/src/video/veo.ts +71 -0
  149. package/packages/jizura/package.json +16 -0
  150. package/packages/jizura/src/deny.ts +73 -0
  151. package/packages/jizura/src/index.ts +7 -0
  152. package/packages/jizura/src/install.ts +269 -0
  153. package/packages/jizura/src/layer.ts +325 -0
  154. package/packages/jizura/src/lyrics.ts +81 -0
  155. package/packages/jizura/src/pin.ts +8 -0
  156. package/packages/jizura/src/vendor.ts +140 -0
  157. package/packages/jizura/src/vite.ts +63 -0
  158. package/packages/jizura/vendor/JIZURA_LICENSE.txt +21 -0
  159. package/packages/jizura/vendor/NOTICE +43 -0
  160. package/packages/jizura/vendor/engine.json +47 -0
  161. package/packages/jizura/vendor/fonts/BlackHanSans-Regular.woff2 +0 -0
  162. package/packages/jizura/vendor/fonts/DotGothic16-Regular.woff2 +0 -0
  163. package/packages/jizura/vendor/fonts/GowunBatang-Bold.woff2 +0 -0
  164. package/packages/jizura/vendor/fonts/IBMPlexMono-Medium.ttf +0 -0
  165. package/packages/jizura/vendor/fonts/IBMPlexMono-SemiBold.ttf +0 -0
  166. package/packages/jizura/vendor/fonts/NotoSansKR-VF.woff2 +0 -0
  167. package/packages/jizura/vendor/fonts/NotoSerifKR-VF.woff2 +0 -0
  168. package/packages/jizura/vendor/fonts/OFL-blackhansans.txt +93 -0
  169. package/packages/jizura/vendor/fonts/OFL-dotgothic16.txt +93 -0
  170. package/packages/jizura/vendor/fonts/OFL-gowunbatang.txt +94 -0
  171. package/packages/jizura/vendor/fonts/OFL-ibmplexmono.txt +93 -0
  172. package/packages/jizura/vendor/fonts/OFL-notosanskr.txt +93 -0
  173. package/packages/jizura/vendor/fonts/OFL-notoserifkr.txt +94 -0
  174. package/packages/jizura/vendor/fonts/fonts.json +52 -0
  175. package/packages/jizura/vendor/jizura_engine.js +35024 -0
  176. package/packages/motion/README.md +153 -0
  177. package/packages/motion/package.json +30 -0
  178. package/packages/motion/src/camera.ts +97 -0
  179. package/packages/motion/src/captions.ts +487 -0
  180. package/packages/motion/src/enter.ts +128 -0
  181. package/packages/motion/src/hangul.ts +129 -0
  182. package/packages/motion/src/impact.ts +100 -0
  183. package/packages/motion/src/index.ts +17 -0
  184. package/packages/motion/src/moves/brushWipe.ts +103 -0
  185. package/packages/motion/src/moves/cloneArray.ts +111 -0
  186. package/packages/motion/src/moves/curtains.ts +81 -0
  187. package/packages/motion/src/moves/dress.ts +191 -0
  188. package/packages/motion/src/moves/echoStack.ts +70 -0
  189. package/packages/motion/src/moves/fit.ts +70 -0
  190. package/packages/motion/src/moves/flood.ts +61 -0
  191. package/packages/motion/src/moves/ghostStack.ts +88 -0
  192. package/packages/motion/src/moves/gridFloor.ts +100 -0
  193. package/packages/motion/src/moves/imageSwarm.ts +113 -0
  194. package/packages/motion/src/moves/index.ts +38 -0
  195. package/packages/motion/src/moves/irisBlades.ts +113 -0
  196. package/packages/motion/src/moves/karaokeLine.ts +108 -0
  197. package/packages/motion/src/moves/ledBoard.ts +94 -0
  198. package/packages/motion/src/moves/liquidTab.ts +72 -0
  199. package/packages/motion/src/moves/markerWrite.ts +63 -0
  200. package/packages/motion/src/moves/menu.ts +138 -0
  201. package/packages/motion/src/moves/morphBox.ts +96 -0
  202. package/packages/motion/src/moves/neonSign.ts +78 -0
  203. package/packages/motion/src/moves/panelPop.ts +78 -0
  204. package/packages/motion/src/moves/paperTear.ts +75 -0
  205. package/packages/motion/src/moves/path.ts +64 -0
  206. package/packages/motion/src/moves/pathText.ts +107 -0
  207. package/packages/motion/src/moves/pointSphere.ts +109 -0
  208. package/packages/motion/src/moves/ransomLetters.ts +73 -0
  209. package/packages/motion/src/moves/shapeMorph.ts +143 -0
  210. package/packages/motion/src/moves/slamWords.ts +96 -0
  211. package/packages/motion/src/moves/sparkTrail.ts +130 -0
  212. package/packages/motion/src/moves/speedLines.ts +91 -0
  213. package/packages/motion/src/moves/splitFlap.ts +149 -0
  214. package/packages/motion/src/moves/stepWords.ts +128 -0
  215. package/packages/motion/src/moves/strikeOut.ts +98 -0
  216. package/packages/motion/src/moves/sunburst.ts +58 -0
  217. package/packages/motion/src/moves/tickerRows.ts +91 -0
  218. package/packages/motion/src/moves/tokenPicker.ts +115 -0
  219. package/packages/motion/src/moves/truchet.ts +85 -0
  220. package/packages/motion/src/moves/wordWall.ts +78 -0
  221. package/packages/motion/src/moves/worldRoll.ts +32 -0
  222. package/packages/motion/src/particles.ts +102 -0
  223. package/packages/motion/src/react/index.tsx +21 -0
  224. package/packages/motion/src/react/parts.tsx +431 -0
  225. package/packages/motion/src/react/time.tsx +97 -0
  226. package/packages/motion/src/text.ts +103 -0
  227. package/packages/motion/src/transition.ts +82 -0
  228. package/packages/motion/src/transition3d/cards.ts +65 -0
  229. package/packages/motion/src/transition3d/chain.ts +84 -0
  230. package/packages/motion/src/transition3d/dive.ts +181 -0
  231. package/packages/motion/src/transition3d/fold.ts +110 -0
  232. package/packages/motion/src/transition3d/gl.ts +219 -0
  233. package/packages/motion/src/transition3d/index.ts +235 -0
  234. package/packages/motion/src/transition3d/live.ts +70 -0
  235. package/packages/motion/src/transition3d/portal.ts +115 -0
  236. package/packages/motion/src/transition3d/react.tsx +29 -0
  237. package/packages/motion/src/transition3d/rig.ts +600 -0
  238. package/packages/motion/src/transition3d/shatter.ts +104 -0
  239. package/packages/motion/src/transition3d/space.ts +173 -0
  240. package/packages/motion/src/transition3d/whip.ts +78 -0
  241. package/packages/motion/src/transition3d/world.ts +737 -0
  242. package/packages/motion/src/transition3d/zoomOut.ts +114 -0
  243. package/packages/motion/src/transitions2d/catalog.ts +127 -0
  244. package/packages/motion/src/transitions2d/css.d.ts +2 -0
  245. package/packages/motion/src/transitions2d/cult-ui/LICENSE.md +9 -0
  246. package/packages/motion/src/transitions2d/cult-ui/RollingNumber.tsx +43 -0
  247. package/packages/motion/src/transitions2d/gl-transitions/LICENSE +25 -0
  248. package/packages/motion/src/transitions2d/gl-transitions/shaders.ts +1931 -0
  249. package/packages/motion/src/transitions2d/index.ts +2 -0
  250. package/packages/motion/src/transitions2d/kokonutui/LICENSE +21 -0
  251. package/packages/motion/src/transitions2d/kokonutui/MatrixText.css +16 -0
  252. package/packages/motion/src/transitions2d/kokonutui/MatrixText.tsx +94 -0
  253. package/packages/motion/src/transitions2d/magicui/HyperText.css +15 -0
  254. package/packages/motion/src/transitions2d/magicui/HyperText.tsx +115 -0
  255. package/packages/motion/src/transitions2d/magicui/LICENSE.md +21 -0
  256. package/packages/motion/src/transitions2d/magicui/MorphingText.css +25 -0
  257. package/packages/motion/src/transitions2d/magicui/MorphingText.tsx +130 -0
  258. package/packages/motion/src/transitions2d/magicui/WordRotate.tsx +42 -0
  259. package/packages/motion/src/transitions2d/react.ts +13 -0
  260. package/packages/motion/src/transitions2d/skiper-ui/ClipDiv.css +39 -0
  261. package/packages/motion/src/transitions2d/skiper-ui/ClipDiv.tsx +32 -0
  262. package/packages/motion/src/transitions2d/smoothui/LICENSE +21 -0
  263. package/packages/motion/src/transitions2d/smoothui/SharedAxisY.tsx +50 -0
  264. package/packages/motion/src/transitions2d/smoothui/TextMorph.tsx +90 -0
  265. package/packages/motion/src/transitions2d/smoothui/shaders.ts +223 -0
  266. package/packages/motion/src/transitions2d/unlumen/LICENSE +21 -0
  267. package/packages/motion/src/transitions2d/unlumen/ScrambleText.tsx +114 -0
  268. package/packages/motion/src/transitions2d/vengeance-ui/LICENSE +21 -0
  269. package/packages/motion/src/transitions2d/vengeance-ui/MorphText.css +69 -0
  270. package/packages/motion/src/transitions2d/vengeance-ui/MorphText.tsx +45 -0
  271. package/packages/motion/src/type.ts +131 -0
  272. package/packages/motion/src/ui.ts +172 -0
  273. package/packages/patterns/package.json +15 -0
  274. package/packages/patterns/src/bars/rank.ts +119 -0
  275. package/packages/patterns/src/before-after/shrink.ts +127 -0
  276. package/packages/patterns/src/before-after/strike.ts +125 -0
  277. package/packages/patterns/src/code/terminal.ts +133 -0
  278. package/packages/patterns/src/compare/bars.ts +142 -0
  279. package/packages/patterns/src/compare/duel.ts +103 -0
  280. package/packages/patterns/src/end/lockup.ts +85 -0
  281. package/packages/patterns/src/end/slam.ts +91 -0
  282. package/packages/patterns/src/flow/graph.ts +250 -0
  283. package/packages/patterns/src/hook-number/counter.ts +101 -0
  284. package/packages/patterns/src/hook-number/roll.ts +179 -0
  285. package/packages/patterns/src/hook-number/slam.ts +91 -0
  286. package/packages/patterns/src/index.ts +38 -0
  287. package/packages/patterns/src/kit.ts +449 -0
  288. package/packages/patterns/src/limit/gauge.ts +117 -0
  289. package/packages/patterns/src/list/cards.ts +131 -0
  290. package/packages/patterns/src/list/cycle.ts +22 -0
  291. package/packages/patterns/src/media/panels.ts +100 -0
  292. package/packages/patterns/src/objects.ts +234 -0
  293. package/packages/patterns/src/quote/karaoke.ts +126 -0
  294. package/packages/patterns/src/quote/slam.ts +22 -0
  295. package/packages/patterns/src/screen/focus.ts +85 -0
  296. package/packages/patterns/src/steps/path.ts +137 -0
  297. package/packages/patterns/src/steps/stack.ts +111 -0
  298. package/packages/patterns/src/term-reveal/chips.ts +92 -0
  299. package/packages/patterns/src/term-reveal/expand.ts +127 -0
  300. package/packages/patterns/src/timeline/axis.ts +144 -0
  301. package/packages/patterns/src/title/mask.ts +85 -0
  302. package/packages/patterns/src/title/slam.ts +23 -0
  303. package/packages/patterns/src/toggle-sim/switch.ts +92 -0
  304. package/packages/patterns/src/trend/columns.ts +108 -0
  305. package/packages/patterns/src/trend/draw.ts +140 -0
  306. package/packages/patterns/src/types.ts +67 -0
  307. package/packages/patterns/src/words/moves.ts +42 -0
  308. package/packages/patterns/src/words/slam.ts +4 -0
  309. package/packages/plan/package.json +15 -0
  310. package/packages/plan/src/asks.ts +256 -0
  311. package/packages/plan/src/brief.ts +383 -0
  312. package/packages/plan/src/captions.ts +78 -0
  313. package/packages/plan/src/detect.ts +93 -0
  314. package/packages/plan/src/index.ts +27 -0
  315. package/packages/plan/src/layout.ts +176 -0
  316. package/packages/plan/src/llm.ts +172 -0
  317. package/packages/plan/src/look.ts +222 -0
  318. package/packages/plan/src/plan.ts +179 -0
  319. package/packages/plan/src/request.ts +55 -0
  320. package/packages/plan/src/series.ts +76 -0
  321. package/packages/plan/src/studio.ts +53 -0
  322. package/packages/plan/src/templates.ts +42 -0
  323. package/packages/plan/src/themes.ts +48 -0
  324. package/packages/plan/src/types.ts +157 -0
  325. package/packages/studio/package.json +12 -0
  326. package/packages/studio/src/index.ts +1 -0
  327. package/packages/studio/src/plugin.ts +236 -0
  328. package/packages/studio/src/ui/main.ts +503 -0
  329. package/packages/studio/src/ui/studio.css +165 -0
  330. package/packages/studio/src/ui/timeline.ts +317 -0
  331. package/packages/themes/kit/THEME-MAKE.md +68 -0
  332. package/packages/themes/kit/THEME.md +30 -0
  333. package/packages/themes/kit/briefs/og-intro-15s/brief.json +16 -0
  334. package/packages/themes/kit/briefs/og-intro-15s/facts.ts +29 -0
  335. package/packages/themes/kit/briefs/og-intro-15s/logo.svg +12 -0
  336. package/packages/themes/kit/effects.ts +5 -0
  337. package/packages/themes/kit/sample/index.html +11 -0
  338. package/packages/themes/kit/sample/main.ts +9 -0
  339. package/packages/themes/kit/sample/scenes/scene.ts +15 -0
  340. package/packages/themes/package.json +19 -0
  341. package/packages/themes/src/factory.ts +296 -0
  342. package/packages/themes/src/fx.ts +14 -0
  343. package/packages/themes/src/index.ts +114 -0
  344. package/packages/themes/src/slotkit.ts +79 -0
  345. package/packages/themes/src/slots.ts +46 -0
  346. package/packages/themes/src/stage3d.ts +197 -0
  347. package/packages/themes/src/types.ts +58 -0
  348. package/presets/kawaii-pastel.json +94 -0
  349. package/presets/neutral-dark.json +82 -0
  350. package/presets/og.json +98 -0
  351. package/series/og-shorts.json +14 -0
  352. package/skills/kasaframe/SKILL.md +33 -0
  353. package/templates/_shared/project/index.html +11 -0
  354. package/templates/_shared/project/lib/react.ts +22 -0
  355. package/templates/_shared/project/lib/stage.ts +292 -0
  356. package/templates/_shared/project/main.ts +9 -0
  357. package/templates/_shared/scenes/default.ts +21 -0
  358. package/templates/_shared/scenes/drop.ts +33 -0
  359. package/templates/_shared/scenes/end.ts +26 -0
  360. package/templates/_shared/scenes/hook.ts +18 -0
  361. package/templates/_shared/scenes/seam.ts +41 -0
  362. package/templates/_shared/scenes/step.ts +49 -0
  363. package/templates/ad/preview.png +0 -0
  364. package/templates/ad/template.json +151 -0
  365. package/templates/demo/preview.png +0 -0
  366. package/templates/demo/template.json +141 -0
  367. package/templates/explainer/preview.png +0 -0
  368. package/templates/explainer/template.json +193 -0
  369. package/templates/launch/preview.png +0 -0
  370. package/templates/launch/template.json +163 -0
  371. package/templates/mv/preview.png +0 -0
  372. package/templates/mv/scenes/default.ts +98 -0
  373. package/templates/mv/template.json +178 -0
  374. package/templates/promo/preview.png +0 -0
  375. package/templates/promo/template.json +185 -0
  376. package/templates/shorts/preview.png +0 -0
  377. package/templates/shorts/scenes/hook.ts +37 -0
  378. package/templates/shorts/template.json +196 -0
  379. package/templates/square/preview.png +0 -0
  380. package/templates/square/template.json +133 -0
  381. package/themes/debug-hud/THEME.md +39 -0
  382. package/themes/debug-hud/effects.ts +389 -0
  383. package/themes/debug-hud/sample/assets/og/logo.svg +12 -0
  384. package/themes/debug-hud/sample/beats.json +1 -0
  385. package/themes/debug-hud/sample/index.html +11 -0
  386. package/themes/debug-hud/sample/kf.json +59 -0
  387. package/themes/debug-hud/sample/lib/hudkit.ts +49 -0
  388. package/themes/debug-hud/sample/lib/og.ts +29 -0
  389. package/themes/debug-hud/sample/main.ts +9 -0
  390. package/themes/debug-hud/sample/scenes/end.ts +62 -0
  391. package/themes/debug-hud/sample/scenes/gather.ts +67 -0
  392. package/themes/debug-hud/sample/scenes/models.ts +70 -0
  393. package/themes/debug-hud/sample/scenes/price.ts +79 -0
  394. package/themes/debug-hud/sample/style.json +91 -0
  395. package/themes/debug-hud/slots.ts +99 -0
  396. package/themes/debug-hud/still.png +0 -0
  397. package/themes/debug-hud/style.json +91 -0
  398. package/themes/debug-hud/theme.json +68 -0
  399. package/themes/og-measure/THEME.md +47 -0
  400. package/themes/og-measure/effects.ts +432 -0
  401. package/themes/og-measure/sample/assets/brand/og-logo-full.svg +12 -0
  402. package/themes/og-measure/sample/assets/brand/og-logo.svg +12 -0
  403. package/themes/og-measure/sample/assets/data/measure.json +353 -0
  404. package/themes/og-measure/sample/beats.json +1 -0
  405. package/themes/og-measure/sample/index.html +11 -0
  406. package/themes/og-measure/sample/kf.json +66 -0
  407. package/themes/og-measure/sample/lib/data.ts +62 -0
  408. package/themes/og-measure/sample/main.ts +9 -0
  409. package/themes/og-measure/sample/measure.ts +78 -0
  410. package/themes/og-measure/sample/scenes/end.ts +47 -0
  411. package/themes/og-measure/sample/scenes/gather.ts +51 -0
  412. package/themes/og-measure/sample/scenes/models.ts +34 -0
  413. package/themes/og-measure/sample/scenes/one.ts +39 -0
  414. package/themes/og-measure/sample/scenes/price.ts +56 -0
  415. package/themes/og-measure/sample/style.json +97 -0
  416. package/themes/og-measure/slots.ts +88 -0
  417. package/themes/og-measure/still.png +0 -0
  418. package/themes/og-measure/style.json +97 -0
  419. package/themes/og-measure/theme.json +79 -0
  420. package/themes/riso-zine/THEME.md +43 -0
  421. package/themes/riso-zine/effects.ts +321 -0
  422. package/themes/riso-zine/sample/assets/og/logo.svg +12 -0
  423. package/themes/riso-zine/sample/beats.json +1 -0
  424. package/themes/riso-zine/sample/index.html +11 -0
  425. package/themes/riso-zine/sample/kf.json +59 -0
  426. package/themes/riso-zine/sample/lib/og.ts +29 -0
  427. package/themes/riso-zine/sample/lib/shot.ts +86 -0
  428. package/themes/riso-zine/sample/lib/zine.ts +42 -0
  429. package/themes/riso-zine/sample/main.ts +9 -0
  430. package/themes/riso-zine/sample/scenes/end.ts +61 -0
  431. package/themes/riso-zine/sample/scenes/gather.ts +62 -0
  432. package/themes/riso-zine/sample/scenes/models.ts +52 -0
  433. package/themes/riso-zine/sample/scenes/price.ts +82 -0
  434. package/themes/riso-zine/sample/style.json +99 -0
  435. package/themes/riso-zine/slots.ts +81 -0
  436. package/themes/riso-zine/still.png +0 -0
  437. package/themes/riso-zine/style.json +99 -0
  438. package/themes/riso-zine/theme.json +75 -0
  439. package/tools/beats/beats.py +372 -0
  440. package/tools/beats/requirements.txt +3 -0
  441. package/tools/lyrics/align.py +164 -0
  442. package/tools/lyrics/requirements.txt +9 -0
@@ -0,0 +1,140 @@
1
+ import { readKey } from "../keys.ts";
2
+ import { dataUri } from "../media.ts";
3
+ import type { RawResult, VideoProvider, VideoRequest } from "../types.ts";
4
+ import { ceilTo, download, json, poll } from "./http.ts";
5
+
6
+ interface FalModel {
7
+ id: string;
8
+ i2v: string;
9
+ t2v: string;
10
+ durations: number[];
11
+ usd(o: VideoRequest, sec: number): number;
12
+ input(o: VideoRequest, sec: number): Record<string, unknown>;
13
+ license: string;
14
+ }
15
+
16
+ const res = (o: VideoRequest) => o.resolution ?? "720p";
17
+ const WAN_PER_SEC: Record<string, number> = { "720p": 0.08, "580p": 0.06, "480p": 0.04 };
18
+ const WAN_TURBO: Record<string, number> = { "720p": 0.1, "580p": 0.075, "480p": 0.05 };
19
+ const WAN_LICENSE = "Wan 2.2 가중치 Apache-2.0 — 출력물 상업 사용 가능, 지역 제한 없음";
20
+ const KLING_LICENSE = "Kling API 약관 — 유료 생성물 상업 사용 가능";
21
+
22
+ // fal 의 Wan 2.2 는 16fps 로 프레임 수를 센다(81 프레임 = 약 5초).
23
+ const wanFrames = (sec: number) => Math.min(161, Math.max(17, Math.round(sec * 16) + 1));
24
+
25
+ export const FAL_MODELS: FalModel[] = [
26
+ {
27
+ id: "wan-2.2-turbo",
28
+ i2v: "fal-ai/wan/v2.2-a14b/image-to-video/turbo",
29
+ t2v: "fal-ai/wan/v2.2-a14b/text-to-video/turbo",
30
+ durations: [5],
31
+ usd: (o) => WAN_TURBO[res(o)] ?? 0.1,
32
+ input: (o) => ({ resolution: res(o), aspect_ratio: "auto", enable_safety_checker: true, ...(o.seed !== undefined ? { seed: o.seed } : {}) }),
33
+ license: WAN_LICENSE,
34
+ },
35
+ {
36
+ id: "wan-2.2",
37
+ i2v: "fal-ai/wan/v2.2-a14b/image-to-video",
38
+ t2v: "fal-ai/wan/v2.2-a14b/text-to-video",
39
+ durations: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
40
+ usd: (o, sec) => (WAN_PER_SEC[res(o)] ?? 0.08) * (wanFrames(sec) / 16),
41
+ input: (o, sec) => ({
42
+ num_frames: wanFrames(sec),
43
+ frames_per_second: 16,
44
+ resolution: res(o),
45
+ aspect_ratio: "auto",
46
+ enable_prompt_expansion: false,
47
+ ...(o.seed !== undefined ? { seed: o.seed } : {}),
48
+ }),
49
+ license: WAN_LICENSE,
50
+ },
51
+ {
52
+ id: "kling-3-std",
53
+ i2v: "fal-ai/kling-video/v3/standard/image-to-video",
54
+ t2v: "fal-ai/kling-video/v3/standard/text-to-video",
55
+ durations: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
56
+ usd: (_o, sec) => 0.084 * sec,
57
+ input: (_o, sec) => ({ duration: String(sec), generate_audio: false }),
58
+ license: KLING_LICENSE,
59
+ },
60
+ {
61
+ id: "kling-3-pro",
62
+ i2v: "fal-ai/kling-video/v3/pro/image-to-video",
63
+ t2v: "fal-ai/kling-video/v3/pro/text-to-video",
64
+ durations: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
65
+ usd: (_o, sec) => 0.112 * sec,
66
+ input: (_o, sec) => ({ duration: String(sec), generate_audio: false }),
67
+ license: KLING_LICENSE,
68
+ },
69
+ ];
70
+
71
+ const isKling = (m: FalModel) => m.id.startsWith("kling");
72
+
73
+ export function falBody(m: FalModel, o: VideoRequest, first?: string, last?: string): { endpoint: string; body: Record<string, unknown> } {
74
+ const sec = ceilTo(m.durations, o.duration);
75
+ const body: Record<string, unknown> = { prompt: o.prompt, ...m.input(o, sec) };
76
+ if (!first) {
77
+ if (last) throw new Error("끝 프레임만 주고 첫 프레임을 안 주면 못 만든다");
78
+ body.aspect_ratio = o.aspect;
79
+ return { endpoint: m.t2v, body };
80
+ }
81
+ if (isKling(m)) {
82
+ body.start_image_url = first;
83
+ if (last) body.end_image_url = last;
84
+ } else {
85
+ body.image_url = first;
86
+ if (last) body.end_image_url = last;
87
+ }
88
+ return { endpoint: m.i2v, body };
89
+ }
90
+
91
+ export function falVideo(modelId = "wan-2.2-turbo"): VideoProvider {
92
+ const m = FAL_MODELS.find((x) => x.id === modelId);
93
+ if (!m) throw new Error(`fal 모델 없음: ${modelId} (있는 것: ${FAL_MODELS.map((x) => x.id).join(" ")})`);
94
+ return {
95
+ id: `fal-${m.id}`,
96
+ kind: "vid",
97
+ model: m.i2v,
98
+ license: m.license,
99
+ region: "fal.ai(미국) API — 한국에서 호출·결제 가능",
100
+ keyName: "fal",
101
+ durations: m.durations,
102
+ firstLast: true,
103
+ estimateUsd: (o) => m.usd(o, ceilTo(m.durations, o.duration)),
104
+
105
+ async generate(o): Promise<RawResult> {
106
+ const key = readKey("fal");
107
+ const headers = { Authorization: `Key ${key}`, "Content-Type": "application/json" };
108
+ const first = o.firstFrame ? await dataUri(o.firstFrame) : undefined;
109
+ const last = o.lastFrame ? await dataUri(o.lastFrame) : undefined;
110
+ const { endpoint, body } = falBody(m, o, first, last);
111
+ const sub = await json(`https://queue.fal.run/${endpoint}`, { method: "POST", headers, body: JSON.stringify(body) }, `fal ${endpoint}`);
112
+ const auth = { Authorization: `Key ${key}` };
113
+ await poll(
114
+ async () => {
115
+ const s = await json(sub.status_url, { headers: auth }, "fal 상태");
116
+ if (s.status === "COMPLETED") return true;
117
+ if (s.status === "FAILED" || s.error) throw new Error(`fal 실패: ${JSON.stringify(s).slice(0, 300)}`);
118
+ return undefined;
119
+ },
120
+ { everyMs: 4000, timeoutMs: 15 * 60_000, who: `fal ${endpoint}` },
121
+ );
122
+ const out = await json(sub.response_url, { headers: auth }, "fal 결과");
123
+ const url = out.video?.url;
124
+ if (!url) throw new Error(`fal 결과에 영상이 없다: ${JSON.stringify(out).slice(0, 300)}`);
125
+ const sec = ceilTo(m.durations, o.duration);
126
+ const params: Record<string, unknown> = { ...body, endpoint, request_id: sub.request_id };
127
+ delete params.prompt;
128
+ for (const k of ["image_url", "start_image_url", "end_image_url"]) if (k in params) params[k] = "<frame>";
129
+ return {
130
+ bytes: await download(url),
131
+ ext: "mp4",
132
+ model: endpoint,
133
+ seed: typeof out.seed === "number" ? out.seed : (o.seed ?? null),
134
+ costUsd: m.usd(o, sec),
135
+ costEstimated: true,
136
+ params,
137
+ };
138
+ },
139
+ };
140
+ }
@@ -0,0 +1,112 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { findKey, whereToPut } from "../keys.ts";
3
+ import type { VideoProvider } from "../types.ts";
4
+ import { ceilTo, download, json, poll } from "./http.ts";
5
+
6
+ // 자체 호스팅한 H3 영상 모델의 SGLang 서버를 직접 부른다(KF_H3_URL — 닫힌 망이면 터널로 닿는다).
7
+ // 주소는 비밀이 아니지만 키와 같은 자리에서 읽어 "설정돼 있으면 쓴다"를 한 규칙으로 둔다.
8
+ // 서버 검증기가 4–15 초만 받는다(그 밖은 400).
9
+ const DURATIONS = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
10
+
11
+ function settings() {
12
+ const env = process.env;
13
+ return {
14
+ // 걸음 수는 서버에 걸린 LoRA 에 묶인다 — 터보면 9, LoRA 없으면 50. 터보가 걸린 채 50 을 주면 그림이 틀어지는데
15
+ // 그 상태는 서버 전체 것이라 여기서 알 수 없으니 기본값을 두지 않는다.
16
+ steps: env.KF_H3_STEPS ? Number(env.KF_H3_STEPS) : undefined,
17
+ shortEdge: Number(env.KF_H3_SHORT_EDGE ?? 768),
18
+ flowShift: Number(env.KF_H3_FLOW_SHIFT ?? 12),
19
+ // 터보 LoRA 처럼 서버마다 다른 필드는 JSON 으로 그대로 얹는다.
20
+ extra: env.KF_H3_EXTRA ? (JSON.parse(env.KF_H3_EXTRA) as Record<string, unknown>) : {},
21
+ };
22
+ }
23
+
24
+ // 키프레임은 첫 프레임이 픽셀 단위로 이어져야 해서 줄이지 않은 PNG 를 data URI 로 보낸다 — 서버가 직접 디코드한다.
25
+ const inline = (path: string) => `data:image/png;base64,${readFileSync(path).toString("base64")}`;
26
+
27
+ export function h3Body(o: { prompt: string; firstFrame?: string; lastFrame?: string; duration: number; aspect: string; seed?: number }, frames: { first?: string; last?: string }) {
28
+ const s = settings();
29
+ const conditions: Record<string, unknown>[] = [];
30
+ if (frames.first) conditions.push({ type: "image", uri: frames.first, role: "keyframe", frame_index: 0 });
31
+ if (frames.last) conditions.push({ type: "image", uri: frames.last, role: "keyframe", frame_index: -1 });
32
+ return {
33
+ model: "MiniMaxAI/MiniMax-H3",
34
+ prompt: o.prompt,
35
+ task: conditions.length ? "fl2va" : "t2va",
36
+ conditions,
37
+ target: { short_edge: s.shortEdge, aspect_ratio: o.aspect, duration_seconds: ceilTo(DURATIONS, o.duration) },
38
+ num_outputs_per_prompt: 1,
39
+ num_inference_steps: s.steps,
40
+ flow_shift: s.flowShift,
41
+ audio_flow_shift: 3,
42
+ // 시드를 비우면 서버가 몰래 골라 같은 판을 다시 못 뽑는다.
43
+ seed: o.seed ?? Math.floor(Math.random() * 2 ** 31),
44
+ ...s.extra,
45
+ };
46
+ }
47
+
48
+ // 공개 가중치 라이선스가 한국·미국·EU·영국을 제외 지역으로 두고 산출물에도 건다 — 사내 파드로 돌려도 같다(API 판은 전 세계 가능)
49
+ export const H3_RESTRICT =
50
+ "MiniMax H3 공개 가중치는 한국 제외 지역 — 회사·바깥 결과물엔 쓰지 않는다(시험·사내 보기만). 밖에 낼 영상은 minimax-h3 API(전 세계 가능)나 다른 공급자로";
51
+
52
+ export function h3Video(): VideoProvider {
53
+ const { steps, shortEdge, flowShift, extra } = settings();
54
+ return {
55
+ id: "h3-pod",
56
+ variant: { steps, shortEdge, flowShift, extra },
57
+ kind: "vid",
58
+ model: "MiniMaxAI/MiniMax-H3",
59
+ license: "공개 가중치 — 지역 제한 확인 필요(MiniMax H3 커뮤니티 라이선스는 한국·미국·EU·영국을 제외 지역으로 둔다)",
60
+ region: "사내 GPU 파드 자체 호스팅 — 한국 사용은 MiniMax 라이선스 신청 여부 확인 필요",
61
+ restrict: H3_RESTRICT,
62
+ keyName: "h3",
63
+ durations: DURATIONS,
64
+ firstLast: true,
65
+ estimateUsd: () => 0,
66
+
67
+ async generate(o) {
68
+ const base = findKey("h3");
69
+ if (!base) throw new Error(`h3 주소 없음 — ${whereToPut("h3")} 에 SGLang 주소(터널이면 http://127.0.0.1:30110)`);
70
+ if (steps === undefined) throw new Error("KF_H3_STEPS 를 정한다 — 서버에 터보 LoRA 가 걸려 있으면 9, 없으면 50(파드 운영자에게 확인)");
71
+ const url = base.replace(/\/+$/, "");
72
+ const body = h3Body(o, { first: o.firstFrame && inline(o.firstFrame), last: o.lastFrame && inline(o.lastFrame) });
73
+ const headers = { "Content-Type": "application/json" };
74
+ // 받기 전에 끊겨도 GPU 가 한 일은 서버에 남는다 — 그 작업 id 를 주면 다시 보내지 않고 받아 온다.
75
+ const resume = process.env.KF_H3_RESUME;
76
+ const id: string = resume || (await json(`${url}/v1/videos`, { method: "POST", headers, body: JSON.stringify(body) }, "H3")).id;
77
+ let job: any;
78
+ try {
79
+ job = await poll(
80
+ async () => {
81
+ const s = await json(`${url}/v1/videos/${id}`, {}, "H3 상태");
82
+ if (s.status === "completed" || s.status === "succeeded") return s;
83
+ if (s.status === "failed" || s.status === "error" || s.status === "cancelled") throw new Error(`H3 ${s.status}: ${String(s.error ?? "").slice(0, 300)}`);
84
+ return undefined;
85
+ },
86
+ { everyMs: 5000, timeoutMs: 30 * 60_000, who: "H3" },
87
+ );
88
+ } catch (e) {
89
+ const why = e instanceof Error ? e.message : String(e);
90
+ throw new Error(`${why} — 작업 ${id}. 서버에서 끝났으면 KF_H3_RESUME=${id} 로 같은 명령을 다시 부르면 받아 온다`);
91
+ }
92
+ // 걸린 시간은 줄 선 시간·내려받기와 섞이지 않게 서버가 잰 추론 시간을 따로 둔다.
93
+ const params: Record<string, unknown> = { ...body, id, inferenceSec: job.inference_time_s, size: job.size, ...(resume ? { resumed: true } : {}) };
94
+ delete params.prompt;
95
+ params.conditions = (body.conditions as Record<string, unknown>[]).map((c) => ({ ...c, uri: "<frame>" }));
96
+ const bytes = await download(`${url}/v1/videos/${id}/content`);
97
+ // SGLang 은 받은 뒤에도 작업 기록을 쥐고 있다 — 기록만 지운다(파일은 파드 쪽 정리 몫).
98
+ await fetch(`${url}/v1/videos/${id}`, { method: "DELETE" }).catch(() => undefined);
99
+ return {
100
+ bytes,
101
+ ext: "mp4",
102
+ model: "MiniMaxAI/MiniMax-H3",
103
+ // 이어 받은 작업의 시드는 처음 보낸 요청에만 있다.
104
+ seed: resume ? null : body.seed,
105
+ costUsd: 0,
106
+ costEstimated: false,
107
+ params,
108
+ notes: ["self-hosted"],
109
+ };
110
+ },
111
+ };
112
+ }
@@ -0,0 +1,44 @@
1
+ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
2
+
3
+ export async function json(url: string, init: RequestInit, who: string): Promise<any> {
4
+ const r = await fetch(url, init);
5
+ const text = await r.text();
6
+ if (!r.ok) {
7
+ const hint = r.status === 402 || r.status === 403 ? " (잔액·권한을 확인)" : "";
8
+ throw Object.assign(new Error(`${who} ${r.status}${hint}: ${text.slice(0, 300)}`), { status: r.status });
9
+ }
10
+ return text ? JSON.parse(text) : {};
11
+ }
12
+
13
+ // 생성 큐는 몇십 초~몇 분 걸린다. 상태 하나만 보고 끝/계속을 가르는 함수를 받아 돈다.
14
+ export async function poll<T>(
15
+ step: () => Promise<T | undefined>,
16
+ o: { everyMs: number; timeoutMs: number; who: string },
17
+ ): Promise<T> {
18
+ const until = Date.now() + o.timeoutMs;
19
+ let dropped = 0;
20
+ while (Date.now() < until) {
21
+ let v: T | undefined;
22
+ try {
23
+ v = await step();
24
+ dropped = 0;
25
+ } catch (e) {
26
+ // 터널·와이파이가 잠깐 끊기면 fetch 가 TypeError 로 죽는다. 몇 분짜리 생성을 그 한 번에 버리지 않는다.
27
+ if (!(e instanceof TypeError) || ++dropped > 24) throw e;
28
+ }
29
+ if (v !== undefined) return v;
30
+ await sleep(o.everyMs);
31
+ }
32
+ throw new Error(`${o.who}: ${Math.round(o.timeoutMs / 1000)}초 안에 안 끝났다`);
33
+ }
34
+
35
+ export async function download(url: string, headers: Record<string, string> = {}): Promise<Uint8Array> {
36
+ const r = await fetch(url, { headers, redirect: "follow" });
37
+ if (!r.ok) throw new Error(`내려받기 ${r.status}: ${url.slice(0, 120)}`);
38
+ return new Uint8Array(await r.arrayBuffer());
39
+ }
40
+
41
+ export function ceilTo(options: number[], v: number): number {
42
+ const sorted = [...options].sort((a, b) => a - b);
43
+ return sorted.find((d) => d >= v - 1e-6) ?? sorted[sorted.length - 1];
44
+ }
@@ -0,0 +1,60 @@
1
+ import { readKey } from "../keys.ts";
2
+ import { dataUri } from "../media.ts";
3
+ import type { VideoProvider } from "../types.ts";
4
+ import { ceilTo, download, json, poll } from "./http.ts";
5
+
6
+ const BASE = "https://api.ltx.io/v2";
7
+ const DURATIONS = [6, 8, 10, 12, 14, 16, 18, 20];
8
+ const PER_SEC: Record<string, number> = { "1920x1080": 0.06, "1080x1920": 0.06, "2560x1440": 0.12, "1440x2560": 0.12 };
9
+
10
+ // 키가 없어 실호출로 검증하지 못했다 — 요청 모양은 LTX API 문서(2026-10) 그대로.
11
+ export function ltxVideo(model = "ltx-2-3-fast"): VideoProvider {
12
+ const resOf = (aspect: string) => (aspect === "9:16" ? "1080x1920" : "1920x1080");
13
+ return {
14
+ id: model,
15
+ kind: "vid",
16
+ model,
17
+ license: "LTX 커뮤니티 라이선스 — 연 매출 1천만 달러 미만 무료, 넘으면 유료 계약",
18
+ region: "API·가중치 전 세계(한국 포함)",
19
+ keyName: "ltx",
20
+ durations: DURATIONS,
21
+ firstLast: true,
22
+ estimateUsd: (o) => (PER_SEC[resOf(o.aspect)] ?? 0.06) * ceilTo(DURATIONS, o.duration),
23
+
24
+ async generate(o) {
25
+ if (!o.firstFrame) throw new Error("ltx 는 v0 에서 첫 프레임이 있는 경우만 감쌌다");
26
+ const key = readKey("ltx");
27
+ const headers = { Authorization: `Bearer ${key}`, "Content-Type": "application/json" };
28
+ const sec = ceilTo(DURATIONS, o.duration);
29
+ const resolution = resOf(o.aspect);
30
+ const body: Record<string, unknown> = {
31
+ image_uri: await dataUri(o.firstFrame),
32
+ prompt: o.prompt,
33
+ model,
34
+ duration: sec,
35
+ resolution,
36
+ generate_audio: false,
37
+ };
38
+ if (o.lastFrame) body.last_frame_uri = await dataUri(o.lastFrame);
39
+ const sub = await json(`${BASE}/image-to-video`, { method: "POST", headers, body: JSON.stringify(body) }, "LTX");
40
+ const url = await poll(
41
+ async () => {
42
+ const s = await json(`${BASE}/image-to-video/${sub.id}`, { headers }, "LTX 상태");
43
+ if (s.status === "completed") return s.result?.video_url as string;
44
+ if (s.status === "failed") throw new Error(`LTX 실패: ${JSON.stringify(s).slice(0, 300)}`);
45
+ return undefined;
46
+ },
47
+ { everyMs: 5000, timeoutMs: 15 * 60_000, who: "LTX" },
48
+ );
49
+ return {
50
+ bytes: await download(url),
51
+ ext: "mp4",
52
+ model,
53
+ seed: null,
54
+ costUsd: (PER_SEC[resolution] ?? 0.06) * sec,
55
+ costEstimated: true,
56
+ params: { duration: sec, resolution, id: sub.id },
57
+ };
58
+ },
59
+ };
60
+ }
@@ -0,0 +1,55 @@
1
+ import { readKey } from "../keys.ts";
2
+ import { dataUri } from "../media.ts";
3
+ import type { VideoProvider } from "../types.ts";
4
+ import { ceilTo, download, json, poll } from "./http.ts";
5
+
6
+ const BASE = "https://api.minimax.io";
7
+ const DURATIONS = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
8
+ const PER_SEC: Record<string, number> = { "768P": 0.08, "2K": 0.13 };
9
+
10
+ // 키가 없어 실호출로 검증하지 못했다 — 요청 모양은 MiniMax 문서(2026-10) 그대로.
11
+ export function minimaxVideo(model = "MiniMax-H3"): VideoProvider {
12
+ const resolution = (r?: string) => (r === "2K" || r === "1080p" ? "2K" : "768P");
13
+ return {
14
+ id: "minimax-h3",
15
+ kind: "vid",
16
+ model,
17
+ license: "MiniMax API 약관 — 출력물 상업 사용 가능(공개 가중치는 한국·미국·EU·영국 제외라 API 로만)",
18
+ region: "API 전 세계(한국 포함). 공개 가중치는 한국 제외",
19
+ keyName: "minimax",
20
+ durations: DURATIONS,
21
+ firstLast: true,
22
+ estimateUsd: (o) => PER_SEC[resolution(o.resolution)] * ceilTo(DURATIONS, o.duration),
23
+
24
+ async generate(o) {
25
+ const key = readKey("minimax");
26
+ const headers = { Authorization: `Bearer ${key}`, "Content-Type": "application/json" };
27
+ const sec = ceilTo(DURATIONS, o.duration);
28
+ const r = resolution(o.resolution);
29
+ const content: unknown[] = [{ type: "text", text: o.prompt }];
30
+ if (o.firstFrame) content.push({ type: "image_url", image_url: { url: await dataUri(o.firstFrame) }, role: "first_frame" });
31
+ if (o.lastFrame) content.push({ type: "image_url", image_url: { url: await dataUri(o.lastFrame) }, role: "last_frame" });
32
+ const body = { model, content, duration: sec, resolution: r, ...(o.firstFrame ? {} : { ratio: o.aspect }) };
33
+ const sub = await json(`${BASE}/v2/video_generation`, { method: "POST", headers, body: JSON.stringify(body) }, "MiniMax");
34
+ const url = await poll(
35
+ async () => {
36
+ const q = await json(`${BASE}/v2/query/video_generation/${sub.task_id}`, { headers }, "MiniMax 상태");
37
+ const t = q.task ?? {};
38
+ if (t.status === "succeeded") return t.content?.url as string;
39
+ if (t.status === "failed" || t.status === "cancelled") throw new Error(`MiniMax ${t.status}: ${JSON.stringify(t.error ?? {})}`);
40
+ return undefined;
41
+ },
42
+ { everyMs: 10_000, timeoutMs: 20 * 60_000, who: "MiniMax" },
43
+ );
44
+ return {
45
+ bytes: await download(url),
46
+ ext: "mp4",
47
+ model,
48
+ seed: null,
49
+ costUsd: PER_SEC[r] * sec,
50
+ costEstimated: true,
51
+ params: { duration: sec, resolution: r, task_id: sub.task_id },
52
+ };
53
+ },
54
+ };
55
+ }
@@ -0,0 +1,71 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { readKey } from "../keys.ts";
3
+ import type { VideoProvider } from "../types.ts";
4
+ import { ceilTo, download, json, poll } from "./http.ts";
5
+
6
+ const BASE = "https://generativelanguage.googleapis.com/v1beta";
7
+ const DURATIONS = [4, 6, 8];
8
+ const PER_SEC: Record<string, Record<string, number>> = {
9
+ "veo-3.1-lite-generate-preview": { "720p": 0.05, "1080p": 0.08 },
10
+ "veo-3.1-fast-generate-preview": { "720p": 0.1, "1080p": 0.12 },
11
+ "veo-3.1-generate-preview": { "720p": 0.4, "1080p": 0.4 },
12
+ };
13
+
14
+ const inline = (path: string) => ({
15
+ inlineData: { mimeType: path.endsWith(".png") ? "image/png" : "image/jpeg", data: readFileSync(path).toString("base64") },
16
+ });
17
+
18
+ // 키가 없어 실호출로 검증하지 못했다 — 요청 모양은 Gemini API Veo 문서(2026-10) 그대로.
19
+ export function veoVideo(model = "veo-3.1-fast-generate-preview"): VideoProvider {
20
+ const resOf = (r?: string) => (r === "1080p" ? "1080p" : "720p");
21
+ // 1080p 는 8초만 받는다.
22
+ const secOf = (d: number, r: string) => (r === "1080p" ? 8 : ceilTo(DURATIONS, d));
23
+ return {
24
+ id: model.replace(/-generate-preview$/, ""),
25
+ kind: "vid",
26
+ model,
27
+ license: "Google 생성형 AI 약관 — 출력물 사용 가능, SynthID 워터마크가 박힌다",
28
+ region: "Gemini API 한국 제공. 사람 생성은 allow_adult 만",
29
+ keyName: "gemini",
30
+ durations: DURATIONS,
31
+ firstLast: true,
32
+ estimateUsd: (o) => {
33
+ const r = resOf(o.resolution);
34
+ return (PER_SEC[model]?.[r] ?? 0.4) * secOf(o.duration, r);
35
+ },
36
+
37
+ async generate(o) {
38
+ const key = readKey("gemini");
39
+ const headers = { "x-goog-api-key": key, "Content-Type": "application/json" };
40
+ const r = resOf(o.resolution);
41
+ const sec = secOf(o.duration, r);
42
+ const instance: Record<string, unknown> = { prompt: o.prompt };
43
+ if (o.firstFrame) instance.image = inline(o.firstFrame);
44
+ if (o.lastFrame) instance.lastFrame = inline(o.lastFrame);
45
+ const aspectRatio = o.aspect === "9:16" ? "9:16" : "16:9";
46
+ const body = { instances: [instance], parameters: { aspectRatio, durationSeconds: String(sec), resolution: r } };
47
+ const op = await json(`${BASE}/models/${model}:predictLongRunning`, { method: "POST", headers, body: JSON.stringify(body) }, "Veo");
48
+ const uri = await poll(
49
+ async () => {
50
+ const s = await json(`${BASE}/${op.name}`, { headers }, "Veo 상태");
51
+ if (s.error) throw new Error(`Veo 실패: ${JSON.stringify(s.error)}`);
52
+ if (!s.done) return undefined;
53
+ const u = s.response?.generateVideoResponse?.generatedSamples?.[0]?.video?.uri;
54
+ if (!u) throw new Error(`Veo 결과에 영상이 없다: ${JSON.stringify(s).slice(0, 300)}`);
55
+ return u as string;
56
+ },
57
+ { everyMs: 10_000, timeoutMs: 15 * 60_000, who: "Veo" },
58
+ );
59
+ return {
60
+ bytes: await download(uri, { "x-goog-api-key": key }),
61
+ ext: "mp4",
62
+ model,
63
+ seed: null,
64
+ costUsd: (PER_SEC[model]?.[r] ?? 0.4) * sec,
65
+ costEstimated: true,
66
+ params: { aspectRatio, durationSeconds: sec, resolution: r },
67
+ notes: ["synthid"],
68
+ };
69
+ },
70
+ };
71
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@kasaframe/jizura",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts",
8
+ "./install": "./src/install.ts",
9
+ "./lyrics": "./src/lyrics.ts",
10
+ "./vite": "./src/vite.ts"
11
+ },
12
+ "dependencies": {
13
+ "@kasaframe/adapters": "^0.0.0",
14
+ "@kasaframe/core": "^0.0.0"
15
+ }
16
+ }
@@ -0,0 +1,73 @@
1
+ // JIZURA 부품 거부 목록 — 가사 층을 장면 위에 투명하게 얹을 때 화면을 통째로 덮거나 톤이 튀는 부품.
2
+ // 출처: vocaloid-style-mv-pipeline(MIT, NikusonP) references/06-jizura.md §5 와 그 틀로 만든 뮤비의 timeline.js.
3
+ // 거기에 front·transparent 에서 화면을 덮은 것으로 확인한 layout filmstrip·justified·circle(글 뒤 불투명 원판), trans whipPan·cover, hold colorRun, decor moonPhases 를 더했다.
4
+ // 이 목록을 바꾸면 그 플랜의 컷이 다시 굴려진다 — 스틸을 다시 본다.
5
+
6
+ export type PartGroup = "layout" | "enter" | "hold" | "exit" | "decor" | "treat" | "bg" | "cam" | "fx" | "trans";
7
+
8
+ const words = (s: string): string[] => s.split(/\s+/).filter(Boolean);
9
+
10
+ export const DENY: Record<PartGroup, string[]> = {
11
+ layout: words(`scatter ring wave labels pill knSeesaw knGearWords knTumble knPadGrid knPathRide zigzag arcTop gridCells bubble ticker
12
+ searchBar chat notification ticket hanging wordCloud bounceLine elastic stickerBomb keycaps bubbles slotMachine flipBoard dotMatrix
13
+ equalizer tape contents numbered poster ema ransom newspaper vinyl cassette bookSpine stampSheet calendar chochin routeMap stationSign
14
+ noren tanzaku omikuji kakejiku shoji clapper warningLabel priceTag nameTag stickyNotes karuta cube flipCards accordion pile blocks
15
+ balloons magnets tiles bulbs ledScroll billboard crowdBubbles crossword wordSearch puzzle dominoes burst fisheye origami zipper
16
+ mosaicTiles stencil tyIndexTable tyStatCount typeSpecimen dictionary proofread magazine headlineDeck footnote ribbon flag pendulum
17
+ knCollide knSlamStack halftoneBig tySquare tyRotBlock subtitleBar tile kaleido polaroid postcard letterPaper genkou panels splitScreen
18
+ splitHalves wall cylinder shadowPlay glitchGrid filmstrip justified circle`),
19
+ // 화면 전체 효과는 섬광 예산과 함께 kasaframe 쪽(dropBurst·전환)이 쥔다
20
+ fx: words(`panelWipe irisTrans doors blindsTrans splitSlide gridRepeat whiteFrame blackFrame strobe colorBars mirrorFlash starGlint
21
+ focusLines speedLines halftone duotone kaleido bulge squash snapshot loopScroll crtOff tvStatic zoomStutter negativeRing edgeDetect
22
+ hueShift posterize ditherBit rotateSnap invert bandInvert flash bloomFlash`),
23
+ decor: words(`dots arrows shapes qrBlock triangleSpin checkerStrip confetti petals heartsStars kamon seigaiha asanoha hanabi chochin
24
+ shimenawa sensu momiji namiGashira memphis zigzagRibbon polkaPatch stripeCircle starburst cursorClick windowChrome progressBar
25
+ toggleSwitch notifBell likeCounter mediaControls volumeBars musicNotes bubbles dandelion vines cloudPuffs tapePieces highlightMark
26
+ staple paperClip indexTabs punchHoles swatches spinner tally bracketsJP loopArrows halfCircles decoCorners tyColophon dateStamp
27
+ tyTypeScale tyTextRule dataColumns timecodeBar rainStreaks moonPhases`),
28
+ // 키네틱 카메라는 컷 전체를 불투명 카드로 감싼다 — 장면 위에 노랑·검정 판이 통째로 뜬다
29
+ cam: words("knCardFlip jelly bounce knJumpCut knTiltKick knReadPan knShearKick knRushIn"),
30
+ treat: words("boxed marker knWordPlate circled sticker splitColor rainbow chrome"),
31
+ enter: words("bubbles stickerPeel crumple loadingBar tokoroten"),
32
+ hold: words("colorRun"),
33
+ exit: words("balloonOff crumpleOut peelOff"),
34
+ bg: words(`argyle tartan chevron houndstooth herringbone polka checker retroGrid eqBars kaleidoscope fireworks sunburst bigStripes
35
+ isoCubes seigaiha asanoha tvBars splitDiag spiralArms squareTunnel moire`),
36
+ trans: words("knCornerSwing knStripSlam checker cubeTurn spinOut doorsOpen pixelate knStutterCut whipPan cover"),
37
+ };
38
+
39
+ /** JIZURA 엔진(window.J)의 부품 등록부 — 필요한 것만 */
40
+ export interface JizuraRegistry {
41
+ GROUP_KEYS: string[];
42
+ order(group: string): string[];
43
+ registry(group: string): Record<string, { tags?: string[]; set?: string } | undefined> | undefined;
44
+ }
45
+
46
+ export interface PoolOptions {
47
+ /** 기본 목록에 더 막을 부품 */
48
+ deny?: Partial<Record<PartGroup, string[]>>;
49
+ /** 기본 목록 대신 이것만 막는다 */
50
+ denyOnly?: Partial<Record<PartGroup, string[]>>;
51
+ /** 이 태그(pop·emotional·graphic·calm·editorial·glitch) 중 하나를 단 부품만. 태그 없는 부품은 남는다 */
52
+ moods?: string[] | null;
53
+ }
54
+
55
+ /** JIZURA project.enabled — 거부 목록·공포 세트·분위기 거르기를 한 번에 */
56
+ export function partPool(J: JizuraRegistry, o: PoolOptions = {}): Record<string, Record<string, boolean>> {
57
+ const base = o.denyOnly ?? DENY;
58
+ const out: Record<string, Record<string, boolean>> = {};
59
+ for (const g of J.GROUP_KEYS) {
60
+ const deny = new Set([...(base[g as PartGroup] ?? []), ...(o.deny?.[g as PartGroup] ?? [])]);
61
+ const reg = J.registry(g) ?? {};
62
+ out[g] = {};
63
+ for (const k of J.order(g)) {
64
+ const d = reg[k] ?? {};
65
+ const tags = Array.isArray(d.tags) ? d.tags : null;
66
+ let ok = !deny.has(k) && !/^hr[A-Z]/.test(k) && d.set !== "horror";
67
+ if (ok && o.moods && tags && !tags.some((m) => o.moods!.includes(m))) ok = false;
68
+ if (k === "none") ok = true;
69
+ out[g][k] = ok;
70
+ }
71
+ }
72
+ return out;
73
+ }
@@ -0,0 +1,7 @@
1
+ export { jizuraLayer, jizuraPlans, jizuraReady, jizuraCuts, loadJizura, lyricsMode } from "./layer.ts";
2
+ export type { DrawMode, JizuraPaint, LayerOptions, LyricsMode, PlanSettings } from "./layer.ts";
3
+ export { DENY, partPool } from "./deny.ts";
4
+ export type { PartGroup, PoolOptions } from "./deny.ts";
5
+ export { toLrc, parseLyrics, wordsOf } from "./lyrics.ts";
6
+ export type { LyricsJson, LyricLine, LyricWord, LrcOptions } from "./lyrics.ts";
7
+ export { JIZURA_COMMIT, JIZURA_VERSION, JIZURA_CREDIT } from "./pin.ts";