kasaframe 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +298 -0
- package/README.md +16 -3
- package/docs/AGENTS.md +1 -1
- package/package.json +1 -1
- package/packages/adapters/src/project.ts +4 -0
- package/packages/adapters/src/texture.ts +94 -0
- package/packages/cli/src/cells.ts +227 -0
- package/packages/cli/src/commands/doctor.ts +15 -2
- package/packages/cli/src/commands/fonts.ts +118 -0
- package/packages/cli/src/commands/make.ts +77 -9
- package/packages/cli/src/commands/new.ts +2 -2
- package/packages/cli/src/commands/theme.ts +141 -10
- package/packages/cli/src/commands/update.ts +66 -0
- package/packages/cli/src/fonts.ts +405 -0
- package/packages/cli/src/kf.ts +5 -0
- package/packages/cli/src/lock.ts +8 -6
- package/packages/cli/src/pick.ts +110 -0
- package/packages/cli/src/update.ts +250 -0
- package/packages/cli/src/vary.ts +124 -0
- package/packages/core/src/index.ts +1 -1
- package/packages/core/src/project.ts +15 -0
- package/packages/director/src/assemble.ts +6 -3
- package/packages/motion/src/moves/menu.ts +2 -1
- package/packages/patterns/src/kit.ts +3 -2
- package/packages/plan/src/asks.ts +25 -16
- package/packages/plan/src/index.ts +2 -2
- package/packages/plan/src/plan.ts +4 -2
- package/packages/plan/src/themes.ts +71 -9
- package/packages/plan/src/types.ts +3 -0
- package/packages/themes/kit/THEME-MAKE.md +2 -1
- package/packages/themes/kit/fonts/display.json +3517 -0
- package/packages/themes/kit/fonts/sans.json +5155 -0
- package/packages/themes/kit/fonts/serif.json +5229 -0
- package/packages/themes/kit/fonts.json +907 -0
- package/packages/themes/package.json +8 -1
- package/packages/themes/src/color.ts +152 -0
- package/packages/themes/src/fontfile.ts +183 -0
- package/packages/themes/src/fonts.ts +296 -0
- package/packages/themes/src/fx.ts +4 -2
- package/packages/themes/src/index.ts +75 -3
- package/packages/themes/src/look.ts +249 -0
- package/packages/themes/src/name.ts +9 -0
- package/packages/themes/src/palette.ts +194 -0
- package/packages/themes/src/slotkit.ts +14 -1
- package/packages/themes/src/slots.ts +17 -2
- package/packages/themes/src/types.ts +89 -0
- package/packages/themes/src/vary.ts +291 -0
- package/presets/kawaii-pastel.json +2 -2
- package/skills/kasaframe/SKILL.md +1 -0
- package/themes/blueprint/THEME.md +64 -0
- package/themes/blueprint/effects.ts +570 -0
- package/themes/blueprint/sample/beats.json +1005 -0
- package/themes/blueprint/sample/index.html +11 -0
- package/themes/blueprint/sample/kf.json +59 -0
- package/themes/blueprint/sample/lib/facts.ts +29 -0
- package/themes/blueprint/sample/lib/sheet.ts +55 -0
- package/themes/blueprint/sample/main.ts +9 -0
- package/themes/blueprint/sample/scenes/end.ts +91 -0
- package/themes/blueprint/sample/scenes/gather.ts +75 -0
- package/themes/blueprint/sample/scenes/models.ts +117 -0
- package/themes/blueprint/sample/scenes/price.ts +94 -0
- package/themes/blueprint/sample/style.json +114 -0
- package/themes/blueprint/slots.ts +88 -0
- package/themes/blueprint/still.png +0 -0
- package/themes/blueprint/style.json +114 -0
- package/themes/blueprint/theme.json +139 -0
- package/themes/blueprint/variants/deep-rose-2.json +90 -0
- package/themes/blueprint/variants/deep-rose-suit.json +117 -0
- package/themes/blueprint/variants/deep-rose.json +117 -0
- package/themes/blueprint/variants/navy-coral-2.json +118 -0
- package/themes/blueprint/variants/navy-coral-suit.json +118 -0
- package/themes/blueprint/variants/navy-coral.json +128 -0
- package/themes/blueprint/variants/navy-orange-2.json +118 -0
- package/themes/blueprint/variants/navy-orange-3.json +91 -0
- package/themes/blueprint/variants/navy-orange-4.json +128 -0
- package/themes/blueprint/variants/navy-orange-mono.json +128 -0
- package/themes/blueprint/variants/navy-orange-plex.json +128 -0
- package/themes/blueprint/variants/navy-orange-suit.json +118 -0
- package/themes/blueprint/variants/navy-orange.json +118 -0
- package/themes/blueprint/variants/navy-rose-plex.json +127 -0
- package/themes/blueprint/variants/navy-rose.json +127 -0
- package/themes/cartography/THEME.md +61 -0
- package/themes/cartography/effects.ts +845 -0
- package/themes/cartography/sample/assets/og/logo.svg +12 -0
- package/themes/cartography/sample/beats.json +1005 -0
- package/themes/cartography/sample/index.html +11 -0
- package/themes/cartography/sample/kf.json +59 -0
- package/themes/cartography/sample/lib/facts.ts +29 -0
- package/themes/cartography/sample/lib/map.ts +39 -0
- package/themes/cartography/sample/main.ts +9 -0
- package/themes/cartography/sample/scenes/end.ts +56 -0
- package/themes/cartography/sample/scenes/gather.ts +46 -0
- package/themes/cartography/sample/scenes/models.ts +43 -0
- package/themes/cartography/sample/scenes/price.ts +73 -0
- package/themes/cartography/sample/style.json +103 -0
- package/themes/cartography/slots.ts +87 -0
- package/themes/cartography/still.png +0 -0
- package/themes/cartography/style.json +103 -0
- package/themes/cartography/theme.json +162 -0
- package/themes/cartography/variants/blush-amber.json +128 -0
- package/themes/cartography/variants/blush-indigo.json +128 -0
- package/themes/cartography/variants/blush-pink.json +136 -0
- package/themes/cartography/variants/chalk-plum.json +137 -0
- package/themes/cartography/variants/chalk-sky.json +100 -0
- package/themes/cartography/variants/cocoa-amber.json +128 -0
- package/themes/cartography/variants/deep-plum.json +125 -0
- package/themes/cartography/variants/dusk-coral.json +128 -0
- package/themes/cartography/variants/forest-lime.json +131 -0
- package/themes/cartography/variants/frost-teal.json +128 -0
- package/themes/cartography/variants/navy-green.json +131 -0
- package/themes/cartography/variants/navy-indigo.json +138 -0
- package/themes/cartography/variants/navy-sky.json +100 -0
- package/themes/cartography/variants/sage-green.json +127 -0
- package/themes/cartography/variants/wine-teal.json +128 -0
- package/themes/circuit-board/THEME.md +61 -0
- package/themes/circuit-board/effects.ts +576 -0
- package/themes/circuit-board/pcb.ts +103 -0
- package/themes/circuit-board/sample/assets/og/logo.svg +12 -0
- package/themes/circuit-board/sample/beats.json +1005 -0
- package/themes/circuit-board/sample/index.html +11 -0
- package/themes/circuit-board/sample/kf.json +59 -0
- package/themes/circuit-board/sample/lib/facts.ts +29 -0
- package/themes/circuit-board/sample/lib/kit.ts +45 -0
- package/themes/circuit-board/sample/main.ts +9 -0
- package/themes/circuit-board/sample/scenes/end.ts +102 -0
- package/themes/circuit-board/sample/scenes/gather.ts +76 -0
- package/themes/circuit-board/sample/scenes/models.ts +101 -0
- package/themes/circuit-board/sample/scenes/price.ts +109 -0
- package/themes/circuit-board/sample/style.json +104 -0
- package/themes/circuit-board/slots.ts +108 -0
- package/themes/circuit-board/still.png +0 -0
- package/themes/circuit-board/style.json +104 -0
- package/themes/circuit-board/theme.json +151 -0
- package/themes/circuit-board/variants/deep-gold-plex.json +133 -0
- package/themes/circuit-board/variants/deep-gold.json +123 -0
- package/themes/circuit-board/variants/dusk-gold-grain.json +138 -0
- package/themes/circuit-board/variants/dusk-gold-pretendard.json +118 -0
- package/themes/circuit-board/variants/dusk-gold.json +97 -0
- package/themes/circuit-board/variants/forest-gold-2.json +96 -0
- package/themes/circuit-board/variants/forest-gold-grain.json +129 -0
- package/themes/circuit-board/variants/forest-gold-mono.json +124 -0
- package/themes/circuit-board/variants/forest-gold.json +128 -0
- package/themes/circuit-board/variants/navy-gold-grain.json +124 -0
- package/themes/circuit-board/variants/navy-gold.json +101 -0
- package/themes/circuit-board/variants/wine-gold-grain.json +128 -0
- package/themes/circuit-board/variants/wine-gold-plex.json +133 -0
- package/themes/circuit-board/variants/wine-gold-pretendard.json +118 -0
- package/themes/circuit-board/variants/wine-gold.json +128 -0
- package/themes/clay-toy/THEME.md +73 -0
- package/themes/clay-toy/effects.ts +643 -0
- package/themes/clay-toy/glyphs.ts +32 -0
- package/themes/clay-toy/sample/assets/og/logo.svg +12 -0
- package/themes/clay-toy/sample/beats.json +1005 -0
- package/themes/clay-toy/sample/index.html +11 -0
- package/themes/clay-toy/sample/kf.json +59 -0
- package/themes/clay-toy/sample/lib/clay.ts +20 -0
- package/themes/clay-toy/sample/lib/facts.ts +29 -0
- package/themes/clay-toy/sample/main.ts +9 -0
- package/themes/clay-toy/sample/scenes/end.ts +81 -0
- package/themes/clay-toy/sample/scenes/gather.ts +53 -0
- package/themes/clay-toy/sample/scenes/models.ts +77 -0
- package/themes/clay-toy/sample/scenes/price.ts +91 -0
- package/themes/clay-toy/sample/style.json +101 -0
- package/themes/clay-toy/slots.ts +132 -0
- package/themes/clay-toy/still.png +0 -0
- package/themes/clay-toy/style.json +101 -0
- package/themes/clay-toy/theme.json +148 -0
- package/themes/clay-toy/variants/blush-coral-grain.json +126 -0
- package/themes/clay-toy/variants/blush-coral.json +123 -0
- package/themes/clay-toy/variants/chalk-indigo.json +97 -0
- package/themes/clay-toy/variants/chalk-jade.json +125 -0
- package/themes/clay-toy/variants/chalk-teal.json +120 -0
- package/themes/clay-toy/variants/frost-orange.json +125 -0
- package/themes/clay-toy/variants/frost-sky-grain.json +126 -0
- package/themes/clay-toy/variants/frost-sky.json +121 -0
- package/themes/clay-toy/variants/frost-teal.json +120 -0
- package/themes/clay-toy/variants/lilac-green.json +120 -0
- package/themes/clay-toy/variants/lilac-plum-round.json +120 -0
- package/themes/clay-toy/variants/lilac-plum.json +124 -0
- package/themes/clay-toy/variants/sage-indigo.json +125 -0
- package/themes/clay-toy/variants/sage-magenta.json +119 -0
- package/themes/clay-toy/variants/sand-amber.json +125 -0
- package/themes/conveyor-factory/THEME.md +71 -0
- package/themes/conveyor-factory/effects.ts +744 -0
- package/themes/conveyor-factory/sample/assets/og/logo.svg +12 -0
- package/themes/conveyor-factory/sample/beats.json +1005 -0
- package/themes/conveyor-factory/sample/index.html +11 -0
- package/themes/conveyor-factory/sample/kf.json +59 -0
- package/themes/conveyor-factory/sample/lib/factory.ts +48 -0
- package/themes/conveyor-factory/sample/lib/facts.ts +29 -0
- package/themes/conveyor-factory/sample/main.ts +9 -0
- package/themes/conveyor-factory/sample/scenes/end.ts +44 -0
- package/themes/conveyor-factory/sample/scenes/gather.ts +47 -0
- package/themes/conveyor-factory/sample/scenes/models.ts +91 -0
- package/themes/conveyor-factory/sample/scenes/price.ts +77 -0
- package/themes/conveyor-factory/sample/style.json +107 -0
- package/themes/conveyor-factory/slots.ts +121 -0
- package/themes/conveyor-factory/still.png +0 -0
- package/themes/conveyor-factory/style.json +107 -0
- package/themes/conveyor-factory/theme.json +144 -0
- package/themes/conveyor-factory/variants/cocoa-gold.json +125 -0
- package/themes/conveyor-factory/variants/dusk-gold.json +135 -0
- package/themes/conveyor-factory/variants/forest-gold.json +135 -0
- package/themes/conveyor-factory/variants/navy-amber.json +93 -0
- package/themes/conveyor-factory/variants/navy-gold.json +120 -0
- package/themes/conveyor-factory/variants/slate-amber-2.json +120 -0
- package/themes/conveyor-factory/variants/slate-amber-grain.json +125 -0
- package/themes/conveyor-factory/variants/slate-amber-suit.json +120 -0
- package/themes/conveyor-factory/variants/slate-amber.json +125 -0
- package/themes/conveyor-factory/variants/slate-gold-bold.json +120 -0
- package/themes/conveyor-factory/variants/slate-gold.json +120 -0
- package/themes/data-journal/THEME.md +74 -0
- package/themes/data-journal/effects.ts +570 -0
- package/themes/data-journal/sample/assets/og/logo.svg +12 -0
- package/themes/data-journal/sample/beats.json +1005 -0
- package/themes/data-journal/sample/index.html +11 -0
- package/themes/data-journal/sample/kf.json +59 -0
- package/themes/data-journal/sample/lib/facts.ts +29 -0
- package/themes/data-journal/sample/lib/page.ts +25 -0
- package/themes/data-journal/sample/main.ts +9 -0
- package/themes/data-journal/sample/scenes/end.ts +69 -0
- package/themes/data-journal/sample/scenes/gather.ts +58 -0
- package/themes/data-journal/sample/scenes/models.ts +51 -0
- package/themes/data-journal/sample/scenes/price.ts +73 -0
- package/themes/data-journal/sample/style.json +114 -0
- package/themes/data-journal/slots.ts +119 -0
- package/themes/data-journal/still.png +0 -0
- package/themes/data-journal/style.json +114 -0
- package/themes/data-journal/theme.json +142 -0
- package/themes/data-journal/variants/blush-green.json +129 -0
- package/themes/data-journal/variants/chalk-magenta.json +138 -0
- package/themes/data-journal/variants/cocoa-amber.json +140 -0
- package/themes/data-journal/variants/cocoa-sky.json +130 -0
- package/themes/data-journal/variants/deep-coral.json +135 -0
- package/themes/data-journal/variants/dusk-plum.json +132 -0
- package/themes/data-journal/variants/forest-green.json +138 -0
- package/themes/data-journal/variants/frost-indigo.json +141 -0
- package/themes/data-journal/variants/lilac-amber.json +141 -0
- package/themes/data-journal/variants/navy-indigo.json +131 -0
- package/themes/data-journal/variants/navy-lime.json +128 -0
- package/themes/data-journal/variants/snow-sky.json +125 -0
- package/themes/data-journal/variants/wine-indigo.json +129 -0
- package/themes/data-journal/variants/wine-magenta.json +138 -0
- package/themes/data-journal/variants/wine-teal.json +97 -0
- package/themes/debug-hud/sample/style.json +1 -1
- package/themes/debug-hud/style.json +1 -1
- package/themes/debug-hud/theme.json +57 -0
- package/themes/debug-hud/variants/cocoa-gold.json +113 -0
- package/themes/debug-hud/variants/cocoa-teal.json +119 -0
- package/themes/debug-hud/variants/deep-cyan.json +85 -0
- package/themes/debug-hud/variants/deep-teal-scan.json +118 -0
- package/themes/debug-hud/variants/deep-teal.json +112 -0
- package/themes/debug-hud/variants/dusk-plum.json +116 -0
- package/themes/debug-hud/variants/dusk-violet.json +112 -0
- package/themes/debug-hud/variants/forest-amber.json +86 -0
- package/themes/debug-hud/variants/forest-lime.json +117 -0
- package/themes/debug-hud/variants/forest-magenta.json +111 -0
- package/themes/debug-hud/variants/navy-jade.json +79 -0
- package/themes/debug-hud/variants/slate-green.json +112 -0
- package/themes/debug-hud/variants/wine-gold.json +119 -0
- package/themes/debug-hud/variants/wine-jade.json +83 -0
- package/themes/debug-hud/variants/wine-sky.json +112 -0
- package/themes/depth-flight/THEME.md +54 -0
- package/themes/depth-flight/effects.ts +396 -0
- package/themes/depth-flight/sample/assets/og/logo.svg +12 -0
- package/themes/depth-flight/sample/beats.json +1005 -0
- package/themes/depth-flight/sample/index.html +11 -0
- package/themes/depth-flight/sample/kf.json +59 -0
- package/themes/depth-flight/sample/lib/facts.ts +29 -0
- package/themes/depth-flight/sample/lib/fly.ts +15 -0
- package/themes/depth-flight/sample/main.ts +9 -0
- package/themes/depth-flight/sample/scenes/end.ts +51 -0
- package/themes/depth-flight/sample/scenes/gather.ts +40 -0
- package/themes/depth-flight/sample/scenes/models.ts +75 -0
- package/themes/depth-flight/sample/scenes/price.ts +50 -0
- package/themes/depth-flight/sample/style.json +94 -0
- package/themes/depth-flight/slots.ts +196 -0
- package/themes/depth-flight/still.png +0 -0
- package/themes/depth-flight/style.json +94 -0
- package/themes/depth-flight/theme.json +121 -0
- package/themes/depth-flight/variants/cocoa-cyan.json +121 -0
- package/themes/depth-flight/variants/cocoa-indigo.json +121 -0
- package/themes/depth-flight/variants/cocoa-violet.json +122 -0
- package/themes/depth-flight/variants/deep-sky.json +126 -0
- package/themes/depth-flight/variants/dusk-violet-bold.json +117 -0
- package/themes/depth-flight/variants/dusk-violet.json +132 -0
- package/themes/depth-flight/variants/forest-blue.json +116 -0
- package/themes/depth-flight/variants/forest-cyan.json +122 -0
- package/themes/depth-flight/variants/navy-sky-grain.json +122 -0
- package/themes/depth-flight/variants/navy-sky.json +116 -0
- package/themes/depth-flight/variants/night-teal.json +116 -0
- package/themes/depth-flight/variants/slate-cyan.json +127 -0
- package/themes/depth-flight/variants/slate-indigo.json +116 -0
- package/themes/depth-flight/variants/slate-sky.json +94 -0
- package/themes/depth-flight/variants/slate-violet.json +121 -0
- package/themes/film-reel/THEME.md +60 -0
- package/themes/film-reel/effects.ts +477 -0
- package/themes/film-reel/sample/assets/og/logo.svg +12 -0
- package/themes/film-reel/sample/beats.json +1005 -0
- package/themes/film-reel/sample/index.html +11 -0
- package/themes/film-reel/sample/kf.json +59 -0
- package/themes/film-reel/sample/lib/facts.ts +29 -0
- package/themes/film-reel/sample/lib/reel.ts +9 -0
- package/themes/film-reel/sample/main.ts +9 -0
- package/themes/film-reel/sample/scenes/end.ts +31 -0
- package/themes/film-reel/sample/scenes/gather.ts +40 -0
- package/themes/film-reel/sample/scenes/models.ts +37 -0
- package/themes/film-reel/sample/scenes/price.ts +63 -0
- package/themes/film-reel/sample/style.json +103 -0
- package/themes/film-reel/slots.ts +75 -0
- package/themes/film-reel/still.png +0 -0
- package/themes/film-reel/style.json +103 -0
- package/themes/film-reel/theme.json +144 -0
- package/themes/film-reel/variants/chalk-amber.json +124 -0
- package/themes/film-reel/variants/chalk-pink.json +123 -0
- package/themes/film-reel/variants/cream-violet.json +121 -0
- package/themes/film-reel/variants/frost-blue.json +126 -0
- package/themes/film-reel/variants/frost-green.json +123 -0
- package/themes/film-reel/variants/frost-indigo.json +121 -0
- package/themes/film-reel/variants/frost-jade.json +120 -0
- package/themes/film-reel/variants/frost-plum.json +120 -0
- package/themes/film-reel/variants/frost-teal.json +93 -0
- package/themes/film-reel/variants/lilac-gold.json +120 -0
- package/themes/film-reel/variants/lilac-jade.json +123 -0
- package/themes/film-reel/variants/lilac-magenta.json +125 -0
- package/themes/film-reel/variants/lilac-violet.json +122 -0
- package/themes/film-reel/variants/sand-cyan.json +124 -0
- package/themes/film-reel/variants/sand-lime.json +96 -0
- package/themes/glass-light/THEME.md +55 -0
- package/themes/glass-light/effects.ts +433 -0
- package/themes/glass-light/sample/assets/og/logo.svg +12 -0
- package/themes/glass-light/sample/beats.json +1005 -0
- package/themes/glass-light/sample/index.html +11 -0
- package/themes/glass-light/sample/kf.json +59 -0
- package/themes/glass-light/sample/lib/facts.ts +29 -0
- package/themes/glass-light/sample/lib/glass.ts +81 -0
- package/themes/glass-light/sample/main.ts +9 -0
- package/themes/glass-light/sample/scenes/end.ts +43 -0
- package/themes/glass-light/sample/scenes/gather.ts +38 -0
- package/themes/glass-light/sample/scenes/models.ts +46 -0
- package/themes/glass-light/sample/scenes/price.ts +46 -0
- package/themes/glass-light/sample/style.json +94 -0
- package/themes/glass-light/slots.ts +198 -0
- package/themes/glass-light/still.png +0 -0
- package/themes/glass-light/style.json +94 -0
- package/themes/glass-light/theme.json +121 -0
- package/themes/glass-light/variants/blush-coral.json +116 -0
- package/themes/glass-light/variants/blush-orange.json +115 -0
- package/themes/glass-light/variants/blush-pink.json +119 -0
- package/themes/glass-light/variants/blush-plum.json +115 -0
- package/themes/glass-light/variants/blush-teal.json +115 -0
- package/themes/glass-light/variants/cream-blue.json +125 -0
- package/themes/glass-light/variants/cream-pink.json +87 -0
- package/themes/glass-light/variants/cream-violet.json +119 -0
- package/themes/glass-light/variants/frost-green.json +115 -0
- package/themes/glass-light/variants/frost-sky-suit.json +115 -0
- package/themes/glass-light/variants/frost-sky.json +121 -0
- package/themes/glass-light/variants/lilac-gold.json +126 -0
- package/themes/glass-light/variants/lilac-plum.json +114 -0
- package/themes/glass-light/variants/sage-green.json +119 -0
- package/themes/glass-light/variants/sage-rose.json +114 -0
- package/themes/ink-wash/THEME.md +59 -0
- package/themes/ink-wash/effects.ts +574 -0
- package/themes/ink-wash/sample/assets/og/logo.svg +12 -0
- package/themes/ink-wash/sample/beats.json +1005 -0
- package/themes/ink-wash/sample/index.html +11 -0
- package/themes/ink-wash/sample/kf.json +59 -0
- package/themes/ink-wash/sample/lib/facts.ts +29 -0
- package/themes/ink-wash/sample/lib/ink.ts +35 -0
- package/themes/ink-wash/sample/main.ts +9 -0
- package/themes/ink-wash/sample/scenes/end.ts +72 -0
- package/themes/ink-wash/sample/scenes/gather.ts +53 -0
- package/themes/ink-wash/sample/scenes/models.ts +58 -0
- package/themes/ink-wash/sample/scenes/price.ts +85 -0
- package/themes/ink-wash/sample/style.json +108 -0
- package/themes/ink-wash/slots.ts +150 -0
- package/themes/ink-wash/still.png +0 -0
- package/themes/ink-wash/style.json +108 -0
- package/themes/ink-wash/theme.json +133 -0
- package/themes/ink-wash/variants/blush-orange.json +125 -0
- package/themes/ink-wash/variants/blush-rose.json +124 -0
- package/themes/ink-wash/variants/chalk-coral-grain.json +125 -0
- package/themes/ink-wash/variants/chalk-coral-serif.json +118 -0
- package/themes/ink-wash/variants/chalk-coral.json +120 -0
- package/themes/ink-wash/variants/chalk-orange-grain.json +97 -0
- package/themes/ink-wash/variants/chalk-orange-serif.json +120 -0
- package/themes/ink-wash/variants/chalk-orange.json +120 -0
- package/themes/ink-wash/variants/frost-coral.json +119 -0
- package/themes/ink-wash/variants/frost-rose.json +91 -0
- package/themes/ink-wash/variants/lilac-coral.json +96 -0
- package/themes/ink-wash/variants/sage-coral.json +119 -0
- package/themes/ink-wash/variants/sage-rose.json +119 -0
- package/themes/ink-wash/variants/sand-coral.json +125 -0
- package/themes/ink-wash/variants/sand-orange.json +120 -0
- package/themes/iso-diorama/THEME.md +54 -0
- package/themes/iso-diorama/effects.ts +374 -0
- package/themes/iso-diorama/sample/assets/og/logo.svg +12 -0
- package/themes/iso-diorama/sample/beats.json +1005 -0
- package/themes/iso-diorama/sample/index.html +11 -0
- package/themes/iso-diorama/sample/kf.json +59 -0
- package/themes/iso-diorama/sample/lib/facts.ts +29 -0
- package/themes/iso-diorama/sample/lib/iso.ts +22 -0
- package/themes/iso-diorama/sample/main.ts +9 -0
- package/themes/iso-diorama/sample/scenes/end.ts +41 -0
- package/themes/iso-diorama/sample/scenes/gather.ts +39 -0
- package/themes/iso-diorama/sample/scenes/models.ts +55 -0
- package/themes/iso-diorama/sample/scenes/price.ts +49 -0
- package/themes/iso-diorama/sample/style.json +98 -0
- package/themes/iso-diorama/slots.ts +222 -0
- package/themes/iso-diorama/still.png +0 -0
- package/themes/iso-diorama/style.json +98 -0
- package/themes/iso-diorama/theme.json +133 -0
- package/themes/iso-diorama/variants/chalk-plum.json +121 -0
- package/themes/iso-diorama/variants/cocoa-gold.json +93 -0
- package/themes/iso-diorama/variants/cocoa-indigo.json +122 -0
- package/themes/iso-diorama/variants/cocoa-rose.json +125 -0
- package/themes/iso-diorama/variants/deep-green.json +125 -0
- package/themes/iso-diorama/variants/deep-plum.json +120 -0
- package/themes/iso-diorama/variants/dusk-blue.json +126 -0
- package/themes/iso-diorama/variants/dusk-coral.json +122 -0
- package/themes/iso-diorama/variants/dusk-jade.json +121 -0
- package/themes/iso-diorama/variants/forest-cyan.json +127 -0
- package/themes/iso-diorama/variants/frost-blue.json +126 -0
- package/themes/iso-diorama/variants/frost-green.json +126 -0
- package/themes/iso-diorama/variants/frost-orange.json +125 -0
- package/themes/iso-diorama/variants/frost-teal.json +123 -0
- package/themes/iso-diorama/variants/navy-amber.json +125 -0
- package/themes/isotype/THEME.md +58 -0
- package/themes/isotype/effects.ts +297 -0
- package/themes/isotype/kit.ts +102 -0
- package/themes/isotype/sample/assets/og/logo.svg +12 -0
- package/themes/isotype/sample/beats.json +1005 -0
- package/themes/isotype/sample/index.html +11 -0
- package/themes/isotype/sample/kf.json +59 -0
- package/themes/isotype/sample/lib/chart.ts +47 -0
- package/themes/isotype/sample/lib/facts.ts +29 -0
- package/themes/isotype/sample/main.ts +9 -0
- package/themes/isotype/sample/scenes/end.ts +80 -0
- package/themes/isotype/sample/scenes/gather.ts +65 -0
- package/themes/isotype/sample/scenes/models.ts +75 -0
- package/themes/isotype/sample/scenes/price.ts +90 -0
- package/themes/isotype/sample/style.json +101 -0
- package/themes/isotype/slots.ts +74 -0
- package/themes/isotype/still.png +0 -0
- package/themes/isotype/style.json +101 -0
- package/themes/isotype/theme.json +141 -0
- package/themes/isotype/variants/blush-rose.json +127 -0
- package/themes/isotype/variants/chalk-amber.json +123 -0
- package/themes/isotype/variants/chalk-coral-bold.json +118 -0
- package/themes/isotype/variants/chalk-coral.json +117 -0
- package/themes/isotype/variants/chalk-orange.json +128 -0
- package/themes/isotype/variants/frost-amber-plex.json +128 -0
- package/themes/isotype/variants/frost-amber.json +123 -0
- package/themes/isotype/variants/frost-coral-grain.json +95 -0
- package/themes/isotype/variants/frost-coral.json +123 -0
- package/themes/isotype/variants/frost-orange.json +123 -0
- package/themes/isotype/variants/lilac-rose.json +127 -0
- package/themes/isotype/variants/sage-coral.json +90 -0
- package/themes/isotype/variants/sand-amber.json +118 -0
- package/themes/isotype/variants/sand-orange.json +128 -0
- package/themes/kawaii-sticker/THEME.md +44 -0
- package/themes/kawaii-sticker/effects.ts +412 -0
- package/themes/kawaii-sticker/sample/assets/stickers/bolt.png +0 -0
- package/themes/kawaii-sticker/sample/assets/stickers/bubble.png +0 -0
- package/themes/kawaii-sticker/sample/assets/stickers/clock.png +0 -0
- package/themes/kawaii-sticker/sample/assets/stickers/coin.png +0 -0
- package/themes/kawaii-sticker/sample/assets/stickers/moon.png +0 -0
- package/themes/kawaii-sticker/sample/assets/stickers/og-logo.svg +12 -0
- package/themes/kawaii-sticker/sample/assets/stickers/server.png +0 -0
- package/themes/kawaii-sticker/sample/beats.json +1 -0
- package/themes/kawaii-sticker/sample/index.html +11 -0
- package/themes/kawaii-sticker/sample/kf.json +66 -0
- package/themes/kawaii-sticker/sample/lib/og.ts +54 -0
- package/themes/kawaii-sticker/sample/main.ts +9 -0
- package/themes/kawaii-sticker/sample/scenes/end.ts +37 -0
- package/themes/kawaii-sticker/sample/scenes/gather.ts +50 -0
- package/themes/kawaii-sticker/sample/scenes/models.ts +43 -0
- package/themes/kawaii-sticker/sample/scenes/one.ts +45 -0
- package/themes/kawaii-sticker/sample/scenes/price.ts +47 -0
- package/themes/kawaii-sticker/sample/style.json +99 -0
- package/themes/kawaii-sticker/slots.ts +95 -0
- package/themes/kawaii-sticker/still.png +0 -0
- package/themes/kawaii-sticker/style.json +99 -0
- package/themes/kawaii-sticker/theme.json +147 -0
- package/themes/kawaii-sticker/variants/blush-coral.json +134 -0
- package/themes/kawaii-sticker/variants/blush-teal.json +129 -0
- package/themes/kawaii-sticker/variants/blush-violet.json +134 -0
- package/themes/kawaii-sticker/variants/cream-gold.json +135 -0
- package/themes/kawaii-sticker/variants/deep-jade.json +134 -0
- package/themes/kawaii-sticker/variants/dusk-green.json +134 -0
- package/themes/kawaii-sticker/variants/dusk-indigo.json +130 -0
- package/themes/kawaii-sticker/variants/dusk-sky.json +135 -0
- package/themes/kawaii-sticker/variants/dusk-teal.json +134 -0
- package/themes/kawaii-sticker/variants/frost-blue.json +135 -0
- package/themes/kawaii-sticker/variants/frost-plum.json +133 -0
- package/themes/kawaii-sticker/variants/lilac-violet.json +133 -0
- package/themes/kawaii-sticker/variants/navy-gold.json +134 -0
- package/themes/kawaii-sticker/variants/navy-plum.json +134 -0
- package/themes/kawaii-sticker/variants/sage-sky.json +107 -0
- package/themes/lab-notebook/THEME.md +56 -0
- package/themes/lab-notebook/effects.ts +423 -0
- package/themes/lab-notebook/kit.ts +84 -0
- package/themes/lab-notebook/sample/assets/og/logo.svg +12 -0
- package/themes/lab-notebook/sample/beats.json +1005 -0
- package/themes/lab-notebook/sample/index.html +11 -0
- package/themes/lab-notebook/sample/kf.json +59 -0
- package/themes/lab-notebook/sample/lib/facts.ts +29 -0
- package/themes/lab-notebook/sample/lib/note.ts +53 -0
- package/themes/lab-notebook/sample/main.ts +9 -0
- package/themes/lab-notebook/sample/scenes/end.ts +127 -0
- package/themes/lab-notebook/sample/scenes/gather.ts +83 -0
- package/themes/lab-notebook/sample/scenes/models.ts +73 -0
- package/themes/lab-notebook/sample/scenes/price.ts +89 -0
- package/themes/lab-notebook/sample/style.json +109 -0
- package/themes/lab-notebook/slots.ts +75 -0
- package/themes/lab-notebook/still.png +0 -0
- package/themes/lab-notebook/style.json +109 -0
- package/themes/lab-notebook/theme.json +142 -0
- package/themes/lab-notebook/variants/blush-orange.json +125 -0
- package/themes/lab-notebook/variants/chalk-coral.json +126 -0
- package/themes/lab-notebook/variants/chalk-orange.json +129 -0
- package/themes/lab-notebook/variants/chalk-rose-2.json +96 -0
- package/themes/lab-notebook/variants/chalk-rose.json +125 -0
- package/themes/lab-notebook/variants/frost-orange.json +101 -0
- package/themes/lab-notebook/variants/frost-rose-grain.json +101 -0
- package/themes/lab-notebook/variants/frost-rose.json +96 -0
- package/themes/lab-notebook/variants/lilac-rose-2.json +96 -0
- package/themes/lab-notebook/variants/lilac-rose.json +128 -0
- package/themes/lab-notebook/variants/sage-rose.json +124 -0
- package/themes/lab-notebook/variants/sand-orange-grain.json +101 -0
- package/themes/lab-notebook/variants/sand-orange.json +129 -0
- package/themes/lab-notebook/variants/snow-orange.json +129 -0
- package/themes/lab-notebook/variants/snow-rose.json +124 -0
- package/themes/manga-panel/THEME.md +60 -0
- package/themes/manga-panel/effects.ts +791 -0
- package/themes/manga-panel/sample/assets/og/logo.svg +12 -0
- package/themes/manga-panel/sample/beats.json +1005 -0
- package/themes/manga-panel/sample/index.html +11 -0
- package/themes/manga-panel/sample/kf.json +59 -0
- package/themes/manga-panel/sample/lib/facts.ts +29 -0
- package/themes/manga-panel/sample/lib/manga.ts +58 -0
- package/themes/manga-panel/sample/main.ts +9 -0
- package/themes/manga-panel/sample/scenes/end.ts +96 -0
- package/themes/manga-panel/sample/scenes/gather.ts +100 -0
- package/themes/manga-panel/sample/scenes/models.ts +96 -0
- package/themes/manga-panel/sample/scenes/price.ts +154 -0
- package/themes/manga-panel/sample/style.json +105 -0
- package/themes/manga-panel/slots.ts +127 -0
- package/themes/manga-panel/still.png +0 -0
- package/themes/manga-panel/style.json +105 -0
- package/themes/manga-panel/theme.json +150 -0
- package/themes/manga-panel/variants/chalk-cyan-dots.json +122 -0
- package/themes/manga-panel/variants/chalk-cyan.json +122 -0
- package/themes/manga-panel/variants/chalk-magenta.json +121 -0
- package/themes/manga-panel/variants/chalk-orange-bold.json +117 -0
- package/themes/manga-panel/variants/chalk-orange.json +122 -0
- package/themes/manga-panel/variants/chalk-rose-dots.json +121 -0
- package/themes/manga-panel/variants/chalk-rose.json +116 -0
- package/themes/manga-panel/variants/chalk-sky-bold.json +117 -0
- package/themes/manga-panel/variants/chalk-sky.json +117 -0
- package/themes/manga-panel/variants/snow-cyan.json +117 -0
- package/themes/manga-panel/variants/snow-magenta-dots.json +121 -0
- package/themes/manga-panel/variants/snow-magenta.json +116 -0
- package/themes/manga-panel/variants/snow-rose-dots.json +121 -0
- package/themes/manga-panel/variants/snow-rose.json +116 -0
- package/themes/manga-panel/variants/snow-sky.json +95 -0
- package/themes/marble-run/THEME.md +73 -0
- package/themes/marble-run/effects.ts +678 -0
- package/themes/marble-run/sample/assets/og/logo.svg +12 -0
- package/themes/marble-run/sample/beats.json +1005 -0
- package/themes/marble-run/sample/index.html +11 -0
- package/themes/marble-run/sample/kf.json +59 -0
- package/themes/marble-run/sample/lib/facts.ts +29 -0
- package/themes/marble-run/sample/lib/run.ts +30 -0
- package/themes/marble-run/sample/main.ts +9 -0
- package/themes/marble-run/sample/scenes/end.ts +58 -0
- package/themes/marble-run/sample/scenes/gather.ts +51 -0
- package/themes/marble-run/sample/scenes/models.ts +73 -0
- package/themes/marble-run/sample/scenes/price.ts +51 -0
- package/themes/marble-run/sample/style.json +109 -0
- package/themes/marble-run/slots.ts +116 -0
- package/themes/marble-run/still.png +0 -0
- package/themes/marble-run/style.json +109 -0
- package/themes/marble-run/theme.json +147 -0
- package/themes/marble-run/variants/cocoa-amber.json +128 -0
- package/themes/marble-run/variants/cocoa-orange.json +142 -0
- package/themes/marble-run/variants/dusk-orange.json +133 -0
- package/themes/marble-run/variants/forest-gold.json +128 -0
- package/themes/marble-run/variants/navy-gold.json +137 -0
- package/themes/marble-run/variants/night-gold-grain.json +105 -0
- package/themes/marble-run/variants/night-gold.json +137 -0
- package/themes/marble-run/variants/slate-gold-grain.json +133 -0
- package/themes/marble-run/variants/slate-gold.json +133 -0
- package/themes/marble-run/variants/slate-orange-grain.json +105 -0
- package/themes/marble-run/variants/slate-orange-mono.json +128 -0
- package/themes/marble-run/variants/slate-orange-serif.json +128 -0
- package/themes/marble-run/variants/slate-orange.json +131 -0
- package/themes/neon-sign/THEME.md +59 -0
- package/themes/neon-sign/effects.ts +424 -0
- package/themes/neon-sign/sample/assets/og/logo.svg +12 -0
- package/themes/neon-sign/sample/beats.json +1005 -0
- package/themes/neon-sign/sample/index.html +11 -0
- package/themes/neon-sign/sample/kf.json +59 -0
- package/themes/neon-sign/sample/lib/facts.ts +29 -0
- package/themes/neon-sign/sample/lib/neon.ts +50 -0
- package/themes/neon-sign/sample/main.ts +9 -0
- package/themes/neon-sign/sample/scenes/end.ts +62 -0
- package/themes/neon-sign/sample/scenes/gather.ts +59 -0
- package/themes/neon-sign/sample/scenes/models.ts +63 -0
- package/themes/neon-sign/sample/scenes/price.ts +74 -0
- package/themes/neon-sign/sample/style.json +104 -0
- package/themes/neon-sign/slots.ts +110 -0
- package/themes/neon-sign/still.png +0 -0
- package/themes/neon-sign/style.json +104 -0
- package/themes/neon-sign/theme.json +137 -0
- package/themes/neon-sign/variants/deep-plum.json +121 -0
- package/themes/neon-sign/variants/deep-violet.json +121 -0
- package/themes/neon-sign/variants/dusk-plum.json +121 -0
- package/themes/neon-sign/variants/forest-magenta.json +126 -0
- package/themes/neon-sign/variants/forest-plum.json +98 -0
- package/themes/neon-sign/variants/night-coral.json +122 -0
- package/themes/neon-sign/variants/night-magenta-round.json +121 -0
- package/themes/neon-sign/variants/night-magenta.json +126 -0
- package/themes/neon-sign/variants/night-pink.json +125 -0
- package/themes/neon-sign/variants/night-violet.json +126 -0
- package/themes/neon-sign/variants/slate-plum.json +120 -0
- package/themes/neon-sign/variants/slate-violet.json +127 -0
- package/themes/neon-sign/variants/wine-coral.json +127 -0
- package/themes/neon-sign/variants/wine-pink.json +125 -0
- package/themes/neon-sign/variants/wine-plum.json +126 -0
- package/themes/newspaper/THEME.md +67 -0
- package/themes/newspaper/effects.ts +347 -0
- package/themes/newspaper/sample/assets/og/logo.svg +12 -0
- package/themes/newspaper/sample/beats.json +1005 -0
- package/themes/newspaper/sample/index.html +11 -0
- package/themes/newspaper/sample/kf.json +59 -0
- package/themes/newspaper/sample/lib/facts.ts +29 -0
- package/themes/newspaper/sample/lib/page.ts +27 -0
- package/themes/newspaper/sample/main.ts +9 -0
- package/themes/newspaper/sample/scenes/end.ts +62 -0
- package/themes/newspaper/sample/scenes/gather.ts +66 -0
- package/themes/newspaper/sample/scenes/models.ts +61 -0
- package/themes/newspaper/sample/scenes/price.ts +103 -0
- package/themes/newspaper/sample/style.json +100 -0
- package/themes/newspaper/slots.ts +73 -0
- package/themes/newspaper/still.png +0 -0
- package/themes/newspaper/style.json +100 -0
- package/themes/newspaper/theme.json +141 -0
- package/themes/newspaper/variants/blush-amber.json +123 -0
- package/themes/newspaper/variants/blush-orange.json +125 -0
- package/themes/newspaper/variants/chalk-cyan.json +119 -0
- package/themes/newspaper/variants/chalk-indigo.json +124 -0
- package/themes/newspaper/variants/chalk-jade.json +95 -0
- package/themes/newspaper/variants/chalk-magenta.json +127 -0
- package/themes/newspaper/variants/frost-plum.json +123 -0
- package/themes/newspaper/variants/frost-sky.json +129 -0
- package/themes/newspaper/variants/frost-violet.json +96 -0
- package/themes/newspaper/variants/lilac-coral.json +124 -0
- package/themes/newspaper/variants/lilac-green.json +127 -0
- package/themes/newspaper/variants/sage-blue.json +124 -0
- package/themes/newspaper/variants/sage-lime.json +117 -0
- package/themes/newspaper/variants/sand-gold.json +124 -0
- package/themes/newspaper/variants/sand-jade.json +123 -0
- package/themes/og-measure/THEME.md +32 -6
- package/themes/og-measure/effects.ts +219 -115
- package/themes/og-measure/sample/assets/brand/og-logo.svg +11 -11
- package/themes/og-measure/sample/lib/data.ts +6 -0
- package/themes/og-measure/sample/scenes/end.ts +14 -10
- package/themes/og-measure/sample/scenes/gather.ts +12 -1
- package/themes/og-measure/sample/scenes/models.ts +13 -2
- package/themes/og-measure/sample/scenes/one.ts +8 -6
- package/themes/og-measure/sample/scenes/price.ts +20 -3
- package/themes/og-measure/sample/style.json +30 -35
- package/themes/og-measure/slots.ts +25 -30
- package/themes/og-measure/still.png +0 -0
- package/themes/og-measure/style.json +30 -35
- package/themes/og-measure/theme.json +91 -14
- package/themes/og-measure/variants/snow-blue.json +90 -0
- package/themes/orbit-system/THEME.md +77 -0
- package/themes/orbit-system/effects.ts +665 -0
- package/themes/orbit-system/sample/assets/og/logo.svg +12 -0
- package/themes/orbit-system/sample/beats.json +1005 -0
- package/themes/orbit-system/sample/index.html +11 -0
- package/themes/orbit-system/sample/kf.json +59 -0
- package/themes/orbit-system/sample/lib/facts.ts +29 -0
- package/themes/orbit-system/sample/lib/orbit.ts +84 -0
- package/themes/orbit-system/sample/main.ts +9 -0
- package/themes/orbit-system/sample/scenes/end.ts +50 -0
- package/themes/orbit-system/sample/scenes/gather.ts +35 -0
- package/themes/orbit-system/sample/scenes/models.ts +49 -0
- package/themes/orbit-system/sample/scenes/price.ts +57 -0
- package/themes/orbit-system/sample/style.json +108 -0
- package/themes/orbit-system/slots.ts +174 -0
- package/themes/orbit-system/still.png +0 -0
- package/themes/orbit-system/style.json +108 -0
- package/themes/orbit-system/theme.json +144 -0
- package/themes/orbit-system/variants/cocoa-amber.json +122 -0
- package/themes/orbit-system/variants/cocoa-coral.json +127 -0
- package/themes/orbit-system/variants/cocoa-cyan.json +127 -0
- package/themes/orbit-system/variants/deep-cyan.json +132 -0
- package/themes/orbit-system/variants/deep-sky.json +122 -0
- package/themes/orbit-system/variants/dusk-magenta.json +121 -0
- package/themes/orbit-system/variants/dusk-plum-pretendard.json +121 -0
- package/themes/orbit-system/variants/dusk-plum.json +127 -0
- package/themes/orbit-system/variants/dusk-violet.json +122 -0
- package/themes/orbit-system/variants/forest-coral.json +126 -0
- package/themes/orbit-system/variants/forest-plum.json +94 -0
- package/themes/orbit-system/variants/navy-amber.json +127 -0
- package/themes/orbit-system/variants/wine-amber.json +127 -0
- package/themes/orbit-system/variants/wine-coral.json +123 -0
- package/themes/orbit-system/variants/wine-magenta.json +126 -0
- package/themes/origami-fold/THEME.md +74 -0
- package/themes/origami-fold/effects.ts +535 -0
- package/themes/origami-fold/sample/assets/og/logo.svg +12 -0
- package/themes/origami-fold/sample/beats.json +1005 -0
- package/themes/origami-fold/sample/index.html +11 -0
- package/themes/origami-fold/sample/kf.json +59 -0
- package/themes/origami-fold/sample/lib/facts.ts +29 -0
- package/themes/origami-fold/sample/lib/fold.ts +53 -0
- package/themes/origami-fold/sample/main.ts +9 -0
- package/themes/origami-fold/sample/scenes/end.ts +51 -0
- package/themes/origami-fold/sample/scenes/gather.ts +37 -0
- package/themes/origami-fold/sample/scenes/models.ts +46 -0
- package/themes/origami-fold/sample/scenes/price.ts +54 -0
- package/themes/origami-fold/sample/style.json +108 -0
- package/themes/origami-fold/slots.ts +176 -0
- package/themes/origami-fold/still.png +0 -0
- package/themes/origami-fold/style.json +108 -0
- package/themes/origami-fold/theme.json +125 -0
- package/themes/origami-fold/variants/deep-amber-sunflower.json +122 -0
- package/themes/origami-fold/variants/deep-amber.json +122 -0
- package/themes/origami-fold/variants/deep-orange.json +128 -0
- package/themes/origami-fold/variants/forest-coral.json +127 -0
- package/themes/origami-fold/variants/forest-pink.json +123 -0
- package/themes/origami-fold/variants/forest-rose.json +100 -0
- package/themes/origami-fold/variants/frost-amber-grain.json +127 -0
- package/themes/origami-fold/variants/frost-amber-paper.json +128 -0
- package/themes/origami-fold/variants/frost-amber.json +95 -0
- package/themes/origami-fold/variants/sage-coral-grain.json +100 -0
- package/themes/origami-fold/variants/sage-coral-sunflower.json +122 -0
- package/themes/origami-fold/variants/sage-coral.json +128 -0
- package/themes/origami-fold/variants/sage-pink-grain.json +127 -0
- package/themes/origami-fold/variants/sage-pink-round.json +124 -0
- package/themes/origami-fold/variants/sage-pink.json +127 -0
- package/themes/paper-cutout/THEME.md +63 -0
- package/themes/paper-cutout/effects.ts +609 -0
- package/themes/paper-cutout/sample/assets/og/logo.svg +12 -0
- package/themes/paper-cutout/sample/beats.json +1005 -0
- package/themes/paper-cutout/sample/index.html +11 -0
- package/themes/paper-cutout/sample/kf.json +59 -0
- package/themes/paper-cutout/sample/lib/facts.ts +29 -0
- package/themes/paper-cutout/sample/lib/paper.ts +91 -0
- package/themes/paper-cutout/sample/main.ts +9 -0
- package/themes/paper-cutout/sample/scenes/end.ts +54 -0
- package/themes/paper-cutout/sample/scenes/gather.ts +63 -0
- package/themes/paper-cutout/sample/scenes/models.ts +93 -0
- package/themes/paper-cutout/sample/scenes/price.ts +100 -0
- package/themes/paper-cutout/sample/style.json +104 -0
- package/themes/paper-cutout/slots.ts +87 -0
- package/themes/paper-cutout/still.png +0 -0
- package/themes/paper-cutout/style.json +104 -0
- package/themes/paper-cutout/theme.json +146 -0
- package/themes/paper-cutout/variants/blush-coral.json +122 -0
- package/themes/paper-cutout/variants/blush-violet.json +122 -0
- package/themes/paper-cutout/variants/chalk-teal.json +122 -0
- package/themes/paper-cutout/variants/cream-gold.json +122 -0
- package/themes/paper-cutout/variants/frost-blue.json +123 -0
- package/themes/paper-cutout/variants/frost-lime.json +121 -0
- package/themes/paper-cutout/variants/frost-plum.json +122 -0
- package/themes/paper-cutout/variants/lilac-blue.json +123 -0
- package/themes/paper-cutout/variants/lilac-gold.json +122 -0
- package/themes/paper-cutout/variants/sage-cyan.json +122 -0
- package/themes/paper-cutout/variants/sage-green.json +122 -0
- package/themes/paper-cutout/variants/sage-pink.json +122 -0
- package/themes/paper-cutout/variants/sand-blue.json +123 -0
- package/themes/paper-cutout/variants/sand-green.json +121 -0
- package/themes/paper-cutout/variants/sand-plum.json +120 -0
- package/themes/pixel-quest/THEME.md +57 -0
- package/themes/pixel-quest/effects.ts +491 -0
- package/themes/pixel-quest/kit.ts +232 -0
- package/themes/pixel-quest/sample/assets/og/logo.svg +12 -0
- package/themes/pixel-quest/sample/beats.json +1005 -0
- package/themes/pixel-quest/sample/index.html +11 -0
- package/themes/pixel-quest/sample/kf.json +59 -0
- package/themes/pixel-quest/sample/lib/facts.ts +29 -0
- package/themes/pixel-quest/sample/lib/game.ts +71 -0
- package/themes/pixel-quest/sample/lib/logo.ts +51 -0
- package/themes/pixel-quest/sample/main.ts +9 -0
- package/themes/pixel-quest/sample/scenes/end.ts +75 -0
- package/themes/pixel-quest/sample/scenes/gather.ts +65 -0
- package/themes/pixel-quest/sample/scenes/models.ts +104 -0
- package/themes/pixel-quest/sample/scenes/price.ts +118 -0
- package/themes/pixel-quest/sample/style.json +115 -0
- package/themes/pixel-quest/slots.ts +94 -0
- package/themes/pixel-quest/still.png +0 -0
- package/themes/pixel-quest/style.json +115 -0
- package/themes/pixel-quest/theme.json +144 -0
- package/themes/pixel-quest/variants/navy-gold-2.json +98 -0
- package/themes/pixel-quest/variants/navy-gold-3.json +127 -0
- package/themes/pixel-quest/variants/navy-gold-4.json +95 -0
- package/themes/pixel-quest/variants/navy-gold-pixel.json +132 -0
- package/themes/pixel-quest/variants/navy-gold-scan.json +99 -0
- package/themes/pixel-quest/variants/navy-gold.json +131 -0
- package/themes/pixel-quest/variants/navy-lime-2.json +93 -0
- package/themes/pixel-quest/variants/navy-lime-3.json +124 -0
- package/themes/pixel-quest/variants/navy-lime-pixel.json +126 -0
- package/themes/pixel-quest/variants/navy-lime-scan.json +97 -0
- package/themes/pixel-quest/variants/navy-lime.json +130 -0
- package/themes/receipt/THEME.md +72 -0
- package/themes/receipt/effects.ts +257 -0
- package/themes/receipt/sample/assets/og/logo.svg +12 -0
- package/themes/receipt/sample/beats.json +1005 -0
- package/themes/receipt/sample/index.html +11 -0
- package/themes/receipt/sample/kf.json +59 -0
- package/themes/receipt/sample/lib/facts.ts +29 -0
- package/themes/receipt/sample/lib/sheet.ts +52 -0
- package/themes/receipt/sample/main.ts +9 -0
- package/themes/receipt/sample/scenes/end.ts +44 -0
- package/themes/receipt/sample/scenes/gather.ts +57 -0
- package/themes/receipt/sample/scenes/models.ts +48 -0
- package/themes/receipt/sample/scenes/price.ts +64 -0
- package/themes/receipt/sample/style.json +104 -0
- package/themes/receipt/slots.ts +89 -0
- package/themes/receipt/still.png +0 -0
- package/themes/receipt/style.json +104 -0
- package/themes/receipt/theme.json +133 -0
- package/themes/receipt/variants/blush-coral.json +89 -0
- package/themes/receipt/variants/sand-coral-2.json +89 -0
- package/themes/receipt/variants/sand-coral-3.json +89 -0
- package/themes/receipt/variants/sand-coral-4.json +90 -0
- package/themes/receipt/variants/sand-coral-5.json +89 -0
- package/themes/receipt/variants/sand-coral-6.json +88 -0
- package/themes/receipt/variants/sand-coral-7.json +88 -0
- package/themes/receipt/variants/sand-coral-8.json +89 -0
- package/themes/receipt/variants/sand-coral-9.json +89 -0
- package/themes/receipt/variants/sand-coral.json +88 -0
- package/themes/receipt/variants/sand-orange-2.json +89 -0
- package/themes/receipt/variants/sand-orange-3.json +90 -0
- package/themes/receipt/variants/sand-orange-4.json +89 -0
- package/themes/receipt/variants/sand-orange-5.json +89 -0
- package/themes/receipt/variants/sand-orange.json +89 -0
- package/themes/riso-zine/sample/style.json +2 -2
- package/themes/riso-zine/style.json +2 -2
- package/themes/riso-zine/theme.json +68 -0
- package/themes/riso-zine/variants/blush-coral.json +120 -0
- package/themes/riso-zine/variants/blush-gold.json +120 -0
- package/themes/riso-zine/variants/blush-pink.json +115 -0
- package/themes/riso-zine/variants/chalk-cyan.json +120 -0
- package/themes/riso-zine/variants/chalk-lime.json +119 -0
- package/themes/riso-zine/variants/cream-amber.json +89 -0
- package/themes/riso-zine/variants/frost-blue.json +119 -0
- package/themes/riso-zine/variants/frost-coral.json +118 -0
- package/themes/riso-zine/variants/frost-cyan.json +120 -0
- package/themes/riso-zine/variants/frost-green.json +120 -0
- package/themes/riso-zine/variants/lilac-plum.json +118 -0
- package/themes/riso-zine/variants/lilac-violet.json +123 -0
- package/themes/riso-zine/variants/sage-green.json +119 -0
- package/themes/riso-zine/variants/sand-jade.json +120 -0
- package/themes/riso-zine/variants/sand-plum.json +113 -0
- package/themes/server-hall/THEME.md +58 -0
- package/themes/server-hall/effects.ts +455 -0
- package/themes/server-hall/kit.ts +138 -0
- package/themes/server-hall/sample/assets/og/logo.svg +12 -0
- package/themes/server-hall/sample/beats.json +1005 -0
- package/themes/server-hall/sample/index.html +11 -0
- package/themes/server-hall/sample/kf.json +59 -0
- package/themes/server-hall/sample/lib/facts.ts +29 -0
- package/themes/server-hall/sample/lib/hall.ts +81 -0
- package/themes/server-hall/sample/main.ts +9 -0
- package/themes/server-hall/sample/scenes/end.ts +57 -0
- package/themes/server-hall/sample/scenes/gather.ts +38 -0
- package/themes/server-hall/sample/scenes/models.ts +49 -0
- package/themes/server-hall/sample/scenes/price.ts +65 -0
- package/themes/server-hall/sample/style.json +107 -0
- package/themes/server-hall/slots.ts +106 -0
- package/themes/server-hall/still.png +0 -0
- package/themes/server-hall/style.json +107 -0
- package/themes/server-hall/theme.json +145 -0
- package/themes/server-hall/variants/navy-indigo.json +140 -0
- package/themes/server-hall/variants/night-indigo.json +120 -0
- package/themes/server-hall/variants/wine-plum.json +130 -0
- package/themes/server-hall/variants/wine-violet.json +103 -0
- package/themes/subway-map/THEME.md +64 -0
- package/themes/subway-map/effects.ts +662 -0
- package/themes/subway-map/sample/assets/og/logo.svg +12 -0
- package/themes/subway-map/sample/beats.json +1005 -0
- package/themes/subway-map/sample/index.html +11 -0
- package/themes/subway-map/sample/kf.json +59 -0
- package/themes/subway-map/sample/lib/facts.ts +29 -0
- package/themes/subway-map/sample/lib/map.ts +58 -0
- package/themes/subway-map/sample/main.ts +9 -0
- package/themes/subway-map/sample/scenes/end.ts +94 -0
- package/themes/subway-map/sample/scenes/gather.ts +64 -0
- package/themes/subway-map/sample/scenes/models.ts +117 -0
- package/themes/subway-map/sample/scenes/price.ts +119 -0
- package/themes/subway-map/sample/style.json +108 -0
- package/themes/subway-map/slots.ts +66 -0
- package/themes/subway-map/still.png +0 -0
- package/themes/subway-map/style.json +108 -0
- package/themes/subway-map/theme.json +163 -0
- package/themes/subway-map/variants/blush-jade.json +131 -0
- package/themes/subway-map/variants/chalk-jade.json +141 -0
- package/themes/subway-map/variants/deep-green.json +131 -0
- package/themes/subway-map/variants/deep-teal.json +132 -0
- package/themes/subway-map/variants/dusk-teal-bold.json +132 -0
- package/themes/subway-map/variants/dusk-teal.json +105 -0
- package/themes/subway-map/variants/forest-green.json +141 -0
- package/themes/subway-map/variants/forest-jade.json +131 -0
- package/themes/subway-map/variants/frost-jade.json +131 -0
- package/themes/subway-map/variants/frost-teal.json +137 -0
- package/themes/subway-map/variants/sage-teal.json +132 -0
- package/themes/subway-map/variants/slate-green.json +136 -0
- package/themes/subway-map/variants/slate-jade.json +131 -0
- package/themes/subway-map/variants/snow-jade.json +136 -0
- package/themes/subway-map/variants/wine-green.json +104 -0
- package/themes/swiss-poster/THEME.md +59 -0
- package/themes/swiss-poster/effects.ts +339 -0
- package/themes/swiss-poster/grid.ts +35 -0
- package/themes/swiss-poster/sample/assets/og/logo.svg +12 -0
- package/themes/swiss-poster/sample/beats.json +1005 -0
- package/themes/swiss-poster/sample/index.html +11 -0
- package/themes/swiss-poster/sample/kf.json +59 -0
- package/themes/swiss-poster/sample/lib/facts.ts +29 -0
- package/themes/swiss-poster/sample/lib/poster.ts +36 -0
- package/themes/swiss-poster/sample/main.ts +9 -0
- package/themes/swiss-poster/sample/scenes/end.ts +70 -0
- package/themes/swiss-poster/sample/scenes/gather.ts +68 -0
- package/themes/swiss-poster/sample/scenes/models.ts +63 -0
- package/themes/swiss-poster/sample/scenes/price.ts +74 -0
- package/themes/swiss-poster/sample/style.json +100 -0
- package/themes/swiss-poster/slots.ts +69 -0
- package/themes/swiss-poster/still.png +0 -0
- package/themes/swiss-poster/style.json +100 -0
- package/themes/swiss-poster/theme.json +133 -0
- package/themes/swiss-poster/variants/blush-green.json +116 -0
- package/themes/swiss-poster/variants/chalk-plum.json +121 -0
- package/themes/swiss-poster/variants/chalk-sky.json +90 -0
- package/themes/swiss-poster/variants/cocoa-cyan.json +117 -0
- package/themes/swiss-poster/variants/dusk-coral.json +89 -0
- package/themes/swiss-poster/variants/dusk-magenta.json +117 -0
- package/themes/swiss-poster/variants/forest-amber.json +127 -0
- package/themes/swiss-poster/variants/forest-blue.json +118 -0
- package/themes/swiss-poster/variants/forest-jade.json +89 -0
- package/themes/swiss-poster/variants/frost-gold.json +128 -0
- package/themes/swiss-poster/variants/frost-indigo.json +117 -0
- package/themes/swiss-poster/variants/sand-magenta.json +126 -0
- package/themes/swiss-poster/variants/slate-orange.json +122 -0
- package/themes/swiss-poster/variants/slate-pink.json +126 -0
- package/themes/swiss-poster/variants/slate-violet.json +89 -0
- package/themes/water-works/THEME.md +61 -0
- package/themes/water-works/effects.ts +609 -0
- package/themes/water-works/sample/assets/og/logo.svg +12 -0
- package/themes/water-works/sample/beats.json +1005 -0
- package/themes/water-works/sample/index.html +11 -0
- package/themes/water-works/sample/kf.json +59 -0
- package/themes/water-works/sample/lib/facts.ts +29 -0
- package/themes/water-works/sample/lib/hall.ts +22 -0
- package/themes/water-works/sample/main.ts +9 -0
- package/themes/water-works/sample/scenes/end.ts +55 -0
- package/themes/water-works/sample/scenes/gather.ts +42 -0
- package/themes/water-works/sample/scenes/models.ts +51 -0
- package/themes/water-works/sample/scenes/price.ts +66 -0
- package/themes/water-works/sample/style.json +105 -0
- package/themes/water-works/slots.ts +140 -0
- package/themes/water-works/still.png +0 -0
- package/themes/water-works/style.json +105 -0
- package/themes/water-works/theme.json +140 -0
- package/themes/water-works/variants/blush-rose.json +123 -0
- package/themes/water-works/variants/chalk-blue.json +124 -0
- package/themes/water-works/variants/chalk-cyan-2.json +96 -0
- package/themes/water-works/variants/chalk-cyan.json +123 -0
- package/themes/water-works/variants/chalk-gold-mono.json +122 -0
- package/themes/water-works/variants/chalk-gold.json +123 -0
- package/themes/water-works/variants/chalk-green.json +123 -0
- package/themes/water-works/variants/frost-indigo.json +123 -0
- package/themes/water-works/variants/frost-rose.json +132 -0
- package/themes/water-works/variants/lilac-violet-mono.json +124 -0
- package/themes/water-works/variants/lilac-violet.json +133 -0
- package/themes/water-works/variants/sage-amber.json +133 -0
- package/themes/water-works/variants/sage-green.json +133 -0
- package/themes/water-works/variants/sand-amber.json +123 -0
- package/themes/water-works/variants/sand-plum.json +121 -0
- package/themes/whiteboard/THEME.md +78 -0
- package/themes/whiteboard/effects.ts +561 -0
- package/themes/whiteboard/sample/assets/og/logo.svg +12 -0
- package/themes/whiteboard/sample/beats.json +1005 -0
- package/themes/whiteboard/sample/index.html +11 -0
- package/themes/whiteboard/sample/kf.json +59 -0
- package/themes/whiteboard/sample/lib/board.ts +93 -0
- package/themes/whiteboard/sample/lib/facts.ts +29 -0
- package/themes/whiteboard/sample/main.ts +9 -0
- package/themes/whiteboard/sample/scenes/end.ts +59 -0
- package/themes/whiteboard/sample/scenes/gather.ts +50 -0
- package/themes/whiteboard/sample/scenes/models.ts +56 -0
- package/themes/whiteboard/sample/scenes/price.ts +79 -0
- package/themes/whiteboard/sample/style.json +106 -0
- package/themes/whiteboard/slots.ts +152 -0
- package/themes/whiteboard/still.png +0 -0
- package/themes/whiteboard/style.json +106 -0
- package/themes/whiteboard/theme.json +145 -0
- package/themes/whiteboard/variants/chalk-coral-do.json +126 -0
- package/themes/whiteboard/variants/chalk-coral.json +132 -0
- package/themes/whiteboard/variants/cream-lime.json +131 -0
- package/themes/whiteboard/variants/cream-plum.json +103 -0
- package/themes/whiteboard/variants/frost-teal-do.json +127 -0
- package/themes/whiteboard/variants/frost-teal.json +127 -0
- package/themes/whiteboard/variants/lilac-indigo.json +132 -0
- package/themes/whiteboard/variants/lilac-jade.json +103 -0
- package/themes/whiteboard/variants/sage-green.json +130 -0
- package/themes/whiteboard/variants/sage-plum.json +126 -0
- package/themes/whiteboard/variants/snow-amber.json +99 -0
- package/themes/whiteboard/variants/snow-cyan.json +132 -0
- package/themes/whiteboard/variants/snow-green.json +126 -0
- package/themes/whiteboard/variants/snow-magenta.json +126 -0
- package/themes/whiteboard/variants/snow-plum.json +130 -0
- /package/themes/{og-measure/sample/assets/brand/og-logo-full.svg → blueprint/sample/assets/og/logo.svg} +0 -0
|
@@ -9,7 +9,14 @@
|
|
|
9
9
|
"./factory": "./src/factory.ts",
|
|
10
10
|
"./stage3d": "./src/stage3d.ts",
|
|
11
11
|
"./slots": "./src/slots.ts",
|
|
12
|
-
"./slotkit": "./src/slotkit.ts"
|
|
12
|
+
"./slotkit": "./src/slotkit.ts",
|
|
13
|
+
"./name": "./src/name.ts",
|
|
14
|
+
"./vary": "./src/vary.ts",
|
|
15
|
+
"./color": "./src/color.ts",
|
|
16
|
+
"./palette": "./src/palette.ts",
|
|
17
|
+
"./look": "./src/look.ts",
|
|
18
|
+
"./fonts": "./src/fonts.ts",
|
|
19
|
+
"./fontfile": "./src/fontfile.ts"
|
|
13
20
|
},
|
|
14
21
|
"dependencies": {
|
|
15
22
|
"@kasaframe/core": "0.0.0",
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// 변주 색 계산 — OKLCH 로 옮기고, WCAG 대비·색각 이상 모의·OKLab 거리로 거른다. Node·페이지 어디서나 도는 순수 함수만.
|
|
2
|
+
|
|
3
|
+
export interface Rgba {
|
|
4
|
+
r: number;
|
|
5
|
+
g: number;
|
|
6
|
+
b: number;
|
|
7
|
+
a: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Lch {
|
|
11
|
+
l: number;
|
|
12
|
+
c: number;
|
|
13
|
+
/** 도 0..360 */
|
|
14
|
+
h: number;
|
|
15
|
+
a: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type Vec = [number, number, number];
|
|
19
|
+
|
|
20
|
+
export function parseColor(s: string): Rgba {
|
|
21
|
+
const t = s.trim();
|
|
22
|
+
const hex = /^#([0-9a-f]{3,8})$/i.exec(t)?.[1];
|
|
23
|
+
if (hex && [3, 6, 8].includes(hex.length)) {
|
|
24
|
+
const full = hex.length === 3 ? [...hex].map((c) => c + c).join("") : hex;
|
|
25
|
+
const n = (i: number) => parseInt(full.slice(i, i + 2), 16);
|
|
26
|
+
return { r: n(0), g: n(2), b: n(4), a: full.length === 8 ? n(6) / 255 : 1 };
|
|
27
|
+
}
|
|
28
|
+
const m = /^rgba?\(([^)]+)\)$/i.exec(t);
|
|
29
|
+
if (m) {
|
|
30
|
+
const p = m[1].split(/[\s,/]+/).filter(Boolean).map(Number);
|
|
31
|
+
if (p.length >= 3 && p.slice(0, 3).every(Number.isFinite)) return { r: p[0], g: p[1], b: p[2], a: Number.isFinite(p[3]) ? p[3] : 1 };
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`색 "${s}" 을 못 읽는다 — #hex 나 rgb()·rgba() 만`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const h2 = (v: number) => Math.round(Math.min(255, Math.max(0, v))).toString(16).padStart(2, "0").toUpperCase();
|
|
37
|
+
|
|
38
|
+
export function formatColor(c: Rgba): string {
|
|
39
|
+
if (c.a >= 0.999) return `#${h2(c.r)}${h2(c.g)}${h2(c.b)}`;
|
|
40
|
+
return `rgba(${Math.round(c.r)}, ${Math.round(c.g)}, ${Math.round(c.b)}, ${+c.a.toFixed(3)})`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const toLin = (v: number) => {
|
|
44
|
+
const x = v / 255;
|
|
45
|
+
return x <= 0.04045 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
|
|
46
|
+
};
|
|
47
|
+
const fromLin = (v: number) => 255 * (v <= 0.0031308 ? 12.92 * v : 1.055 * v ** (1 / 2.4) - 0.055);
|
|
48
|
+
|
|
49
|
+
const linOf = (c: Rgba): Vec => [toLin(c.r), toLin(c.g), toLin(c.b)];
|
|
50
|
+
|
|
51
|
+
function labOfLin([r, g, b]: Vec): Vec {
|
|
52
|
+
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
53
|
+
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
54
|
+
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
55
|
+
return [
|
|
56
|
+
0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
|
|
57
|
+
1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
|
|
58
|
+
0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function linOfLab([L, A, B]: Vec): Vec {
|
|
63
|
+
const l = (L + 0.3963377774 * A + 0.2158037573 * B) ** 3;
|
|
64
|
+
const m = (L - 0.1055613458 * A - 0.0638541728 * B) ** 3;
|
|
65
|
+
const s = (L - 0.0894841775 * A - 1.291485548 * B) ** 3;
|
|
66
|
+
return [
|
|
67
|
+
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
|
|
68
|
+
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
|
|
69
|
+
-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const labOf = (c: Rgba): Vec => labOfLin(linOf(c));
|
|
74
|
+
|
|
75
|
+
export function toLch(c: Rgba): Lch {
|
|
76
|
+
const [l, a, b] = labOf(c);
|
|
77
|
+
const h = (Math.atan2(b, a) * 180) / Math.PI;
|
|
78
|
+
return { l, c: Math.hypot(a, b), h: h < 0 ? h + 360 : h, a: c.a };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const inGamut = (v: Vec) => v.every((x) => x >= -1e-4 && x <= 1 + 1e-4);
|
|
82
|
+
|
|
83
|
+
/** sRGB 밖이면 명도·색상은 두고 채도만 줄여 넣는다 */
|
|
84
|
+
export function fromLch(o: Lch): Rgba {
|
|
85
|
+
const l = Math.min(1, Math.max(0, o.l));
|
|
86
|
+
const rad = (o.h * Math.PI) / 180;
|
|
87
|
+
const lin = (c: number) => linOfLab([l, c * Math.cos(rad), c * Math.sin(rad)]);
|
|
88
|
+
let c = Math.max(0, o.c);
|
|
89
|
+
if (!inGamut(lin(c))) {
|
|
90
|
+
let lo = 0;
|
|
91
|
+
let hi = c;
|
|
92
|
+
for (let i = 0; i < 24; i++) {
|
|
93
|
+
const mid = (lo + hi) / 2;
|
|
94
|
+
if (inGamut(lin(mid))) lo = mid;
|
|
95
|
+
else hi = mid;
|
|
96
|
+
}
|
|
97
|
+
c = lo;
|
|
98
|
+
}
|
|
99
|
+
const [r, g, b] = lin(c).map((x) => fromLin(Math.min(1, Math.max(0, x))));
|
|
100
|
+
return { r, g, b, a: o.a };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** 반투명 색을 바탕 위에 얹은 색 */
|
|
104
|
+
export const over = (c: Rgba, bg: Rgba): Rgba => ({ r: c.r * c.a + bg.r * (1 - c.a), g: c.g * c.a + bg.g * (1 - c.a), b: c.b * c.a + bg.b * (1 - c.a), a: 1 });
|
|
105
|
+
|
|
106
|
+
export function luminance(c: Rgba): number {
|
|
107
|
+
const [r, g, b] = linOf(c);
|
|
108
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** WCAG 2 대비 — 반투명 글은 바탕에 얹어 잰다 */
|
|
112
|
+
export function contrast(fg: Rgba, bg: Rgba): number {
|
|
113
|
+
const a = luminance(over(fg, bg));
|
|
114
|
+
const b = luminance(bg);
|
|
115
|
+
return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** OKLab 거리 — 0.02 아래는 거의 같은 색, 0.1 넘으면 뚜렷이 다른 색 */
|
|
119
|
+
export function deltaE(x: Rgba, y: Rgba): number {
|
|
120
|
+
const [a1, b1, c1] = labOf(x);
|
|
121
|
+
const [a2, b2, c2] = labOf(y);
|
|
122
|
+
return Math.hypot(a1 - a2, b1 - b2, c1 - c2);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export type Cvd = "protan" | "deutan" | "tritan";
|
|
126
|
+
export const CVDS: readonly Cvd[] = ["protan", "deutan", "tritan"];
|
|
127
|
+
|
|
128
|
+
// Machado·Oliveira·Fernandes 2009, 심한 정도 1.0 — 선형 RGB 에 곱한다
|
|
129
|
+
const MACHADO: Record<Cvd, number[]> = {
|
|
130
|
+
protan: [0.152286, 1.052583, -0.204868, 0.114503, 0.786281, 0.099216, -0.003882, -0.048116, 1.051998],
|
|
131
|
+
deutan: [0.367322, 0.860646, -0.227968, 0.280085, 0.672501, 0.047413, -0.01182, 0.04294, 0.968881],
|
|
132
|
+
tritan: [1.255528, -0.076749, -0.178779, -0.078411, 0.930809, 0.147602, 0.004733, 0.691367, 0.3039],
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export function simulate(c: Rgba, kind: Cvd): Rgba {
|
|
136
|
+
const m = MACHADO[kind];
|
|
137
|
+
const [r, g, b] = linOf(c);
|
|
138
|
+
const mix = (i: number) => fromLin(Math.min(1, Math.max(0, m[i] * r + m[i + 1] * g + m[i + 2] * b)));
|
|
139
|
+
return { r: mix(0), g: mix(3), b: mix(6), a: c.a };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** 정상 시각과 세 색각 이상 모의 중 가장 작은 OKLab 거리 — 바탕에 얹은 색으로 */
|
|
143
|
+
export function worstDelta(x: Rgba, y: Rgba, bg: Rgba): number {
|
|
144
|
+
const a = over(x, bg);
|
|
145
|
+
const b = over(y, bg);
|
|
146
|
+
return Math.min(deltaE(a, b), ...CVDS.map((k) => deltaE(simulate(a, k), simulate(b, k))));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const hueDist = (a: number, b: number): number => {
|
|
150
|
+
const d = Math.abs(a - b) % 360;
|
|
151
|
+
return d > 180 ? 360 - d : d;
|
|
152
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// 글꼴 파일에서 짝 검사에 드는 표(cmap·OS/2·fvar·name)만 읽는다 — TTF/OTF·WOFF·WOFF2.
|
|
2
|
+
// fontTools 대신 직접 읽는 까닭: 드는 것이 글자 목록·굵기·이름뿐이고, WOFF2 도 cmap 은 변환 없이 brotli 한 줄기에 있어
|
|
3
|
+
// node zlib 만으로 풀린다. 파이썬 venv 를 깔 일이 없어 kf fonts check 가 vitest 안에서도, 처음 쓰는 기계에서도 바로 돈다.
|
|
4
|
+
import { brotliDecompressSync, inflateSync } from "node:zlib";
|
|
5
|
+
|
|
6
|
+
export interface FontFileInfo {
|
|
7
|
+
format: "ttf" | "otf" | "woff" | "woff2";
|
|
8
|
+
/** cmap 이 글리프로 잇는 코드 포인트(글리프 0 은 뺀다) */
|
|
9
|
+
cps: Set<number>;
|
|
10
|
+
/** OS/2 usWeightClass */
|
|
11
|
+
weight: number;
|
|
12
|
+
/** 가변 글꼴의 wght 축 [최소, 최대] */
|
|
13
|
+
wght?: [number, number];
|
|
14
|
+
/** name 표 family(16·1) — 크롬이 찍은 글꼴 이름을 이것에 댄다 */
|
|
15
|
+
families: string[];
|
|
16
|
+
postScript: string;
|
|
17
|
+
/** name 13(라이선스 설명) — 배포처 페이지가 JS 로 그려져 글을 못 받을 때 파일 자체가 밝힌 라이선스 */
|
|
18
|
+
licenseText: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const tag = (b: Buffer, o: number) => b.toString("latin1", o, o + 4);
|
|
22
|
+
|
|
23
|
+
// WOFF2 표 머리의 알려진 태그 63개(차례가 곧 번호)
|
|
24
|
+
const WOFF2_TAGS = "cmap head hhea hmtx maxp name OS/2 post cvt fpgm glyf loca prep CFF VORG EBDT EBLC gasp hdmx kern LTSH PCLT VDMX vhea vmtx BASE GDEF GPOS GSUB EBSC JSTF MATH CBDT CBLC COLR CPAL SVG sbix acnt avar bdat bloc bsln cvar fdsc feat fmtx fvar gvar hsty just lcar mort morx opbd prop trak Zapf Silf Glat Gloc Feat Sill"
|
|
25
|
+
.match(/.{4}(?: |$)/g)!
|
|
26
|
+
.map((s) => s.slice(0, 4));
|
|
27
|
+
|
|
28
|
+
function base128(b: Buffer, o: { at: number }): number {
|
|
29
|
+
let v = 0;
|
|
30
|
+
for (let i = 0; i < 5; i++) {
|
|
31
|
+
const x = b[o.at++];
|
|
32
|
+
v = v * 128 + (x & 0x7f);
|
|
33
|
+
if (!(x & 0x80)) return v;
|
|
34
|
+
}
|
|
35
|
+
throw new Error("WOFF2 UIntBase128 이 길다");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function tablesOf(b: Buffer): { format: FontFileInfo["format"]; tables: Map<string, Buffer> } {
|
|
39
|
+
const sig = tag(b, 0);
|
|
40
|
+
const tables = new Map<string, Buffer>();
|
|
41
|
+
if (sig === "wOF2") {
|
|
42
|
+
if (tag(b, 4) === "ttcf") throw new Error("WOFF2 글꼴 모음(ttc)은 읽지 않는다");
|
|
43
|
+
const n = b.readUInt16BE(12);
|
|
44
|
+
const compressed = b.readUInt32BE(20);
|
|
45
|
+
const o = { at: 48 };
|
|
46
|
+
const dir: { tag: string; len: number }[] = [];
|
|
47
|
+
for (let i = 0; i < n; i++) {
|
|
48
|
+
const flags = b[o.at++];
|
|
49
|
+
const t = (flags & 0x3f) === 63 ? tag(b, (o.at += 4) - 4) : WOFF2_TAGS[flags & 0x3f];
|
|
50
|
+
const version = flags >> 6;
|
|
51
|
+
const orig = base128(b, o);
|
|
52
|
+
const transformed = t === "glyf" || t === "loca" ? version !== 3 : version !== 0;
|
|
53
|
+
dir.push({ tag: t, len: transformed ? base128(b, o) : orig });
|
|
54
|
+
}
|
|
55
|
+
const data = brotliDecompressSync(b.subarray(o.at, o.at + compressed));
|
|
56
|
+
let at = 0;
|
|
57
|
+
for (const d of dir) {
|
|
58
|
+
tables.set(d.tag, data.subarray(at, at + d.len));
|
|
59
|
+
at += d.len;
|
|
60
|
+
}
|
|
61
|
+
return { format: "woff2", tables };
|
|
62
|
+
}
|
|
63
|
+
if (sig === "wOFF") {
|
|
64
|
+
const n = b.readUInt16BE(12);
|
|
65
|
+
for (let i = 0; i < n; i++) {
|
|
66
|
+
const r = 44 + i * 20;
|
|
67
|
+
const [off, comp, orig] = [b.readUInt32BE(r + 4), b.readUInt32BE(r + 8), b.readUInt32BE(r + 12)];
|
|
68
|
+
const raw = b.subarray(off, off + comp);
|
|
69
|
+
tables.set(tag(b, r), comp < orig ? inflateSync(raw) : raw);
|
|
70
|
+
}
|
|
71
|
+
return { format: "woff", tables };
|
|
72
|
+
}
|
|
73
|
+
const v = b.readUInt32BE(0);
|
|
74
|
+
if (v !== 0x00010000 && sig !== "OTTO" && sig !== "true") throw new Error(`글꼴 파일이 아니다(머리 ${JSON.stringify(sig)})`);
|
|
75
|
+
const n = b.readUInt16BE(4);
|
|
76
|
+
for (let i = 0; i < n; i++) {
|
|
77
|
+
const r = 12 + i * 16;
|
|
78
|
+
tables.set(tag(b, r), b.subarray(b.readUInt32BE(r + 8), b.readUInt32BE(r + 8) + b.readUInt32BE(r + 12)));
|
|
79
|
+
}
|
|
80
|
+
return { format: sig === "OTTO" ? "otf" : "ttf", tables };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function cmapOf(t: Buffer): Set<number> {
|
|
84
|
+
const out = new Set<number>();
|
|
85
|
+
const n = t.readUInt16BE(2);
|
|
86
|
+
const seen = new Set<number>();
|
|
87
|
+
for (let i = 0; i < n; i++) {
|
|
88
|
+
const [platform, encoding, off] = [t.readUInt16BE(4 + i * 8), t.readUInt16BE(6 + i * 8), t.readUInt32BE(8 + i * 8)];
|
|
89
|
+
if (!(platform === 0 || (platform === 3 && (encoding === 1 || encoding === 10))) || seen.has(off)) continue;
|
|
90
|
+
seen.add(off);
|
|
91
|
+
const s = t.subarray(off);
|
|
92
|
+
const format = s.readUInt16BE(0);
|
|
93
|
+
if (format === 4) {
|
|
94
|
+
const seg = s.readUInt16BE(6) / 2;
|
|
95
|
+
const ends = 14, starts = 16 + seg * 2, deltas = starts + seg * 2, ranges = deltas + seg * 2;
|
|
96
|
+
for (let k = 0; k < seg; k++) {
|
|
97
|
+
const [end, start, delta, range] = [s.readUInt16BE(ends + k * 2), s.readUInt16BE(starts + k * 2), s.readInt16BE(deltas + k * 2), s.readUInt16BE(ranges + k * 2)];
|
|
98
|
+
for (let c = start; c <= end && c !== 0xffff; c++) {
|
|
99
|
+
let g: number;
|
|
100
|
+
if (range === 0) g = (c + delta) & 0xffff;
|
|
101
|
+
else {
|
|
102
|
+
const at = ranges + k * 2 + range + (c - start) * 2;
|
|
103
|
+
g = at + 2 <= s.length ? s.readUInt16BE(at) : 0;
|
|
104
|
+
if (g) g = (g + delta) & 0xffff;
|
|
105
|
+
}
|
|
106
|
+
if (g) out.add(c);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} else if (format === 12 || format === 13) {
|
|
110
|
+
const groups = s.readUInt32BE(12);
|
|
111
|
+
for (let k = 0; k < groups; k++) {
|
|
112
|
+
const r = 16 + k * 12;
|
|
113
|
+
const [start, end, g0] = [s.readUInt32BE(r), s.readUInt32BE(r + 4), s.readUInt32BE(r + 8)];
|
|
114
|
+
for (let c = start; c <= end; c++) if (format === 13 ? g0 : g0 + (c - start)) out.add(c);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** name 표에서 이 번호들의 글 — 플랫폼·언어별로 다른 것까지 모두, 겹치면 하나 */
|
|
122
|
+
function namesOf(t: Buffer | undefined, ids: number[]): string[] {
|
|
123
|
+
if (!t) return [];
|
|
124
|
+
const count = t.readUInt16BE(2);
|
|
125
|
+
const strings = t.readUInt16BE(4);
|
|
126
|
+
const out = new Set<string>();
|
|
127
|
+
for (const id of ids)
|
|
128
|
+
for (let i = 0; i < count; i++) {
|
|
129
|
+
const r = 6 + i * 12;
|
|
130
|
+
const [platform, , , nameId, len, off] = [0, 2, 4, 6, 8, 10].map((k) => t.readUInt16BE(r + k));
|
|
131
|
+
if (nameId !== id) continue;
|
|
132
|
+
const raw = t.subarray(strings + off, strings + off + len);
|
|
133
|
+
const s = platform === 1 ? raw.toString("latin1") : Buffer.from(raw).swap16().toString("utf16le");
|
|
134
|
+
if (s) out.add(s);
|
|
135
|
+
}
|
|
136
|
+
return [...out];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function readFontFile(b: Buffer): FontFileInfo {
|
|
140
|
+
const { format, tables } = tablesOf(b);
|
|
141
|
+
const cmap = tables.get("cmap");
|
|
142
|
+
if (!cmap) throw new Error("cmap 표가 없다");
|
|
143
|
+
const os2 = tables.get("OS/2");
|
|
144
|
+
const fvar = tables.get("fvar");
|
|
145
|
+
let wght: [number, number] | undefined;
|
|
146
|
+
if (fvar) {
|
|
147
|
+
const [axes, count, size] = [fvar.readUInt16BE(4), fvar.readUInt16BE(8), fvar.readUInt16BE(10)];
|
|
148
|
+
for (let i = 0; i < count; i++) {
|
|
149
|
+
const r = axes + i * size;
|
|
150
|
+
if (tag(fvar, r) === "wght") wght = [Math.round(fvar.readInt32BE(r + 4) / 65536), Math.round(fvar.readInt32BE(r + 12) / 65536)];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
format,
|
|
155
|
+
cps: cmapOf(cmap),
|
|
156
|
+
weight: os2 ? os2.readUInt16BE(4) : 400,
|
|
157
|
+
...(wght ? { wght } : {}),
|
|
158
|
+
families: namesOf(tables.get("name"), [16, 1]),
|
|
159
|
+
postScript: namesOf(tables.get("name"), [6])[0] ?? "",
|
|
160
|
+
licenseText: namesOf(tables.get("name"), [13]).join("\n"),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const HANGUL = [0xac00, 0xd7a3] as const;
|
|
165
|
+
let ks: number[] | null = null;
|
|
166
|
+
/** KS X 1001 완성형 한글 2,350자 — EUC-KR 의 B0A1–C8FE */
|
|
167
|
+
export function ksHangul(): number[] {
|
|
168
|
+
if (ks) return ks;
|
|
169
|
+
const bytes: number[] = [];
|
|
170
|
+
for (let hi = 0xb0; hi <= 0xc8; hi++) for (let lo = 0xa1; lo <= 0xfe; lo++) bytes.push(hi, lo);
|
|
171
|
+
return (ks = [...new TextDecoder("euc-kr").decode(Uint8Array.from(bytes))].map((c) => c.codePointAt(0)!));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export type HangulCover = "full" | "ks2350" | "partial" | "none";
|
|
175
|
+
|
|
176
|
+
/** 한글 음절 덮음 — full 은 11,172자 전부, ks2350 은 KS X 1001 2,350자를 다 가진 것 */
|
|
177
|
+
export function hangulCover(cps: Set<number>): { cover: HangulCover; syllables: number; ksMissing: number } {
|
|
178
|
+
let syllables = 0;
|
|
179
|
+
for (let c = HANGUL[0]; c <= HANGUL[1]; c++) if (cps.has(c)) syllables++;
|
|
180
|
+
const ksMissing = ksHangul().filter((c) => !cps.has(c)).length;
|
|
181
|
+
const cover = syllables === HANGUL[1] - HANGUL[0] + 1 ? "full" : syllables && !ksMissing ? "ks2350" : syllables ? "partial" : "none";
|
|
182
|
+
return { cover, syllables, ksMissing };
|
|
183
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// 글꼴 짝 — kit/fonts.json 과 kit/fonts/<무리>.json 을 합친 목록, 그리고 짝을 넣기 전에 거르는 판정(SPEC 9 변주의 글꼴 축).
|
|
2
|
+
// 판정은 순수 함수로 두고, 주소를 받고 크롬으로 찍어 보는 일은 kf fonts check(packages/cli/src/fonts.ts)가 한다.
|
|
3
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import type { FontFace } from "@kasaframe/core";
|
|
7
|
+
import { hangulCover, type HangulCover } from "./fontfile.ts";
|
|
8
|
+
|
|
9
|
+
export interface PairFace extends FontFace {
|
|
10
|
+
/** OFL-1.1 · Apache-2.0 · 그만큼 열린 자체 라이선스 이름 */
|
|
11
|
+
license: string;
|
|
12
|
+
/** 라이선스 글 주소 — 판을 박은 것이면 kf fonts check 가 글까지 대 본다 */
|
|
13
|
+
source: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface FontPair {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
ko: string;
|
|
20
|
+
tags: string[];
|
|
21
|
+
/** 한글 음절 덮음 — full 은 11,172자, ks2350 은 KS X 1001 2,350자(드문 음절은 다음 글꼴로 떨어져 섞여 보일 수 있다) */
|
|
22
|
+
hangul: "full" | "ks2350";
|
|
23
|
+
sans: string;
|
|
24
|
+
display: string;
|
|
25
|
+
mono: string;
|
|
26
|
+
faces: PairFace[];
|
|
27
|
+
weightDisplay: number;
|
|
28
|
+
weightBody: number;
|
|
29
|
+
tracking: string;
|
|
30
|
+
monoAll?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface FontGroup {
|
|
34
|
+
/** kit/fonts.json 은 core, kit/fonts/<무리>.json 은 <무리> */
|
|
35
|
+
group: string;
|
|
36
|
+
file: string;
|
|
37
|
+
pairs: FontPair[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const kitDir = fileURLToPath(new URL("../kit/", import.meta.url));
|
|
41
|
+
|
|
42
|
+
export function fontGroups(dir = kitDir): FontGroup[] {
|
|
43
|
+
const sub = join(dir, "fonts");
|
|
44
|
+
const files = existsSync(sub) ? readdirSync(sub).filter((f) => f.endsWith(".json")).sort() : [];
|
|
45
|
+
return [{ group: "core", file: join(dir, "fonts.json") }, ...files.map((f) => ({ group: f.slice(0, -5), file: join(sub, f) }))].map((g) => {
|
|
46
|
+
const pairs: FontPair[] = JSON.parse(readFileSync(g.file, "utf8"));
|
|
47
|
+
if (!Array.isArray(pairs)) throw new Error(`${g.file}: 글꼴 짝 배열이 아니다`);
|
|
48
|
+
return { ...g, pairs };
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** 무리 파일은 학생마다 따로 채워서, 합칠 때 id 가 겹치면 어느 쪽이 이기는지 숨지 않게 멈춘다 */
|
|
53
|
+
export function loadFontPairs(dir = kitDir): FontPair[] {
|
|
54
|
+
const seen = new Map<string, string>();
|
|
55
|
+
return fontGroups(dir).flatMap((g) =>
|
|
56
|
+
g.pairs.map((p) => {
|
|
57
|
+
const prev = seen.get(p.id);
|
|
58
|
+
if (prev) throw new Error(`글꼴 짝 id ${p.id} 이 겹친다 — ${prev} · ${g.file}`);
|
|
59
|
+
seen.set(p.id, g.file);
|
|
60
|
+
return p;
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let pairs: FontPair[] | null = null;
|
|
66
|
+
export const fontPairs = (): FontPair[] => (pairs ??= loadFontPairs());
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 라이선스 글에서 찾을 문구 — 이름만 적고 글이 다르면 잡는다. 목록 밖 이름은 사람이 글을 읽는다.
|
|
70
|
+
* OFL 은 머리줄 없이 본문만 넣은 글꼴(배민 name 13)이 있어 1.1 본문에만 있는 낱말도 받는다 — 1.0 은 cooperative·distributed using
|
|
71
|
+
*/
|
|
72
|
+
export const LICENSE_TEXT: Record<string, RegExp> = {
|
|
73
|
+
"OFL-1.1": /SIL OPEN FONT LICENSE[\s\S]{0,40}Version 1\.1|collaborative\s+font\s+projects[\s\S]*distributed\s+entirely\s+under\s+this\s+license/i,
|
|
74
|
+
"Apache-2.0": /Apache License[\s\S]{0,40}Version 2\.0/i,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const COMMIT = /^[0-9a-f]{7,40}$/;
|
|
78
|
+
const VERSION = /^v?\d+(\.\d+)*([-+][\w.]+)?$/;
|
|
79
|
+
const EXACT = /^\d+\.\d+\.\d+([-+][\w.]+)?$/;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 라이선스를 무엇으로 확인했나 — 라이선스 글(source)이나, 판 박힌 그 파일의 name 13 에 이름에 맞는 문구가 있으면 된다.
|
|
83
|
+
* 「OFL」 석 자만 적은 배포처 문구는 받지 않는다(판도 조건도 안 보인다). 목록 밖 이름은 사람이 읽을 것으로 남긴다
|
|
84
|
+
*/
|
|
85
|
+
export function licenseVerdict(f: Pick<PairFace, "family" | "weight" | "license" | "source">, sourceText: string, fileText: string): { problem?: string; note?: string } {
|
|
86
|
+
const where = `${f.family}(${f.weight})`;
|
|
87
|
+
const want = LICENSE_TEXT[f.license];
|
|
88
|
+
if (!want) return { note: `${where} 라이선스 ${f.license} — 목록 밖이라 사람이 글을 읽고 상업·영상 사용·출처 표기 의무를 확인한다` };
|
|
89
|
+
if (want.test(sourceText) || want.test(fileText)) return {};
|
|
90
|
+
return { problem: `${where} 라이선스 글(${f.source})에도 파일 name 13 에도 ${f.license} 문구가 없다 — 판 박힌 라이선스 글 주소를 찾거나, 배포처의 자체 표기뿐이면 license 를 목록 밖 이름으로 적어 사람 확인으로` };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** 주소가 판을 박았나 — @main·@latest·범위는 저쪽이 바뀌면 같은 영상이 다르게 구워진다 */
|
|
94
|
+
export function pinProblem(url: string): string | null {
|
|
95
|
+
let u: URL;
|
|
96
|
+
try {
|
|
97
|
+
u = new URL(url);
|
|
98
|
+
} catch {
|
|
99
|
+
return `주소가 아니다: ${url}`;
|
|
100
|
+
}
|
|
101
|
+
if (u.protocol !== "https:") return `https 가 아니다: ${url}`;
|
|
102
|
+
if (u.hostname === "fonts.googleapis.com") return "구글 폰트 CSS 는 unicode-range 조각이라 글이 투명하게 찍힌다 — 파일 하나 주소로";
|
|
103
|
+
if (u.hostname === "cdn.jsdelivr.net") {
|
|
104
|
+
const gh = /^\/gh\/([^/]+)\/([^/@]+)(?:@([^/]+))?\//.exec(u.pathname);
|
|
105
|
+
if (gh) {
|
|
106
|
+
const ref = gh[3];
|
|
107
|
+
if (!ref) return `판이 없다 — gh/${gh[1]}/${gh[2]}@<커밋|태그>`;
|
|
108
|
+
if (gh[1] === "google" && gh[2] === "fonts") return COMMIT.test(ref) && ref.length === 40 ? null : `google/fonts 는 커밋(40자)으로 박는다 — @${ref}`;
|
|
109
|
+
return COMMIT.test(ref) || VERSION.test(ref) ? null : `브랜치처럼 보인다 — @${ref}, 커밋이나 판 태그로`;
|
|
110
|
+
}
|
|
111
|
+
const npm = /^\/npm\/((?:@[^/]+\/)?[^/@]+)(?:@([^/]+))?\//.exec(u.pathname);
|
|
112
|
+
if (npm) {
|
|
113
|
+
if (!npm[2]) return `판이 없다 — npm/${npm[1]}@<x.y.z>`;
|
|
114
|
+
return EXACT.test(npm[2]) ? null : `판 범위 @${npm[2]} — 정확한 판(x.y.z)으로`;
|
|
115
|
+
}
|
|
116
|
+
return `jsdelivr 의 gh·npm 꼴이 아니다: ${u.pathname}`;
|
|
117
|
+
}
|
|
118
|
+
if (u.hostname === "raw.githubusercontent.com") {
|
|
119
|
+
const ref = u.pathname.split("/")[3] ?? "";
|
|
120
|
+
return COMMIT.test(ref) && ref.length === 40 ? null : `raw.githubusercontent 는 커밋(40자)으로 박는다 — ${ref}`;
|
|
121
|
+
}
|
|
122
|
+
return `판이 박혔는지 모르는 배포처 ${u.hostname} — jsdelivr gh@커밋·npm@판으로`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** CSS font-family 값 → 이름 차례(따옴표 벗김) */
|
|
126
|
+
export const stackFamilies = (stack: string): string[] => stack.split(",").map((s) => s.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
|
|
127
|
+
|
|
128
|
+
const FALLBACK = "Pretendard Variable";
|
|
129
|
+
const fileExt = (src: string) => /\.(woff2|woff|ttf|otf)$/i.exec(new URL(src).pathname)?.[1];
|
|
130
|
+
|
|
131
|
+
/** 받지 않고 보는 것 — 칸·판·라이선스 이름·스택 */
|
|
132
|
+
export function pairProblems(p: FontPair): string[] {
|
|
133
|
+
const bad: string[] = [];
|
|
134
|
+
for (const k of ["id", "name", "ko", "sans", "display", "mono", "tracking"] as const) if (!p[k]) bad.push(`${k} 가 비었다`);
|
|
135
|
+
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(p.id)) bad.push(`id ${p.id} 은 소문자·숫자·하이픈`);
|
|
136
|
+
if (!p.tags?.length) bad.push("tags 가 비었다");
|
|
137
|
+
if (p.hangul !== "full" && p.hangul !== "ks2350") bad.push(`hangul 은 full·ks2350 — ${p.hangul}`);
|
|
138
|
+
for (const k of ["weightDisplay", "weightBody"] as const) if (!(p[k] >= 1 && p[k] <= 1000)) bad.push(`${k} ${p[k]} 이 1–1000 밖`);
|
|
139
|
+
if (!/^(0|-?\d*\.?\d+em)$/.test(p.tracking ?? "")) bad.push(`tracking ${p.tracking} 은 em 값`);
|
|
140
|
+
if (!p.faces?.length) return [...bad, "faces 가 비었다"];
|
|
141
|
+
const families = new Set(p.faces.map((f) => f.family));
|
|
142
|
+
for (const k of ["sans", "display", "mono"] as const) {
|
|
143
|
+
const st = stackFamilies(p[k] ?? "");
|
|
144
|
+
if (!families.has(st[0])) bad.push(`${k} 첫 글꼴 ${st[0]} 의 face 가 없다 — 기계마다 다른 시스템 글꼴로 찍힌다`);
|
|
145
|
+
if (!st.includes(FALLBACK)) bad.push(`${k} 가 ${FALLBACK} 로 끝나지 않는다 — 한글이 설 바닥이 없다`);
|
|
146
|
+
}
|
|
147
|
+
if (!families.has(FALLBACK)) bad.push(`${FALLBACK} face 가 없다`);
|
|
148
|
+
const used = new Set(["sans", "display", "mono"].flatMap((k) => stackFamilies(p[k as "sans"])));
|
|
149
|
+
for (const f of p.faces) {
|
|
150
|
+
const where = `${f.family}(${f.weight ?? "?"})`;
|
|
151
|
+
if (!used.has(f.family)) bad.push(`${where} 를 어느 스택도 안 쓴다`);
|
|
152
|
+
const pin = pinProblem(f.src);
|
|
153
|
+
if (pin) bad.push(`${where} 주소 — ${pin}`);
|
|
154
|
+
else if (!fileExt(f.src)) bad.push(`${where} 주소가 .woff2·.woff·.ttf·.otf 파일이 아니다`);
|
|
155
|
+
if (!f.weight || !/^\d+( \d+)?$/.test(f.weight)) bad.push(`${where} weight 는 "700" 이나 가변 범위 "100 900"`);
|
|
156
|
+
if (!f.license) bad.push(`${where} license 가 없다`);
|
|
157
|
+
if (!f.source) bad.push(`${where} source 가 없다`);
|
|
158
|
+
else if (!/^https:\/\/\S+$/.test(f.source)) bad.push(`${where} source 가 https 주소가 아니다`);
|
|
159
|
+
}
|
|
160
|
+
return bad;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** 파일에서 읽은 것 중 판정에 드는 것 */
|
|
164
|
+
export interface FaceFile {
|
|
165
|
+
cps: Set<number>;
|
|
166
|
+
weight: number;
|
|
167
|
+
wght?: [number, number];
|
|
168
|
+
/** 파일 name 표의 family(1·16) — 크롬이 찍은 글꼴을 이 이름으로 댄다 */
|
|
169
|
+
names: string[];
|
|
170
|
+
licenseText?: string;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const rangeOf = (f: FontFace): [number, number] => {
|
|
174
|
+
const [a, b = a] = (f.weight ?? "100 900").split(" ").map(Number);
|
|
175
|
+
return [a, b];
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/** CSS 글꼴 고르기의 굵기 차례 — 같은 이름 face 중 w 에 쓸 하나 */
|
|
179
|
+
export function faceFor(faces: PairFace[], family: string, w: number): PairFace | undefined {
|
|
180
|
+
const c = faces.filter((f) => f.family === family);
|
|
181
|
+
const hit = c.find((f) => rangeOf(f)[0] <= w && w <= rangeOf(f)[1]);
|
|
182
|
+
if (hit || !c.length) return hit;
|
|
183
|
+
const below = c.filter((f) => rangeOf(f)[1] < w).sort((a, b) => rangeOf(b)[1] - rangeOf(a)[1]);
|
|
184
|
+
const above = c.filter((f) => rangeOf(f)[0] > w).sort((a, b) => rangeOf(a)[0] - rangeOf(b)[0]);
|
|
185
|
+
if (w >= 400 && w <= 500) {
|
|
186
|
+
const upTo500 = above.filter((f) => rangeOf(f)[0] <= 500);
|
|
187
|
+
return upTo500[0] ?? below[0] ?? above[0];
|
|
188
|
+
}
|
|
189
|
+
return w < 400 ? (below[0] ?? above[0]) : (above[0] ?? below[0]);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** 이 굵기로 고른 face 가 실제로 내는 굵기 */
|
|
193
|
+
const servedWeight = (f: PairFace, w: number): number => {
|
|
194
|
+
const [a, b] = rangeOf(f);
|
|
195
|
+
return Math.min(b, Math.max(a, w));
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/** 글자 하나를 스택에서 누가 그리나 — 짝의 face 가 아무도 없으면 null(시스템 글꼴로 떨어짐) */
|
|
199
|
+
export function faceForChar(p: FontPair, stack: string, w: number, cp: number, files: Map<string, FaceFile>): PairFace | null {
|
|
200
|
+
for (const fam of stackFamilies(stack)) {
|
|
201
|
+
const f = faceFor(p.faces, fam, w);
|
|
202
|
+
if (f && files.get(f.src)?.cps.has(cp)) return f;
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** 견본 — 한글 줄은 KS X 1001 안 낱자만이라 ks2350 짝도 한 글꼴로 찍혀야 한다 */
|
|
208
|
+
export const FONT_SAMPLE = { ko: "다람쥐 헌 쳇바퀴에 타고파", latin: "Sphinx of black quartz, judge my vow", num: "₩12,000 · 45% → 0123456789" } as const;
|
|
209
|
+
export type SampleLine = keyof typeof FONT_SAMPLE;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 제목에 실제로 쓸 굵기 — 제목 스택 첫 글꼴(monoAll 이면 고정폭)의 가장 굵은 실제 단을 넘지 않게.
|
|
213
|
+
* 넘겨 두면 크롬이 가짜 굵기로 그리고, 도트 글꼴은 화소가 번진다(galmuri 짝의 GalmuriMono11 은 400 하나뿐)
|
|
214
|
+
*/
|
|
215
|
+
export function titleWeight(p: FontPair, monoAll = false): number {
|
|
216
|
+
const fam = stackFamilies(monoAll ? p.mono : p.display)[0];
|
|
217
|
+
const faces = p.faces.filter((f) => f.family === fam);
|
|
218
|
+
return faces.length ? Math.min(p.weightDisplay, Math.max(...faces.map((f) => rangeOf(f)[1]))) : p.weightDisplay;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export type StackName = "display" | "sans" | "mono";
|
|
222
|
+
/** 짝이 실제로 쓰는 (스택, 굵기) — monoAll 짝은 고정폭이 제목·본문을 다 맡는다 */
|
|
223
|
+
export function stackUses(p: FontPair): { stack: StackName; css: string; weight: number }[] {
|
|
224
|
+
const out = [
|
|
225
|
+
{ stack: "display" as const, css: p.display, weight: titleWeight(p) },
|
|
226
|
+
{ stack: "sans" as const, css: p.sans, weight: p.weightBody },
|
|
227
|
+
{ stack: "mono" as const, css: p.mono, weight: p.weightBody },
|
|
228
|
+
];
|
|
229
|
+
if (p.monoAll) out.push({ stack: "mono", css: p.mono, weight: titleWeight(p, true) });
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export interface PairMeasure {
|
|
234
|
+
/** 스택마다 한글을 그리는 face 의 덮음 중 가장 나쁜 것 */
|
|
235
|
+
hangul: HangulCover;
|
|
236
|
+
syllables: number;
|
|
237
|
+
/** 제목 한글 글꼴의 가장 굵은 단 */
|
|
238
|
+
boldest: number;
|
|
239
|
+
problems: string[];
|
|
240
|
+
notes: string[];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const RANK: Record<HangulCover, number> = { full: 3, ks2350: 2, partial: 1, none: 0 };
|
|
244
|
+
|
|
245
|
+
/** 스택에서 한글을 맡는 face — 음절을 하나라도 가진 첫 face(「가」 하나로 보면 그 글자만 빠진 글꼴을 놓친다) */
|
|
246
|
+
function hangulFace(p: FontPair, stack: string, w: number, files: Map<string, FaceFile>): PairFace | null {
|
|
247
|
+
for (const fam of stackFamilies(stack)) {
|
|
248
|
+
const f = faceFor(p.faces, fam, w);
|
|
249
|
+
if (f && hangulCover(files.get(f.src)?.cps ?? new Set()).syllables) return f;
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** 받은 파일로 보는 것 — 한글 덮음·굵기·견본 글자가 시스템 글꼴로 떨어지는지 */
|
|
255
|
+
export function measurePair(p: FontPair, files: Map<string, FaceFile>): PairMeasure {
|
|
256
|
+
const problems: string[] = [];
|
|
257
|
+
const notes: string[] = [];
|
|
258
|
+
for (const f of p.faces) {
|
|
259
|
+
const file = files.get(f.src);
|
|
260
|
+
if (!file) continue;
|
|
261
|
+
const [a, b] = rangeOf(f);
|
|
262
|
+
const where = `${f.family}(${f.weight})`;
|
|
263
|
+
// 한 굵기 제목체를 제목 굵기로 적은 것은 가짜 굵기를 막으려는 뜻이라 알리지 않는다
|
|
264
|
+
const meant = a === p.weightDisplay && p.faces.filter((x) => x.family === f.family).length === 1;
|
|
265
|
+
if (a !== b) {
|
|
266
|
+
if (!file.wght) problems.push(`${where} 는 가변이 아닌데 굵기 범위를 적었다 — 파일은 고정 ${file.weight}`);
|
|
267
|
+
else if (a < file.wght[0] || b > file.wght[1]) problems.push(`${where} 범위가 파일 wght ${file.wght.join("–")} 밖`);
|
|
268
|
+
} else if (!file.wght && !meant && Math.abs(file.weight - a) >= 100) notes.push(`${where} 파일 usWeightClass 는 ${file.weight}`);
|
|
269
|
+
}
|
|
270
|
+
let worst: HangulCover = "full";
|
|
271
|
+
let syllables = 11172;
|
|
272
|
+
for (const u of stackUses(p)) {
|
|
273
|
+
for (const [line, text] of Object.entries(FONT_SAMPLE)) {
|
|
274
|
+
const sys = [...new Set([...text].filter((ch) => ch !== " " && !faceForChar(p, u.css, u.weight, ch.codePointAt(0)!, files)))];
|
|
275
|
+
if (sys.length) problems.push(`${u.stack} ${line} 줄 ${sys.join("")} 를 짝의 글꼴이 못 그려 시스템 글꼴로 떨어진다`);
|
|
276
|
+
}
|
|
277
|
+
const ko = hangulFace(p, u.css, u.weight, files);
|
|
278
|
+
const cov = ko ? hangulCover(files.get(ko.src)!.cps) : { cover: "none" as const, syllables: 0 };
|
|
279
|
+
if (RANK[cov.cover] < RANK[worst] || (cov.cover === worst && cov.syllables < syllables)) [worst, syllables] = [cov.cover, cov.syllables];
|
|
280
|
+
for (const fam of new Set([ko?.family, stackFamilies(u.css)[0]])) {
|
|
281
|
+
const f = fam && faceFor(p.faces, fam, u.weight);
|
|
282
|
+
if (!f) continue;
|
|
283
|
+
const got = servedWeight(f, u.weight);
|
|
284
|
+
if (u.weight >= 600 && got < 600) problems.push(`${u.stack} ${fam} 에 굵기 ${u.weight} 단이 없어 크롬이 가짜 굵기로 그린다(있는 단 ${got})`);
|
|
285
|
+
else if (Math.abs(got - u.weight) >= 150) notes.push(`${u.stack} ${fam} 굵기 ${u.weight} 대신 ${got}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (worst === "partial" || worst === "none") problems.push(`한글 음절 ${syllables}자 — KS X 1001 2,350자를 다 덮지 못한다`);
|
|
289
|
+
else if (worst !== p.hangul) problems.push(`hangul 을 ${p.hangul} 로 적었는데 재 보면 ${worst}`);
|
|
290
|
+
const ko = hangulFace(p, p.display, titleWeight(p), files);
|
|
291
|
+
const boldest = ko ? Math.max(...p.faces.filter((f) => f.family === ko.family).map((f) => rangeOf(f)[1])) : 0;
|
|
292
|
+
if (titleWeight(p) < p.weightDisplay) notes.push(`제목 굵기 ${p.weightDisplay} → ${titleWeight(p)}(제목 첫 글꼴의 가장 굵은 단)`);
|
|
293
|
+
if (p.monoAll && titleWeight(p, true) < p.weightDisplay) notes.push(`monoAll 세계에서는 제목 굵기 ${p.weightDisplay} → ${titleWeight(p, true)}(고정폭 face 의 가장 굵은 단)`);
|
|
294
|
+
if (ko && boldest < 600 && !p.tags.includes("display")) notes.push(`제목 굵은 단이 없다 — 한글 제목 글꼴 ${ko.family} 의 가장 굵은 단 ${boldest}`);
|
|
295
|
+
return { hangul: worst, syllables, boldest, problems, notes };
|
|
296
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
// 페이지(Vite) 쪽. 테마 폴더를 늘려도 이 파일은 그대로 — themes/*/effects.ts 를 모아 id 로 꺼낸다.
|
|
3
|
+
import { worldOf } from "./name.ts";
|
|
4
|
+
|
|
3
5
|
const mods = import.meta.glob("../../../themes/*/effects.ts", { eager: true }) as Record<string, Record<string, unknown>>;
|
|
4
6
|
|
|
5
7
|
const byId = new Map(Object.entries(mods).map(([path, mod]) => [path.split("/").at(-2)!, mod]));
|
|
6
8
|
|
|
7
9
|
export const themeIds = (): string[] => [...byId.keys()].sort();
|
|
8
10
|
|
|
9
|
-
/** 장면에서: `const { plateStamp, misregister } = fx("riso-zine")` */
|
|
11
|
+
/** 장면에서: `const { plateStamp, misregister } = fx("riso-zine")` — 변주 이름(`riso-zine/night-teal`)이면 그 세계 효과 */
|
|
10
12
|
export function fx<T = Record<string, (...args: any[]) => any>>(id: string): T {
|
|
11
|
-
const mod = byId.get(id);
|
|
13
|
+
const mod = byId.get(worldOf(id));
|
|
12
14
|
if (!mod) throw new Error(`테마 효과 "${id}" 가 없다. 있는 테마: ${themeIds().join(" ") || "(없음)"}`);
|
|
13
15
|
return mod as T;
|
|
14
16
|
}
|