@vanillaskyai/video 0.1.0
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/CHANGELOG.md +45 -0
- package/LICENSE +201 -0
- package/PUBLIC-API.md +276 -0
- package/README.md +152 -0
- package/SECURITY.md +22 -0
- package/SUPPORT.md +28 -0
- package/bin/vanillasky.js +3 -0
- package/dist/bg-confetti-WNQXS7ZS.js +89 -0
- package/dist/bg-emoji-ETI47GLS.js +150 -0
- package/dist/bg-media-SVUZAMGX.js +99 -0
- package/dist/brand-message-F3RD4P3P.js +253 -0
- package/dist/builtin-server-F7YXRVCH.js +8 -0
- package/dist/catalog-types-BIhSpOWK.d.ts +74 -0
- package/dist/chart-bar-CNSC7BNK.js +304 -0
- package/dist/chart-counter-MCRHKN77.js +299 -0
- package/dist/chart-progress-ring-RMI4A7ZK.js +296 -0
- package/dist/check-runtime.d.ts +15 -0
- package/dist/check-runtime.js +102 -0
- package/dist/chunk-2E6T633S.js +27 -0
- package/dist/chunk-352ZZCET.js +83 -0
- package/dist/chunk-73NTSFFI.js +81 -0
- package/dist/chunk-A2X45FET.js +46 -0
- package/dist/chunk-ABPKM4GK.js +187 -0
- package/dist/chunk-ABZACD4G.js +216 -0
- package/dist/chunk-BCRUUJ6A.js +756 -0
- package/dist/chunk-CHLGW22F.js +3551 -0
- package/dist/chunk-CJ7F2CE6.js +104 -0
- package/dist/chunk-CR7FE7BW.js +109 -0
- package/dist/chunk-E7CL7UPB.js +109 -0
- package/dist/chunk-EE6PK6XC.js +18 -0
- package/dist/chunk-ERY4S6QV.js +385 -0
- package/dist/chunk-EVG2ZYIY.js +125 -0
- package/dist/chunk-FNAEQ7QO.js +155 -0
- package/dist/chunk-FQ47WXAJ.js +38 -0
- package/dist/chunk-FSG2PYWG.js +336 -0
- package/dist/chunk-GHJEAP4O.js +504 -0
- package/dist/chunk-GRMUV7QY.js +262 -0
- package/dist/chunk-IFOW65Z6.js +205 -0
- package/dist/chunk-IXCUY3B3.js +786 -0
- package/dist/chunk-L5BTTCZV.js +97 -0
- package/dist/chunk-LMG3GHLL.js +107 -0
- package/dist/chunk-OOPIETLW.js +101 -0
- package/dist/chunk-P22HHFDK.js +43 -0
- package/dist/chunk-QAAO7OAY.js +19 -0
- package/dist/chunk-S7HFLYNN.js +35 -0
- package/dist/chunk-SPVTJH3F.js +24 -0
- package/dist/chunk-W2XJKG6C.js +3177 -0
- package/dist/chunk-XB7LH4J2.js +301 -0
- package/dist/chunk-XGRA2MUV.js +242 -0
- package/dist/chunk-XTOXFMLM.js +105 -0
- package/dist/chunk-YC5MFXA5.js +132 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1681 -0
- package/dist/compose-video-CN3VSWST.js +13 -0
- package/dist/cta-logo-SVA5GP2A.js +209 -0
- package/dist/cta-media-E7VLLK42.js +215 -0
- package/dist/events-_wfjbmEp.d.ts +81 -0
- package/dist/incoming-call-62MFZL3Z.js +320 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +14 -0
- package/dist/infographic-before-after-AXCDAWND.js +460 -0
- package/dist/infographic-feature-list-GW5GUE6O.js +266 -0
- package/dist/infographic-problem-solution-7NURJANA.js +340 -0
- package/dist/infographic-stat-row-J7IZR6ZN.js +241 -0
- package/dist/infographic-steps-VSZM6F5M.js +400 -0
- package/dist/kit-BMNcHqa9.d.ts +79 -0
- package/dist/prompt-input-LOUCRXCY.js +236 -0
- package/dist/react.d.ts +81 -0
- package/dist/react.js +835 -0
- package/dist/reaction-QG4CYZLQ.js +119 -0
- package/dist/server.d.ts +141 -0
- package/dist/server.js +498 -0
- package/dist/showcase-code-RUTFZR3H.js +351 -0
- package/dist/showcase-phone-6CP6XEU5.js +272 -0
- package/dist/showcase-terminal-MGAEQWPR.js +306 -0
- package/dist/showcase-web-Y4W2HVRJ.js +390 -0
- package/dist/social-conversation-75SD2637.js +695 -0
- package/dist/social-milestone-XI2QE5BO.js +292 -0
- package/dist/social-notification-2UOHQCUY.js +244 -0
- package/dist/social-review-stack-FDYZGZTJ.js +239 -0
- package/dist/social-testimonial-OQ7N52GJ.js +284 -0
- package/dist/social-tweet-5OLCNPPC.js +295 -0
- package/dist/state-DZcKuS32.d.ts +3 -0
- package/dist/system-prompt-A6UXNTPK.js +8 -0
- package/dist/template-catalog.d.ts +24 -0
- package/dist/template-catalog.js +6 -0
- package/dist/templates.d.ts +25 -0
- package/dist/templates.js +19 -0
- package/dist/test.d.ts +149 -0
- package/dist/test.js +272 -0
- package/dist/text-stream-6XHC7J47.js +10 -0
- package/dist/types-B-PZR6G_.d.ts +208 -0
- package/dist/validate-T7GBU2YF.js +8 -0
- package/docs/agent-integration.md +39 -0
- package/docs/architecture.md +84 -0
- package/docs/branding-and-personalization.md +87 -0
- package/docs/concepts.md +106 -0
- package/docs/custom-templates.md +329 -0
- package/docs/customization.md +141 -0
- package/docs/errors.md +70 -0
- package/docs/getting-started.md +111 -0
- package/docs/input-and-first-scene.md +41 -0
- package/docs/integrate-nextjs.md +79 -0
- package/docs/media-and-audio.md +69 -0
- package/docs/motion-and-effects.md +107 -0
- package/docs/persistence.md +95 -0
- package/docs/production.md +122 -0
- package/docs/prompt-and-input.md +185 -0
- package/docs/provider-integration.md +115 -0
- package/docs/reference/protocol.md +96 -0
- package/docs/reference/provider-adapters.md +158 -0
- package/docs/responsive-orientation.md +37 -0
- package/docs/security.md +38 -0
- package/docs/streaming-protocol.md +15 -0
- package/docs/testing.md +137 -0
- package/docs/use-cases.md +59 -0
- package/examples/custom-template/README.md +19 -0
- package/examples/custom-template/minimal-text.tsx +82 -0
- package/examples/custom-template/structured-data.tsx +104 -0
- package/examples/custom-template/supplied-media.tsx +92 -0
- package/examples/nextjs-quickstart/.env.example +2 -0
- package/examples/nextjs-quickstart/README.md +22 -0
- package/examples/nextjs-quickstart/next-env.d.ts +4 -0
- package/examples/nextjs-quickstart/package.json +24 -0
- package/examples/nextjs-quickstart/src/app/api/video/route.ts +22 -0
- package/examples/nextjs-quickstart/src/app/layout.tsx +5 -0
- package/examples/nextjs-quickstart/src/app/page.tsx +31 -0
- package/examples/nextjs-quickstart/tsconfig.json +26 -0
- package/package.json +167 -0
- package/registry/items/backgrounds.json +52 -0
- package/registry/items/barChart.json +237 -0
- package/registry/items/beforeAfter.json +180 -0
- package/registry/items/bigNumber.json +225 -0
- package/registry/items/brandMessage.json +158 -0
- package/registry/items/cardList.json +206 -0
- package/registry/items/chatMessenger.json +151 -0
- package/registry/items/chatWhatsapp.json +151 -0
- package/registry/items/codeEditor.json +184 -0
- package/registry/items/confetti.json +107 -0
- package/registry/items/ctaLogo.json +160 -0
- package/registry/items/ctaMedia.json +193 -0
- package/registry/items/emojiBurst.json +101 -0
- package/registry/items/incomingCall.json +170 -0
- package/registry/items/media.json +179 -0
- package/registry/items/milestone.json +205 -0
- package/registry/items/motion.json +45 -0
- package/registry/items/notification.json +182 -0
- package/registry/items/phoneMockup.json +254 -0
- package/registry/items/problemSolution.json +186 -0
- package/registry/items/progressRing.json +199 -0
- package/registry/items/promptInput.json +148 -0
- package/registry/items/reaction.json +170 -0
- package/registry/items/reviewStack.json +224 -0
- package/registry/items/steps.json +207 -0
- package/registry/items/template-context.json +31 -0
- package/registry/items/terminal.json +212 -0
- package/registry/items/testimonial.json +164 -0
- package/registry/items/theme.json +47 -0
- package/registry/items/tripleStats.json +228 -0
- package/registry/items/tweet.json +203 -0
- package/registry/items/typography.json +47 -0
- package/registry/items/webMockup.json +273 -0
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Emoji,
|
|
3
|
+
planTypewriterEmoji,
|
|
4
|
+
renderWithEmoji
|
|
5
|
+
} from "./chunk-L5BTTCZV.js";
|
|
6
|
+
import {
|
|
7
|
+
Easing,
|
|
8
|
+
SPRING_CRISP,
|
|
9
|
+
interpolate,
|
|
10
|
+
spring
|
|
11
|
+
} from "./chunk-XTOXFMLM.js";
|
|
12
|
+
|
|
13
|
+
// src/visual-system/scene-templates/text-archetypes.ts
|
|
14
|
+
var TEXT_ARCHETYPES = [
|
|
15
|
+
"subtle",
|
|
16
|
+
"typewriter",
|
|
17
|
+
"wordStagger",
|
|
18
|
+
"slam",
|
|
19
|
+
"cinematic",
|
|
20
|
+
"heroWord"
|
|
21
|
+
];
|
|
22
|
+
function normalizeArchetype(input) {
|
|
23
|
+
if (typeof input !== "string" || input.length === 0) return "subtle";
|
|
24
|
+
if (TEXT_ARCHETYPES.includes(input)) return input;
|
|
25
|
+
return "subtle";
|
|
26
|
+
}
|
|
27
|
+
var ARCHETYPE_SPECS = {
|
|
28
|
+
subtle: {
|
|
29
|
+
name: "subtle",
|
|
30
|
+
description: "Quiet caption. Visual is the star, text supports.",
|
|
31
|
+
entrance: 0.5,
|
|
32
|
+
exit: 0.7,
|
|
33
|
+
allowedCanvas: ["tight", "open"],
|
|
34
|
+
roles: ["body", "closer"],
|
|
35
|
+
sizeRange: [0.7, 1],
|
|
36
|
+
minDuration: 1.5,
|
|
37
|
+
maxDuration: 5
|
|
38
|
+
},
|
|
39
|
+
typewriter: {
|
|
40
|
+
name: "typewriter",
|
|
41
|
+
description: "Char-by-char reveal with blinking cursor. Slide-left cascade exit, top line first.",
|
|
42
|
+
entrance: 1.2,
|
|
43
|
+
exit: 0.85,
|
|
44
|
+
allowedCanvas: ["tight", "open"],
|
|
45
|
+
roles: ["body", "hook"],
|
|
46
|
+
sizeRange: [0.8, 1],
|
|
47
|
+
minDuration: 1.8,
|
|
48
|
+
maxDuration: 6
|
|
49
|
+
},
|
|
50
|
+
wordStagger: {
|
|
51
|
+
name: "wordStagger",
|
|
52
|
+
description: "Sequential word build with active-word focus. Slide-left cascade exit, top line first.",
|
|
53
|
+
entrance: 1.4,
|
|
54
|
+
exit: 0.7,
|
|
55
|
+
allowedCanvas: ["tight", "open"],
|
|
56
|
+
roles: ["body"],
|
|
57
|
+
sizeRange: [0.8, 1],
|
|
58
|
+
minDuration: 1.8,
|
|
59
|
+
maxDuration: 6
|
|
60
|
+
},
|
|
61
|
+
slam: {
|
|
62
|
+
name: "slam",
|
|
63
|
+
description: "In-place impact with violent squash-and-stretch, frame shake, and letter crunch.",
|
|
64
|
+
entrance: 0.5,
|
|
65
|
+
exit: 0.5,
|
|
66
|
+
allowedCanvas: ["tight", "open"],
|
|
67
|
+
roles: ["hook", "body"],
|
|
68
|
+
sizeRange: [0.9, 1.2],
|
|
69
|
+
minDuration: 1.2,
|
|
70
|
+
maxDuration: 4
|
|
71
|
+
},
|
|
72
|
+
cinematic: {
|
|
73
|
+
name: "cinematic",
|
|
74
|
+
description: "Trailer FlyIn from depth \u2192 recedes back into background.",
|
|
75
|
+
entrance: 0.7,
|
|
76
|
+
exit: 0.8,
|
|
77
|
+
allowedCanvas: ["open"],
|
|
78
|
+
roles: ["hook", "body", "closer"],
|
|
79
|
+
sizeRange: [0.9, 1.1],
|
|
80
|
+
minDuration: 1.8,
|
|
81
|
+
maxDuration: 6
|
|
82
|
+
},
|
|
83
|
+
heroWord: {
|
|
84
|
+
name: "heroWord",
|
|
85
|
+
description: "Single word fills frame. Simple fast exit so words get the most time.",
|
|
86
|
+
entrance: 0.35,
|
|
87
|
+
exit: 0.25,
|
|
88
|
+
allowedCanvas: ["open"],
|
|
89
|
+
roles: ["hook", "body", "closer"],
|
|
90
|
+
sizeRange: [2, 3],
|
|
91
|
+
minDuration: 1,
|
|
92
|
+
maxDuration: 5
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var ENTRANCE_START = 0.02;
|
|
96
|
+
var ENTRANCE_PHASE_CAP = 0.4;
|
|
97
|
+
var EXIT_PHASE_CAP = 0.4;
|
|
98
|
+
function computePhases(spec, sceneDuration, progress, phaseScale = 1) {
|
|
99
|
+
const entrancePhase = Math.min(ENTRANCE_PHASE_CAP, spec.entrance * phaseScale / sceneDuration);
|
|
100
|
+
const exitPhase = Math.min(EXIT_PHASE_CAP, spec.exit * phaseScale / sceneDuration);
|
|
101
|
+
const exitStart = 1 - exitPhase;
|
|
102
|
+
return { entrancePhase, exitPhase, exitStart, inExit: progress >= exitStart };
|
|
103
|
+
}
|
|
104
|
+
function rawEntrance(progress, entrancePhase, startAt = ENTRANCE_START) {
|
|
105
|
+
return interpolate(progress, [startAt, startAt + entrancePhase], [0, 1], {
|
|
106
|
+
extrapolateLeft: "clamp",
|
|
107
|
+
extrapolateRight: "clamp"
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function rawExit(progress, exitStart) {
|
|
111
|
+
if (progress < exitStart) return 0;
|
|
112
|
+
return interpolate(progress, [exitStart, 1], [0, 1], {
|
|
113
|
+
extrapolateLeft: "clamp",
|
|
114
|
+
extrapolateRight: "clamp"
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function holdT(progress, entrancePhase, exitStart, startAt = ENTRANCE_START) {
|
|
118
|
+
const holdStart = startAt + entrancePhase;
|
|
119
|
+
if (progress < holdStart || progress > exitStart || exitStart <= holdStart) return 0;
|
|
120
|
+
return (progress - holdStart) / (exitStart - holdStart);
|
|
121
|
+
}
|
|
122
|
+
function breathe(t, magnitude, cycles = 1) {
|
|
123
|
+
return Math.sin(t * Math.PI * 2 * cycles) * magnitude / 2;
|
|
124
|
+
}
|
|
125
|
+
function renderArchetype(archetypeRaw, progress, scale, text, sceneDuration, phaseScale = 1, motionProgress = progress) {
|
|
126
|
+
const archetype = normalizeArchetype(archetypeRaw);
|
|
127
|
+
const spec = ARCHETYPE_SPECS[archetype];
|
|
128
|
+
const phases = computePhases(spec, sceneDuration, motionProgress, phaseScale);
|
|
129
|
+
const presentationProgress = Math.max(progress, motionProgress);
|
|
130
|
+
switch (archetype) {
|
|
131
|
+
case "subtle":
|
|
132
|
+
return renderSubtle(motionProgress, scale, text, phases);
|
|
133
|
+
case "typewriter":
|
|
134
|
+
return renderTypewriter(presentationProgress, motionProgress, scale, text, phases, sceneDuration);
|
|
135
|
+
case "wordStagger":
|
|
136
|
+
return renderWordStagger(presentationProgress, motionProgress, scale, text, sceneDuration);
|
|
137
|
+
case "slam":
|
|
138
|
+
return renderSlam(motionProgress, scale, text, phases);
|
|
139
|
+
case "cinematic":
|
|
140
|
+
return renderCinematic(motionProgress, scale, text, phases);
|
|
141
|
+
case "heroWord":
|
|
142
|
+
return renderHeroWord(presentationProgress, motionProgress, text, phases);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function renderSubtle(progress, _scale, text, phases) {
|
|
146
|
+
const raw = rawEntrance(progress, phases.entrancePhase);
|
|
147
|
+
const ex = rawExit(progress, phases.exitStart);
|
|
148
|
+
const opacityIn = Easing.out(Easing.cubic)(raw);
|
|
149
|
+
const opacityOut = 1 - Easing.in(Easing.cubic)(ex);
|
|
150
|
+
const opacity = opacityIn * (phases.inExit ? opacityOut : 1);
|
|
151
|
+
return {
|
|
152
|
+
kind: "block",
|
|
153
|
+
block: {
|
|
154
|
+
opacity,
|
|
155
|
+
transform: "none"
|
|
156
|
+
},
|
|
157
|
+
text
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function renderTypewriter(progress, motionProgress, scale, text, phases, sceneDuration) {
|
|
161
|
+
const raw = rawEntrance(progress, phases.entrancePhase);
|
|
162
|
+
const visibleChars = Math.max(0, Math.floor(raw * text.length));
|
|
163
|
+
const seconds = progress * sceneDuration;
|
|
164
|
+
const cursorOn = Math.sin(seconds * Math.PI * 3) > 0;
|
|
165
|
+
const stillTyping = visibleChars < text.length;
|
|
166
|
+
const opacityIn = Math.min(1, raw * 8);
|
|
167
|
+
const cursor = cursorOn && (stillTyping || !phases.inExit && opacityIn > 0.5);
|
|
168
|
+
let charExits;
|
|
169
|
+
if (phases.inExit && text.length > 0) {
|
|
170
|
+
const STAGGER_OUT = 0.025;
|
|
171
|
+
const PER_CHAR_OUT = 0.3;
|
|
172
|
+
const SLIDE_LEFT_DISTANCE = 350 * scale;
|
|
173
|
+
const actualExitSec = phases.exitPhase * sceneDuration;
|
|
174
|
+
const idealExitSec = (text.length - 1) * STAGGER_OUT + PER_CHAR_OUT;
|
|
175
|
+
const outScale = Math.min(1, actualExitSec / idealExitSec);
|
|
176
|
+
const stagOutNorm = STAGGER_OUT * outScale / sceneDuration;
|
|
177
|
+
const perCharOutNorm = PER_CHAR_OUT * outScale / sceneDuration;
|
|
178
|
+
charExits = Array.from({ length: text.length }, (_, i) => {
|
|
179
|
+
const charExitStart = phases.exitStart + i * stagOutNorm;
|
|
180
|
+
const charExitEnd = charExitStart + perCharOutNorm;
|
|
181
|
+
const charExitRaw = interpolate(motionProgress, [charExitStart, charExitEnd], [0, 1], {
|
|
182
|
+
extrapolateLeft: "clamp",
|
|
183
|
+
extrapolateRight: "clamp"
|
|
184
|
+
});
|
|
185
|
+
const charExitEased = Easing.in(Easing.cubic)(charExitRaw);
|
|
186
|
+
return {
|
|
187
|
+
opacity: 1 - charExitEased,
|
|
188
|
+
translateX: -charExitEased * SLIDE_LEFT_DISTANCE
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
kind: "typewriter",
|
|
194
|
+
visibleChars,
|
|
195
|
+
cursor,
|
|
196
|
+
opacity: opacityIn,
|
|
197
|
+
transform: "none",
|
|
198
|
+
charExits
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function renderWordStagger(progress, motionProgress, scale, text, sceneDuration) {
|
|
202
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
203
|
+
const wordCount = Math.max(words.length, 1);
|
|
204
|
+
const STAGGER_MIN = 0.28;
|
|
205
|
+
const STAGGER_MAX = 0.45;
|
|
206
|
+
const PER_WORD_ANIM = 0.5;
|
|
207
|
+
const minEntrance = (wordCount - 1) * STAGGER_MIN + PER_WORD_ANIM;
|
|
208
|
+
const TARGET_ENTRANCE_FRACTION = 0.55;
|
|
209
|
+
const targetEntrance = sceneDuration * TARGET_ENTRANCE_FRACTION;
|
|
210
|
+
const exitSec = ARCHETYPE_SPECS.wordStagger.exit;
|
|
211
|
+
const minHold = 0.5;
|
|
212
|
+
const maxEntrance = Math.max(0.6, sceneDuration - exitSec - minHold);
|
|
213
|
+
let entranceSec = Math.min(maxEntrance, Math.max(minEntrance, targetEntrance));
|
|
214
|
+
if (wordCount > 1) {
|
|
215
|
+
const impliedStagger = (entranceSec - PER_WORD_ANIM) / (wordCount - 1);
|
|
216
|
+
if (impliedStagger > STAGGER_MAX) {
|
|
217
|
+
entranceSec = (wordCount - 1) * STAGGER_MAX + PER_WORD_ANIM;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const entrancePhase = entranceSec / sceneDuration;
|
|
221
|
+
const exitPhase = Math.min(0.4, exitSec / sceneDuration);
|
|
222
|
+
const exitStart = 1 - exitPhase;
|
|
223
|
+
const staggerIn = wordCount > 1 ? (entranceSec - PER_WORD_ANIM) / (wordCount - 1) / sceneDuration : 0;
|
|
224
|
+
const wordEntranceDur = PER_WORD_ANIM / sceneDuration;
|
|
225
|
+
const STAGGER_OUT = 0.06;
|
|
226
|
+
const PER_WORD_OUT = 0.32;
|
|
227
|
+
const SLIDE_LEFT_DISTANCE = 350 * scale;
|
|
228
|
+
const actualExitSec = exitPhase * sceneDuration;
|
|
229
|
+
const idealExitSec = (wordCount - 1) * STAGGER_OUT + PER_WORD_OUT;
|
|
230
|
+
const outScale = Math.min(1, actualExitSec / idealExitSec);
|
|
231
|
+
const stagOutNorm = STAGGER_OUT * outScale / sceneDuration;
|
|
232
|
+
const perWordOutNorm = PER_WORD_OUT * outScale / sceneDuration;
|
|
233
|
+
const ht = holdT(motionProgress, entrancePhase, exitStart);
|
|
234
|
+
const ACTIVE_DIM = 0.85;
|
|
235
|
+
const perWord = words.map((w, i) => {
|
|
236
|
+
const wordStart = ENTRANCE_START + i * staggerIn;
|
|
237
|
+
const wordEnd = wordStart + wordEntranceDur;
|
|
238
|
+
const wordRaw = interpolate(progress, [wordStart, wordEnd], [0, 1], {
|
|
239
|
+
extrapolateLeft: "clamp",
|
|
240
|
+
extrapolateRight: "clamp"
|
|
241
|
+
});
|
|
242
|
+
const sp = spring(wordRaw, SPRING_CRISP);
|
|
243
|
+
const yIn = interpolate(sp, [0, 1], [55 * scale, 0]);
|
|
244
|
+
const rotIn = interpolate(sp, [0, 1], [-3, 0]);
|
|
245
|
+
const opacityIn = Math.min(1, wordRaw * 7);
|
|
246
|
+
const yBreathe = breathe(ht, 1 * scale, 1) * Math.cos(i / wordCount * Math.PI);
|
|
247
|
+
let dimProgress = 0;
|
|
248
|
+
if (i < words.length - 1) {
|
|
249
|
+
const nextStart = ENTRANCE_START + (i + 1) * staggerIn;
|
|
250
|
+
const SNAP_RAMP = 0.02;
|
|
251
|
+
dimProgress = interpolate(progress, [nextStart, nextStart + SNAP_RAMP], [0, 1], {
|
|
252
|
+
extrapolateLeft: "clamp",
|
|
253
|
+
extrapolateRight: "clamp"
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
const focusOpacity = 1 - dimProgress * (1 - ACTIVE_DIM);
|
|
257
|
+
const wordExitStart = exitStart + i * stagOutNorm;
|
|
258
|
+
const wordExitEnd = wordExitStart + perWordOutNorm;
|
|
259
|
+
const wordExitRaw = interpolate(motionProgress, [wordExitStart, wordExitEnd], [0, 1], {
|
|
260
|
+
extrapolateLeft: "clamp",
|
|
261
|
+
extrapolateRight: "clamp"
|
|
262
|
+
});
|
|
263
|
+
const wordExitEased = Easing.in(Easing.cubic)(wordExitRaw);
|
|
264
|
+
const exitX = -wordExitEased * SLIDE_LEFT_DISTANCE;
|
|
265
|
+
const isExiting = wordExitRaw > 0;
|
|
266
|
+
const opacity = opacityIn * focusOpacity * (1 - wordExitEased);
|
|
267
|
+
const y = yIn + (isExiting ? 0 : yBreathe);
|
|
268
|
+
const rot = isExiting ? 0 : rotIn;
|
|
269
|
+
return {
|
|
270
|
+
text: w,
|
|
271
|
+
style: {
|
|
272
|
+
opacity,
|
|
273
|
+
transform: `translate(${exitX}px, ${y}px) rotate(${rot}deg)`
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
});
|
|
277
|
+
return {
|
|
278
|
+
kind: "words",
|
|
279
|
+
words: perWord,
|
|
280
|
+
blockOpacity: 1,
|
|
281
|
+
blockTransform: "none"
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function renderSlam(progress, scale, text, phases) {
|
|
285
|
+
const raw = rawEntrance(progress, phases.entrancePhase);
|
|
286
|
+
const ex = rawExit(progress, phases.exitStart);
|
|
287
|
+
const ht = holdT(progress, phases.entrancePhase, phases.exitStart);
|
|
288
|
+
const sxIn = interpolate(raw, [0, 0.3, 0.45, 0.6, 1], [0.92, 1.22, 0.94, 1.02, 1]);
|
|
289
|
+
const syIn = interpolate(raw, [0, 0.3, 0.45, 0.6, 1], [1.18, 0.74, 1.12, 0.99, 1]);
|
|
290
|
+
const rotIn = interpolate(raw, [0, 0.3, 0.45, 1], [-1.5, 0, 0.8, 0]);
|
|
291
|
+
const lsInEm = interpolate(raw, [0, 0.2, 0.3, 0.45, 1], [0, -0.04, -0.06, 0, 0]);
|
|
292
|
+
const shakeWindow = Math.max(0, 1 - Math.max(0, raw - 0.3) / 0.2);
|
|
293
|
+
const shakeX = Math.sin(raw * 70) * 14 * scale * shakeWindow;
|
|
294
|
+
const shakeY = Math.cos(raw * 80) * 10 * scale * shakeWindow;
|
|
295
|
+
const opacityIn = Math.min(1, raw * 8);
|
|
296
|
+
const sBreathe = 1 + breathe(ht, 8e-3, 1);
|
|
297
|
+
const easedExit = Easing.in(Easing.exp)(ex);
|
|
298
|
+
const sOut = interpolate(easedExit, [0, 1], [1, 0.85]);
|
|
299
|
+
const opacityOut = interpolate(ex, [0, 0.55, 1], [1, 0.8, 0], {
|
|
300
|
+
extrapolateLeft: "clamp",
|
|
301
|
+
extrapolateRight: "clamp"
|
|
302
|
+
});
|
|
303
|
+
const sx = phases.inExit ? sOut : sxIn * sBreathe;
|
|
304
|
+
const sy = phases.inExit ? sOut : syIn * sBreathe;
|
|
305
|
+
const tx = phases.inExit ? 0 : shakeX;
|
|
306
|
+
const ty = phases.inExit ? 0 : shakeY;
|
|
307
|
+
const rot = phases.inExit ? 0 : rotIn;
|
|
308
|
+
const ls = phases.inExit ? 0 : lsInEm;
|
|
309
|
+
const opacity = opacityIn * (phases.inExit ? opacityOut : 1);
|
|
310
|
+
return {
|
|
311
|
+
kind: "block",
|
|
312
|
+
block: {
|
|
313
|
+
opacity,
|
|
314
|
+
transform: `translate(${tx}px, ${ty}px) rotate(${rot}deg) scale(${sx}, ${sy})`,
|
|
315
|
+
letterSpacing: `${ls}em`
|
|
316
|
+
},
|
|
317
|
+
text
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function renderCinematic(progress, scale, text, phases) {
|
|
321
|
+
const raw = rawEntrance(progress, phases.entrancePhase);
|
|
322
|
+
const ex = rawExit(progress, phases.exitStart);
|
|
323
|
+
const easedIn = Easing.out(Easing.circle)(raw);
|
|
324
|
+
const sIn = interpolate(easedIn, [0, 1], [3, 1]);
|
|
325
|
+
const yIn = interpolate(easedIn, [0, 1], [18 * scale, 0]);
|
|
326
|
+
const opacityIn = interpolate(raw, [0, 0.4], [0, 1], {
|
|
327
|
+
extrapolateLeft: "clamp",
|
|
328
|
+
extrapolateRight: "clamp"
|
|
329
|
+
});
|
|
330
|
+
const easedExit = Easing.in(Easing.exp)(ex);
|
|
331
|
+
const sOut = interpolate(easedExit, [0, 1], [1, 0.4]);
|
|
332
|
+
const yOut = interpolate(easedExit, [0, 1], [0, -10 * scale]);
|
|
333
|
+
const opacityOut = interpolate(ex, [0, 0.5, 1], [1, 0.85, 0], {
|
|
334
|
+
extrapolateLeft: "clamp",
|
|
335
|
+
extrapolateRight: "clamp"
|
|
336
|
+
});
|
|
337
|
+
const s = phases.inExit ? sOut : sIn;
|
|
338
|
+
const y = phases.inExit ? yOut : yIn;
|
|
339
|
+
const opacity = opacityIn * (phases.inExit ? opacityOut : 1);
|
|
340
|
+
return {
|
|
341
|
+
kind: "block",
|
|
342
|
+
block: {
|
|
343
|
+
opacity,
|
|
344
|
+
transform: `translateY(${y}px) scale(${s})`,
|
|
345
|
+
willChange: "transform, opacity"
|
|
346
|
+
},
|
|
347
|
+
text
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function renderHeroWord(progress, motionProgress, text, phases) {
|
|
351
|
+
const words = text.split(/\s+/).filter(Boolean);
|
|
352
|
+
if (words.length === 0) {
|
|
353
|
+
return { kind: "hero", word: "", index: 0, total: 0, opacity: 0, transform: "scale(1)" };
|
|
354
|
+
}
|
|
355
|
+
const sequenceStart = ENTRANCE_START + phases.entrancePhase * 0.2;
|
|
356
|
+
const sequenceEnd = phases.exitStart;
|
|
357
|
+
const sequenceDur = Math.max(0.01, sequenceEnd - sequenceStart);
|
|
358
|
+
const localProgress = Math.max(0, Math.min(0.999, (progress - sequenceStart) / sequenceDur));
|
|
359
|
+
const idx = Math.min(words.length - 1, Math.floor(localProgress / (1 / words.length)));
|
|
360
|
+
const local = localProgress * words.length - idx;
|
|
361
|
+
const isLast = idx === words.length - 1;
|
|
362
|
+
const ENTER_FRAC = 0.28;
|
|
363
|
+
const HOLD_FRAC = 0.55;
|
|
364
|
+
const HANDOFF_FRAC = 0.85;
|
|
365
|
+
let scale;
|
|
366
|
+
let opacity;
|
|
367
|
+
if (local < ENTER_FRAC) {
|
|
368
|
+
const entRaw = local / ENTER_FRAC;
|
|
369
|
+
const sp = spring(entRaw, SPRING_CRISP);
|
|
370
|
+
scale = sp < 0.7 ? interpolate(sp, [0, 0.7], [0.5, 1.06]) : interpolate(sp, [0.7, 1], [1.06, 1]);
|
|
371
|
+
opacity = Math.min(1, entRaw * 6);
|
|
372
|
+
} else if (local < HOLD_FRAC) {
|
|
373
|
+
const ht = (local - ENTER_FRAC) / (HOLD_FRAC - ENTER_FRAC);
|
|
374
|
+
scale = 1 + breathe(ht, 0.01, 1);
|
|
375
|
+
opacity = 1;
|
|
376
|
+
} else if (local < HANDOFF_FRAC && !isLast) {
|
|
377
|
+
const handoffRaw = (local - HOLD_FRAC) / (HANDOFF_FRAC - HOLD_FRAC);
|
|
378
|
+
scale = interpolate(handoffRaw, [0, 1], [1, 1.04]);
|
|
379
|
+
opacity = handoffRaw < 0.3 ? 1 : interpolate(handoffRaw, [0.3, 1], [1, 0]);
|
|
380
|
+
} else if (!isLast) {
|
|
381
|
+
scale = 1.04;
|
|
382
|
+
opacity = 0;
|
|
383
|
+
} else {
|
|
384
|
+
scale = 1;
|
|
385
|
+
opacity = 1;
|
|
386
|
+
}
|
|
387
|
+
if (isLast && phases.inExit) {
|
|
388
|
+
const ex = rawExit(motionProgress, phases.exitStart);
|
|
389
|
+
const easedExit = Easing.in(Easing.cubic)(ex);
|
|
390
|
+
scale = interpolate(easedExit, [0, 1], [1, 1.08]);
|
|
391
|
+
opacity = 1 - easedExit;
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
kind: "hero",
|
|
395
|
+
word: words[idx],
|
|
396
|
+
index: idx,
|
|
397
|
+
total: words.length,
|
|
398
|
+
opacity,
|
|
399
|
+
transform: `scale(${scale})`
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/visual-system/scene-templates/template-text.tsx
|
|
404
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
405
|
+
var DEFAULT_SAFE_ZONE = { top: 24, right: 24, bottom: 24, left: 24 };
|
|
406
|
+
var DEFAULT_FONT = "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
|
|
407
|
+
var TYPO = {
|
|
408
|
+
headline: {
|
|
409
|
+
letterSpacing: "-0.022em",
|
|
410
|
+
wordSpacing: "0.04em",
|
|
411
|
+
lineHeight: 1.1
|
|
412
|
+
},
|
|
413
|
+
caption: {
|
|
414
|
+
letterSpacing: "-0.012em",
|
|
415
|
+
wordSpacing: "0.06em",
|
|
416
|
+
lineHeight: 1.25
|
|
417
|
+
},
|
|
418
|
+
label: {
|
|
419
|
+
letterSpacing: "-0.005em",
|
|
420
|
+
wordSpacing: "0.10em",
|
|
421
|
+
lineHeight: 1.4
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
var FONT_FEATURES = '"kern" 1, "liga" 1';
|
|
425
|
+
function dropShadowFor(textColor) {
|
|
426
|
+
const dark = isLikelyDark(textColor);
|
|
427
|
+
const tone = dark ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.2)";
|
|
428
|
+
return `0 1px 2px ${tone}`;
|
|
429
|
+
}
|
|
430
|
+
function isLikelyDark(color) {
|
|
431
|
+
const m = color.replace("#", "");
|
|
432
|
+
if (m.length === 3) {
|
|
433
|
+
const r = parseInt(m[0] + m[0], 16);
|
|
434
|
+
const g = parseInt(m[1] + m[1], 16);
|
|
435
|
+
const b = parseInt(m[2] + m[2], 16);
|
|
436
|
+
return (r * 299 + g * 587 + b * 114) / 1e3 < 128;
|
|
437
|
+
}
|
|
438
|
+
if (m.length === 6) {
|
|
439
|
+
const r = parseInt(m.slice(0, 2), 16);
|
|
440
|
+
const g = parseInt(m.slice(2, 4), 16);
|
|
441
|
+
const b = parseInt(m.slice(4, 6), 16);
|
|
442
|
+
return (r * 299 + g * 587 + b * 114) / 1e3 < 128;
|
|
443
|
+
}
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
function computeHeroFontSize(wordChars, position, width, height, safeZone) {
|
|
447
|
+
const chars = Math.max(wordChars, 1);
|
|
448
|
+
const s_min = Math.min(width, height) / 1080;
|
|
449
|
+
const widthBudget = (width - safeZone.left - safeZone.right) * 0.92;
|
|
450
|
+
const pxPerChar = 0.58;
|
|
451
|
+
const widthCap = widthBudget / (chars * pxPerChar);
|
|
452
|
+
if (position === "center") {
|
|
453
|
+
return Math.min(480 * s_min, widthCap);
|
|
454
|
+
}
|
|
455
|
+
const heightCap = height * 0.28 * 0.8;
|
|
456
|
+
return Math.min(220 * s_min, widthCap, heightCap);
|
|
457
|
+
}
|
|
458
|
+
function smoothSize(chars, maxChars, max, min) {
|
|
459
|
+
const t = Math.max(0, Math.min(1, chars / maxChars));
|
|
460
|
+
const eased = t * t;
|
|
461
|
+
return max - (max - min) * eased;
|
|
462
|
+
}
|
|
463
|
+
function computeFontSize(archetype, text, role, position, width, height, safeZone) {
|
|
464
|
+
const s_min = Math.min(width, height) / 1080;
|
|
465
|
+
if (archetype === "heroWord") {
|
|
466
|
+
const longestChars = text.split(/\s+/).filter(Boolean).reduce((m, w) => Math.max(m, w.length), 1);
|
|
467
|
+
return {
|
|
468
|
+
fontSize: computeHeroFontSize(longestChars, position, width, height, safeZone),
|
|
469
|
+
fontWeight: 800
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
const chars = text.length;
|
|
473
|
+
if (role === "headline") {
|
|
474
|
+
const refSize2 = smoothSize(
|
|
475
|
+
chars,
|
|
476
|
+
/* maxChars */
|
|
477
|
+
70,
|
|
478
|
+
/* max */
|
|
479
|
+
88,
|
|
480
|
+
/* min */
|
|
481
|
+
60
|
|
482
|
+
);
|
|
483
|
+
return { fontSize: refSize2 * s_min, fontWeight: 700 };
|
|
484
|
+
}
|
|
485
|
+
if (role === "caption") {
|
|
486
|
+
const refSize2 = smoothSize(chars, 80, 48, 32);
|
|
487
|
+
return { fontSize: refSize2 * s_min, fontWeight: 600 };
|
|
488
|
+
}
|
|
489
|
+
const refSize = smoothSize(chars, 80, 34, 24);
|
|
490
|
+
return { fontSize: refSize * s_min, fontWeight: 500 };
|
|
491
|
+
}
|
|
492
|
+
function positionStyle(position, height, safeZone) {
|
|
493
|
+
switch (position) {
|
|
494
|
+
case "top":
|
|
495
|
+
return {
|
|
496
|
+
top: safeZone.top + height * 0.06,
|
|
497
|
+
height: height * 0.28,
|
|
498
|
+
alignItems: "flex-start"
|
|
499
|
+
};
|
|
500
|
+
case "bottom":
|
|
501
|
+
return {
|
|
502
|
+
bottom: safeZone.bottom + height * 0.06,
|
|
503
|
+
height: height * 0.28,
|
|
504
|
+
alignItems: "flex-end"
|
|
505
|
+
};
|
|
506
|
+
case "center":
|
|
507
|
+
default:
|
|
508
|
+
return {
|
|
509
|
+
top: 0,
|
|
510
|
+
bottom: 0,
|
|
511
|
+
alignItems: "center"
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
var TemplateText = ({
|
|
516
|
+
archetype: archetypeRaw,
|
|
517
|
+
text: textRaw,
|
|
518
|
+
progress,
|
|
519
|
+
motionProgress = progress,
|
|
520
|
+
sceneDuration,
|
|
521
|
+
width,
|
|
522
|
+
height,
|
|
523
|
+
position = "center",
|
|
524
|
+
sizeRole = "headline",
|
|
525
|
+
typeTreatment,
|
|
526
|
+
safeZone = DEFAULT_SAFE_ZONE,
|
|
527
|
+
font = DEFAULT_FONT,
|
|
528
|
+
color = "#FFFFFF",
|
|
529
|
+
beatIntensity = 0
|
|
530
|
+
}) => {
|
|
531
|
+
const textSafe = typeof textRaw === "string" ? textRaw : "";
|
|
532
|
+
const isDevelopment = import.meta.env?.DEV;
|
|
533
|
+
if (textRaw !== void 0 && typeof textRaw !== "string" && isDevelopment) {
|
|
534
|
+
console.warn("[TemplateText] received non-string text:", textRaw);
|
|
535
|
+
}
|
|
536
|
+
const text = textSafe.replace(/\s*\|\s*/g, "\n");
|
|
537
|
+
const archetype = normalizeArchetype(archetypeRaw);
|
|
538
|
+
const scale = Math.min(width, height) / 1080;
|
|
539
|
+
const result = renderArchetype(
|
|
540
|
+
archetype,
|
|
541
|
+
progress,
|
|
542
|
+
scale,
|
|
543
|
+
text,
|
|
544
|
+
sceneDuration,
|
|
545
|
+
typeTreatment?.phaseScale ?? 1,
|
|
546
|
+
motionProgress
|
|
547
|
+
);
|
|
548
|
+
const { fontSize, fontWeight } = computeFontSize(
|
|
549
|
+
archetype,
|
|
550
|
+
text,
|
|
551
|
+
sizeRole,
|
|
552
|
+
position,
|
|
553
|
+
width,
|
|
554
|
+
height,
|
|
555
|
+
safeZone
|
|
556
|
+
);
|
|
557
|
+
void beatIntensity;
|
|
558
|
+
const baseTypo = TYPO[sizeRole];
|
|
559
|
+
const tt = sizeRole === "headline" ? typeTreatment : void 0;
|
|
560
|
+
const typo = tt ? {
|
|
561
|
+
...baseTypo,
|
|
562
|
+
...tt.trackingDeltaEm !== 0 ? {
|
|
563
|
+
letterSpacing: `${Number(
|
|
564
|
+
(parseFloat(baseTypo.letterSpacing) + tt.trackingDeltaEm).toFixed(4)
|
|
565
|
+
)}em`
|
|
566
|
+
} : {},
|
|
567
|
+
...tt.transform ? { textTransform: tt.transform } : {}
|
|
568
|
+
} : baseTypo;
|
|
569
|
+
const presetWeight = tt ? Math.min(900, Math.max(100, fontWeight + tt.weightDelta)) : fontWeight;
|
|
570
|
+
const presetSize = tt ? fontSize * tt.sizeScale : fontSize;
|
|
571
|
+
const textShadow = dropShadowFor(color);
|
|
572
|
+
const containerStyle = {
|
|
573
|
+
position: "absolute",
|
|
574
|
+
left: 0,
|
|
575
|
+
right: 0,
|
|
576
|
+
display: "flex",
|
|
577
|
+
justifyContent: "center",
|
|
578
|
+
padding: `0 ${safeZone.right}px 0 ${safeZone.left}px`,
|
|
579
|
+
color,
|
|
580
|
+
fontFamily: font,
|
|
581
|
+
fontWeight: presetWeight,
|
|
582
|
+
fontSize: presetSize,
|
|
583
|
+
textAlign: "center",
|
|
584
|
+
pointerEvents: "none",
|
|
585
|
+
fontFeatureSettings: FONT_FEATURES,
|
|
586
|
+
textRendering: "optimizeLegibility",
|
|
587
|
+
WebkitFontSmoothing: "antialiased",
|
|
588
|
+
MozOsxFontSmoothing: "grayscale",
|
|
589
|
+
// Respect explicit newlines — the centralized `|` → `\n` conversion
|
|
590
|
+
// above (and callers passing real newlines) rely on this. Multiple
|
|
591
|
+
// spaces still collapse normally; only `\n` and CRLF break.
|
|
592
|
+
whiteSpace: "pre-line",
|
|
593
|
+
...textShadow ? { textShadow } : {},
|
|
594
|
+
...typo,
|
|
595
|
+
...positionStyle(position, height, safeZone)
|
|
596
|
+
};
|
|
597
|
+
if (result.kind === "block") {
|
|
598
|
+
return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsx(
|
|
599
|
+
"div",
|
|
600
|
+
{
|
|
601
|
+
style: {
|
|
602
|
+
opacity: result.block.opacity,
|
|
603
|
+
transform: `${result.block.transform}`,
|
|
604
|
+
// Inherit letter-spacing from the container's typography defaults
|
|
605
|
+
// unless the archetype explicitly overrides (e.g., for animated tracking).
|
|
606
|
+
...result.block.letterSpacing ? { letterSpacing: result.block.letterSpacing } : {},
|
|
607
|
+
...result.block.willChange ? { willChange: result.block.willChange } : {},
|
|
608
|
+
maxWidth: "85%"
|
|
609
|
+
},
|
|
610
|
+
children: renderWithEmoji(result.text, fontSize)
|
|
611
|
+
}
|
|
612
|
+
) });
|
|
613
|
+
}
|
|
614
|
+
if (result.kind === "typewriter") {
|
|
615
|
+
const chars = text.split("");
|
|
616
|
+
const emojiPlan = planTypewriterEmoji(text);
|
|
617
|
+
const cursorBar = {
|
|
618
|
+
position: "absolute",
|
|
619
|
+
width: "0.08em",
|
|
620
|
+
height: "0.88em",
|
|
621
|
+
background: "currentColor",
|
|
622
|
+
borderRadius: "0.01em",
|
|
623
|
+
pointerEvents: "none"
|
|
624
|
+
};
|
|
625
|
+
return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsxs(
|
|
626
|
+
"div",
|
|
627
|
+
{
|
|
628
|
+
style: {
|
|
629
|
+
opacity: result.opacity,
|
|
630
|
+
maxWidth: "85%",
|
|
631
|
+
whiteSpace: "pre-wrap",
|
|
632
|
+
position: "relative"
|
|
633
|
+
},
|
|
634
|
+
children: [
|
|
635
|
+
chars.map((ch, i) => {
|
|
636
|
+
const isTyped = i < result.visibleChars;
|
|
637
|
+
const isLastTyped = i === result.visibleChars - 1;
|
|
638
|
+
const anchorCursor = isLastTyped && result.cursor;
|
|
639
|
+
const charExit = result.charExits?.[i];
|
|
640
|
+
const baseOpacity = isTyped ? 1 : 0;
|
|
641
|
+
const finalOpacity = baseOpacity * (charExit?.opacity ?? 1);
|
|
642
|
+
const exitStyle = charExit ? {
|
|
643
|
+
display: "inline-block",
|
|
644
|
+
transform: `translateX(${charExit.translateX}px)`,
|
|
645
|
+
whiteSpace: "pre"
|
|
646
|
+
} : null;
|
|
647
|
+
const emojiChar = emojiPlan?.starts.get(i);
|
|
648
|
+
if (emojiPlan?.covered.has(i)) return null;
|
|
649
|
+
return /* @__PURE__ */ jsxs(
|
|
650
|
+
"span",
|
|
651
|
+
{
|
|
652
|
+
style: {
|
|
653
|
+
opacity: finalOpacity,
|
|
654
|
+
position: anchorCursor ? "relative" : "static",
|
|
655
|
+
...exitStyle ?? {}
|
|
656
|
+
},
|
|
657
|
+
children: [
|
|
658
|
+
emojiChar ? /* @__PURE__ */ jsx(Emoji, { char: emojiChar, size: fontSize }) : ch,
|
|
659
|
+
anchorCursor && /* @__PURE__ */ jsx(
|
|
660
|
+
"span",
|
|
661
|
+
{
|
|
662
|
+
"aria-hidden": true,
|
|
663
|
+
style: {
|
|
664
|
+
...cursorBar,
|
|
665
|
+
left: "100%",
|
|
666
|
+
top: "0.08em",
|
|
667
|
+
marginLeft: "0.12em"
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
)
|
|
671
|
+
]
|
|
672
|
+
},
|
|
673
|
+
i
|
|
674
|
+
);
|
|
675
|
+
}),
|
|
676
|
+
result.visibleChars === 0 && result.cursor && /* @__PURE__ */ jsx(
|
|
677
|
+
"span",
|
|
678
|
+
{
|
|
679
|
+
"aria-hidden": true,
|
|
680
|
+
style: {
|
|
681
|
+
...cursorBar,
|
|
682
|
+
left: 0,
|
|
683
|
+
top: "0.08em"
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
)
|
|
687
|
+
]
|
|
688
|
+
}
|
|
689
|
+
) });
|
|
690
|
+
}
|
|
691
|
+
if (result.kind === "words") {
|
|
692
|
+
return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsx(
|
|
693
|
+
"div",
|
|
694
|
+
{
|
|
695
|
+
style: {
|
|
696
|
+
opacity: result.blockOpacity,
|
|
697
|
+
transform: `${result.blockTransform} `,
|
|
698
|
+
maxWidth: "85%"
|
|
699
|
+
},
|
|
700
|
+
children: result.words.map((w, i) => /* @__PURE__ */ jsxs("span", { children: [
|
|
701
|
+
/* @__PURE__ */ jsx(
|
|
702
|
+
"span",
|
|
703
|
+
{
|
|
704
|
+
style: {
|
|
705
|
+
display: "inline-block",
|
|
706
|
+
opacity: w.style.opacity,
|
|
707
|
+
transform: w.style.transform
|
|
708
|
+
},
|
|
709
|
+
children: renderWithEmoji(w.text, fontSize)
|
|
710
|
+
}
|
|
711
|
+
),
|
|
712
|
+
i < result.words.length - 1 ? " " : ""
|
|
713
|
+
] }, i))
|
|
714
|
+
}
|
|
715
|
+
) });
|
|
716
|
+
}
|
|
717
|
+
if (result.kind === "hero") {
|
|
718
|
+
const perWordFontSize = computeHeroFontSize(
|
|
719
|
+
result.word.length,
|
|
720
|
+
position,
|
|
721
|
+
width,
|
|
722
|
+
height,
|
|
723
|
+
safeZone
|
|
724
|
+
);
|
|
725
|
+
const slotHeight = position === "center" ? 480 * scale : Math.min(220 * scale, height * 0.28 * 0.8);
|
|
726
|
+
return /* @__PURE__ */ jsx("div", { style: { ...containerStyle, fontSize: perWordFontSize }, children: /* @__PURE__ */ jsx(
|
|
727
|
+
"div",
|
|
728
|
+
{
|
|
729
|
+
style: {
|
|
730
|
+
height: slotHeight,
|
|
731
|
+
display: "flex",
|
|
732
|
+
alignItems: "center",
|
|
733
|
+
justifyContent: "center"
|
|
734
|
+
},
|
|
735
|
+
children: /* @__PURE__ */ jsx(
|
|
736
|
+
"div",
|
|
737
|
+
{
|
|
738
|
+
style: {
|
|
739
|
+
opacity: result.opacity,
|
|
740
|
+
transform: `${result.transform} `,
|
|
741
|
+
lineHeight: 1,
|
|
742
|
+
...result.letterSpacing ? { letterSpacing: result.letterSpacing } : {}
|
|
743
|
+
},
|
|
744
|
+
children: renderWithEmoji(result.word, perWordFontSize)
|
|
745
|
+
}
|
|
746
|
+
)
|
|
747
|
+
}
|
|
748
|
+
) });
|
|
749
|
+
}
|
|
750
|
+
return null;
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
export {
|
|
754
|
+
smoothSize,
|
|
755
|
+
TemplateText
|
|
756
|
+
};
|