kasaframe 0.1.4 โ†’ 0.1.6

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 (374) hide show
  1. package/NOTICE +311 -0
  2. package/README.md +18 -5
  3. package/docs/AGENTS.md +2 -1
  4. package/package.json +1 -1
  5. package/packages/adapters/src/project.ts +2 -0
  6. package/packages/check/package.json +1 -0
  7. package/packages/check/src/check.ts +8 -2
  8. package/packages/check/src/rules/frames.ts +4 -5
  9. package/packages/cli/src/commands/doctor.ts +15 -2
  10. package/packages/cli/src/commands/fonts.ts +118 -0
  11. package/packages/cli/src/commands/make.ts +15 -5
  12. package/packages/cli/src/commands/render.ts +34 -7
  13. package/packages/cli/src/commands/update.ts +66 -0
  14. package/packages/cli/src/fonts.ts +405 -0
  15. package/packages/cli/src/kf.ts +5 -0
  16. package/packages/cli/src/mix.ts +66 -4
  17. package/packages/cli/src/segments.ts +1 -1
  18. package/packages/cli/src/sfx.ts +429 -0
  19. package/packages/cli/src/update.ts +250 -0
  20. package/packages/core/src/frame.ts +7 -4
  21. package/packages/core/src/index.ts +3 -1
  22. package/packages/core/src/project.ts +19 -0
  23. package/packages/core/src/sfx.ts +70 -0
  24. package/packages/director/src/assemble.ts +7 -4
  25. package/packages/director/src/index.ts +1 -1
  26. package/packages/director/src/pattern.ts +2 -0
  27. package/packages/director/src/scene.ts +14 -5
  28. package/packages/director/src/transitions.ts +21 -8
  29. package/packages/motion/src/enter.ts +7 -1
  30. package/packages/motion/src/impact.ts +5 -2
  31. package/packages/motion/src/moves/dress.ts +15 -2
  32. package/packages/motion/src/moves/menu.ts +11 -5
  33. package/packages/motion/src/transition.ts +3 -1
  34. package/packages/motion/src/transition3d/index.ts +3 -1
  35. package/packages/motion/src/transition3d/live.ts +5 -3
  36. package/packages/patterns/src/bars/rank.ts +6 -7
  37. package/packages/patterns/src/before-after/shrink.ts +3 -4
  38. package/packages/patterns/src/before-after/strike.ts +4 -5
  39. package/packages/patterns/src/code/terminal.ts +25 -16
  40. package/packages/patterns/src/end/lockup.ts +13 -10
  41. package/packages/patterns/src/end/slam.ts +7 -5
  42. package/packages/patterns/src/hook-number/counter.ts +15 -9
  43. package/packages/patterns/src/hook-number/slam.ts +6 -8
  44. package/packages/patterns/src/kit.ts +281 -58
  45. package/packages/patterns/src/limit/gauge.ts +16 -11
  46. package/packages/patterns/src/quote/karaoke.ts +17 -12
  47. package/packages/patterns/src/sfx.ts +80 -0
  48. package/packages/patterns/src/term-reveal/chips.ts +24 -10
  49. package/packages/patterns/src/term-reveal/expand.ts +17 -13
  50. package/packages/patterns/src/title/mask.ts +9 -8
  51. package/packages/patterns/src/title/slam.ts +1 -1
  52. package/packages/patterns/src/types.ts +1 -0
  53. package/packages/plan/src/asks.ts +1 -1
  54. package/packages/plan/src/brief.ts +5 -2
  55. package/packages/plan/src/themes.ts +8 -2
  56. package/packages/themes/kit/THEME-MAKE.md +2 -2
  57. package/packages/themes/kit/fonts/display.json +3517 -0
  58. package/packages/themes/kit/fonts/sans.json +5155 -0
  59. package/packages/themes/kit/fonts/serif.json +5229 -0
  60. package/packages/themes/kit/fonts.json +243 -79
  61. package/packages/themes/package.json +3 -1
  62. package/packages/themes/src/fontfile.ts +183 -0
  63. package/packages/themes/src/fonts.ts +296 -0
  64. package/packages/themes/src/index.ts +32 -2
  65. package/packages/themes/src/look.ts +34 -8
  66. package/packages/themes/src/slots.ts +32 -2
  67. package/packages/themes/src/types.ts +33 -0
  68. package/packages/themes/src/vary.ts +35 -34
  69. package/presets/kawaii-pastel.json +2 -2
  70. package/sfx/README.md +82 -0
  71. package/sfx/click/appear.wav +0 -0
  72. package/sfx/click/burst.wav +0 -0
  73. package/sfx/click/drop.wav +0 -0
  74. package/sfx/click/end.wav +0 -0
  75. package/sfx/click/hop.wav +0 -0
  76. package/sfx/click/land.wav +0 -0
  77. package/sfx/click/select.wav +0 -0
  78. package/sfx/click/stick.wav +0 -0
  79. package/sfx/click/tick.wav +0 -0
  80. package/sfx/click/type.wav +0 -0
  81. package/sfx/click/whoosh.wav +0 -0
  82. package/sfx/pop/appear.wav +0 -0
  83. package/sfx/pop/burst.wav +0 -0
  84. package/sfx/pop/drop.wav +0 -0
  85. package/sfx/pop/end.wav +0 -0
  86. package/sfx/pop/hop.wav +0 -0
  87. package/sfx/pop/land.wav +0 -0
  88. package/sfx/pop/select.wav +0 -0
  89. package/sfx/pop/stick.wav +0 -0
  90. package/sfx/pop/tick.wav +0 -0
  91. package/sfx/pop/type.wav +0 -0
  92. package/sfx/pop/whoosh.wav +0 -0
  93. package/sfx/tags.json +545 -0
  94. package/sfx/ui/appear.wav +0 -0
  95. package/sfx/ui/burst.wav +0 -0
  96. package/sfx/ui/drop.wav +0 -0
  97. package/sfx/ui/end.wav +0 -0
  98. package/sfx/ui/hop.wav +0 -0
  99. package/sfx/ui/land.wav +0 -0
  100. package/sfx/ui/select.wav +0 -0
  101. package/sfx/ui/stick.wav +0 -0
  102. package/sfx/ui/tick.wav +0 -0
  103. package/sfx/ui/type.wav +0 -0
  104. package/sfx/ui/whoosh.wav +0 -0
  105. package/skills/kasaframe/SKILL.md +1 -0
  106. package/themes/blueprint/sample/style.json +5 -5
  107. package/themes/blueprint/style.json +5 -5
  108. package/themes/blueprint/theme.json +3 -1
  109. package/themes/blueprint/variants/deep-rose-suit.json +1 -1
  110. package/themes/blueprint/variants/navy-coral-2.json +1 -1
  111. package/themes/blueprint/variants/navy-coral-suit.json +1 -1
  112. package/themes/blueprint/variants/navy-orange-2.json +1 -1
  113. package/themes/blueprint/variants/navy-orange-suit.json +1 -1
  114. package/themes/cartography/sample/style.json +2 -2
  115. package/themes/cartography/style.json +2 -2
  116. package/themes/cartography/theme.json +3 -1
  117. package/themes/cartography/variants/deep-plum.json +1 -1
  118. package/themes/cartography/variants/frost-teal.json +1 -1
  119. package/themes/cartography/variants/navy-green.json +1 -1
  120. package/themes/circuit-board/sample/style.json +2 -2
  121. package/themes/circuit-board/style.json +2 -2
  122. package/themes/circuit-board/theme.json +3 -1
  123. package/themes/circuit-board/variants/dusk-gold-pretendard.json +1 -1
  124. package/themes/circuit-board/variants/navy-gold-grain.json +1 -1
  125. package/themes/circuit-board/variants/wine-gold-pretendard.json +1 -1
  126. package/themes/clay-toy/sample/style.json +2 -2
  127. package/themes/clay-toy/style.json +2 -2
  128. package/themes/clay-toy/theme.json +3 -1
  129. package/themes/clay-toy/variants/blush-coral-grain.json +1 -1
  130. package/themes/clay-toy/variants/chalk-teal.json +1 -1
  131. package/themes/clay-toy/variants/frost-sky.json +1 -1
  132. package/themes/clay-toy/variants/frost-teal.json +1 -1
  133. package/themes/clay-toy/variants/lilac-plum-round.json +1 -1
  134. package/themes/clay-toy/variants/lilac-plum.json +1 -1
  135. package/themes/clay-toy/variants/sage-indigo.json +1 -1
  136. package/themes/conveyor-factory/sample/style.json +3 -3
  137. package/themes/conveyor-factory/style.json +3 -3
  138. package/themes/conveyor-factory/theme.json +3 -1
  139. package/themes/conveyor-factory/variants/cocoa-gold.json +1 -1
  140. package/themes/conveyor-factory/variants/navy-gold.json +1 -1
  141. package/themes/conveyor-factory/variants/slate-amber-2.json +1 -1
  142. package/themes/conveyor-factory/variants/slate-amber-suit.json +1 -1
  143. package/themes/conveyor-factory/variants/slate-gold-bold.json +1 -1
  144. package/themes/data-journal/theme.json +3 -1
  145. package/themes/data-journal/variants/deep-coral.json +1 -1
  146. package/themes/data-journal/variants/navy-lime.json +1 -1
  147. package/themes/data-journal/variants/wine-indigo.json +1 -1
  148. package/themes/debug-hud/sample/style.json +1 -1
  149. package/themes/debug-hud/style.json +1 -1
  150. package/themes/debug-hud/theme.json +2 -1
  151. package/themes/debug-hud/variants/cocoa-teal.json +2 -2
  152. package/themes/debug-hud/variants/deep-teal-scan.json +2 -2
  153. package/themes/debug-hud/variants/dusk-plum.json +2 -2
  154. package/themes/debug-hud/variants/forest-lime.json +2 -2
  155. package/themes/debug-hud/variants/slate-green.json +2 -2
  156. package/themes/debug-hud/variants/wine-gold.json +2 -2
  157. package/themes/depth-flight/sample/style.json +1 -1
  158. package/themes/depth-flight/style.json +1 -1
  159. package/themes/depth-flight/theme.json +3 -1
  160. package/themes/depth-flight/variants/cocoa-cyan.json +1 -1
  161. package/themes/depth-flight/variants/cocoa-violet.json +1 -1
  162. package/themes/depth-flight/variants/deep-sky.json +1 -1
  163. package/themes/depth-flight/variants/forest-blue.json +1 -1
  164. package/themes/depth-flight/variants/navy-sky-grain.json +1 -1
  165. package/themes/depth-flight/variants/navy-sky.json +1 -1
  166. package/themes/depth-flight/variants/night-teal.json +1 -1
  167. package/themes/depth-flight/variants/slate-indigo.json +1 -1
  168. package/themes/depth-flight/variants/slate-violet.json +1 -1
  169. package/themes/film-reel/sample/style.json +2 -2
  170. package/themes/film-reel/style.json +2 -2
  171. package/themes/film-reel/theme.json +3 -1
  172. package/themes/film-reel/variants/chalk-pink.json +1 -1
  173. package/themes/film-reel/variants/lilac-gold.json +1 -1
  174. package/themes/film-reel/variants/lilac-violet.json +1 -1
  175. package/themes/glass-light/sample/style.json +1 -1
  176. package/themes/glass-light/style.json +1 -1
  177. package/themes/glass-light/theme.json +3 -1
  178. package/themes/glass-light/variants/blush-pink.json +1 -1
  179. package/themes/glass-light/variants/blush-plum.json +1 -1
  180. package/themes/glass-light/variants/cream-violet.json +1 -1
  181. package/themes/glass-light/variants/frost-sky-suit.json +1 -1
  182. package/themes/glass-light/variants/frost-sky.json +1 -1
  183. package/themes/glass-light/variants/sage-green.json +1 -1
  184. package/themes/ink-wash/sample/style.json +3 -3
  185. package/themes/ink-wash/style.json +3 -3
  186. package/themes/ink-wash/theme.json +3 -1
  187. package/themes/iso-diorama/sample/style.json +1 -1
  188. package/themes/iso-diorama/style.json +1 -1
  189. package/themes/iso-diorama/theme.json +3 -1
  190. package/themes/iso-diorama/variants/chalk-plum.json +1 -1
  191. package/themes/iso-diorama/variants/cocoa-indigo.json +1 -1
  192. package/themes/iso-diorama/variants/dusk-blue.json +1 -1
  193. package/themes/iso-diorama/variants/dusk-coral.json +1 -1
  194. package/themes/iso-diorama/variants/dusk-jade.json +1 -1
  195. package/themes/iso-diorama/variants/forest-cyan.json +1 -1
  196. package/themes/iso-diorama/variants/frost-green.json +1 -1
  197. package/themes/iso-diorama/variants/frost-teal.json +1 -1
  198. package/themes/iso-diorama/variants/navy-amber.json +1 -1
  199. package/themes/isotype/sample/style.json +2 -2
  200. package/themes/isotype/style.json +2 -2
  201. package/themes/isotype/theme.json +2 -1
  202. package/themes/isotype/variants/blush-rose.json +1 -1
  203. package/themes/isotype/variants/chalk-coral.json +1 -1
  204. package/themes/isotype/variants/chalk-orange.json +1 -1
  205. package/themes/isotype/variants/frost-amber.json +1 -1
  206. package/themes/isotype/variants/frost-coral.json +1 -1
  207. package/themes/isotype/variants/frost-orange.json +1 -1
  208. package/themes/isotype/variants/sand-amber.json +1 -1
  209. package/themes/kawaii-sticker/effects.ts +15 -3
  210. package/themes/kawaii-sticker/sample/style.json +2 -2
  211. package/themes/kawaii-sticker/style.json +2 -2
  212. package/themes/kawaii-sticker/theme.json +4 -1
  213. package/themes/kawaii-sticker/variants/cream-gold.json +1 -1
  214. package/themes/kawaii-sticker/variants/dusk-green.json +1 -1
  215. package/themes/kawaii-sticker/variants/dusk-indigo.json +1 -1
  216. package/themes/kawaii-sticker/variants/dusk-sky.json +1 -1
  217. package/themes/kawaii-sticker/variants/dusk-teal.json +1 -1
  218. package/themes/kawaii-sticker/variants/frost-blue.json +1 -1
  219. package/themes/kawaii-sticker/variants/frost-plum.json +1 -1
  220. package/themes/kawaii-sticker/variants/lilac-violet.json +1 -1
  221. package/themes/kawaii-sticker/variants/navy-gold.json +1 -1
  222. package/themes/kawaii-sticker/variants/navy-plum.json +1 -1
  223. package/themes/lab-notebook/sample/style.json +3 -3
  224. package/themes/lab-notebook/style.json +3 -3
  225. package/themes/lab-notebook/theme.json +2 -1
  226. package/themes/lab-notebook/variants/blush-orange.json +1 -1
  227. package/themes/lab-notebook/variants/chalk-coral.json +1 -1
  228. package/themes/lab-notebook/variants/chalk-orange.json +1 -1
  229. package/themes/lab-notebook/variants/chalk-rose.json +1 -1
  230. package/themes/lab-notebook/variants/lilac-rose.json +1 -1
  231. package/themes/lab-notebook/variants/sage-rose.json +1 -1
  232. package/themes/lab-notebook/variants/sand-orange.json +1 -1
  233. package/themes/lab-notebook/variants/snow-orange.json +1 -1
  234. package/themes/lab-notebook/variants/snow-rose.json +1 -1
  235. package/themes/manga-panel/sample/style.json +3 -3
  236. package/themes/manga-panel/style.json +3 -3
  237. package/themes/manga-panel/theme.json +3 -1
  238. package/themes/manga-panel/variants/chalk-cyan-dots.json +1 -1
  239. package/themes/manga-panel/variants/chalk-rose.json +1 -1
  240. package/themes/manga-panel/variants/snow-cyan.json +1 -1
  241. package/themes/manga-panel/variants/snow-magenta-dots.json +1 -1
  242. package/themes/marble-run/sample/style.json +2 -2
  243. package/themes/marble-run/style.json +2 -2
  244. package/themes/marble-run/theme.json +3 -1
  245. package/themes/marble-run/variants/slate-orange.json +1 -1
  246. package/themes/neon-sign/sample/style.json +2 -2
  247. package/themes/neon-sign/style.json +2 -2
  248. package/themes/neon-sign/theme.json +2 -1
  249. package/themes/neon-sign/variants/dusk-plum.json +1 -1
  250. package/themes/neon-sign/variants/forest-magenta.json +1 -1
  251. package/themes/neon-sign/variants/night-pink.json +1 -1
  252. package/themes/neon-sign/variants/night-violet.json +1 -1
  253. package/themes/neon-sign/variants/slate-violet.json +1 -1
  254. package/themes/newspaper/sample/style.json +2 -2
  255. package/themes/newspaper/style.json +2 -2
  256. package/themes/newspaper/theme.json +2 -1
  257. package/themes/newspaper/variants/blush-amber.json +1 -1
  258. package/themes/newspaper/variants/chalk-magenta.json +1 -1
  259. package/themes/newspaper/variants/frost-sky.json +1 -1
  260. package/themes/newspaper/variants/lilac-green.json +1 -1
  261. package/themes/newspaper/variants/sage-lime.json +1 -1
  262. package/themes/og-measure/THEME.md +41 -6
  263. package/themes/og-measure/effects.ts +236 -115
  264. package/themes/og-measure/sample/assets/brand/og-logo.svg +11 -11
  265. package/themes/og-measure/sample/lib/data.ts +6 -0
  266. package/themes/og-measure/sample/scenes/end.ts +14 -10
  267. package/themes/og-measure/sample/scenes/gather.ts +4 -1
  268. package/themes/og-measure/sample/scenes/models.ts +5 -2
  269. package/themes/og-measure/sample/scenes/one.ts +9 -6
  270. package/themes/og-measure/sample/scenes/price.ts +8 -3
  271. package/themes/og-measure/sample/style.json +30 -35
  272. package/themes/og-measure/slots.ts +54 -30
  273. package/themes/og-measure/still.png +0 -0
  274. package/themes/og-measure/style.json +30 -35
  275. package/themes/og-measure/theme.json +72 -49
  276. package/themes/og-measure/variants/snow-blue.json +90 -0
  277. package/themes/orbit-system/sample/style.json +3 -3
  278. package/themes/orbit-system/style.json +3 -3
  279. package/themes/orbit-system/theme.json +3 -1
  280. package/themes/orbit-system/variants/cocoa-amber.json +1 -1
  281. package/themes/orbit-system/variants/cocoa-cyan.json +1 -1
  282. package/themes/orbit-system/variants/dusk-magenta.json +1 -1
  283. package/themes/orbit-system/variants/dusk-violet.json +1 -1
  284. package/themes/orbit-system/variants/forest-coral.json +1 -1
  285. package/themes/orbit-system/variants/wine-amber.json +1 -1
  286. package/themes/orbit-system/variants/wine-magenta.json +1 -1
  287. package/themes/origami-fold/sample/style.json +1 -1
  288. package/themes/origami-fold/style.json +1 -1
  289. package/themes/origami-fold/theme.json +3 -1
  290. package/themes/origami-fold/variants/deep-amber-sunflower.json +1 -1
  291. package/themes/origami-fold/variants/deep-orange.json +1 -1
  292. package/themes/origami-fold/variants/forest-coral.json +1 -1
  293. package/themes/origami-fold/variants/forest-pink.json +1 -1
  294. package/themes/origami-fold/variants/frost-amber-paper.json +1 -1
  295. package/themes/origami-fold/variants/sage-coral-sunflower.json +1 -1
  296. package/themes/origami-fold/variants/sage-coral.json +1 -1
  297. package/themes/origami-fold/variants/sage-pink-round.json +1 -1
  298. package/themes/origami-fold/variants/sage-pink.json +1 -1
  299. package/themes/paper-cutout/sample/style.json +2 -2
  300. package/themes/paper-cutout/style.json +2 -2
  301. package/themes/paper-cutout/theme.json +3 -1
  302. package/themes/paper-cutout/variants/blush-violet.json +1 -1
  303. package/themes/paper-cutout/variants/frost-lime.json +1 -1
  304. package/themes/paper-cutout/variants/lilac-gold.json +1 -1
  305. package/themes/paper-cutout/variants/sage-cyan.json +1 -1
  306. package/themes/paper-cutout/variants/sage-green.json +1 -1
  307. package/themes/paper-cutout/variants/sage-pink.json +1 -1
  308. package/themes/paper-cutout/variants/sand-blue.json +1 -1
  309. package/themes/pixel-quest/theme.json +2 -1
  310. package/themes/receipt/sample/style.json +2 -2
  311. package/themes/receipt/style.json +2 -2
  312. package/themes/riso-zine/sample/style.json +2 -2
  313. package/themes/riso-zine/style.json +2 -2
  314. package/themes/riso-zine/theme.json +3 -1
  315. package/themes/riso-zine/variants/chalk-lime.json +1 -1
  316. package/themes/riso-zine/variants/frost-green.json +1 -1
  317. package/themes/riso-zine/variants/lilac-plum.json +1 -1
  318. package/themes/riso-zine/variants/lilac-violet.json +1 -1
  319. package/themes/server-hall/sample/style.json +2 -2
  320. package/themes/server-hall/style.json +2 -2
  321. package/themes/server-hall/theme.json +3 -1
  322. package/themes/server-hall/variants/night-indigo.json +1 -1
  323. package/themes/server-hall/variants/wine-plum.json +1 -1
  324. package/themes/subway-map/sample/style.json +2 -2
  325. package/themes/subway-map/style.json +2 -2
  326. package/themes/subway-map/theme.json +3 -1
  327. package/themes/subway-map/variants/deep-green.json +1 -1
  328. package/themes/subway-map/variants/deep-teal.json +1 -1
  329. package/themes/subway-map/variants/frost-jade.json +1 -1
  330. package/themes/subway-map/variants/frost-teal.json +1 -1
  331. package/themes/subway-map/variants/slate-green.json +1 -1
  332. package/themes/subway-map/variants/snow-jade.json +1 -1
  333. package/themes/swiss-poster/sample/style.json +2 -2
  334. package/themes/swiss-poster/style.json +2 -2
  335. package/themes/swiss-poster/theme.json +3 -1
  336. package/themes/swiss-poster/variants/chalk-plum.json +1 -1
  337. package/themes/swiss-poster/variants/cocoa-cyan.json +1 -1
  338. package/themes/swiss-poster/variants/dusk-magenta.json +1 -1
  339. package/themes/swiss-poster/variants/frost-indigo.json +1 -1
  340. package/themes/swiss-poster/variants/slate-orange.json +1 -1
  341. package/themes/water-works/sample/style.json +1 -1
  342. package/themes/water-works/style.json +1 -1
  343. package/themes/water-works/theme.json +3 -1
  344. package/themes/water-works/variants/chalk-cyan.json +1 -1
  345. package/themes/water-works/variants/frost-indigo.json +1 -1
  346. package/themes/water-works/variants/sand-amber.json +1 -1
  347. package/themes/water-works/variants/sand-plum.json +1 -1
  348. package/themes/whiteboard/sample/style.json +2 -2
  349. package/themes/whiteboard/style.json +2 -2
  350. package/themes/whiteboard/theme.json +3 -1
  351. package/themes/whiteboard/variants/chalk-coral.json +1 -1
  352. package/themes/whiteboard/variants/cream-lime.json +1 -1
  353. package/themes/whiteboard/variants/frost-teal.json +1 -1
  354. package/themes/whiteboard/variants/lilac-indigo.json +1 -1
  355. package/themes/whiteboard/variants/sage-plum.json +1 -1
  356. package/themes/whiteboard/variants/snow-cyan.json +1 -1
  357. package/themes/whiteboard/variants/snow-green.json +1 -1
  358. package/themes/whiteboard/variants/snow-magenta.json +1 -1
  359. package/themes/og-measure/sample/assets/brand/og-logo-full.svg +0 -12
  360. package/themes/og-measure/variants/chalk-orange.json +0 -102
  361. package/themes/og-measure/variants/cocoa-amber.json +0 -138
  362. package/themes/og-measure/variants/cocoa-cyan.json +0 -138
  363. package/themes/og-measure/variants/cocoa-gold.json +0 -129
  364. package/themes/og-measure/variants/cream-lime.json +0 -137
  365. package/themes/og-measure/variants/deep-jade.json +0 -100
  366. package/themes/og-measure/variants/dusk-violet.json +0 -133
  367. package/themes/og-measure/variants/forest-cyan.json +0 -123
  368. package/themes/og-measure/variants/frost-amber.json +0 -129
  369. package/themes/og-measure/variants/frost-cyan.json +0 -133
  370. package/themes/og-measure/variants/lilac-plum.json +0 -127
  371. package/themes/og-measure/variants/lilac-violet.json +0 -139
  372. package/themes/og-measure/variants/sage-green.json +0 -137
  373. package/themes/og-measure/variants/wine-jade.json +0 -127
  374. package/themes/og-measure/variants/wine-plum.json +0 -127
