@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,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
|
+
};
|