kasaframe 0.1.4 → 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/cli/src/commands/doctor.ts +15 -2
- package/packages/cli/src/commands/fonts.ts +118 -0
- 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/update.ts +250 -0
- package/packages/plan/src/asks.ts +1 -1
- package/packages/plan/src/themes.ts +8 -2
- package/packages/themes/kit/THEME-MAKE.md +2 -2
- 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 +243 -79
- package/packages/themes/package.json +3 -1
- package/packages/themes/src/fontfile.ts +183 -0
- package/packages/themes/src/fonts.ts +296 -0
- package/packages/themes/src/index.ts +10 -2
- package/packages/themes/src/look.ts +34 -8
- package/packages/themes/src/types.ts +5 -0
- package/packages/themes/src/vary.ts +35 -34
- package/presets/kawaii-pastel.json +2 -2
- package/skills/kasaframe/SKILL.md +1 -0
- package/themes/blueprint/sample/style.json +5 -5
- package/themes/blueprint/style.json +5 -5
- package/themes/blueprint/theme.json +3 -1
- package/themes/blueprint/variants/deep-rose-suit.json +1 -1
- package/themes/blueprint/variants/navy-coral-2.json +1 -1
- package/themes/blueprint/variants/navy-coral-suit.json +1 -1
- package/themes/blueprint/variants/navy-orange-2.json +1 -1
- package/themes/blueprint/variants/navy-orange-suit.json +1 -1
- package/themes/cartography/sample/style.json +2 -2
- package/themes/cartography/style.json +2 -2
- package/themes/cartography/theme.json +3 -1
- package/themes/cartography/variants/deep-plum.json +1 -1
- package/themes/cartography/variants/frost-teal.json +1 -1
- package/themes/cartography/variants/navy-green.json +1 -1
- package/themes/circuit-board/sample/style.json +2 -2
- package/themes/circuit-board/style.json +2 -2
- package/themes/circuit-board/theme.json +3 -1
- package/themes/circuit-board/variants/dusk-gold-pretendard.json +1 -1
- package/themes/circuit-board/variants/navy-gold-grain.json +1 -1
- package/themes/circuit-board/variants/wine-gold-pretendard.json +1 -1
- package/themes/clay-toy/sample/style.json +2 -2
- package/themes/clay-toy/style.json +2 -2
- package/themes/clay-toy/theme.json +3 -1
- package/themes/clay-toy/variants/blush-coral-grain.json +1 -1
- package/themes/clay-toy/variants/chalk-teal.json +1 -1
- package/themes/clay-toy/variants/frost-sky.json +1 -1
- package/themes/clay-toy/variants/frost-teal.json +1 -1
- package/themes/clay-toy/variants/lilac-plum-round.json +1 -1
- package/themes/clay-toy/variants/lilac-plum.json +1 -1
- package/themes/clay-toy/variants/sage-indigo.json +1 -1
- package/themes/conveyor-factory/sample/style.json +3 -3
- package/themes/conveyor-factory/style.json +3 -3
- package/themes/conveyor-factory/theme.json +3 -1
- package/themes/conveyor-factory/variants/cocoa-gold.json +1 -1
- package/themes/conveyor-factory/variants/navy-gold.json +1 -1
- package/themes/conveyor-factory/variants/slate-amber-2.json +1 -1
- package/themes/conveyor-factory/variants/slate-amber-suit.json +1 -1
- package/themes/conveyor-factory/variants/slate-gold-bold.json +1 -1
- package/themes/data-journal/theme.json +3 -1
- package/themes/data-journal/variants/deep-coral.json +1 -1
- package/themes/data-journal/variants/navy-lime.json +1 -1
- package/themes/data-journal/variants/wine-indigo.json +1 -1
- package/themes/debug-hud/sample/style.json +1 -1
- package/themes/debug-hud/style.json +1 -1
- package/themes/debug-hud/theme.json +2 -1
- package/themes/debug-hud/variants/cocoa-teal.json +2 -2
- package/themes/debug-hud/variants/deep-teal-scan.json +2 -2
- package/themes/debug-hud/variants/dusk-plum.json +2 -2
- package/themes/debug-hud/variants/forest-lime.json +2 -2
- package/themes/debug-hud/variants/slate-green.json +2 -2
- package/themes/debug-hud/variants/wine-gold.json +2 -2
- package/themes/depth-flight/sample/style.json +1 -1
- package/themes/depth-flight/style.json +1 -1
- package/themes/depth-flight/theme.json +3 -1
- package/themes/depth-flight/variants/cocoa-cyan.json +1 -1
- package/themes/depth-flight/variants/cocoa-violet.json +1 -1
- package/themes/depth-flight/variants/deep-sky.json +1 -1
- package/themes/depth-flight/variants/forest-blue.json +1 -1
- package/themes/depth-flight/variants/navy-sky-grain.json +1 -1
- package/themes/depth-flight/variants/navy-sky.json +1 -1
- package/themes/depth-flight/variants/night-teal.json +1 -1
- package/themes/depth-flight/variants/slate-indigo.json +1 -1
- package/themes/depth-flight/variants/slate-violet.json +1 -1
- package/themes/film-reel/sample/style.json +2 -2
- package/themes/film-reel/style.json +2 -2
- package/themes/film-reel/theme.json +3 -1
- package/themes/film-reel/variants/chalk-pink.json +1 -1
- package/themes/film-reel/variants/lilac-gold.json +1 -1
- package/themes/film-reel/variants/lilac-violet.json +1 -1
- package/themes/glass-light/sample/style.json +1 -1
- package/themes/glass-light/style.json +1 -1
- package/themes/glass-light/theme.json +3 -1
- package/themes/glass-light/variants/blush-pink.json +1 -1
- package/themes/glass-light/variants/blush-plum.json +1 -1
- package/themes/glass-light/variants/cream-violet.json +1 -1
- package/themes/glass-light/variants/frost-sky-suit.json +1 -1
- package/themes/glass-light/variants/frost-sky.json +1 -1
- package/themes/glass-light/variants/sage-green.json +1 -1
- package/themes/ink-wash/sample/style.json +3 -3
- package/themes/ink-wash/style.json +3 -3
- package/themes/ink-wash/theme.json +3 -1
- package/themes/iso-diorama/sample/style.json +1 -1
- package/themes/iso-diorama/style.json +1 -1
- package/themes/iso-diorama/theme.json +3 -1
- package/themes/iso-diorama/variants/chalk-plum.json +1 -1
- package/themes/iso-diorama/variants/cocoa-indigo.json +1 -1
- package/themes/iso-diorama/variants/dusk-blue.json +1 -1
- package/themes/iso-diorama/variants/dusk-coral.json +1 -1
- package/themes/iso-diorama/variants/dusk-jade.json +1 -1
- package/themes/iso-diorama/variants/forest-cyan.json +1 -1
- package/themes/iso-diorama/variants/frost-green.json +1 -1
- package/themes/iso-diorama/variants/frost-teal.json +1 -1
- package/themes/iso-diorama/variants/navy-amber.json +1 -1
- package/themes/isotype/sample/style.json +2 -2
- package/themes/isotype/style.json +2 -2
- package/themes/isotype/theme.json +2 -1
- package/themes/isotype/variants/blush-rose.json +1 -1
- package/themes/isotype/variants/chalk-coral.json +1 -1
- package/themes/isotype/variants/chalk-orange.json +1 -1
- package/themes/isotype/variants/frost-amber.json +1 -1
- package/themes/isotype/variants/frost-coral.json +1 -1
- package/themes/isotype/variants/frost-orange.json +1 -1
- package/themes/isotype/variants/sand-amber.json +1 -1
- package/themes/kawaii-sticker/sample/style.json +2 -2
- package/themes/kawaii-sticker/style.json +2 -2
- package/themes/kawaii-sticker/theme.json +3 -1
- package/themes/kawaii-sticker/variants/cream-gold.json +1 -1
- package/themes/kawaii-sticker/variants/dusk-green.json +1 -1
- package/themes/kawaii-sticker/variants/dusk-indigo.json +1 -1
- package/themes/kawaii-sticker/variants/dusk-sky.json +1 -1
- package/themes/kawaii-sticker/variants/dusk-teal.json +1 -1
- package/themes/kawaii-sticker/variants/frost-blue.json +1 -1
- package/themes/kawaii-sticker/variants/frost-plum.json +1 -1
- package/themes/kawaii-sticker/variants/lilac-violet.json +1 -1
- package/themes/kawaii-sticker/variants/navy-gold.json +1 -1
- package/themes/kawaii-sticker/variants/navy-plum.json +1 -1
- package/themes/lab-notebook/sample/style.json +3 -3
- package/themes/lab-notebook/style.json +3 -3
- package/themes/lab-notebook/theme.json +2 -1
- package/themes/lab-notebook/variants/blush-orange.json +1 -1
- package/themes/lab-notebook/variants/chalk-coral.json +1 -1
- package/themes/lab-notebook/variants/chalk-orange.json +1 -1
- package/themes/lab-notebook/variants/chalk-rose.json +1 -1
- package/themes/lab-notebook/variants/lilac-rose.json +1 -1
- package/themes/lab-notebook/variants/sage-rose.json +1 -1
- package/themes/lab-notebook/variants/sand-orange.json +1 -1
- package/themes/lab-notebook/variants/snow-orange.json +1 -1
- package/themes/lab-notebook/variants/snow-rose.json +1 -1
- package/themes/manga-panel/sample/style.json +3 -3
- package/themes/manga-panel/style.json +3 -3
- package/themes/manga-panel/theme.json +3 -1
- package/themes/manga-panel/variants/chalk-cyan-dots.json +1 -1
- package/themes/manga-panel/variants/chalk-rose.json +1 -1
- package/themes/manga-panel/variants/snow-cyan.json +1 -1
- package/themes/manga-panel/variants/snow-magenta-dots.json +1 -1
- package/themes/marble-run/sample/style.json +2 -2
- package/themes/marble-run/style.json +2 -2
- package/themes/marble-run/theme.json +3 -1
- package/themes/marble-run/variants/slate-orange.json +1 -1
- package/themes/neon-sign/sample/style.json +2 -2
- package/themes/neon-sign/style.json +2 -2
- package/themes/neon-sign/theme.json +2 -1
- package/themes/neon-sign/variants/dusk-plum.json +1 -1
- package/themes/neon-sign/variants/forest-magenta.json +1 -1
- package/themes/neon-sign/variants/night-pink.json +1 -1
- package/themes/neon-sign/variants/night-violet.json +1 -1
- package/themes/neon-sign/variants/slate-violet.json +1 -1
- package/themes/newspaper/sample/style.json +2 -2
- package/themes/newspaper/style.json +2 -2
- package/themes/newspaper/theme.json +2 -1
- package/themes/newspaper/variants/blush-amber.json +1 -1
- package/themes/newspaper/variants/chalk-magenta.json +1 -1
- package/themes/newspaper/variants/frost-sky.json +1 -1
- package/themes/newspaper/variants/lilac-green.json +1 -1
- package/themes/newspaper/variants/sage-lime.json +1 -1
- 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 +64 -49
- package/themes/og-measure/variants/snow-blue.json +90 -0
- package/themes/orbit-system/sample/style.json +3 -3
- package/themes/orbit-system/style.json +3 -3
- package/themes/orbit-system/theme.json +3 -1
- package/themes/orbit-system/variants/cocoa-amber.json +1 -1
- package/themes/orbit-system/variants/cocoa-cyan.json +1 -1
- package/themes/orbit-system/variants/dusk-magenta.json +1 -1
- package/themes/orbit-system/variants/dusk-violet.json +1 -1
- package/themes/orbit-system/variants/forest-coral.json +1 -1
- package/themes/orbit-system/variants/wine-amber.json +1 -1
- package/themes/orbit-system/variants/wine-magenta.json +1 -1
- package/themes/origami-fold/sample/style.json +1 -1
- package/themes/origami-fold/style.json +1 -1
- package/themes/origami-fold/theme.json +3 -1
- package/themes/origami-fold/variants/deep-amber-sunflower.json +1 -1
- package/themes/origami-fold/variants/deep-orange.json +1 -1
- package/themes/origami-fold/variants/forest-coral.json +1 -1
- package/themes/origami-fold/variants/forest-pink.json +1 -1
- package/themes/origami-fold/variants/frost-amber-paper.json +1 -1
- package/themes/origami-fold/variants/sage-coral-sunflower.json +1 -1
- package/themes/origami-fold/variants/sage-coral.json +1 -1
- package/themes/origami-fold/variants/sage-pink-round.json +1 -1
- package/themes/origami-fold/variants/sage-pink.json +1 -1
- package/themes/paper-cutout/sample/style.json +2 -2
- package/themes/paper-cutout/style.json +2 -2
- package/themes/paper-cutout/theme.json +3 -1
- package/themes/paper-cutout/variants/blush-violet.json +1 -1
- package/themes/paper-cutout/variants/frost-lime.json +1 -1
- package/themes/paper-cutout/variants/lilac-gold.json +1 -1
- package/themes/paper-cutout/variants/sage-cyan.json +1 -1
- package/themes/paper-cutout/variants/sage-green.json +1 -1
- package/themes/paper-cutout/variants/sage-pink.json +1 -1
- package/themes/paper-cutout/variants/sand-blue.json +1 -1
- package/themes/pixel-quest/theme.json +2 -1
- package/themes/receipt/sample/style.json +2 -2
- package/themes/receipt/style.json +2 -2
- package/themes/riso-zine/sample/style.json +2 -2
- package/themes/riso-zine/style.json +2 -2
- package/themes/riso-zine/theme.json +3 -1
- package/themes/riso-zine/variants/chalk-lime.json +1 -1
- package/themes/riso-zine/variants/frost-green.json +1 -1
- package/themes/riso-zine/variants/lilac-plum.json +1 -1
- package/themes/riso-zine/variants/lilac-violet.json +1 -1
- package/themes/server-hall/sample/style.json +2 -2
- package/themes/server-hall/style.json +2 -2
- package/themes/server-hall/theme.json +3 -1
- package/themes/server-hall/variants/night-indigo.json +1 -1
- package/themes/server-hall/variants/wine-plum.json +1 -1
- package/themes/subway-map/sample/style.json +2 -2
- package/themes/subway-map/style.json +2 -2
- package/themes/subway-map/theme.json +3 -1
- package/themes/subway-map/variants/deep-green.json +1 -1
- package/themes/subway-map/variants/deep-teal.json +1 -1
- package/themes/subway-map/variants/frost-jade.json +1 -1
- package/themes/subway-map/variants/frost-teal.json +1 -1
- package/themes/subway-map/variants/slate-green.json +1 -1
- package/themes/subway-map/variants/snow-jade.json +1 -1
- package/themes/swiss-poster/sample/style.json +2 -2
- package/themes/swiss-poster/style.json +2 -2
- package/themes/swiss-poster/theme.json +3 -1
- package/themes/swiss-poster/variants/chalk-plum.json +1 -1
- package/themes/swiss-poster/variants/cocoa-cyan.json +1 -1
- package/themes/swiss-poster/variants/dusk-magenta.json +1 -1
- package/themes/swiss-poster/variants/frost-indigo.json +1 -1
- package/themes/swiss-poster/variants/slate-orange.json +1 -1
- package/themes/water-works/sample/style.json +1 -1
- package/themes/water-works/style.json +1 -1
- package/themes/water-works/theme.json +3 -1
- package/themes/water-works/variants/chalk-cyan.json +1 -1
- package/themes/water-works/variants/frost-indigo.json +1 -1
- package/themes/water-works/variants/sand-amber.json +1 -1
- package/themes/water-works/variants/sand-plum.json +1 -1
- package/themes/whiteboard/sample/style.json +2 -2
- package/themes/whiteboard/style.json +2 -2
- package/themes/whiteboard/theme.json +3 -1
- package/themes/whiteboard/variants/chalk-coral.json +1 -1
- package/themes/whiteboard/variants/cream-lime.json +1 -1
- package/themes/whiteboard/variants/frost-teal.json +1 -1
- package/themes/whiteboard/variants/lilac-indigo.json +1 -1
- package/themes/whiteboard/variants/sage-plum.json +1 -1
- package/themes/whiteboard/variants/snow-cyan.json +1 -1
- package/themes/whiteboard/variants/snow-green.json +1 -1
- package/themes/whiteboard/variants/snow-magenta.json +1 -1
- package/themes/og-measure/sample/assets/brand/og-logo-full.svg +0 -12
- package/themes/og-measure/variants/chalk-orange.json +0 -102
- package/themes/og-measure/variants/cocoa-amber.json +0 -138
- package/themes/og-measure/variants/cocoa-cyan.json +0 -138
- package/themes/og-measure/variants/cocoa-gold.json +0 -129
- package/themes/og-measure/variants/cream-lime.json +0 -137
- package/themes/og-measure/variants/deep-jade.json +0 -100
- package/themes/og-measure/variants/dusk-violet.json +0 -133
- package/themes/og-measure/variants/forest-cyan.json +0 -123
- package/themes/og-measure/variants/frost-amber.json +0 -129
- package/themes/og-measure/variants/frost-cyan.json +0 -133
- package/themes/og-measure/variants/lilac-plum.json +0 -127
- package/themes/og-measure/variants/lilac-violet.json +0 -139
- package/themes/og-measure/variants/sage-green.json +0 -137
- package/themes/og-measure/variants/wine-jade.json +0 -127
- package/themes/og-measure/variants/wine-plum.json +0 -127
|
@@ -16,6 +16,12 @@ export const best = medians.indexOf(Math.min(...medians));
|
|
|
16
16
|
/** 장면 시작 이후의 박 시각(장면 안 초) */
|
|
17
17
|
export const beatsOf = (beats: { beats: number[] }) => beats.beats.filter((b) => b > -0.01);
|
|
18
18
|
|
|
19
|
+
/** 로고 잉크 — 마스터 로고는 한 색이고 어두운 바탕엔 흰색, 밝은 바탕엔 검정 두 가지뿐이다(스타일가이드 7절·BrandFoundation). 브랜드 파랑으로 칠하지 않는다 */
|
|
20
|
+
export const logoInk = (ctx: { style: { scheme: "light" | "dark" } }) => (ctx.style.scheme === "dark" ? "#FFFFFF" : "#000000");
|
|
21
|
+
|
|
22
|
+
/** 마스터 로고 원문을 상자에 채운다 — 루트 <svg> 의 width·height(1140×650)만 걷는다(안쪽 rect 의 width 는 그대로) */
|
|
23
|
+
export const fitSvg = (svg: string) => svg.replace(/<svg\b[^>]*>/, (tag) => tag.replace(/\s(width|height)="[^"]*"/g, "").replace("<svg", '<svg width="100%" height="100%"'));
|
|
24
|
+
|
|
19
25
|
/** 카메라가 잡는 무대 — 왼쪽 위를 축으로 trackCam 을 붙인다 */
|
|
20
26
|
export function stageOf(root: HTMLElement): HTMLElement {
|
|
21
27
|
const stage = document.createElement("div");
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { applyStyle, ease, progress, type SceneCtx } from "@kasaframe/core";
|
|
2
2
|
import { fx } from "@kasaframe/themes/fx";
|
|
3
|
-
import
|
|
3
|
+
import logo from "../assets/brand/og-logo.svg?raw";
|
|
4
|
+
import { beatsOf, best, data, fitSvg, logoInk, medians, stageOf } from "../lib/data.ts";
|
|
4
5
|
|
|
5
|
-
const { graphPaper, meter, stamp, flag, trackCam } = fx("og-measure");
|
|
6
|
+
const { graphPaper, meter, stamp, flag, pickFly, trackCam } = fx("og-measure");
|
|
6
7
|
|
|
7
8
|
// 실측 숫자 하나(직접 잰 첫 토큰 가운데값) → 끝 도장 → OG 로고 · 주소. 마지막 박까지 카메라가 툭 친다.
|
|
8
9
|
export default {
|
|
@@ -11,9 +12,9 @@ export default {
|
|
|
11
12
|
const stage = stageOf(root);
|
|
12
13
|
const b = beatsOf(ctx.beats);
|
|
13
14
|
const B = (i: number) => b[i] ?? i * 0.353;
|
|
14
|
-
const who = flag(stage, { x: 100, y: 90, text: `${data.models[best]} · 첫 토큰 가운데값`, at: 0, tone: "
|
|
15
|
+
const who = flag(stage, { x: 100, y: 90, text: `${data.models[best]} · 첫 토큰 가운데값`, at: 0, tone: "plain", size: 40 });
|
|
15
16
|
const big = document.createElement("div");
|
|
16
|
-
big.style.cssText = "position:absolute;left:100px;top:150px;font-family:var(--kf-font-display);font-weight:800;font-size:230px;line-height:1;letter-spacing:-0.
|
|
17
|
+
big.style.cssText = "position:absolute;left:100px;top:150px;font-family:var(--kf-font-display);font-weight:800;font-size:230px;line-height:1;letter-spacing:-0.055em;color:var(--kf-ink);font-variant-numeric:tabular-nums";
|
|
17
18
|
const num = document.createElement("span");
|
|
18
19
|
const unit = document.createElement("span");
|
|
19
20
|
unit.textContent = "ms";
|
|
@@ -21,11 +22,13 @@ export default {
|
|
|
21
22
|
big.append(num, unit);
|
|
22
23
|
stage.append(big);
|
|
23
24
|
const seal = stamp(stage, { x: 960, y: 430, at: B(1), date: data.measured_at_kst, calls: data.calls.length, cost: data.cost_usd, size: 30 });
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
stage.append(
|
|
25
|
+
const mark = document.createElement("div");
|
|
26
|
+
mark.style.cssText = `position:absolute;left:960px;top:560px;width:${(220 * 1140) / 650}px;height:220px;color:${logoInk(ctx)}`;
|
|
27
|
+
mark.innerHTML = fitSvg(logo);
|
|
28
|
+
stage.append(mark);
|
|
28
29
|
const site = flag(stage, { x: 600, y: 830, text: "opengateway.ai", at: B(3), tone: "plain", size: 80, mono: true });
|
|
30
|
+
// 로고는 translate 로 가운데를 맞춰 자리 재기에 안 잡힌다 — 그 칸은 좌표로 준다
|
|
31
|
+
const read = pickFly(stage, { items: [{ stops: [{ at: 0, target: who.el }, { at: B(0) + 0.2, target: num, pad: 8 }, { at: B(2), x: 960, y: 670, w: (220 * 1140) / 650 + 60, h: 280 }, { at: B(3), target: site.el }], hold: true }], size: 40 });
|
|
29
32
|
const shots = [
|
|
30
33
|
{ at: 0, x: 960, y: 540, kick: 0.08 },
|
|
31
34
|
{ at: B(2), x: 960, y: 560, zoom: 1.02, kick: 0.05 },
|
|
@@ -38,9 +41,10 @@ export default {
|
|
|
38
41
|
num.textContent = meter(t, { to: medians[best], at: 0, dur: 0.6, decimals: 1 });
|
|
39
42
|
seal(t);
|
|
40
43
|
const p = ease.snappy(progress(t, B(2), 0.22));
|
|
41
|
-
applyStyle(
|
|
42
|
-
|
|
44
|
+
applyStyle(mark, { opacity: p, y: 6 * (1 - p) });
|
|
45
|
+
mark.style.translate = "-50% 0";
|
|
43
46
|
site(t);
|
|
47
|
+
read(t);
|
|
44
48
|
applyStyle(stage, trackCam(t, { shots, scene: ctx.id }));
|
|
45
49
|
};
|
|
46
50
|
},
|
|
@@ -27,7 +27,7 @@ export default {
|
|
|
27
27
|
const SIZE = 62;
|
|
28
28
|
const label = (i: number) => `${OG.owners[i][0]} ${OG.owners[i][1]}`;
|
|
29
29
|
// 첫 칩은 0초에 — 첫 박(0.29초)까지 화면이 멈춰 있지 않게
|
|
30
|
-
const chips = OG.owners.map((_, i) => flag(stage, { x: SPOTS[i][0], y: SPOTS[i][1], text: label(i), at: i === 0 ? 0 : B(AT[i]), until: B(6), tone: "
|
|
30
|
+
const chips = OG.owners.map((_, i) => flag(stage, { x: SPOTS[i][0], y: SPOTS[i][1], text: label(i), at: i === 0 ? 0 : B(AT[i]), until: B(6), tone: "plain", size: SIZE, mono: true }));
|
|
31
31
|
const width = (s: string) => s.length * SIZE * 0.6 + 40;
|
|
32
32
|
const fly = pickFly(stage, {
|
|
33
33
|
items: OG.owners.map((o, i) => ({ at: B(6), text: o[0], from: { x: SPOTS[i][0] + width(label(i)) / 2, y: SPOTS[i][1] + SIZE * 0.7 }, to: { x: 960, y: 540 } })),
|
|
@@ -35,6 +35,16 @@ export default {
|
|
|
35
35
|
size: 52,
|
|
36
36
|
});
|
|
37
37
|
const card = callLine(stage, { x: 160, y: 430, w: 1600, at: B(6) + 0.2, size: 84, url: [OG.host, "/v1"], urlAt: [B(6) + 0.24, B(7)] });
|
|
38
|
+
// 읽는 칸 — 칩이 뜨는 박마다 그 칩을 짚고(같은 박에 둘이면 반 박 뒤 둘째), 모인 뒤엔 주소 조각으로
|
|
39
|
+
const half = 60 / ctx.beats.bpm / 2;
|
|
40
|
+
const seen = new Set<number>();
|
|
41
|
+
const reads = OG.owners.map((_, i) => {
|
|
42
|
+
const at = i === 0 ? 0 : B(AT[i]);
|
|
43
|
+
const t = seen.has(at) ? at + half : at;
|
|
44
|
+
seen.add(at);
|
|
45
|
+
return { at: t, target: chips[i].el };
|
|
46
|
+
});
|
|
47
|
+
const read = pickFly(stage, { items: [{ stops: [...reads, { at: B(6) + 0.24, target: card.parts[0] }, { at: B(7), target: card.parts[1] }], hold: true }], size: SIZE });
|
|
38
48
|
const shots = [
|
|
39
49
|
{ at: 0, x: 960, y: 540 },
|
|
40
50
|
{ at: B(4), x: 920, y: 530, zoom: 1.04, kick: 0.04 },
|
|
@@ -45,6 +55,7 @@ export default {
|
|
|
45
55
|
for (const c of chips) c(t);
|
|
46
56
|
fly(t);
|
|
47
57
|
card(t);
|
|
58
|
+
read(t);
|
|
48
59
|
applyStyle(stage, trackCam(t, { shots, scene: ctx.id }));
|
|
49
60
|
};
|
|
50
61
|
},
|
|
@@ -2,7 +2,7 @@ import { applyStyle, type SceneCtx } from "@kasaframe/core";
|
|
|
2
2
|
import { fx } from "@kasaframe/themes/fx";
|
|
3
3
|
import { OG, beatsOf, stageOf } from "../lib/data.ts";
|
|
4
4
|
|
|
5
|
-
const { graphPaper, callLine, rawLog, flag, trackCam } = fx("og-measure");
|
|
5
|
+
const { graphPaper, callLine, rawLog, flag, pickFly, trackCam } = fx("og-measure");
|
|
6
6
|
|
|
7
7
|
// 같은 주소로 가는 요청 줄 — 박마다 모델 ID 하나, 드롭 앞 마지막 마디는 반 박마다 하나씩 몰아 올린다. 결과는 붙이지 않는다(부르지 않은 호출).
|
|
8
8
|
export default {
|
|
@@ -16,7 +16,17 @@ export default {
|
|
|
16
16
|
const lines = OG.requests.map(([path, model]) => `POST ${path.padEnd(24)} ${model}`);
|
|
17
17
|
const times = lines.map((_, i) => (i < 8 ? B(i) : B(8 + Math.floor((i - 8) / 2)) + ((i - 8) % 2) * half));
|
|
18
18
|
const log = rawLog(stage, { x: 90, y: 230, w: 1740, rows: 9, lines, times, pick: /([a-z-]+\/[\w.-]+)$/i, size: 40 });
|
|
19
|
-
|
|
19
|
+
// 읽는 칸 — 새로 든 줄 하나를 통째로 짚는다(표 행의 고름 칠). 줄 칸은 rawLog 와 같은 셈(왼쪽 90 + 안쪽 28, 줄 높이 60)
|
|
20
|
+
const read = pickFly(stage, {
|
|
21
|
+
items: [{
|
|
22
|
+
stops: OG.requests.map((_, i) => ({ at: times[i], x: 960, y: 230 + 28 + Math.min(i, 8) * 60 + 30, w: 1740 - 28, h: 58 })),
|
|
23
|
+
hold: true,
|
|
24
|
+
// 줄은 박에 바로 든다 — 기다리지 않고 함께 닿는다
|
|
25
|
+
lag: 0,
|
|
26
|
+
}],
|
|
27
|
+
size: 40,
|
|
28
|
+
});
|
|
29
|
+
const one = flag(stage, { x: 90, y: 920, text: "주소 1개", at: B(4), tone: "accent", size: 48 });
|
|
20
30
|
const shots = [
|
|
21
31
|
{ at: 0, x: 960, y: 540, kick: 0.05 },
|
|
22
32
|
{ at: B(4), x: 940, y: 530, zoom: 1.03, kick: 0.03 },
|
|
@@ -28,6 +38,7 @@ export default {
|
|
|
28
38
|
head(t);
|
|
29
39
|
log(t);
|
|
30
40
|
one(t);
|
|
41
|
+
read(t);
|
|
31
42
|
applyStyle(stage, trackCam(t, { shots, scene: ctx.id }));
|
|
32
43
|
};
|
|
33
44
|
},
|
|
@@ -2,7 +2,7 @@ import { applyStyle, type SceneCtx } from "@kasaframe/core";
|
|
|
2
2
|
import { fx } from "@kasaframe/themes/fx";
|
|
3
3
|
import { OG, beatsOf, stageOf } from "../lib/data.ts";
|
|
4
4
|
|
|
5
|
-
const { graphPaper, meter, flag, trackCam } = fx("og-measure");
|
|
5
|
+
const { graphPaper, meter, flag, pickFly, trackCam } = fx("og-measure");
|
|
6
6
|
|
|
7
7
|
// 드롭: 102 가 앞자리부터 멈춰 서며 카메라가 크게 툭 → 박마다 주소 · OpenAI 형식 · Anthropic 형식.
|
|
8
8
|
export default {
|
|
@@ -14,17 +14,18 @@ export default {
|
|
|
14
14
|
const big = document.createElement("div");
|
|
15
15
|
big.style.cssText = "position:absolute;left:100px;top:110px;display:flex;align-items:baseline;gap:40px;font-family:var(--kf-font-display);font-weight:800;color:var(--kf-ink);font-variant-numeric:tabular-nums";
|
|
16
16
|
const num = document.createElement("span");
|
|
17
|
-
num.style.cssText = "font-size:430px;line-height:1.1;letter-spacing:-0.
|
|
17
|
+
num.style.cssText = "font-size:430px;line-height:1.1;letter-spacing:-0.055em";
|
|
18
18
|
const unit = document.createElement("span");
|
|
19
19
|
unit.textContent = "모델";
|
|
20
20
|
unit.style.cssText = "font-family:var(--kf-font-sans);font-size:150px;font-weight:700;color:var(--kf-dim)";
|
|
21
21
|
big.append(num, unit);
|
|
22
22
|
stage.append(big);
|
|
23
|
-
const host = flag(stage, { x: 100, y: 660, text: `https://${OG.host}/v1`, at: B(1), tone: "
|
|
24
|
-
const a = flag(stage, { x: 100, y: 840, text: "OpenAI 형식", at: B(2), size: 60 });
|
|
25
|
-
const c = flag(stage, { x: 560, y: 840, text: "Anthropic 형식", at: B(3), size: 60 });
|
|
23
|
+
const host = flag(stage, { x: 100, y: 660, text: `https://${OG.host}/v1`, at: B(1), tone: "accent", size: 80, mono: true });
|
|
24
|
+
const a = flag(stage, { x: 100, y: 840, text: "OpenAI 형식", at: B(2), tone: "plain", size: 60 });
|
|
25
|
+
const c = flag(stage, { x: 560, y: 840, text: "Anthropic 형식", at: B(3), tone: "plain", size: 60 });
|
|
26
|
+
const read = pickFly(stage, { items: [{ stops: [{ at: 0, target: num, pad: 10 }, { at: B(1), target: host.el }, { at: B(2), target: a.el }, { at: B(3), target: c.el }], hold: true }], size: 60 });
|
|
26
27
|
const shots = [
|
|
27
|
-
{ at: 0, x: 960, y: 540, kick: 0.
|
|
28
|
+
{ at: 0, x: 960, y: 540, kick: 0.06 },
|
|
28
29
|
{ at: B(2), x: 950, y: 560, zoom: 1.02, kick: 0.03 },
|
|
29
30
|
];
|
|
30
31
|
return (t: number) => {
|
|
@@ -33,6 +34,7 @@ export default {
|
|
|
33
34
|
host(t);
|
|
34
35
|
a(t);
|
|
35
36
|
c(t);
|
|
37
|
+
read(t);
|
|
36
38
|
applyStyle(stage, trackCam(t, { shots, scene: ctx.id }));
|
|
37
39
|
};
|
|
38
40
|
},
|
|
@@ -2,7 +2,7 @@ import { applyStyle, type SceneCtx } from "@kasaframe/core";
|
|
|
2
2
|
import { fx } from "@kasaframe/themes/fx";
|
|
3
3
|
import { OG, beatsOf, stageOf } from "../lib/data.ts";
|
|
4
4
|
|
|
5
|
-
const { graphPaper, rankBars, flag, trackCam } = fx("og-measure");
|
|
5
|
+
const { graphPaper, rankBars, flag, pickFly, trackCam } = fx("og-measure");
|
|
6
6
|
|
|
7
7
|
// 시간대 가격 — 한국 00–10시 창에서 같은 모델 ID 가 정가의 10%. 박마다 막대 하나·차이 칩 하나.
|
|
8
8
|
export default {
|
|
@@ -13,8 +13,8 @@ export default {
|
|
|
13
13
|
const B = (i: number) => b[i] ?? i * 0.353;
|
|
14
14
|
const P = OG.price;
|
|
15
15
|
const off = (a: number, z: number) => `−${Math.round((1 - z / a) * 100)}%`;
|
|
16
|
-
const model = flag(stage, { x: 90, y:
|
|
17
|
-
const win = flag(stage, { x: 1300, y: 56, text: "KST 00:00–10:00", at: B(1), tone: "
|
|
16
|
+
const model = flag(stage, { x: 90, y: 54, text: P.model, at: 0, tone: "plain", size: 48, mono: true });
|
|
17
|
+
const win = flag(stage, { x: 1300, y: 56, text: "KST 00:00–10:00", at: B(1), tone: "accent", size: 46, mono: true });
|
|
18
18
|
const bars = rankBars(stage, {
|
|
19
19
|
x: 90,
|
|
20
20
|
y: 190,
|
|
@@ -36,6 +36,22 @@ export default {
|
|
|
36
36
|
});
|
|
37
37
|
const glm = flag(stage, { x: 90, y: 840, text: `${P.glm} · 밤 −70%`, at: B(8), tone: "plain", size: 44, mono: true });
|
|
38
38
|
const auto = flag(stage, { x: 90, y: 950, text: "신청 없이 · 같은 모델 ID", at: B(9), tone: "soft", size: 44 });
|
|
39
|
+
// 읽는 칸 — 박마다 새로 선 값을 짚는다. 막대는 이름·막대 한 줄을 통째로(표 행의 고름 칠, 줄 칸은 rankBars 와 같은 셈)
|
|
40
|
+
const read = pickFly(stage, {
|
|
41
|
+
items: [{
|
|
42
|
+
stops: [
|
|
43
|
+
{ at: 0, target: model.el },
|
|
44
|
+
{ at: B(1), target: win.el },
|
|
45
|
+
...[2, 3, 5, 6].map((k, i) => ({ at: B(k), x: 960, y: 190 + i * (70 + 26 + 56) + 63, w: 1780, h: 140 })),
|
|
46
|
+
{ at: B(8), target: glm.el },
|
|
47
|
+
{ at: B(9), target: auto.el },
|
|
48
|
+
],
|
|
49
|
+
hold: true,
|
|
50
|
+
// 배지·막대는 박에 바로 선다 — 기다리지 않고 함께 닿는다
|
|
51
|
+
lag: 0,
|
|
52
|
+
}],
|
|
53
|
+
size: 44,
|
|
54
|
+
});
|
|
39
55
|
const shots = [
|
|
40
56
|
{ at: 0, x: 960, y: 540, kick: 0.05 },
|
|
41
57
|
{ at: B(4), x: 940, y: 530, zoom: 1.03, kick: 0.03 },
|
|
@@ -50,6 +66,7 @@ export default {
|
|
|
50
66
|
bars(t);
|
|
51
67
|
glm(t);
|
|
52
68
|
auto(t);
|
|
69
|
+
read(t);
|
|
53
70
|
applyStyle(stage, trackCam(t, { shots, scene: ctx.id }));
|
|
54
71
|
};
|
|
55
72
|
},
|
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "og-measure",
|
|
3
3
|
"label": "OG 측정 화면 — 재 온 숫자가 쌓이는 계측기",
|
|
4
|
-
"scheme": "
|
|
4
|
+
"scheme": "dark",
|
|
5
5
|
"colors": {
|
|
6
|
-
"bg": "#
|
|
7
|
-
"surface": "#
|
|
8
|
-
"raised": "#
|
|
9
|
-
"line": "#
|
|
10
|
-
"ink": "#
|
|
11
|
-
"inkSoft": "#
|
|
12
|
-
"dim": "#
|
|
13
|
-
"accent": "#
|
|
14
|
-
"accentSoft": "rgba(0,
|
|
15
|
-
"accentText": "#
|
|
6
|
+
"bg": "#090A0C",
|
|
7
|
+
"surface": "#13151B",
|
|
8
|
+
"raised": "#252A37",
|
|
9
|
+
"line": "#394051",
|
|
10
|
+
"ink": "#C5CBD8",
|
|
11
|
+
"inkSoft": "#C5CBD8",
|
|
12
|
+
"dim": "#7C869C",
|
|
13
|
+
"accent": "#0084FF",
|
|
14
|
+
"accentSoft": "rgba(0, 132, 255, 0.45)",
|
|
15
|
+
"accentText": "#3B99F3",
|
|
16
16
|
"onAccent": "#FFFFFF",
|
|
17
|
-
"tint": "rgba(0,
|
|
18
|
-
"success": "#
|
|
19
|
-
"danger": "#
|
|
17
|
+
"tint": "rgba(0, 132, 255, 0.1)",
|
|
18
|
+
"success": "#10B981",
|
|
19
|
+
"danger": "#EF4343",
|
|
20
20
|
"warning": "#F59E0B",
|
|
21
|
-
"
|
|
22
|
-
"darkSurface": "#13151B",
|
|
23
|
-
"darkLine": "#39404F",
|
|
24
|
-
"darkInk": "#C6CAD6",
|
|
25
|
-
"darkAccent": "#0085FF",
|
|
26
|
-
"bar": "rgba(100, 116, 139, 0.28)"
|
|
21
|
+
"bar": "rgba(124, 134, 156, 0.35)"
|
|
27
22
|
},
|
|
28
23
|
"fonts": {
|
|
29
24
|
"sans": "\"Pretendard Variable\", Pretendard, \"Noto Sans KR\", \"Apple SD Gothic Neo\", sans-serif",
|
|
@@ -37,12 +32,12 @@
|
|
|
37
32
|
},
|
|
38
33
|
{
|
|
39
34
|
"family": "Geist",
|
|
40
|
-
"src": "https://cdn.jsdelivr.net/
|
|
35
|
+
"src": "https://cdn.jsdelivr.net/gh/google/fonts@bd8f81ddb5c74d5c8897b36ad88b440266245103/ofl/geist/Geist%5Bwght%5D.ttf",
|
|
41
36
|
"weight": "100 900"
|
|
42
37
|
},
|
|
43
38
|
{
|
|
44
39
|
"family": "Geist Mono",
|
|
45
|
-
"src": "https://cdn.jsdelivr.net/
|
|
40
|
+
"src": "https://cdn.jsdelivr.net/gh/google/fonts@bd8f81ddb5c74d5c8897b36ad88b440266245103/ofl/geistmono/GeistMono%5Bwght%5D.ttf",
|
|
46
41
|
"weight": "100 900"
|
|
47
42
|
}
|
|
48
43
|
]
|
|
@@ -67,13 +62,11 @@
|
|
|
67
62
|
"lift": "none"
|
|
68
63
|
},
|
|
69
64
|
"motion": {
|
|
70
|
-
"beatPulse": "
|
|
65
|
+
"beatPulse": "off",
|
|
71
66
|
"shake": {
|
|
72
|
-
"on": [
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"maxPerPhrase": 1,
|
|
76
|
-
"maxPx": 8
|
|
67
|
+
"on": [],
|
|
68
|
+
"maxPerPhrase": 0,
|
|
69
|
+
"maxPx": 0
|
|
77
70
|
},
|
|
78
71
|
"cameraMove": "free",
|
|
79
72
|
"repeatSameMove": false,
|
|
@@ -87,11 +80,13 @@
|
|
|
87
80
|
}
|
|
88
81
|
},
|
|
89
82
|
"notes": [
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
83
|
+
"정본: opengateway cp-fe-og-styleguide(style/og-brand-styleguide-20261007, 46366b8) design.md 1–9절과 src/app/globals.css. 색·움직임 값은 그 코드에서 옮겼다 — 고칠 때도 거기부터 본다.",
|
|
84
|
+
"팔레트는 한 벌씩 두 벌이다: 다크(.dark — 기본)가 이 파일, 라이트(:root)는 theme.json vary.palettes.light. bg=--background, surface=--card, raised=--secondary, line=--border, ink=--foreground, dim=--muted-foreground, accent=--primary, accentText=--on-primary(파랑 70%+글 잉크 30%), onAccent=--primary-foreground, tint=고른 상태 bg-primary/10, success=emerald-500, danger=--destructive, warning=amber-500. 정본에 없는 색은 세우지 않는다.",
|
|
85
|
+
"파랑은 가장 좋은 값 하나에만(차트 칠·고른 상태). 비교 대상은 회색 막대(bar=--muted-foreground 반투명), 빨강은 오류에만. 그림자·그라데이션·네온·섬광 없음.",
|
|
86
|
+
"가두지 않는다: 구획은 제목·여백, 항목은 가는 구분선(line). 틀이 곧 뜻인 것(호출·응답 패널, 원자료 로그 = 코드 블록)만 면(surface)에 12px. 칩·배지는 속을 칠하지 않고 흐린 테두리(파랑 /45, 무채색 line)와 글자색만.",
|
|
87
|
+
"모서리는 역할: 알약(pill) = 칩·고름 칠, 12px(md·lg) = 면, 0(sm) = 그 밖(막대·구분선·눈금). 사이 값은 쓰지 않는다.",
|
|
88
|
+
"글꼴: 한국어·라벨 Pretendard, 큰 숫자·워드마크 Geist 800 자간 −0.055em(한글은 Pretendard 로 떨어진다), 모델 ID·주소·로그·시각은 Geist Mono, 숫자는 tabular-nums.",
|
|
89
|
+
"움직임: 150(색)·220(나타남·상태)·300ms(자리 옮김), 감속 cubic-bezier(.22,1,.36,1)·스프링 cubic-bezier(.34,1.3,.64,1), 나타남은 6px 올라오며. 누르듯 커졌다 줄기·흔들기 없음. 카메라도 300ms 스프링으로 한 번 옮기고 선다.",
|
|
90
|
+
"로고는 마스터 public/logo.svg 하나(sample/assets/brand/og-logo.svg 와 바이트가 같다) — 한 색, 어두운 바탕 흰색·밝은 바탕 검정. 파랑으로 칠하지 않고 og-wave 후보·캠페인 그림은 쓰지 않는다."
|
|
96
91
|
]
|
|
97
|
-
}
|
|
92
|
+
}
|
|
@@ -1,39 +1,33 @@
|
|
|
1
|
-
import { clamp, ease, progress, type Style } from "@kasaframe/core";
|
|
1
|
+
import { bezier, clamp, ease, progress, type Style } from "@kasaframe/core";
|
|
2
2
|
import { dropBurst } from "@kasaframe/motion";
|
|
3
3
|
import type { ThemeSlots } from "@kasaframe/themes/slots";
|
|
4
4
|
import { layer, letters, partial, shownCount, strokePath, traceCanvas } from "@kasaframe/themes/slotkit";
|
|
5
5
|
|
|
6
|
-
// 장면 문법 패턴이 테마 이름 없이 부르는 자리 — 계측기
|
|
7
|
-
// 움직임은
|
|
8
|
-
const
|
|
6
|
+
// 장면 문법 패턴이 테마 이름 없이 부르는 자리 — 계측기 화면을 OpenGateway 스타일가이드 결로. 바탕은 서 있는 옅은 모눈,
|
|
7
|
+
// 판은 상자가 아니라 구분선(design.md 2절), 움직임은 220ms 감속·6px 나타남과 300ms 자리 옮김뿐(8절). 색은 모두 --kf-* 토큰
|
|
8
|
+
const out = ease.snappy;
|
|
9
|
+
const springy = bezier(0.34, 1.3, 0.64, 1);
|
|
9
10
|
|
|
10
11
|
export const slots: ThemeSlots = {
|
|
11
12
|
surface(el) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"inset:0;background:var(--kf-bg);background-image:linear-gradient(rgba(203,213,225,.55) 1px,transparent 1px),linear-gradient(90deg,rgba(203,213,225,.55) 1px,transparent 1px);background-size:56px 56px",
|
|
15
|
-
);
|
|
13
|
+
const grid = "color-mix(in srgb, var(--kf-line) 45%, transparent)";
|
|
14
|
+
layer(el, `inset:0;background:var(--kf-bg);background-image:linear-gradient(${grid} 1px,transparent 1px),linear-gradient(90deg,${grid} 1px,transparent 1px);background-size:56px 56px`);
|
|
16
15
|
return () => {};
|
|
17
16
|
},
|
|
18
17
|
|
|
19
|
-
//
|
|
18
|
+
// 가두지 않는다 — 칠한 면·테두리 상자 대신 위에 구분선 한 줄(구획 = 제목·여백, 항목 = 가는 구분선). 강조 판은 그 선이 파랑
|
|
20
19
|
panel(el, { at, tone }) {
|
|
21
|
-
const
|
|
22
|
-
el,
|
|
23
|
-
`inset:0;border-radius:var(--kf-radius-md);border:1px solid var(--kf-line);box-shadow:0 1px 2px rgba(2,8,23,.06),0 8px 24px rgba(2,8,23,.06);background:${tone === "accent" ? "var(--kf-accent)" : "var(--kf-bg)"}`,
|
|
24
|
-
true,
|
|
25
|
-
);
|
|
20
|
+
const rule = layer(el, `left:0;right:0;top:0;height:3px;background:${tone === "accent" ? "var(--kf-accent)" : "var(--kf-line)"};transform-origin:0 50%`, true);
|
|
26
21
|
return (t) => {
|
|
27
|
-
const p =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
card.style.transform = `translateY(${((1 - p) * -14).toFixed(2)}px)`;
|
|
22
|
+
const p = out(progress(t, at, 0.3));
|
|
23
|
+
rule.style.visibility = t >= at ? "visible" : "hidden";
|
|
24
|
+
rule.style.transform = `scaleX(${p.toFixed(4)})`;
|
|
31
25
|
};
|
|
32
26
|
},
|
|
33
27
|
|
|
34
|
-
// 값이
|
|
28
|
+
// 값이 앉는 순간 — 크기는 그대로 6px 올라와 선다(누름·도착에 커졌다 줄기 없음)
|
|
35
29
|
land(_el, { at }) {
|
|
36
|
-
return (t): Style => (t < at || t > at + 0.3 ? {} : {
|
|
30
|
+
return (t): Style => (t < at || t > at + 0.3 ? {} : { y: 6 * (1 - out(progress(t, at, 0.22))) });
|
|
37
31
|
},
|
|
38
32
|
|
|
39
33
|
// 계기판처럼 — 글자는 앞에서부터 서고, 숫자 칸은 서기 전까지 돌고 있다(meter 와 같은 결)
|
|
@@ -50,11 +44,11 @@ export const slots: ThemeSlots = {
|
|
|
50
44
|
};
|
|
51
45
|
},
|
|
52
46
|
|
|
53
|
-
//
|
|
47
|
+
// 추이 선 — 3px 선이 그어지고 꺾이는 점마다 표본 점이 선다. 다 그은 선을 다시 그리지 않는다
|
|
54
48
|
trace(el, { points, at, dur, tone }) {
|
|
55
49
|
const { g, clear } = traceCanvas(el, points);
|
|
56
50
|
const css = getComputedStyle(el);
|
|
57
|
-
const color = (tone === "accent" ? css.getPropertyValue("--kf-accent") : css.getPropertyValue("--kf-ink")).trim()
|
|
51
|
+
const color = (tone === "accent" ? css.getPropertyValue("--kf-accent") : css.getPropertyValue("--kf-ink")).trim();
|
|
58
52
|
return (t) => {
|
|
59
53
|
clear();
|
|
60
54
|
const p = clamp((t - at) / Math.max(0.05, dur));
|
|
@@ -73,16 +67,17 @@ export const slots: ThemeSlots = {
|
|
|
73
67
|
};
|
|
74
68
|
},
|
|
75
69
|
|
|
76
|
-
// 드롭 —
|
|
70
|
+
// 드롭 — 섬광·고리 대신 파란 구분선 한 줄이 가운데에서 양옆으로 300ms 스프링으로 그어졌다 220ms 로 빠진다(흔들림·섬광 없음)
|
|
77
71
|
drop(el, { ctx, drop }) {
|
|
78
|
-
const
|
|
79
|
-
const
|
|
72
|
+
const at = ctx.beats.drop(drop);
|
|
73
|
+
const line = layer(el, "left:0;right:0;top:50%;height:4px;margin-top:-2px;background:var(--kf-accent);transform:scaleX(0);opacity:0");
|
|
80
74
|
return (t) => {
|
|
81
|
-
const s = dropBurst(t, { beats: ctx.beats, drop, rules: ctx.style.motion, scene: ctx.id, source: "og-drop" });
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
const s = dropBurst(t, { beats: ctx.beats, drop, rules: ctx.style.motion, scene: ctx.id, source: "og-drop", emit: false });
|
|
76
|
+
const grow = springy(progress(t, at, 0.3));
|
|
77
|
+
const fade = 1 - out(progress(t, at + 0.45, 0.22));
|
|
78
|
+
line.style.transform = `scaleX(${(t >= at ? grow : 0).toFixed(4)})`;
|
|
79
|
+
line.style.opacity = String(t >= at ? fade : 0);
|
|
80
|
+
return { ...s, flash: 0, ring: { scale: 0, opacity: 0 }, punch: {} };
|
|
86
81
|
};
|
|
87
82
|
},
|
|
88
83
|
};
|
|
Binary file
|
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "og-measure",
|
|
3
3
|
"label": "OG 측정 화면 — 재 온 숫자가 쌓이는 계측기",
|
|
4
|
-
"scheme": "
|
|
4
|
+
"scheme": "dark",
|
|
5
5
|
"colors": {
|
|
6
|
-
"bg": "#
|
|
7
|
-
"surface": "#
|
|
8
|
-
"raised": "#
|
|
9
|
-
"line": "#
|
|
10
|
-
"ink": "#
|
|
11
|
-
"inkSoft": "#
|
|
12
|
-
"dim": "#
|
|
13
|
-
"accent": "#
|
|
14
|
-
"accentSoft": "rgba(0,
|
|
15
|
-
"accentText": "#
|
|
6
|
+
"bg": "#090A0C",
|
|
7
|
+
"surface": "#13151B",
|
|
8
|
+
"raised": "#252A37",
|
|
9
|
+
"line": "#394051",
|
|
10
|
+
"ink": "#C5CBD8",
|
|
11
|
+
"inkSoft": "#C5CBD8",
|
|
12
|
+
"dim": "#7C869C",
|
|
13
|
+
"accent": "#0084FF",
|
|
14
|
+
"accentSoft": "rgba(0, 132, 255, 0.45)",
|
|
15
|
+
"accentText": "#3B99F3",
|
|
16
16
|
"onAccent": "#FFFFFF",
|
|
17
|
-
"tint": "rgba(0,
|
|
18
|
-
"success": "#
|
|
19
|
-
"danger": "#
|
|
17
|
+
"tint": "rgba(0, 132, 255, 0.1)",
|
|
18
|
+
"success": "#10B981",
|
|
19
|
+
"danger": "#EF4343",
|
|
20
20
|
"warning": "#F59E0B",
|
|
21
|
-
"
|
|
22
|
-
"darkSurface": "#13151B",
|
|
23
|
-
"darkLine": "#39404F",
|
|
24
|
-
"darkInk": "#C6CAD6",
|
|
25
|
-
"darkAccent": "#0085FF",
|
|
26
|
-
"bar": "rgba(100, 116, 139, 0.28)"
|
|
21
|
+
"bar": "rgba(124, 134, 156, 0.35)"
|
|
27
22
|
},
|
|
28
23
|
"fonts": {
|
|
29
24
|
"sans": "\"Pretendard Variable\", Pretendard, \"Noto Sans KR\", \"Apple SD Gothic Neo\", sans-serif",
|
|
@@ -37,12 +32,12 @@
|
|
|
37
32
|
},
|
|
38
33
|
{
|
|
39
34
|
"family": "Geist",
|
|
40
|
-
"src": "https://cdn.jsdelivr.net/
|
|
35
|
+
"src": "https://cdn.jsdelivr.net/gh/google/fonts@bd8f81ddb5c74d5c8897b36ad88b440266245103/ofl/geist/Geist%5Bwght%5D.ttf",
|
|
41
36
|
"weight": "100 900"
|
|
42
37
|
},
|
|
43
38
|
{
|
|
44
39
|
"family": "Geist Mono",
|
|
45
|
-
"src": "https://cdn.jsdelivr.net/
|
|
40
|
+
"src": "https://cdn.jsdelivr.net/gh/google/fonts@bd8f81ddb5c74d5c8897b36ad88b440266245103/ofl/geistmono/GeistMono%5Bwght%5D.ttf",
|
|
46
41
|
"weight": "100 900"
|
|
47
42
|
}
|
|
48
43
|
]
|
|
@@ -67,13 +62,11 @@
|
|
|
67
62
|
"lift": "none"
|
|
68
63
|
},
|
|
69
64
|
"motion": {
|
|
70
|
-
"beatPulse": "
|
|
65
|
+
"beatPulse": "off",
|
|
71
66
|
"shake": {
|
|
72
|
-
"on": [
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"maxPerPhrase": 1,
|
|
76
|
-
"maxPx": 8
|
|
67
|
+
"on": [],
|
|
68
|
+
"maxPerPhrase": 0,
|
|
69
|
+
"maxPx": 0
|
|
77
70
|
},
|
|
78
71
|
"cameraMove": "free",
|
|
79
72
|
"repeatSameMove": false,
|
|
@@ -87,11 +80,13 @@
|
|
|
87
80
|
}
|
|
88
81
|
},
|
|
89
82
|
"notes": [
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
83
|
+
"정본: opengateway cp-fe-og-styleguide(style/og-brand-styleguide-20261007, 46366b8) design.md 1–9절과 src/app/globals.css. 색·움직임 값은 그 코드에서 옮겼다 — 고칠 때도 거기부터 본다.",
|
|
84
|
+
"팔레트는 한 벌씩 두 벌이다: 다크(.dark — 기본)가 이 파일, 라이트(:root)는 theme.json vary.palettes.light. bg=--background, surface=--card, raised=--secondary, line=--border, ink=--foreground, dim=--muted-foreground, accent=--primary, accentText=--on-primary(파랑 70%+글 잉크 30%), onAccent=--primary-foreground, tint=고른 상태 bg-primary/10, success=emerald-500, danger=--destructive, warning=amber-500. 정본에 없는 색은 세우지 않는다.",
|
|
85
|
+
"파랑은 가장 좋은 값 하나에만(차트 칠·고른 상태). 비교 대상은 회색 막대(bar=--muted-foreground 반투명), 빨강은 오류에만. 그림자·그라데이션·네온·섬광 없음.",
|
|
86
|
+
"가두지 않는다: 구획은 제목·여백, 항목은 가는 구분선(line). 틀이 곧 뜻인 것(호출·응답 패널, 원자료 로그 = 코드 블록)만 면(surface)에 12px. 칩·배지는 속을 칠하지 않고 흐린 테두리(파랑 /45, 무채색 line)와 글자색만.",
|
|
87
|
+
"모서리는 역할: 알약(pill) = 칩·고름 칠, 12px(md·lg) = 면, 0(sm) = 그 밖(막대·구분선·눈금). 사이 값은 쓰지 않는다.",
|
|
88
|
+
"글꼴: 한국어·라벨 Pretendard, 큰 숫자·워드마크 Geist 800 자간 −0.055em(한글은 Pretendard 로 떨어진다), 모델 ID·주소·로그·시각은 Geist Mono, 숫자는 tabular-nums.",
|
|
89
|
+
"움직임: 150(색)·220(나타남·상태)·300ms(자리 옮김), 감속 cubic-bezier(.22,1,.36,1)·스프링 cubic-bezier(.34,1.3,.64,1), 나타남은 6px 올라오며. 누르듯 커졌다 줄기·흔들기 없음. 카메라도 300ms 스프링으로 한 번 옮기고 선다.",
|
|
90
|
+
"로고는 마스터 public/logo.svg 하나(sample/assets/brand/og-logo.svg 와 바이트가 같다) — 한 색, 어두운 바탕 흰색·밝은 바탕 검정. 파랑으로 칠하지 않고 og-wave 후보·캠페인 그림은 쓰지 않는다."
|
|
96
91
|
]
|
|
97
|
-
}
|
|
92
|
+
}
|