@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.
Files changed (162) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +201 -0
  3. package/PUBLIC-API.md +276 -0
  4. package/README.md +152 -0
  5. package/SECURITY.md +22 -0
  6. package/SUPPORT.md +28 -0
  7. package/bin/vanillasky.js +3 -0
  8. package/dist/bg-confetti-WNQXS7ZS.js +89 -0
  9. package/dist/bg-emoji-ETI47GLS.js +150 -0
  10. package/dist/bg-media-SVUZAMGX.js +99 -0
  11. package/dist/brand-message-F3RD4P3P.js +253 -0
  12. package/dist/builtin-server-F7YXRVCH.js +8 -0
  13. package/dist/catalog-types-BIhSpOWK.d.ts +74 -0
  14. package/dist/chart-bar-CNSC7BNK.js +304 -0
  15. package/dist/chart-counter-MCRHKN77.js +299 -0
  16. package/dist/chart-progress-ring-RMI4A7ZK.js +296 -0
  17. package/dist/check-runtime.d.ts +15 -0
  18. package/dist/check-runtime.js +102 -0
  19. package/dist/chunk-2E6T633S.js +27 -0
  20. package/dist/chunk-352ZZCET.js +83 -0
  21. package/dist/chunk-73NTSFFI.js +81 -0
  22. package/dist/chunk-A2X45FET.js +46 -0
  23. package/dist/chunk-ABPKM4GK.js +187 -0
  24. package/dist/chunk-ABZACD4G.js +216 -0
  25. package/dist/chunk-BCRUUJ6A.js +756 -0
  26. package/dist/chunk-CHLGW22F.js +3551 -0
  27. package/dist/chunk-CJ7F2CE6.js +104 -0
  28. package/dist/chunk-CR7FE7BW.js +109 -0
  29. package/dist/chunk-E7CL7UPB.js +109 -0
  30. package/dist/chunk-EE6PK6XC.js +18 -0
  31. package/dist/chunk-ERY4S6QV.js +385 -0
  32. package/dist/chunk-EVG2ZYIY.js +125 -0
  33. package/dist/chunk-FNAEQ7QO.js +155 -0
  34. package/dist/chunk-FQ47WXAJ.js +38 -0
  35. package/dist/chunk-FSG2PYWG.js +336 -0
  36. package/dist/chunk-GHJEAP4O.js +504 -0
  37. package/dist/chunk-GRMUV7QY.js +262 -0
  38. package/dist/chunk-IFOW65Z6.js +205 -0
  39. package/dist/chunk-IXCUY3B3.js +786 -0
  40. package/dist/chunk-L5BTTCZV.js +97 -0
  41. package/dist/chunk-LMG3GHLL.js +107 -0
  42. package/dist/chunk-OOPIETLW.js +101 -0
  43. package/dist/chunk-P22HHFDK.js +43 -0
  44. package/dist/chunk-QAAO7OAY.js +19 -0
  45. package/dist/chunk-S7HFLYNN.js +35 -0
  46. package/dist/chunk-SPVTJH3F.js +24 -0
  47. package/dist/chunk-W2XJKG6C.js +3177 -0
  48. package/dist/chunk-XB7LH4J2.js +301 -0
  49. package/dist/chunk-XGRA2MUV.js +242 -0
  50. package/dist/chunk-XTOXFMLM.js +105 -0
  51. package/dist/chunk-YC5MFXA5.js +132 -0
  52. package/dist/cli.d.ts +1 -0
  53. package/dist/cli.js +1681 -0
  54. package/dist/compose-video-CN3VSWST.js +13 -0
  55. package/dist/cta-logo-SVA5GP2A.js +209 -0
  56. package/dist/cta-media-E7VLLK42.js +215 -0
  57. package/dist/events-_wfjbmEp.d.ts +81 -0
  58. package/dist/incoming-call-62MFZL3Z.js +320 -0
  59. package/dist/index.d.ts +15 -0
  60. package/dist/index.js +14 -0
  61. package/dist/infographic-before-after-AXCDAWND.js +460 -0
  62. package/dist/infographic-feature-list-GW5GUE6O.js +266 -0
  63. package/dist/infographic-problem-solution-7NURJANA.js +340 -0
  64. package/dist/infographic-stat-row-J7IZR6ZN.js +241 -0
  65. package/dist/infographic-steps-VSZM6F5M.js +400 -0
  66. package/dist/kit-BMNcHqa9.d.ts +79 -0
  67. package/dist/prompt-input-LOUCRXCY.js +236 -0
  68. package/dist/react.d.ts +81 -0
  69. package/dist/react.js +835 -0
  70. package/dist/reaction-QG4CYZLQ.js +119 -0
  71. package/dist/server.d.ts +141 -0
  72. package/dist/server.js +498 -0
  73. package/dist/showcase-code-RUTFZR3H.js +351 -0
  74. package/dist/showcase-phone-6CP6XEU5.js +272 -0
  75. package/dist/showcase-terminal-MGAEQWPR.js +306 -0
  76. package/dist/showcase-web-Y4W2HVRJ.js +390 -0
  77. package/dist/social-conversation-75SD2637.js +695 -0
  78. package/dist/social-milestone-XI2QE5BO.js +292 -0
  79. package/dist/social-notification-2UOHQCUY.js +244 -0
  80. package/dist/social-review-stack-FDYZGZTJ.js +239 -0
  81. package/dist/social-testimonial-OQ7N52GJ.js +284 -0
  82. package/dist/social-tweet-5OLCNPPC.js +295 -0
  83. package/dist/state-DZcKuS32.d.ts +3 -0
  84. package/dist/system-prompt-A6UXNTPK.js +8 -0
  85. package/dist/template-catalog.d.ts +24 -0
  86. package/dist/template-catalog.js +6 -0
  87. package/dist/templates.d.ts +25 -0
  88. package/dist/templates.js +19 -0
  89. package/dist/test.d.ts +149 -0
  90. package/dist/test.js +272 -0
  91. package/dist/text-stream-6XHC7J47.js +10 -0
  92. package/dist/types-B-PZR6G_.d.ts +208 -0
  93. package/dist/validate-T7GBU2YF.js +8 -0
  94. package/docs/agent-integration.md +39 -0
  95. package/docs/architecture.md +84 -0
  96. package/docs/branding-and-personalization.md +87 -0
  97. package/docs/concepts.md +106 -0
  98. package/docs/custom-templates.md +329 -0
  99. package/docs/customization.md +141 -0
  100. package/docs/errors.md +70 -0
  101. package/docs/getting-started.md +111 -0
  102. package/docs/input-and-first-scene.md +41 -0
  103. package/docs/integrate-nextjs.md +79 -0
  104. package/docs/media-and-audio.md +69 -0
  105. package/docs/motion-and-effects.md +107 -0
  106. package/docs/persistence.md +95 -0
  107. package/docs/production.md +122 -0
  108. package/docs/prompt-and-input.md +185 -0
  109. package/docs/provider-integration.md +115 -0
  110. package/docs/reference/protocol.md +96 -0
  111. package/docs/reference/provider-adapters.md +158 -0
  112. package/docs/responsive-orientation.md +37 -0
  113. package/docs/security.md +38 -0
  114. package/docs/streaming-protocol.md +15 -0
  115. package/docs/testing.md +137 -0
  116. package/docs/use-cases.md +59 -0
  117. package/examples/custom-template/README.md +19 -0
  118. package/examples/custom-template/minimal-text.tsx +82 -0
  119. package/examples/custom-template/structured-data.tsx +104 -0
  120. package/examples/custom-template/supplied-media.tsx +92 -0
  121. package/examples/nextjs-quickstart/.env.example +2 -0
  122. package/examples/nextjs-quickstart/README.md +22 -0
  123. package/examples/nextjs-quickstart/next-env.d.ts +4 -0
  124. package/examples/nextjs-quickstart/package.json +24 -0
  125. package/examples/nextjs-quickstart/src/app/api/video/route.ts +22 -0
  126. package/examples/nextjs-quickstart/src/app/layout.tsx +5 -0
  127. package/examples/nextjs-quickstart/src/app/page.tsx +31 -0
  128. package/examples/nextjs-quickstart/tsconfig.json +26 -0
  129. package/package.json +167 -0
  130. package/registry/items/backgrounds.json +52 -0
  131. package/registry/items/barChart.json +237 -0
  132. package/registry/items/beforeAfter.json +180 -0
  133. package/registry/items/bigNumber.json +225 -0
  134. package/registry/items/brandMessage.json +158 -0
  135. package/registry/items/cardList.json +206 -0
  136. package/registry/items/chatMessenger.json +151 -0
  137. package/registry/items/chatWhatsapp.json +151 -0
  138. package/registry/items/codeEditor.json +184 -0
  139. package/registry/items/confetti.json +107 -0
  140. package/registry/items/ctaLogo.json +160 -0
  141. package/registry/items/ctaMedia.json +193 -0
  142. package/registry/items/emojiBurst.json +101 -0
  143. package/registry/items/incomingCall.json +170 -0
  144. package/registry/items/media.json +179 -0
  145. package/registry/items/milestone.json +205 -0
  146. package/registry/items/motion.json +45 -0
  147. package/registry/items/notification.json +182 -0
  148. package/registry/items/phoneMockup.json +254 -0
  149. package/registry/items/problemSolution.json +186 -0
  150. package/registry/items/progressRing.json +199 -0
  151. package/registry/items/promptInput.json +148 -0
  152. package/registry/items/reaction.json +170 -0
  153. package/registry/items/reviewStack.json +224 -0
  154. package/registry/items/steps.json +207 -0
  155. package/registry/items/template-context.json +31 -0
  156. package/registry/items/terminal.json +212 -0
  157. package/registry/items/testimonial.json +164 -0
  158. package/registry/items/theme.json +47 -0
  159. package/registry/items/tripleStats.json +228 -0
  160. package/registry/items/tweet.json +203 -0
  161. package/registry/items/typography.json +47 -0
  162. package/registry/items/webMockup.json +273 -0