@@ -0,0 +1,118 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { basename, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { type FontPair, fontGroups, fontPairs } from "@kasaframe/themes/fonts";
7
+ import { type Command, parseFlags } from "../command.ts";
8
+ import { checkFonts, fontCacheDir, fontSheet, noticeSection, type PairReport, replaceNotice } from "../fonts.ts";
9
+
10
+ const cmd: Command = {
11
+ summary: "๊ธ€๊ผด ์ง ๊ฒ€์‚ฌยท๊ฒฌ๋ณธ ๊ฒฉ์ž โ€” ์ฃผ์†ŒยทํŒยท๋ผ์ด์„ ์Šค ๊ธ€ยทํ•œ๊ธ€ ๋ฎ์Œยท๊ตต๊ธฐ, ํฌ๋กฌ์œผ๋กœ ์ฐ์–ด ํˆฌ๋ช…ยท๋‘๋ถ€ยท๋–จ์–ด์ง",
12
+ usage: `kf fonts list [--group <๋ฌด๋ฆฌ>]
13
+ kf fonts check [--group <๋ฌด๋ฆฌ>[,โ€ฆ]] [--id <์ง>[,โ€ฆ]] [--json] [--no-render] [--offline]
14
+ kf fonts sheet [--group <๋ฌด๋ฆฌ>[,โ€ฆ]] [--id <์ง>[,โ€ฆ]] [--out ํŒŒ์ผ.png] [--share]
15
+ kf fonts notice [--write]
16
+
17
+ ์ง ๋ชฉ๋ก์€ packages/themes/kit/fonts.json(๋ฌด๋ฆฌ core)๊ณผ kit/fonts/<๋ฌด๋ฆฌ>.json ์„ ํ•ฉ์นœ ๊ฒƒ.
18
+ check ๋Š” ์ง๋งˆ๋‹ค
19
+ - ์นธ: hangul(full|ks2350)ยทface ๋งˆ๋‹ค licenseยทsource, ์Šคํƒ ์ฒซ ๊ธ€๊ผด์˜ face, ๋ชจ๋“  ์Šคํƒ์ด Pretendard Variable ๋กœ ๋๋‚จ
20
+ - ์ฃผ์†Œ: ํŒŒ์ผยท๋ผ์ด์„ ์Šค ๊ธ€์ด 200, ํŒ ๋ฐ•ํž˜(jsdelivr gh@์ปค๋ฐ‹|ํƒœ๊ทธยทnpm@x.y.z โ€” @mainยท@latestยท@5 ๋Š” ์•ˆ ๋œ๋‹ค)
21
+ - ๋ผ์ด์„ ์Šค ๊ธ€: OFL-1.1ยทApache-2.0 ์€ ๊ธ€์— ๊ทธ ๋ฌธ๊ตฌ๊ฐ€ ์žˆ๋Š”์ง€. ๋ชฉ๋ก ๋ฐ– ์ด๋ฆ„์€ ์‚ฌ๋žŒ์ด ์ฝ์„ ๊ฒƒ์œผ๋กœ ๋‚จ๊ธด๋‹ค
22
+ - ํŒŒ์ผ: ํ•œ๊ธ€ ์Œ์ ˆ ์ˆ˜(11,172 = full, KS X 1001 2,350 ๋‹ค = ks2350, ๊ทธ ์•„๋ž˜๋Š” ํƒˆ๋ฝ)์™€ ์ ์€ hangul ์ด ๋งž๋Š”์ง€,
23
+ ์ ์€ ๊ตต๊ธฐ ๋ฒ”์œ„๊ฐ€ ํŒŒ์ผ(wght ์ถ•ยทusWeightClass)๊ณผ ๋งž๋Š”์ง€, ์ œ๋ชฉ ๊ตต๊ธฐ ๋‹จ์ด ์žˆ๋Š”์ง€(์—†์œผ๋ฉด ๊ฐ€์งœ ๊ตต๊ธฐ)
24
+ - ํฌ๋กฌ: ์Šคํƒ๋งˆ๋‹ค ํ•œ๊ธ€ยท๋ผํ‹ดยท์ˆซ์ž ๊ฒฌ๋ณธ ํ•œ ์ค„์„ ์ฐ์–ด ํˆฌ๋ช…ยท๋‘๋ถ€ยท์‹œ์Šคํ…œ ๊ธ€๊ผด๋กœ ๋–จ์–ด์งยท์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅธ face ๋ฅผ ์žก๋Š”๋‹ค
25
+ ๋ฐ›์€ ํŒŒ์ผ์€ ${fontCacheDir()}/files ์— ๋‘๊ณ  ๋‹ค์Œ๋ถ€ํ„ฐ๋Š” ์ฃผ์†Œ๊ฐ€ ์‚ด์•˜๋Š”์ง€๋งŒ ๋ฌป๋Š”๋‹ค. ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ์ง์ด ์žˆ์œผ๋ฉด ์ข…๋ฃŒ 1.
26
+ --json { pairs: [{ id, group, ok, hangul, syllables, boldest, licenses, problems, notes }], summary }
27
+ --no-render ํฌ๋กฌ ์—†์ด(ํŒŒ์ผยท์ฃผ์†Œ๋งŒ)
28
+ --offline ์ฃผ์†Œ๋ฅผ ๋ฌป์ง€ ์•Š๊ณ  ์บ์‹œ๋งŒ(๋ผ์ด์„ ์Šค ๊ธ€ ๋Œ€์กฐ๋„ ๊ฑด๋„ˆ๋›ด๋‹ค)
29
+ sheet ๋Š” ์ง๋งˆ๋‹ค ์ œ๋ชฉยท๋ณธ๋ฌธยท๊ณ ์ •ํญ ๊ฒฌ๋ณธ ์นธ์„ ๊ฒฉ์ž ํ•œ ์žฅ์œผ๋กœ โ€” ๊ธฐ๋ณธ ${fontCacheDir()}/sheet[-<๋ฌด๋ฆฌ>].png.
30
+ --share ๋ฉด kasaterm ๊ณต์œ  ํด๋” kasaframe-๊ธ€๊ผด ๋กœ ์˜ฎ๊ธด๋‹ค.
31
+ notice ๋Š” ๋ชจ๋“  ๋ฌด๋ฆฌ๋ฅผ ํ•ฉ์นœ ๋ชฉ๋ก์œผ๋กœ NOTICE ์˜ ๊ธ€๊ผด ์ ˆ(๊ธ€๊ผด ์ด๋ฆ„ ยท ๋ผ์ด์„ ์Šค ยท ๋ผ์ด์„ ์Šค ๊ธ€)์„ ์ฐ๋Š”๋‹ค. --write ๋ฉด ๋ ˆํฌ NOTICE ์—
32
+ ๊ทธ ์ ˆ์„ ๊ฐˆ์•„ ๋ผ์šด๋‹ค โ€” ๋ฌด๋ฆฌ ํŒŒ์ผ์„ ๊ณ ์นœ ๋’ค ํ•œ ๋ฒˆ.`,
33
+ async run(argv) {
34
+ const [sub, ...rest] = argv;
35
+ const { opts } = parseFlags(rest);
36
+ const list = pick(opts);
37
+ if (!list) return 2;
38
+ if (sub === "list" || !sub) {
39
+ for (const { group, pair } of list) console.log(`${group.padEnd(10)} ${pair.id.padEnd(26)} ${String(pair.hangul ?? "?").padEnd(7)} ${pair.tags.join(",").padEnd(30)} ${pair.name}`);
40
+ console.log(`์ง ${list.length}`);
41
+ return 0;
42
+ }
43
+ if (sub === "check") {
44
+ const reports = await checkFonts(list, { offline: !!opts.offline, render: !opts["no-render"], log: (l) => console.error(l) });
45
+ const summary = summarize(reports);
46
+ if (opts.json) console.log(JSON.stringify({ pairs: reports, summary }, null, 1));
47
+ else print(reports, summary);
48
+ return reports.every((r) => r.ok) ? 0 : 1;
49
+ }
50
+ if (sub === "notice") {
51
+ const section = noticeSection(fontPairs());
52
+ if (!opts.write) {
53
+ process.stdout.write(section);
54
+ return 0;
55
+ }
56
+ const file = fileURLToPath(new URL("../../../../NOTICE", import.meta.url));
57
+ writeFileSync(file, replaceNotice(readFileSync(file, "utf8"), section));
58
+ console.log(file);
59
+ return 0;
60
+ }
61
+ if (sub === "sheet") {
62
+ const groups = typeof opts.group === "string" ? opts.group.replace(/,/g, "+") : "";
63
+ const out = resolve(typeof opts.out === "string" ? opts.out : join(fontCacheDir(), `sheet${groups ? `-${groups}` : ""}.png`));
64
+ await fontSheet(list, out, { log: (l) => console.error(l) });
65
+ console.log(out);
66
+ if (opts.share) {
67
+ const r = spawnSync("kasaterm-cli", ["share", "new", "kasaframe-๊ธ€๊ผด"], { encoding: "utf8" });
68
+ let dir = r.status === 0 ? (r.stdout.trim().split("\n").pop() ?? "") : "";
69
+ if (!dir || !existsSync(dir)) {
70
+ dir = join(homedir(), "Desktop", "kasaframe-๊ธ€๊ผด");
71
+ mkdirSync(dir, { recursive: true });
72
+ }
73
+ copyFileSync(out, join(dir, basename(out)));
74
+ console.log(`๊ณต์œ  ํด๋” ${dir}`);
75
+ }
76
+ return 0;
77
+ }
78
+ console.error(`kf fonts: ๋ชจ๋ฅด๋Š” ํ•˜์œ„ ๋ช…๋ น ${sub} โ€” listยทcheckยทsheetยทnotice`);
79
+ return 2;
80
+ },
81
+ };
82
+
83
+ function pick(opts: Record<string, string | boolean | undefined>): { group: string; pair: FontPair }[] | null {
84
+ const groups = fontGroups();
85
+ const want = typeof opts.group === "string" ? opts.group.split(",") : null;
86
+ const unknown = want?.filter((g) => !groups.some((x) => x.group === g));
87
+ if (unknown?.length) {
88
+ console.error(`kf fonts: ๋ฌด๋ฆฌ ${unknown.join(",")} ๊ฐ€ ์—†๋‹ค โ€” ์žˆ๋Š” ๋ฌด๋ฆฌ: ${groups.map((g) => g.group).join(" ")}`);
89
+ return null;
90
+ }
91
+ const ids = typeof opts.id === "string" ? new Set(opts.id.split(",")) : null;
92
+ const list = groups.filter((g) => !want || want.includes(g.group)).flatMap((g) => g.pairs.map((pair) => ({ group: g.group, pair }))).filter(({ pair }) => !ids || ids.has(pair.id));
93
+ if (ids && list.length < ids.size) console.error(`kf fonts: ์—†๋Š” ์ง โ€” ${[...ids].filter((i) => !list.some((x) => x.pair.id === i)).join(",")}`);
94
+ return list;
95
+ }
96
+
97
+ const tally = (xs: string[]) => Object.fromEntries([...new Set(xs)].sort().map((x) => [x, xs.filter((y) => y === x).length]));
98
+
99
+ function summarize(reports: PairReport[]) {
100
+ return {
101
+ pairs: reports.length,
102
+ ok: reports.filter((r) => r.ok).length,
103
+ hangul: tally(reports.map((r) => r.hangul ?? "?")),
104
+ licenses: tally(reports.flatMap((r) => r.licenses)),
105
+ };
106
+ }
107
+
108
+ function print(reports: PairReport[], s: ReturnType<typeof summarize>): void {
109
+ for (const r of reports) {
110
+ console.log(`${r.ok ? "ok " : "๋ฌธ์ œ"} ${r.group.padEnd(10)} ${r.id.padEnd(26)} ํ•œ๊ธ€ ${r.hangul ?? "?"}(${r.syllables ?? "?"}) ยท ๊ตต์€ ๋‹จ ${r.boldest ?? "?"} ยท ${r.licenses.join(",")}`);
111
+ for (const p of r.problems) console.log(` ! ${p}`);
112
+ for (const n of r.notes) console.log(` ยท ${n}`);
113
+ }
114
+ const fmt = (o: Record<string, number>) => Object.entries(o).map(([k, v]) => `${k} ${v}`).join(" ยท ");
115
+ console.log(`\n์ง ${s.pairs} โ€” ํ†ต๊ณผ ${s.ok} ยท ๋ฌธ์ œ ${s.pairs - s.ok} | ํ•œ๊ธ€ ${fmt(s.hangul)} | ๋ผ์ด์„ ์Šค(์ง ๊ธฐ์ค€) ${fmt(s.licenses)}`);
116
+ }
117
+
118
+ export default cmd;
@@ -103,6 +103,7 @@ kf make --answer <ํด๋”> --image <๊ฐ’> --video <๊ฐ’> โ€ฆ Claude Code ๋ชจ๋“œ
103
103
  --video-provider ์˜์ƒ ๊ณต๊ธ‰์ž๋ฅผ ๋ชป ๋ฐ•๋Š”๋‹ค(์˜ˆ: h3-pod โ€” KF_H3_URLยทKF_H3_STEPS ํ•„์š”). ๊ธฐ๋ณธ์€ KF_VIDEO_PROVIDER, ์—†์œผ๋ฉด ์ƒ์„ฑ ๋‹ด๋‹น ๊ธฐ๋ณธ
104
104
  --fix ๊ฒ€์‚ฌ๊ฐ€ ๋–จ์–ด์กŒ์„ ๋•Œ ๊ณ ์น˜๊ธฐ ํšŸ์ˆ˜(๊ธฐ๋ณธ 2)
105
105
  --captions on|off|fit ํ™”๋ฉด ์ž๋ง‰(fit ์€ ์žฅ๋ฉด์„ ์ž๋ง‰ ๋  ์œ„๋กœ ์ค„์—ฌ ์˜ฌ๋ฆฐ๋‹ค) โ€” ์ƒˆ๋กœ ๋งŒ๋“ค ๋• kf.json ์— ์ ๊ณ , --finish ์—์„  ๊ทธ ํŒ๋งŒ ๋ฎ๋Š”๋‹ค(๋‹ค์‹œ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ์ผœ๊ณ  ๋„๊ธฐ). srtยทvtt ๋Š” ๋‘˜ ๋‹ค ๋‚ธ๋‹ค
106
+ --sfx on|off ํšจ๊ณผ์Œ(๊ธฐ๋ณธ ์ผฌ โ€” ํ…Œ๋งˆ๊ฐ€ ๊ณ ๋ฅธ ๊ฒฐ๋กœ ์žฅ๋ฉด ์›€์ง์ž„ ์ž๋ฆฌ์— ๊ณก ์•„๋ž˜ ์ž‘๊ฒŒ) โ€” ์ƒˆ๋กœ ๋งŒ๋“ค ๋• off ๋ฅผ kf.json ์— sfx: false ๋กœ ์ ๊ณ , --finish ์—์„  ๊ทธ ํŒ๋งŒ ๋ฎ๋Š”๋‹ค
106
107
  --suffix [๊ธ€] --finish ๊ฒฐ๊ณผ ์ด๋ฆ„ ๋’ค์— ๋ถ™์ธ๋‹ค(๊ฐ’ ์—†์ด๋ฉด -์ž๋ง‰ ยท -์ž๋ง‰์—†์Œ) โ€” ์›๋ณธ video.mp4 ๋ฅผ ๋ฎ์ง€ ์•Š๊ณ  ๋‘˜ ๋‹ค ๋‚จ๊ธด๋‹ค
107
108
  --no-gen ์ƒ์„ฑ ๋ชจ๋ธ์„ ๋ถ€๋ฅด์ง€ ์•Š๋Š”๋‹ค โ€” ๋ฌป์ง€ ์•Š๊ณ  ๊ทธ๋ฆผยท์˜์ƒ ๋ชจ๋ธ์€ ๋นผ๊ณ , Blender ๊ฐ€ ์—†์œผ๋ฉด three ๋กœ ๋Œ€์‹ ํ•œ๋‹ค
108
109
 
@@ -171,6 +172,11 @@ async function make(argv: string[], at: Track): Promise<number> {
171
172
  return 2;
172
173
  }
173
174
  const suffix = str("suffix") ?? (opts.suffix ? (captions === "off" ? "-์ž๋ง‰์—†์Œ" : "-์ž๋ง‰") : "");
175
+ const sfx = str("sfx");
176
+ if (sfx !== undefined && sfx !== "on" && sfx !== "off") {
177
+ console.error(`kf make: --sfx ๋Š” onยทoff โ€” ${sfx}`);
178
+ return 2;
179
+ }
174
180
 
175
181
  if (opts.answer) {
176
182
  at.quiet = true;
@@ -192,7 +198,7 @@ async function make(argv: string[], at: Track): Promise<number> {
192
198
  const narrate = sayChanged(dir, ids) ? [] : ["--no-narrate"];
193
199
  const d = await relay(["direct", dir, "--scenes", ids.join(","), ...narrate, "--no-check", ...pick]);
194
200
  if (d !== 0) return d;
195
- return finish(dir, { model: "direct", vision, fixes: 0, scenes: ids });
201
+ return finish(dir, { model: "direct", vision, fixes: 0, scenes: ids, sfx });
196
202
  }
197
203
  // ๋‹ค์‹œ ์“ฐ๊ธฐ๋Š” ๊ทธ ์ผ์„ ๋งŒ๋“  ๋ชจ๋ธ๋กœ โ€” ๊ธฐ๋ณธ ๋ชจ๋ธ๋กœ ๋ชฐ๋ž˜ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค
198
204
  const own = str("model") ?? readPlan(dir).model;
@@ -208,7 +214,7 @@ async function make(argv: string[], at: Track): Promise<number> {
208
214
  }
209
215
  writeFileSync(join(dir, "MAKE.md"), projectBrief(dir));
210
216
  await writeScenes(dir, own, ids);
211
- return finish(dir, { model: own, vision, fixes, scenes: ids });
217
+ return finish(dir, { model: own, vision, fixes, scenes: ids, sfx });
212
218
  }
213
219
  if (opts.finish) {
214
220
  const dir = resolve(typeof opts.finish === "string" ? opts.finish : (args[0] ?? "."));
@@ -220,7 +226,7 @@ async function make(argv: string[], at: Track): Promise<number> {
220
226
  if (studio) console.log(`์ž‘์—…์‹ค: ${studio.url}`);
221
227
  await studio?.send({ phase: "render" });
222
228
  const scenes = str("scenes")?.split(",").filter(Boolean);
223
- const code = await finish(dir, { model, vision, fixes, gen: opts["no-gen"] ? undefined : gen, captions, suffix, ...(scenes?.length ? { scenes } : {}) });
229
+ const code = await finish(dir, { model, vision, fixes, gen: opts["no-gen"] ? undefined : gen, captions, suffix, sfx, ...(scenes?.length ? { scenes } : {}) });
224
230
  await unmirror?.();
225
231
  await studio?.send({ done: true, ok: code === 0 });
226
232
  return code;
@@ -352,6 +358,10 @@ async function make(argv: string[], at: Track): Promise<number> {
352
358
  writeJson(kfPath, { ...JSON.parse(readFileSync(kfPath, "utf8")), captions });
353
359
  plan.captions = captionMode(captions);
354
360
  }
361
+ if (sfx === "off") {
362
+ const kfPath = join(dir, "kf.json");
363
+ writeJson(kfPath, { ...JSON.parse(readFileSync(kfPath, "utf8")), sfx: false });
364
+ }
355
365
  if (!str("purpose") && !series) {
356
366
  plan.confidence = chosen?.p ?? det.confidence;
357
367
  plan.alternatives = det.ranked.filter((r) => r.purpose !== purpose).slice(0, 2).map((r) => ({ purpose: r.purpose, p: r.p }));
@@ -901,7 +911,7 @@ async function seamStills(dir: string, only?: string[]): Promise<void> {
901
911
 
902
912
  // captions ๋ฅผ ์ฃผ๋ฉด ๊ทธ ํŒ๋งŒ ํ™”๋ฉด ์ž๋ง‰์„ ์ผœ๊ณ  ๋ˆ๋‹ค(kf.json ์€ ๊ทธ๋Œ€๋กœ) โ€” ๊ตฝ๊ธฐยท๊ฒ€์‚ฌยท์ž๋ง‰ ํŠธ๋ž™์ด ๊ฐ™์€ ๊ฐ’์„ ๋ณธ๋‹ค
903
913
  // scenes ๋ฅผ ์ฃผ๋ฉด ๊ทธ ์žฅ๋ฉด ๋‘˜๋ ˆ(๊ทธ ์žฅ๋ฉด + ๋ถ™์€ ์ด์Œ๋งค)๋งŒ โ€” ์ด์Œ ํ™”๋ฉดยท๊ฒ€์‚ฌ๋ฅผ ๊ทธ ๊ตฌ๊ฐ„์—์„œ. ๊ตฝ๊ธฐ๋Š” kf render ๊ฐ€ ๋ฐ”๋€ ์กฐ๊ฐ๋งŒ ๋‹ค์‹œ ์ฐ๋Š”๋‹ค
904
- async function finish(dir: string, o: { model: string; vision: string; fixes: number; gen?: GenOpts; captions?: "on" | "off" | "fit"; suffix?: string; scenes?: string[] }): Promise<number> {
914
+ async function finish(dir: string, o: { model: string; vision: string; fixes: number; gen?: GenOpts; captions?: "on" | "off" | "fit"; suffix?: string; scenes?: string[]; sfx?: string }): Promise<number> {
905
915
  if (!existsSync(join(dir, "kf.json"))) {
906
916
  console.error(`kf make: ${dir} ์— kf.json ์ด ์—†๋‹ค`);
907
917
  return 2;
@@ -921,7 +931,7 @@ async function finish(dir: string, o: { model: string; vision: string; fixes: nu
921
931
  if (round > 0) await seamStills(dir, touched);
922
932
  // ๊ตฝ๊ธฐ์™€ ๊ฒ€์‚ฌ ์‚ฌ์ด์— ์ž ๊ธˆ์„ ๋†“์œผ๋ฉด ๊ธฐ๋‹ค๋ฆฌ๋˜ ๋‚จ์˜ ๊ธด ๋ Œ๋”๊ฐ€ ๋ผ์–ด๋“ ๋‹ค โ€” ๋‘˜์„ ํ•œ ๋ฒˆ์— ์ฅ๊ณ  ์ž์‹์€ ์ž ๊ธˆ ์—†์ด
923
933
  const release = await acquireRenderLock(dir, 3600);
924
- const r = await relay(["render", dir, "--out", out, "--tabs", "2", "--no-lock", ...capFlag]);
934
+ const r = await relay(["render", dir, "--out", out, "--tabs", "2", "--no-lock", ...capFlag, ...(o.sfx ? ["--sfx", o.sfx] : [])]);
925
935
  if (r !== 0) {
926
936
  release();
927
937
  console.error("kf make: ๋ Œ๋”๊ฐ€ ์‹คํŒจํ–ˆ๋‹ค");
@@ -8,7 +8,8 @@ import { acquireRenderLock } from "../lock.ts";
8
8
  import { blurOpts, captureBlurred, projectBlur, rgbaToPng } from "../blur.ts";
9
9
  import { outPath } from "../out.ts";
10
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";
11
+ import { mixAudio, mixSettings, SFX_AUDIO, type SfxMix } from "../mix.ts";
12
+ import { cuesOf, loadBank, placeSfx, projectDrops, sfxSettings } from "../sfx.ts";
12
13
 
13
14
  const MAX_TABS = 3;
14
15
 
@@ -61,7 +62,7 @@ function withSuffix(p: string, suffix: string): string {
61
62
 
62
63
  const cmd: Command = {
63
64
  summary: "MP4(๋˜๋Š” ProResยทPNG ๋ฌถ์Œ)๋กœ ๊ตฝ๊ธฐ โ€” ํƒญ ๋ณ‘๋ ฌ, ๊ณก ์–น๊ธฐ, ์ž ๊ธˆ",
64
- usage: `kf render <ํด๋”> [--out x.mp4] [--scenes a,b] [--from ์ดˆ --to ์ดˆ] [--tabs 3] [--prores | --png] [--music ๊ณก | --no-music] [--captions on|off|fit [--suffix]]
65
+ usage: `kf render <ํด๋”> [--out x.mp4] [--scenes a,b] [--from ์ดˆ --to ์ดˆ] [--tabs 3] [--prores | --png] [--music ๊ณก | --no-music] [--sfx on|off] [--captions on|off|fit [--suffix]]
65
66
  --out ๊ธฐ๋ณธ <ํด๋”>/out/render.mp4 (--prores ๋ฉด .mov, --png ๋ฉด ํด๋”)
66
67
  seek ๋ชจ๋“œ๋Š” ์žฅ๋ฉด ์กฐ๊ฐ์œผ๋กœ ๊ตฝ๋Š”๋‹ค โ€” ์žฅ๋ฉด๋งˆ๋‹ค out/segments/<์žฅ๋ฉด>-<ํ•ด์‹œ>.mp4(ํ•ด์‹œ: ๊ทธ ์žฅ๋ฉด์ด ์‹ค์ œ๋กœ ๋ถˆ๋Ÿฌ์˜จ ๋ชจ๋“ˆยทkf.json ๊ทธ ์žฅ๋ฉด ์นธยท
67
68
  ์ž์‚ฐยท์ž๋ง‰ ์ค„ยท๊ตฝ๊ธฐ ์„ค์ •, ์ด์Œ๋งค๋Š” ์•ž๋’ค ์žฅ๋ฉด๊นŒ์ง€). ํ•ด์‹œ๊ฐ€ ๊ฐ™์€ ์กฐ๊ฐ์€ ๋‹ค์‹œ ์ฐ์ง€ ์•Š๊ณ  ์ด์–ด ๋ถ™์ธ ๋’ค ๊ณกยท๋ชฉ์†Œ๋ฆฌ๋ฅผ ์ „์ฒด๋กœ ์–น๋Š”๋‹ค
@@ -71,6 +72,8 @@ const cmd: Command = {
71
72
  --tabs ํฌ๋กฌ ํƒญ ์ˆ˜(์ตœ๋Œ€ ${MAX_TABS}). seek ๋ชจ๋“œ๋Š” ๊ตฌ๊ฐ„์„ ๋‚˜๋ˆ  ์ฐ๊ณ , step ๋ชจ๋“œ๋Š” ํƒญ๋งˆ๋‹ค ๊ตฌ๊ฐ„ ์•ž๊นŒ์ง€ ๋นจ๋ฆฌ ๋Œ๋ฆฐ ๋’ค ์ฐ๋Š”๋‹ค
72
73
  --music ๊ธฐ๋ณธ์€ ํŽ˜์ด์ง€ meta.music(kf.json ์˜ music). ๊ณก์€ beats.json ์˜ from ์ดˆ๋ถ€ํ„ฐ ์–น๋Š”๋‹ค
73
74
  --music-from ๊ณก์—์„œ ์‹œ์ž‘ํ•  ์ดˆ๋ฅผ ์ง์ ‘ ์ค€๋‹ค(--music ์œผ๋กœ ๋‹ค๋ฅธ ๊ณก์„ ์ค„ ๋•Œ)
75
+ --sfx on|off ํšจ๊ณผ์Œ(๊ธฐ๋ณธ ์ผฌ) โ€” ์žฅ๋ฉด ์›€์ง์ž„์ด ๋‚จ๊ธด ์†Œ๋ฆฌ ์ž๋ฆฌ(__kf.events ์˜ sfx)์™€ ๊ณก์˜ ๋“œ๋กญ์— ํ…Œ๋งˆ๊ฐ€ ๊ณ ๋ฅธ ๊ฒฐ(theme.json sfx)์˜ ์†Œ๋ฆฌ๋ฅผ
76
+ ๊ณก ์•„๋ž˜๋กœ ์ž‘๊ฒŒ ๊น๋‹ค. off ๋ฉด ์ด ํŒ๋งŒ ๋„๊ณ (ํšจ๊ณผ์Œ ์ด์ „๊ณผ ๊ฐ™์€ ์†Œ๋ฆฌ), on ์ด๋ฉด kf.json sfx: false ๋„ ์ด ํŒ๋งŒ ์ผ ๋‹ค. --no-music ์ด๋ฉด ์—†๋‹ค
74
77
  --crf H.264 ํ™”์งˆ(๊ธฐ๋ณธ 16, ๋‚ฎ์„์ˆ˜๋ก ์ข‹๋‹ค)
75
78
  --motion-blur N ํ•œ ํ”„๋ ˆ์ž„์„ ํ•˜์œ„ ํ”„๋ ˆ์ž„ N ์žฅ(์˜ˆ 8)์œผ๋กœ ์ฐ์–ด ํ‰๊ท  โ€” ๋น ๋ฅธ ๊ฒƒ๋งŒ ๋ฒˆ์ง„๋‹ค. 3D ์ „ํ™˜์€ ๊ฐ™์€ ํ‰๊ท ์œผ๋กœ ํ”ผ์‚ฌ๊ณ„ ์‹ฌ๋„๋„. seek ๋ชจ๋“œ๋งŒ, N ๋ฐฐ ๋А๋ฆฌ๋‹ค.
76
79
  ์•ˆ ์ฃผ๋ฉด kf.json ์˜ render.motionBlur(์‡ผ์ธ ยทํ™๋ณด ํ‹€์€ ์ผœ ๋‘”๋‹ค), 0 ์ด๋ฉด ๋ˆ๋‹ค
@@ -91,6 +94,8 @@ const cmd: Command = {
91
94
  const format: VideoFormat | "png" = opts.png ? "png" : opts.prores ? "prores" : "h264";
92
95
  const ext = format === "prores" ? ".mov" : format === "png" ? "" : ".mp4";
93
96
  const captions = opts.captions === undefined ? undefined : String(opts.captions);
97
+ const sfxFlag = opts.sfx === undefined ? undefined : String(opts.sfx);
98
+ if (sfxFlag !== undefined && sfxFlag !== "on" && sfxFlag !== "off") throw new Error(`--sfx ๋Š” onยทoff: ${sfxFlag}`);
94
99
  if (captions !== undefined && captions !== "on" && captions !== "off" && captions !== "fit") throw new Error(`--captions ๋Š” onยทoffยทfit: ${captions}`);
95
100
  const suffix = typeof opts.suffix === "string" ? opts.suffix : opts.suffix ? (captions === "off" ? "-์ž๋ง‰์—†์Œ" : "-์ž๋ง‰") : "";
96
101
  const out = withSuffix(outPath(dir, opts.out, format === "png" ? "frames/x" : `render${ext}`).replace(/\/x$/, ""), suffix);
@@ -139,6 +144,8 @@ const cmd: Command = {
139
144
  console.error(` ${done}/${todo} (${((done / Math.max(1, todo)) * 100).toFixed(0)}%) ${(done / sec).toFixed(1)} ์žฅ/์ดˆ`);
140
145
  }, 5000);
141
146
 
147
+ let sfx: SfxMix | undefined;
148
+ let sfxCues = 0;
142
149
  let segFiles: string[] = [];
143
150
  let events: MotionEvent[] = [];
144
151
  let segments: { rendered: string[]; reused: string[] } | undefined;
@@ -209,13 +216,24 @@ const cmd: Command = {
209
216
  if (format !== "png") {
210
217
  const list = join(tmp, "list.txt");
211
218
  writeFileSync(list, segFiles.map((f) => `file '${f.replace(/'/g, "'\\''")}'`).join("\n") + "\n");
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") })
219
+ // ํšจ๊ณผ์Œ โ€” ์ฐ๋Š” ๋™์•ˆ ํŽ˜์ด์ง€๊ฐ€ ๋‚จ๊ธด ์†Œ๋ฆฌ ์ž๋ฆฌ๋ฅผ ๋ฐ€๋„๋Œ€๋กœ ๊ฑธ๋Ÿฌ ๊น๋‹ค. ๋„๋ฉด(--sfx offยทkf.json sfx: false) ์•„๋ž˜๋Š” ํšจ๊ณผ์Œ ์ด์ „๊ณผ ๊ฐ™์€ ๊ธธ
220
+ const settings = opts["no-music"] ? null : sfxSettings(resolve(dir), sfxFlag);
221
+ if (settings) {
222
+ const bank = loadBank();
223
+ if (!bank.sets.includes(settings.set)) throw new Error(`ํšจ๊ณผ์Œ ๊ฒฐ "${settings.set}" ์ด ์—†๋‹ค โ€” ์žˆ๋Š” ๊ฒฐ: ${bank.sets.join(" ยท ")}`);
224
+ const cues = cuesOf(events, settings.only && !settings.only.includes("drop") ? [] : projectDrops(resolve(dir)));
225
+ sfx = { hits: placeSfx(cues, settings, bank, { from: startF / fps, to: endF / fps }), settings, bank };
226
+ sfxCues = cues.filter((c) => c.t >= startF / fps && c.t < endF / fps).length;
227
+ }
228
+ // kf.json ์— audio(์„ž๊ธฐ ๊ฐ’)๊ฐ€ ์žˆ์œผ๋ฉด ๋‚ด๋ ˆ์ด์…˜ยท๊ณก์„ ์„ž์€ ์†Œ๋ฆฌ๋ฅผ ์–น๋Š”๋‹ค(๋•ํ‚นยท๋ ํŽ˜์ด๋“œยทLUFS) โ€” ์—†์œผ๋ฉด ๊ณก๋งŒ. ํšจ๊ณผ์Œ์„ ๊น”๋ฉด audio ๊ฐ€ ์—†์–ด๋„ ์„ž๋Š”๋‹ค
229
+ const found = opts["no-music"] ? null : mixSettings(resolve(dir));
230
+ const mix = found ?? (sfx?.hits.length ? { audio: SFX_AUDIO, voice: undefined } : null);
231
+ const mixed = mix && (mix.voice || music || sfx?.hits.length)
232
+ ? 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"), sfx })
216
233
  : undefined;
217
234
  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`);
235
+ const layers = [mix?.voice ? "๋‚ด๋ ˆ์ด์…˜" : "", music ? "๊ณก" : "", sfx?.hits.length ? `ํšจ๊ณผ์Œ ${sfx.hits.length}(${sfx.settings.set})` : ""].filter(Boolean);
236
+ if (mixed) console.error(`์†Œ๋ฆฌ: ${layers.join(" + ")} โ†’ ${mix!.audio.lufs} LUFS`);
219
237
  }
220
238
  const sec = (Date.now() - started) / 1000;
221
239
  const report = {
@@ -230,6 +248,15 @@ const cmd: Command = {
230
248
  motionBlur: blur,
231
249
  captions: captions ?? null,
232
250
  music: music ?? null,
251
+ sfx: sfx
252
+ ? {
253
+ ...sfx.settings,
254
+ cues: sfxCues,
255
+ placed: sfx.hits.length,
256
+ ...(sfx.measured ? { lufs: { bed: sfx.measured.bed, sfx: sfx.measured.sfx, lift: sfx.measured.lift } } : {}),
257
+ hits: sfx.measured?.hits ?? sfx.hits,
258
+ }
259
+ : null,
233
260
  seconds: Math.round(sec * 10) / 10,
234
261
  ...(segments ? { segments } : {}),
235
262
  markers: s.info.markers,
@@ -0,0 +1,66 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { type Command, parseFlags } from "../command.ts";
3
+ import { commandFor, current, isNewer, noticeLine, refresh, upgrade } from "../update.ts";
4
+
5
+ async function confirm(q: string): Promise<boolean> {
6
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
7
+ try {
8
+ return /^(|y|yes|ใ…‡|์‘|๋„ค)$/i.test((await rl.question(q)).trim());
9
+ } catch {
10
+ return false;
11
+ } finally {
12
+ rl.close();
13
+ }
14
+ }
15
+
16
+ const cmd: Command = {
17
+ summary: "์ƒˆ ํŒ ํ™•์ธยท์˜ฌ๋ฆฌ๊ธฐ โ€” ์ง€๊ธˆ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋ฅผ ๋ณด๊ณ  ์ƒˆ ํŒ์ด๋ฉด ๊น ๋ฐฉ์‹(npmยทpnpmยทbunยทyarn)๋Œ€๋กœ ์˜ฌ๋ฆฐ๋‹ค",
18
+ usage: `kf update ์ง€๊ธˆ ํ™•์ธํ•˜๊ณ  ์ƒˆ ํŒ์ด๋ฉด ์˜ฌ๋ฆฐ๋‹ค(ํ„ฐ๋ฏธ๋„์ด๋ฉด ํ•œ ๋ฒˆ ๋ฌป๋Š”๋‹ค)
19
+ kf update --check ์˜ฌ๋ฆฌ์ง€ ์•Š๊ณ  ๋ณด๊ธฐ๋งŒ
20
+ kf update --json {current, latest, update, command} ๋ฅผ ํ‘œ์ค€ ์ถœ๋ ฅ์— โ€” ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค
21
+ kf update --yes ๋ฌป์ง€ ์•Š๊ณ  ์˜ฌ๋ฆฐ๋‹ค(์—์ด์ „ํŠธ์šฉ โ€” ํ‘œ์ค€ ์˜ค๋ฅ˜์— ใ€Œ[kasaframe] ์ƒˆ ํŒ โ€ฆใ€ ์ค„์ด ๋ณด์ด๋ฉด)
22
+ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋Š” npm ์„ค์ •(npm_config_registryยท~/.npmrc)์„ ๋”ฐ๋ฅธ๋‹ค. kf ๋Š” ๋ช‡ ์‹œ๊ฐ„์— ํ•œ ๋ฒˆ ๋’ค์—์„œ ํ™•์ธํ•ด ๋‘๊ณ ,
23
+ ์ƒˆ ํŒ์„ ์•Œ๋ฉด ํ„ฐ๋ฏธ๋„์—์„  ๋ช…๋ น ์ „์— ๋ฌป๊ณ  ์•„๋‹ˆ๋ฉด ํ‘œ์ค€ ์˜ค๋ฅ˜์— ํ•œ ์ค„ ์ฐ๋Š”๋‹ค. ๋„๊ธฐ: KF_NO_UPDATE_CHECK=1 ยท CI
24
+ ์ข…๋ฃŒ ์ฝ”๋“œ: 0 ์ตœ์‹ ์ด๊ฑฐ๋‚˜ ์˜ฌ๋ฆผ ยท 1 ํ™•์ธยท์„ค์น˜ ์‹คํŒจ ยท 2 ์˜ฌ๋ฆฌ์ง€ ์•Š์Œ(๋ฌป๊ธฐ ๊ฑฐ์ ˆยท--yes ์—†์ด ํ„ฐ๋ฏธ๋„ ๋ฐ–ยท๋ ˆํฌ ์ฒดํฌ์•„์›ƒ)`,
25
+ async run(argv) {
26
+ const { opts } = parseFlags(argv);
27
+ let latest: string;
28
+ try {
29
+ latest = await refresh();
30
+ } catch (e) {
31
+ const error = (e as Error).message;
32
+ if (opts.json) console.log(JSON.stringify({ current, latest: null, update: false, command: null, error }));
33
+ else console.error(`kf update: ์ƒˆ ํŒ์„ ํ™•์ธํ•˜์ง€ ๋ชปํ–ˆ๋‹ค โ€” ${error}`);
34
+ return 1;
35
+ }
36
+ const update = isNewer(latest, current);
37
+ const command = update ? commandFor(latest) : null;
38
+ if (opts.json) {
39
+ console.log(JSON.stringify({ current, latest, update, command: command?.join(" ") ?? null }));
40
+ return 0;
41
+ }
42
+ if (!update) {
43
+ console.log(`kasaframe ${current} โ€” ์ตœ์‹ ์ด๋‹ค${latest === current ? "" : `(๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ตœ์‹  ${latest})`}`);
44
+ return 0;
45
+ }
46
+ if (!command) {
47
+ console.log(`${noticeLine(latest)}\n๋ ˆํฌ ์ฒดํฌ์•„์›ƒ์—์„œ ๋Œ๊ณ  ์žˆ๋‹ค โ€” ์„ค์น˜๋ณธ์ด ์•„๋‹ˆ๋ผ ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค(git pull)`);
48
+ return opts.check ? 0 : 2;
49
+ }
50
+ if (opts.check) {
51
+ console.log(`${noticeLine(latest)}\n์˜ฌ๋ฆฌ๋Š” ๋ช…๋ น: ${command.join(" ")}`);
52
+ return 0;
53
+ }
54
+ if (!opts.yes) {
55
+ if (!(process.stdin.isTTY && process.stdout.isTTY)) {
56
+ console.error(`${noticeLine(latest)}\nํ„ฐ๋ฏธ๋„์ด ์•„๋‹ˆ๋ผ ๋ฌป์ง€ ๋ชปํ•œ๋‹ค โ€” ๋ฌป์ง€ ์•Š๊ณ  ์˜ฌ๋ฆฌ๋ ค๋ฉด kf update --yes`);
57
+ return 2;
58
+ }
59
+ if (!(await confirm(`kasaframe ${current} โ†’ ${latest} ๋กœ ์˜ฌ๋ฆด๊นŒ(${command.join(" ")})? [Y/n] `))) return 2;
60
+ }
61
+ if (!upgrade(command, latest)) return 1;
62
+ console.log(`kasaframe ${latest} ๋กœ ์˜ฌ๋ ธ๋‹ค`);
63
+ return 0;
64
+ },
65
+ };
66
+ export default cmd;