@@ -0,0 +1,74 @@
1
+ interface TemplateJsonSchemaProperty {
2
+ readonly type?: "string" | "number" | "integer" | "boolean" | "array" | "object";
3
+ /** Semantic formats enforced by runtime validation instead of inferred from field names. */
4
+ readonly format?: "uri" | "supplied-image" | "grounded-quote" | "grounded-stat" | "stock-media-keyword" | "emoji";
5
+ readonly enum?: readonly string[];
6
+ readonly title?: string;
7
+ readonly description?: string;
8
+ readonly default?: unknown;
9
+ readonly examples?: readonly unknown[];
10
+ readonly minItems?: number;
11
+ readonly maxItems?: number;
12
+ readonly minLength?: number;
13
+ readonly maxLength?: number;
14
+ readonly minimum?: number;
15
+ readonly maximum?: number;
16
+ readonly items?: TemplateJsonSchemaProperty;
17
+ readonly properties?: Readonly<Record<string, TemplateJsonSchemaProperty>>;
18
+ readonly required?: readonly string[];
19
+ readonly additionalProperties?: boolean;
20
+ }
21
+ interface TemplateJsonSchema {
22
+ readonly type: "object";
23
+ readonly properties: Readonly<Record<string, TemplateJsonSchemaProperty>>;
24
+ readonly required?: readonly string[];
25
+ readonly additionalProperties?: boolean;
26
+ readonly "x-vanillasky"?: {
27
+ readonly requiresStat?: boolean;
28
+ readonly allowsStockMedia?: boolean;
29
+ /** Every group requires at least one declared top-level field with a meaningful non-empty value. */
30
+ readonly requiredAnyOf?: readonly (readonly string[])[];
31
+ };
32
+ }
33
+ type TemplateJob = "setup" | "claim" | "proof" | "atmosphere" | "payoff" | "punctuation" | "ask";
34
+ type TemplateRegister = "motion-led" | "typography-led" | "device-led" | "card-led" | "mockup-led";
35
+ type TemplateFamily = "Media & motion" | "Data & metrics" | "Product showcase" | "Social & messaging" | "Explainers" | "Calls to action";
36
+ interface TemplateTimingMetadata {
37
+ /** Schema fields whose rendered content may inform a future adaptive duration. */
38
+ readonly contentFields: readonly string[];
39
+ /** How content in contentFields should be measured; no runtime behavior is implied. */
40
+ readonly contentUnit: "words" | "characters" | "items";
41
+ }
42
+ interface TemplateTransitionTiming {
43
+ /** Earliest readable local motion point used when this scene enters from a global transition. */
44
+ readonly entryReadyProgress: number;
45
+ /** Audited readable checkpoint before the template's own exit animation begins. */
46
+ readonly holdProgress: number;
47
+ }
48
+ interface SceneTemplateMetadata {
49
+ id: string;
50
+ label?: string;
51
+ description?: string;
52
+ category?: string | null;
53
+ /** Human-readable browse group. */
54
+ family?: TemplateFamily;
55
+ jobs?: TemplateJob[];
56
+ register?: TemplateRegister;
57
+ useWhen?: string;
58
+ avoidWhen?: string;
59
+ usesGlobalTextEffect: boolean;
60
+ /** Opts into renderer-owned transitions. Defaults to false for customer templates. */
61
+ usesGlobalTransition: boolean;
62
+ /** Required exactly when usesGlobalTransition is true. */
63
+ transitionTiming?: TemplateTransitionTiming;
64
+ usesGlobalBackgroundEffect: boolean;
65
+ textCanvas?: "tight" | "open";
66
+ /** The only authoring contract for defaults, validation, planning, grounding, and editor fields. */
67
+ schema: TemplateJsonSchema;
68
+ minDuration?: number;
69
+ preferredDuration?: number;
70
+ /** Declarative hints for future content-aware duration selection. */
71
+ timing?: TemplateTimingMetadata;
72
+ }
73
+
74
+ export type { SceneTemplateMetadata as S, TemplateFamily as T, TemplateJob as a, TemplateRegister as b, TemplateTimingMetadata as c, TemplateJsonSchema as d, TemplateTransitionTiming as e, TemplateJsonSchemaProperty as f };
@@ -0,0 +1,304 @@
1
+ import {
2
+ SceneBackground,
3
+ getMediaBackgroundProps
4
+ } from "./chunk-IFOW65Z6.js";
5
+ import {
6
+ TemplateText
7
+ } from "./chunk-BCRUUJ6A.js";
8
+ import "./chunk-L5BTTCZV.js";
9
+ import {
10
+ TOP_TEXT_AREA_RATIO,
11
+ withOpacity
12
+ } from "./chunk-ABZACD4G.js";
13
+ import {
14
+ SPRING_SNAPPY,
15
+ interpolate,
16
+ spring
17
+ } from "./chunk-XTOXFMLM.js";
18
+ import {
19
+ resolveTokens
20
+ } from "./chunk-FNAEQ7QO.js";
21
+
22
+ // src/visual-system/primitives/charts/BarChart.tsx
23
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
24
+ var MAX_BAR_ITEMS = 6;
25
+ function exactValue(value) {
26
+ return String(value);
27
+ }
28
+ var BarChart = ({
29
+ progress,
30
+ data,
31
+ width,
32
+ height,
33
+ chartColor = "#ffffff",
34
+ textColor = "#ffffff",
35
+ beatIntensity = 0
36
+ }) => {
37
+ const values = data.filter(({ value }) => Number.isFinite(value)).slice(0, MAX_BAR_ITEMS);
38
+ const s = Math.min(width, height) / 1080;
39
+ const portrait = height > width;
40
+ const chartWidth = width * (portrait ? 0.88 : 0.84);
41
+ const chartHeight = height * (portrait ? 0.56 : 0.52);
42
+ const bottom = (portrait ? 112 : 60) * s;
43
+ const labelHeight = (portrait ? 94 : 66) * s;
44
+ const valueReserve = (portrait ? 52 : 44) * s;
45
+ const maxBarHeight = chartHeight - labelHeight - valueReserve;
46
+ const gap = (portrait ? 14 : 20) * s;
47
+ const barCount = Math.max(values.length, 1);
48
+ const itemWidth = (chartWidth - gap * (barCount - 1)) / barCount;
49
+ const barWidth = Math.max(itemWidth * 0.68, 2 * s);
50
+ const maxValue = Math.max(1, ...values.map(({ value }) => Math.max(0, value)));
51
+ const beatScale = 1 + beatIntensity * 0.03;
52
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
53
+ /* @__PURE__ */ jsx(
54
+ "div",
55
+ {
56
+ role: "img",
57
+ "aria-label": values.map(({ label, value }) => `${label || "Value"}: ${exactValue(value)}`).join(", "),
58
+ style: {
59
+ position: "absolute",
60
+ bottom,
61
+ left: (width - chartWidth) / 2,
62
+ width: chartWidth,
63
+ height: chartHeight,
64
+ display: "flex",
65
+ alignItems: "flex-end",
66
+ gap
67
+ },
68
+ children: values.map(({ label, value }, index) => {
69
+ const barStart = 0.06 + index * 0.05;
70
+ const barProgress = interpolate(progress, [barStart, barStart + 0.34], [0, 1], {
71
+ extrapolateLeft: "clamp",
72
+ extrapolateRight: "clamp"
73
+ });
74
+ const springValue = spring(barProgress, SPRING_SNAPPY);
75
+ const targetHeight = Math.max(0, value) / maxValue * maxBarHeight;
76
+ return /* @__PURE__ */ jsxs(
77
+ "div",
78
+ {
79
+ "data-bar-chart-item": "true",
80
+ style: {
81
+ width: itemWidth,
82
+ height: chartHeight,
83
+ display: "flex",
84
+ flexDirection: "column",
85
+ justifyContent: "flex-end",
86
+ alignItems: "center",
87
+ minWidth: 0
88
+ },
89
+ children: [
90
+ /* @__PURE__ */ jsx(
91
+ "div",
92
+ {
93
+ style: {
94
+ width: "100%",
95
+ height: maxBarHeight + valueReserve,
96
+ display: "flex",
97
+ alignItems: "flex-end",
98
+ justifyContent: "center"
99
+ },
100
+ children: /* @__PURE__ */ jsx(
101
+ "div",
102
+ {
103
+ style: {
104
+ position: "relative",
105
+ width: barWidth,
106
+ height: springValue * targetHeight,
107
+ background: `linear-gradient(to top, ${withOpacity(chartColor, 0.82)}, ${withOpacity(chartColor, 0.42)})`,
108
+ border: `1px solid ${withOpacity(chartColor, 0.7)}`,
109
+ borderRadius: `${10 * s}px ${10 * s}px 0 0`,
110
+ boxShadow: `0 0 ${24 * s}px ${withOpacity(chartColor, 0.18)}`,
111
+ transform: `scaleY(${beatScale})`,
112
+ transformOrigin: "bottom"
113
+ },
114
+ children: /* @__PURE__ */ jsx(
115
+ "div",
116
+ {
117
+ "data-bar-chart-value": "true",
118
+ style: {
119
+ position: "absolute",
120
+ bottom: "100%",
121
+ left: "50%",
122
+ transform: `translate(-50%, ${-10 * s}px)`,
123
+ color: textColor,
124
+ fontSize: (portrait ? 34 : 30) * s,
125
+ lineHeight: 1,
126
+ fontWeight: 750,
127
+ fontVariantNumeric: "tabular-nums",
128
+ whiteSpace: "nowrap",
129
+ opacity: barProgress,
130
+ textShadow: `0 ${2 * s}px ${8 * s}px rgba(0,0,0,0.55)`
131
+ },
132
+ children: exactValue(value)
133
+ }
134
+ )
135
+ }
136
+ )
137
+ }
138
+ ),
139
+ /* @__PURE__ */ jsx(
140
+ "div",
141
+ {
142
+ "data-bar-chart-label": "true",
143
+ style: {
144
+ width: "100%",
145
+ height: labelHeight,
146
+ paddingTop: 14 * s,
147
+ boxSizing: "border-box",
148
+ color: withOpacity(textColor, 0.9),
149
+ fontSize: (portrait ? 24 : 22) * s,
150
+ lineHeight: 1.15,
151
+ fontWeight: 650,
152
+ textAlign: "center",
153
+ overflow: "hidden",
154
+ overflowWrap: "anywhere",
155
+ opacity: barProgress
156
+ },
157
+ children: label
158
+ }
159
+ )
160
+ ]
161
+ },
162
+ `${label}-${index}`
163
+ );
164
+ })
165
+ }
166
+ ),
167
+ /* @__PURE__ */ jsx(
168
+ "div",
169
+ {
170
+ style: {
171
+ position: "absolute",
172
+ bottom: 0,
173
+ left: 0,
174
+ right: 0,
175
+ height: chartHeight * 0.7,
176
+ background: `linear-gradient(to top, ${withOpacity(chartColor, 0.07)}, transparent)`,
177
+ pointerEvents: "none"
178
+ }
179
+ }
180
+ )
181
+ ] });
182
+ };
183
+
184
+ // src/visual-system/scene-templates/chart-bar.tsx
185
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
186
+ function parseBarDatum(value) {
187
+ if (typeof value === "number") {
188
+ return Number.isFinite(value) ? { label: "", value } : void 0;
189
+ }
190
+ if (typeof value === "object" && value !== null) {
191
+ const datum = value;
192
+ const numberValue2 = Number(datum.value);
193
+ if (!Number.isFinite(numberValue2)) return void 0;
194
+ return { label: String(datum.label ?? "").trim(), value: numberValue2 };
195
+ }
196
+ if (typeof value !== "string") return void 0;
197
+ const trimmed = value.trim();
198
+ if (!trimmed) return void 0;
199
+ const separator = trimmed.lastIndexOf(":");
200
+ const label = separator >= 0 ? trimmed.slice(0, separator).trim() : "";
201
+ const numberValue = Number(separator >= 0 ? trimmed.slice(separator + 1).trim() : trimmed);
202
+ return Number.isFinite(numberValue) ? { label, value: numberValue } : void 0;
203
+ }
204
+ function parseBarData(value) {
205
+ let items;
206
+ if (Array.isArray(value)) {
207
+ items = value;
208
+ } else if (typeof value === "string") {
209
+ const trimmed = value.trim();
210
+ if (!trimmed) return [];
211
+ try {
212
+ const parsed = JSON.parse(trimmed);
213
+ items = Array.isArray(parsed) ? parsed : [trimmed];
214
+ } catch {
215
+ items = trimmed.split(",");
216
+ }
217
+ } else {
218
+ items = [value];
219
+ }
220
+ return items.map(parseBarDatum).filter((datum) => datum !== void 0);
221
+ }
222
+ var ChartBarTemplate = ({
223
+ variables,
224
+ style,
225
+ progress,
226
+ motionProgress = progress,
227
+ beatIntensity,
228
+ width,
229
+ height,
230
+ safeZone,
231
+ sceneDuration,
232
+ backgroundEffect,
233
+ isPlaying = true
234
+ }) => {
235
+ const { primary, foreground, font } = resolveTokens(style);
236
+ const chartColor = primary;
237
+ const textColor = foreground;
238
+ const textsRaw = String(variables.texts || "");
239
+ const data = parseBarData(variables.bars);
240
+ return /* @__PURE__ */ jsxs2(
241
+ "div",
242
+ {
243
+ style: {
244
+ width,
245
+ height,
246
+ backgroundColor: "#000",
247
+ position: "relative",
248
+ overflow: "hidden",
249
+ fontFamily: font
250
+ },
251
+ children: [
252
+ /* @__PURE__ */ jsx2(
253
+ SceneBackground,
254
+ {
255
+ style,
256
+ progress,
257
+ sceneDuration,
258
+ width,
259
+ height,
260
+ ...getMediaBackgroundProps(variables),
261
+ backgroundEffect,
262
+ seed: textsRaw,
263
+ isPlaying,
264
+ beatIntensity
265
+ }
266
+ ),
267
+ /* @__PURE__ */ jsx2(
268
+ BarChart,
269
+ {
270
+ progress,
271
+ data,
272
+ width,
273
+ height,
274
+ chartColor,
275
+ textColor,
276
+ beatIntensity
277
+ }
278
+ ),
279
+ /* @__PURE__ */ jsx2("div", { style: { position: "absolute", top: 0, left: 0, right: 0, height: height * TOP_TEXT_AREA_RATIO, overflow: "visible" }, children: /* @__PURE__ */ jsx2(
280
+ TemplateText,
281
+ {
282
+ motionProgress,
283
+ typeTreatment: resolveTokens(style).preset.type,
284
+ archetype: "subtle",
285
+ text: String(variables.texts ?? ""),
286
+ progress,
287
+ sceneDuration: sceneDuration ?? 3,
288
+ width,
289
+ height,
290
+ position: "top",
291
+ sizeRole: "headline",
292
+ safeZone,
293
+ font,
294
+ color: textColor,
295
+ beatIntensity
296
+ }
297
+ ) })
298
+ ]
299
+ }
300
+ );
301
+ };
302
+ export {
303
+ ChartBarTemplate
304
+ };
@@ -0,0 +1,299 @@
1
+ import {
2
+ getResponsiveFontSize
3
+ } from "./chunk-S7HFLYNN.js";
4
+ import {
5
+ SceneBackground,
6
+ getMediaBackgroundProps
7
+ } from "./chunk-IFOW65Z6.js";
8
+ import {
9
+ ConfettiLayer
10
+ } from "./chunk-CJ7F2CE6.js";
11
+ import "./chunk-QAAO7OAY.js";
12
+ import {
13
+ TemplateText
14
+ } from "./chunk-BCRUUJ6A.js";
15
+ import "./chunk-L5BTTCZV.js";
16
+ import {
17
+ TOP_TEXT_AREA_RATIO,
18
+ withOpacity
19
+ } from "./chunk-ABZACD4G.js";
20
+ import {
21
+ interpolate,
22
+ spring
23
+ } from "./chunk-XTOXFMLM.js";
24
+ import {
25
+ TOKEN_DEFAULTS,
26
+ resolveTokens
27
+ } from "./chunk-FNAEQ7QO.js";
28
+
29
+ // src/visual-system/primitives/typography/CountUpNumber.tsx
30
+ import { jsx, jsxs } from "react/jsx-runtime";
31
+ function suppliedDecimalPlaces(value) {
32
+ if (!Number.isFinite(value) || Number.isInteger(value)) return 0;
33
+ const normalized = String(value).toLowerCase();
34
+ if (normalized.includes("e-")) {
35
+ const [coefficient, exponentText] = normalized.split("e-");
36
+ const coefficientDecimals = coefficient.split(".")[1]?.length ?? 0;
37
+ return Math.min(6, Number(exponentText) + coefficientDecimals);
38
+ }
39
+ return Math.min(6, normalized.split(".")[1]?.length ?? 0);
40
+ }
41
+ var CountUpNumber = ({
42
+ progress,
43
+ motionProgress = progress,
44
+ target,
45
+ decimalPlaces: decimalPlacesOverride,
46
+ prefix = "",
47
+ unit = "",
48
+ label,
49
+ width,
50
+ height,
51
+ numberColor = "#ffffff",
52
+ labelColor = "rgba(255,255,255,0.6)",
53
+ font = TOKEN_DEFAULTS.font,
54
+ hasMediaShadow = false,
55
+ beatIntensity = 0,
56
+ labelFontSize
57
+ }) => {
58
+ const s = Math.min(width, height) / 1080;
59
+ const displayValue = interpolate(
60
+ progress,
61
+ [0, 0.55],
62
+ [0, target],
63
+ { extrapolateRight: "clamp" }
64
+ );
65
+ const decimalPlaces = Number.isFinite(decimalPlacesOverride) ? Math.max(0, Math.min(6, Math.trunc(decimalPlacesOverride ?? 0))) : suppliedDecimalPlaces(target);
66
+ const formattedNumber = displayValue.toLocaleString("en-US", {
67
+ minimumFractionDigits: decimalPlaces,
68
+ maximumFractionDigits: decimalPlaces
69
+ });
70
+ const formattedTarget = target.toLocaleString("en-US", {
71
+ minimumFractionDigits: decimalPlaces,
72
+ maximumFractionDigits: decimalPlaces
73
+ });
74
+ const semanticValueIsTransient = formattedNumber !== formattedTarget;
75
+ const fullNumberStr = `${prefix}${formattedNumber}${unit}`;
76
+ const baseSize = 140 * s;
77
+ const charCount = fullNumberStr.length;
78
+ const numberFontSize = charCount <= 6 ? baseSize : baseSize * Math.max(0.35, 6 / charCount);
79
+ const numberSpring = spring(
80
+ interpolate(motionProgress, [0, 0.22], [0, 1], {
81
+ extrapolateLeft: "clamp",
82
+ extrapolateRight: "clamp"
83
+ }),
84
+ { damping: 9, stiffness: 200 }
85
+ );
86
+ const numberScale = interpolate(numberSpring, [0, 1], [0.3, 1]);
87
+ const numberSettled = numberSpring > 0.99;
88
+ const breathe = numberSettled ? 1 + Math.sin(progress * Math.PI * 4) * 8e-3 : 1;
89
+ const beatScale = 1 + beatIntensity * 0.03;
90
+ const labelOpacity = interpolate(
91
+ motionProgress,
92
+ [0.2, 0.4],
93
+ [0, 1],
94
+ { extrapolateLeft: "clamp", extrapolateRight: "clamp" }
95
+ );
96
+ const mediaTextShadow = hasMediaShadow ? "0 2px 8px rgba(0,0,0,0.55), 0 6px 16px rgba(0,0,0,0.35)" : void 0;
97
+ const finalLabelFontSize = labelFontSize ?? 36 * s * 1.3;
98
+ return /* @__PURE__ */ jsxs(
99
+ "div",
100
+ {
101
+ style: {
102
+ position: "absolute",
103
+ top: 0,
104
+ left: 0,
105
+ right: 0,
106
+ bottom: 0,
107
+ display: "flex",
108
+ flexDirection: "column",
109
+ alignItems: "center",
110
+ justifyContent: "center",
111
+ pointerEvents: "none",
112
+ // font is intentionally NOT set here — caller's outer wrapper
113
+ // (chart-counter root div or composed.tsx root) sets fontFamily
114
+ // and the children inherit. void to silence unused-arg lint.
115
+ ...(void font, {})
116
+ },
117
+ children: [
118
+ /* @__PURE__ */ jsxs(
119
+ "div",
120
+ {
121
+ "data-transition-semantic": semanticValueIsTransient ? "transient" : void 0,
122
+ style: {
123
+ fontSize: numberFontSize,
124
+ fontWeight: 800,
125
+ color: numberColor,
126
+ textAlign: "center",
127
+ lineHeight: 1.1,
128
+ position: "relative",
129
+ zIndex: 1,
130
+ maxWidth: "90%",
131
+ overflow: "visible",
132
+ ...semanticValueIsTransient ? { visibility: "var(--vanillasky-transition-semantic-visibility,visible)" } : {},
133
+ transform: `scale(${numberScale * beatScale * breathe})`,
134
+ ...mediaTextShadow ? { textShadow: mediaTextShadow } : {}
135
+ },
136
+ children: [
137
+ prefix,
138
+ formattedNumber,
139
+ unit
140
+ ]
141
+ }
142
+ ),
143
+ /* @__PURE__ */ jsx(
144
+ "div",
145
+ {
146
+ style: {
147
+ opacity: labelOpacity,
148
+ fontSize: finalLabelFontSize,
149
+ color: labelColor,
150
+ textTransform: "uppercase",
151
+ letterSpacing: 2 * s,
152
+ textAlign: "center",
153
+ marginTop: 12 * s,
154
+ position: "relative",
155
+ zIndex: 1,
156
+ ...mediaTextShadow ? { textShadow: mediaTextShadow } : {}
157
+ },
158
+ children: label
159
+ }
160
+ )
161
+ ]
162
+ }
163
+ );
164
+ };
165
+
166
+ // src/visual-system/scene-templates/chart-counter.tsx
167
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
168
+ var ChartCounterTemplate = ({
169
+ variables,
170
+ style,
171
+ progress,
172
+ motionProgress = progress,
173
+ beatIntensity,
174
+ width,
175
+ height,
176
+ safeZone,
177
+ sceneDuration,
178
+ isPlaying = true,
179
+ backgroundEffect
180
+ }) => {
181
+ const s = Math.min(width, height) / 1080;
182
+ const { primary, foreground, font, background } = resolveTokens(style);
183
+ const chartColor = primary;
184
+ const textColor = foreground;
185
+ const textsRaw = String(variables.texts || "");
186
+ const gradSeed = textsRaw.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0);
187
+ const parsedValue = Number(variables.value);
188
+ const targetValue = Number.isFinite(parsedValue) ? parsedValue : 1e3;
189
+ const parsedDecimalPlaces = Number(variables.decimalPlaces);
190
+ const decimalPlaces = Number.isFinite(parsedDecimalPlaces) ? Math.max(0, Math.min(6, Math.trunc(parsedDecimalPlaces))) : void 0;
191
+ const label = String(variables.label || "Total users");
192
+ const prefix = String(variables.prefix || "");
193
+ const unit = String(variables.unit || "");
194
+ const hasMedia = !!String(variables.mediaUrl || "").trim() && String(variables.mediaType || "auto") !== "gradient";
195
+ const numberColor = hasMedia ? "#ffffff" : textColor;
196
+ const labelColor = hasMedia ? "#ffffff" : withOpacity(textColor, 0.6);
197
+ const confettiActive = variables.confetti === true && motionProgress >= 0.55;
198
+ const confettiProgress = confettiActive ? (motionProgress - 0.55) / 0.45 : 0;
199
+ const labelFontSize = getResponsiveFontSize(label, width, "subtitle", height) * 1.3;
200
+ return /* @__PURE__ */ jsxs2(
201
+ "div",
202
+ {
203
+ style: {
204
+ width,
205
+ height,
206
+ backgroundColor: "#000",
207
+ position: "relative",
208
+ overflow: "hidden",
209
+ fontFamily: font
210
+ },
211
+ children: [
212
+ /* @__PURE__ */ jsx2(
213
+ SceneBackground,
214
+ {
215
+ style,
216
+ progress,
217
+ sceneDuration,
218
+ width,
219
+ height,
220
+ ...getMediaBackgroundProps(variables),
221
+ backgroundEffect,
222
+ seed: gradSeed,
223
+ isPlaying,
224
+ beatIntensity
225
+ }
226
+ ),
227
+ /* @__PURE__ */ jsx2(
228
+ "div",
229
+ {
230
+ style: {
231
+ position: "absolute",
232
+ top: "50%",
233
+ left: "50%",
234
+ transform: "translate(-50%, -55%)",
235
+ width: 500 * s,
236
+ height: 500 * s,
237
+ borderRadius: "50%",
238
+ background: `radial-gradient(circle, ${withOpacity(primary, 0.15)} 0%, transparent 70%)`,
239
+ pointerEvents: "none"
240
+ }
241
+ }
242
+ ),
243
+ confettiActive && /* @__PURE__ */ jsx2(
244
+ ConfettiLayer,
245
+ {
246
+ progress: confettiProgress,
247
+ width,
248
+ height,
249
+ beatIntensity,
250
+ bgColor: hasMedia ? void 0 : background.type === "solid" ? background.color : background.colors[1]
251
+ }
252
+ ),
253
+ /* @__PURE__ */ jsx2("div", { style: { position: "absolute", top: 0, left: 0, right: 0, height: height * TOP_TEXT_AREA_RATIO, overflow: "visible" }, children: /* @__PURE__ */ jsx2(
254
+ TemplateText,
255
+ {
256
+ motionProgress,
257
+ typeTreatment: resolveTokens(style).preset.type,
258
+ archetype: "subtle",
259
+ text: String(variables.texts ?? ""),
260
+ progress,
261
+ sceneDuration: sceneDuration ?? 3,
262
+ width,
263
+ height,
264
+ position: "top",
265
+ sizeRole: "headline",
266
+ safeZone,
267
+ font,
268
+ color: textColor,
269
+ beatIntensity
270
+ }
271
+ ) }),
272
+ /* @__PURE__ */ jsx2(
273
+ CountUpNumber,
274
+ {
275
+ progress,
276
+ motionProgress,
277
+ target: targetValue,
278
+ decimalPlaces,
279
+ prefix,
280
+ unit,
281
+ label,
282
+ width,
283
+ height,
284
+ numberColor,
285
+ labelColor,
286
+ chartColor,
287
+ font,
288
+ hasMediaShadow: hasMedia,
289
+ beatIntensity,
290
+ labelFontSize
291
+ }
292
+ )
293
+ ]
294
+ }
295
+ );
296
+ };
297
+ export {
298
+ ChartCounterTemplate
299
+ };