@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,296 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getResponsiveFontSize,
|
|
3
|
+
stripPipe
|
|
4
|
+
} from "./chunk-S7HFLYNN.js";
|
|
5
|
+
import {
|
|
6
|
+
SceneBackground,
|
|
7
|
+
getMediaBackgroundProps
|
|
8
|
+
} from "./chunk-IFOW65Z6.js";
|
|
9
|
+
import {
|
|
10
|
+
TemplateText
|
|
11
|
+
} from "./chunk-BCRUUJ6A.js";
|
|
12
|
+
import "./chunk-L5BTTCZV.js";
|
|
13
|
+
import {
|
|
14
|
+
TOP_TEXT_AREA_RATIO,
|
|
15
|
+
withOpacity
|
|
16
|
+
} from "./chunk-ABZACD4G.js";
|
|
17
|
+
import {
|
|
18
|
+
interpolate
|
|
19
|
+
} from "./chunk-XTOXFMLM.js";
|
|
20
|
+
import {
|
|
21
|
+
TOKEN_DEFAULTS,
|
|
22
|
+
resolveTokens
|
|
23
|
+
} from "./chunk-FNAEQ7QO.js";
|
|
24
|
+
|
|
25
|
+
// src/visual-system/primitives/charts/ProgressRing.tsx
|
|
26
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
27
|
+
var ProgressRing = ({
|
|
28
|
+
progress,
|
|
29
|
+
motionProgress = progress,
|
|
30
|
+
targetValue,
|
|
31
|
+
decimalPlaces,
|
|
32
|
+
unit = "%",
|
|
33
|
+
chartColor = "#ffffff",
|
|
34
|
+
textColor = "#ffffff",
|
|
35
|
+
label: rawLabel = "",
|
|
36
|
+
font = TOKEN_DEFAULTS.font,
|
|
37
|
+
width,
|
|
38
|
+
height,
|
|
39
|
+
beatIntensity = 0
|
|
40
|
+
}) => {
|
|
41
|
+
const s = Math.min(width, height) / 1080;
|
|
42
|
+
const label = stripPipe(String(rawLabel || ""));
|
|
43
|
+
const radius = Math.min(width, height) * 0.2;
|
|
44
|
+
const circumference = 2 * Math.PI * radius;
|
|
45
|
+
const cx = width / 2;
|
|
46
|
+
const cy = height / 2;
|
|
47
|
+
const strokeW = 34 * s;
|
|
48
|
+
const fillPercent = interpolate(progress, [0.05, 0.75], [0, targetValue], {
|
|
49
|
+
extrapolateLeft: "clamp",
|
|
50
|
+
extrapolateRight: "clamp"
|
|
51
|
+
});
|
|
52
|
+
const dashOffset = circumference * (1 - fillPercent / 100);
|
|
53
|
+
const targetDecimals = Number.isFinite(decimalPlaces) ? Math.max(0, Math.min(2, Math.trunc(decimalPlaces ?? 0))) : Number.isInteger(targetValue) ? 0 : Math.min(2, (String(targetValue).split(".")[1] || "1").length);
|
|
54
|
+
const displayNumber = targetDecimals === 0 ? String(Math.round(fillPercent)) : fillPercent.toFixed(targetDecimals);
|
|
55
|
+
const targetDisplayNumber = targetDecimals === 0 ? String(Math.round(targetValue)) : targetValue.toFixed(targetDecimals);
|
|
56
|
+
const semanticValueIsTransient = displayNumber !== targetDisplayNumber;
|
|
57
|
+
const beatStroke = strokeW + beatIntensity * 4 * s;
|
|
58
|
+
const labelFontSize = getResponsiveFontSize(label, width, "subtitle", height) * 1.3;
|
|
59
|
+
const labelOpacity = interpolate(motionProgress, [0.35, 0.55], [0, 1], {
|
|
60
|
+
extrapolateLeft: "clamp",
|
|
61
|
+
extrapolateRight: "clamp"
|
|
62
|
+
});
|
|
63
|
+
const labelRise = interpolate(motionProgress, [0.35, 0.55], [16 * s, 0], {
|
|
64
|
+
extrapolateLeft: "clamp",
|
|
65
|
+
extrapolateRight: "clamp"
|
|
66
|
+
});
|
|
67
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
68
|
+
/* @__PURE__ */ jsx(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
style: {
|
|
72
|
+
position: "absolute",
|
|
73
|
+
top: cy - radius * 1.2,
|
|
74
|
+
left: cx - radius * 1.2,
|
|
75
|
+
width: radius * 2.4,
|
|
76
|
+
height: radius * 2.4,
|
|
77
|
+
borderRadius: "50%",
|
|
78
|
+
background: `radial-gradient(circle at 50% 50%, ${withOpacity(chartColor, 0.08)} 0%, transparent 70%)`,
|
|
79
|
+
pointerEvents: "none"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
),
|
|
83
|
+
/* @__PURE__ */ jsxs(
|
|
84
|
+
"svg",
|
|
85
|
+
{
|
|
86
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
87
|
+
style: {
|
|
88
|
+
position: "absolute",
|
|
89
|
+
inset: 0,
|
|
90
|
+
width,
|
|
91
|
+
height
|
|
92
|
+
},
|
|
93
|
+
children: [
|
|
94
|
+
/* @__PURE__ */ jsx(
|
|
95
|
+
"circle",
|
|
96
|
+
{
|
|
97
|
+
cx,
|
|
98
|
+
cy,
|
|
99
|
+
r: radius,
|
|
100
|
+
fill: "none",
|
|
101
|
+
stroke: withOpacity(chartColor, 0.1),
|
|
102
|
+
strokeWidth: strokeW
|
|
103
|
+
}
|
|
104
|
+
),
|
|
105
|
+
/* @__PURE__ */ jsx(
|
|
106
|
+
"circle",
|
|
107
|
+
{
|
|
108
|
+
cx,
|
|
109
|
+
cy,
|
|
110
|
+
r: radius,
|
|
111
|
+
fill: "none",
|
|
112
|
+
stroke: chartColor,
|
|
113
|
+
strokeWidth: beatStroke,
|
|
114
|
+
strokeLinecap: "round",
|
|
115
|
+
strokeDasharray: circumference,
|
|
116
|
+
strokeDashoffset: dashOffset,
|
|
117
|
+
transform: `rotate(-90, ${cx}, ${cy})`,
|
|
118
|
+
style: { transition: "none" }
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
),
|
|
124
|
+
/* @__PURE__ */ jsxs(
|
|
125
|
+
"div",
|
|
126
|
+
{
|
|
127
|
+
"data-transition-semantic": semanticValueIsTransient ? "transient" : void 0,
|
|
128
|
+
style: {
|
|
129
|
+
position: "absolute",
|
|
130
|
+
left: cx - radius,
|
|
131
|
+
top: cy - radius,
|
|
132
|
+
width: radius * 2,
|
|
133
|
+
height: radius * 2,
|
|
134
|
+
display: "flex",
|
|
135
|
+
alignItems: "center",
|
|
136
|
+
justifyContent: "center",
|
|
137
|
+
pointerEvents: "none",
|
|
138
|
+
...semanticValueIsTransient ? { visibility: "var(--vanillasky-transition-semantic-visibility,visible)" } : {}
|
|
139
|
+
},
|
|
140
|
+
children: [
|
|
141
|
+
/* @__PURE__ */ jsx(
|
|
142
|
+
"span",
|
|
143
|
+
{
|
|
144
|
+
style: {
|
|
145
|
+
fontSize: 120 * s,
|
|
146
|
+
fontWeight: 800,
|
|
147
|
+
color: textColor,
|
|
148
|
+
lineHeight: 1
|
|
149
|
+
},
|
|
150
|
+
children: displayNumber
|
|
151
|
+
}
|
|
152
|
+
),
|
|
153
|
+
/* @__PURE__ */ jsx(
|
|
154
|
+
"span",
|
|
155
|
+
{
|
|
156
|
+
style: {
|
|
157
|
+
fontSize: 56 * s,
|
|
158
|
+
fontWeight: 700,
|
|
159
|
+
color: chartColor,
|
|
160
|
+
marginLeft: 4 * s
|
|
161
|
+
},
|
|
162
|
+
children: unit
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
),
|
|
168
|
+
label && /* @__PURE__ */ jsx(
|
|
169
|
+
"div",
|
|
170
|
+
{
|
|
171
|
+
style: {
|
|
172
|
+
position: "absolute",
|
|
173
|
+
top: cy + radius + 48 * s,
|
|
174
|
+
left: 0,
|
|
175
|
+
right: 0,
|
|
176
|
+
display: "flex",
|
|
177
|
+
justifyContent: "center",
|
|
178
|
+
pointerEvents: "none"
|
|
179
|
+
},
|
|
180
|
+
children: /* @__PURE__ */ jsx(
|
|
181
|
+
"div",
|
|
182
|
+
{
|
|
183
|
+
style: {
|
|
184
|
+
fontSize: labelFontSize,
|
|
185
|
+
color: withOpacity(textColor, 0.6),
|
|
186
|
+
textTransform: "uppercase",
|
|
187
|
+
letterSpacing: 2 * s,
|
|
188
|
+
textAlign: "center",
|
|
189
|
+
maxWidth: "80%",
|
|
190
|
+
opacity: labelOpacity,
|
|
191
|
+
transform: `translateY(${labelRise}px)`,
|
|
192
|
+
fontFamily: font
|
|
193
|
+
},
|
|
194
|
+
children: label
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
] });
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// src/visual-system/scene-templates/chart-progress-ring.tsx
|
|
203
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
204
|
+
var ChartProgressRingTemplate = ({
|
|
205
|
+
variables,
|
|
206
|
+
style,
|
|
207
|
+
progress,
|
|
208
|
+
motionProgress = progress,
|
|
209
|
+
beatIntensity,
|
|
210
|
+
width,
|
|
211
|
+
height,
|
|
212
|
+
safeZone,
|
|
213
|
+
sceneDuration,
|
|
214
|
+
backgroundEffect,
|
|
215
|
+
isPlaying = true
|
|
216
|
+
}) => {
|
|
217
|
+
const { primary, foreground, font } = resolveTokens(style);
|
|
218
|
+
const chartColor = primary;
|
|
219
|
+
const textColor = foreground;
|
|
220
|
+
const textsRaw = String(variables.texts || "");
|
|
221
|
+
const parsedValue = Number(variables.value);
|
|
222
|
+
const targetValue = Math.max(0, Math.min(100, Number.isFinite(parsedValue) ? parsedValue : 75));
|
|
223
|
+
const parsedDecimalPlaces = Number(variables.decimalPlaces);
|
|
224
|
+
const decimalPlaces = Number.isFinite(parsedDecimalPlaces) ? Math.max(0, Math.min(2, Math.trunc(parsedDecimalPlaces))) : void 0;
|
|
225
|
+
const unit = String(variables.unit ?? "%");
|
|
226
|
+
const label = String(variables.label ?? "");
|
|
227
|
+
return /* @__PURE__ */ jsxs2(
|
|
228
|
+
"div",
|
|
229
|
+
{
|
|
230
|
+
style: {
|
|
231
|
+
width,
|
|
232
|
+
height,
|
|
233
|
+
backgroundColor: "#000",
|
|
234
|
+
position: "relative",
|
|
235
|
+
overflow: "hidden",
|
|
236
|
+
fontFamily: font
|
|
237
|
+
},
|
|
238
|
+
children: [
|
|
239
|
+
/* @__PURE__ */ jsx2(
|
|
240
|
+
SceneBackground,
|
|
241
|
+
{
|
|
242
|
+
style,
|
|
243
|
+
progress,
|
|
244
|
+
sceneDuration,
|
|
245
|
+
width,
|
|
246
|
+
height,
|
|
247
|
+
...getMediaBackgroundProps(variables),
|
|
248
|
+
backgroundEffect,
|
|
249
|
+
seed: textsRaw,
|
|
250
|
+
isPlaying,
|
|
251
|
+
beatIntensity
|
|
252
|
+
}
|
|
253
|
+
),
|
|
254
|
+
/* @__PURE__ */ jsx2("div", { style: { position: "absolute", top: 0, left: 0, right: 0, height: height * TOP_TEXT_AREA_RATIO, overflow: "visible" }, children: /* @__PURE__ */ jsx2(
|
|
255
|
+
TemplateText,
|
|
256
|
+
{
|
|
257
|
+
motionProgress,
|
|
258
|
+
typeTreatment: resolveTokens(style).preset.type,
|
|
259
|
+
archetype: "subtle",
|
|
260
|
+
text: String(variables.texts ?? ""),
|
|
261
|
+
progress,
|
|
262
|
+
sceneDuration: sceneDuration ?? 3,
|
|
263
|
+
width,
|
|
264
|
+
height,
|
|
265
|
+
position: "top",
|
|
266
|
+
sizeRole: "headline",
|
|
267
|
+
safeZone,
|
|
268
|
+
font,
|
|
269
|
+
color: textColor,
|
|
270
|
+
beatIntensity
|
|
271
|
+
}
|
|
272
|
+
) }),
|
|
273
|
+
/* @__PURE__ */ jsx2(
|
|
274
|
+
ProgressRing,
|
|
275
|
+
{
|
|
276
|
+
progress,
|
|
277
|
+
motionProgress,
|
|
278
|
+
targetValue,
|
|
279
|
+
decimalPlaces,
|
|
280
|
+
unit,
|
|
281
|
+
chartColor,
|
|
282
|
+
textColor,
|
|
283
|
+
label,
|
|
284
|
+
font,
|
|
285
|
+
width,
|
|
286
|
+
height,
|
|
287
|
+
beatIntensity
|
|
288
|
+
}
|
|
289
|
+
)
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
};
|
|
294
|
+
export {
|
|
295
|
+
ChartProgressRingTemplate
|
|
296
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface TemplateRenderCheckInput {
|
|
2
|
+
sourceUrl: string;
|
|
3
|
+
exportName: string;
|
|
4
|
+
templateId: string;
|
|
5
|
+
exampleName: string;
|
|
6
|
+
variables: Record<string, unknown>;
|
|
7
|
+
duration: number;
|
|
8
|
+
timeoutMs: number;
|
|
9
|
+
}
|
|
10
|
+
interface TemplateRenderCheckResult {
|
|
11
|
+
renders: number;
|
|
12
|
+
}
|
|
13
|
+
declare function renderTemplateChecks(input: TemplateRenderCheckInput): Promise<TemplateRenderCheckResult>;
|
|
14
|
+
|
|
15
|
+
export { type TemplateRenderCheckInput, type TemplateRenderCheckResult, renderTemplateChecks };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VideoFrame
|
|
3
|
+
} from "./chunk-XB7LH4J2.js";
|
|
4
|
+
import "./chunk-FNAEQ7QO.js";
|
|
5
|
+
import "./chunk-SPVTJH3F.js";
|
|
6
|
+
import {
|
|
7
|
+
resolveVideoBrand
|
|
8
|
+
} from "./chunk-XGRA2MUV.js";
|
|
9
|
+
import "./chunk-73NTSFFI.js";
|
|
10
|
+
|
|
11
|
+
// src/cli/check-runtime.ts
|
|
12
|
+
import * as React from "react";
|
|
13
|
+
import { createElement } from "react";
|
|
14
|
+
import { PassThrough } from "stream";
|
|
15
|
+
import { renderToPipeableStream } from "react-dom/server";
|
|
16
|
+
function renderFrame(element, timeoutMs) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const output = new PassThrough();
|
|
19
|
+
const chunks = [];
|
|
20
|
+
let renderError;
|
|
21
|
+
output.on("data", (chunk) => chunks.push(chunk));
|
|
22
|
+
output.on("error", (error) => {
|
|
23
|
+
clearTimeout(timeout);
|
|
24
|
+
reject(error);
|
|
25
|
+
});
|
|
26
|
+
output.on("end", () => {
|
|
27
|
+
clearTimeout(timeout);
|
|
28
|
+
if (renderError == null) resolve(Buffer.concat(chunks).toString("utf8"));
|
|
29
|
+
else reject(renderError);
|
|
30
|
+
});
|
|
31
|
+
const stream = renderToPipeableStream(element, {
|
|
32
|
+
onAllReady: () => stream.pipe(output),
|
|
33
|
+
onError: (error) => {
|
|
34
|
+
renderError ??= error;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
const timeout = setTimeout(() => stream.abort(new Error(`SSR timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async function renderTemplateChecks(input) {
|
|
41
|
+
Object.assign(globalThis, { React });
|
|
42
|
+
const progressPoints = [0, 0.5, 0.999];
|
|
43
|
+
let renders = 0;
|
|
44
|
+
let importSequence = 0;
|
|
45
|
+
for (const orientation of ["portrait", "landscape"]) {
|
|
46
|
+
const [width, height] = orientation === "portrait" ? [1080, 1920] : [1920, 1080];
|
|
47
|
+
const config = {
|
|
48
|
+
schemaVersion: "0.1",
|
|
49
|
+
orientation,
|
|
50
|
+
scenes: [{
|
|
51
|
+
id: "authoring-check",
|
|
52
|
+
templateId: input.templateId,
|
|
53
|
+
variables: input.variables,
|
|
54
|
+
timing: { startTime: 0, endTime: input.duration, fixedDuration: input.duration }
|
|
55
|
+
}],
|
|
56
|
+
style: {
|
|
57
|
+
brand: resolveVideoBrand({
|
|
58
|
+
font: "Inter, sans-serif",
|
|
59
|
+
colors: { primary: "#6D5EF5", secondary: "#17122F", foreground: "#FFFFFF" }
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
for (const progress of progressPoints) {
|
|
64
|
+
let first;
|
|
65
|
+
let second;
|
|
66
|
+
try {
|
|
67
|
+
const frame = async () => {
|
|
68
|
+
const loaded = await import(`${input.sourceUrl}?vanillasky-render=${Date.now()}-${importSequence++}`);
|
|
69
|
+
const template = loaded[input.exportName];
|
|
70
|
+
if (!template || typeof template.component !== "function") throw new Error(`could not resolve export ${JSON.stringify(input.exportName)}`);
|
|
71
|
+
const kit = {
|
|
72
|
+
templates: [template],
|
|
73
|
+
capabilities: { templates: [template.id] },
|
|
74
|
+
getTemplate: (id) => id === template.id ? template : void 0,
|
|
75
|
+
getTemplateMetadata: (id) => id === template.id ? template : void 0,
|
|
76
|
+
listTemplateMetadata: () => [template]
|
|
77
|
+
};
|
|
78
|
+
return renderFrame(createElement(VideoFrame, {
|
|
79
|
+
kit,
|
|
80
|
+
config,
|
|
81
|
+
time: progress * input.duration,
|
|
82
|
+
width,
|
|
83
|
+
height
|
|
84
|
+
}), input.timeoutMs);
|
|
85
|
+
};
|
|
86
|
+
first = await frame();
|
|
87
|
+
second = await frame();
|
|
88
|
+
} catch (error) {
|
|
89
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
90
|
+
throw new Error(`${input.templateId} example ${JSON.stringify(input.exampleName)} ${orientation} at progress ${progress}: ${detail}`);
|
|
91
|
+
}
|
|
92
|
+
if (first !== second) {
|
|
93
|
+
throw new Error(`${input.templateId} example ${JSON.stringify(input.exampleName)} ${orientation} at progress ${progress} is nondeterministic`);
|
|
94
|
+
}
|
|
95
|
+
renders += 2;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { renders };
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
renderTemplateChecks
|
|
102
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/visual-system/catalog/transition-contract.ts
|
|
2
|
+
var TRANSITION_TIMING_KEYS = /* @__PURE__ */ new Set(["entryReadyProgress", "holdProgress"]);
|
|
3
|
+
function assertTemplateTransitionMetadata(template) {
|
|
4
|
+
const timing = template.transitionTiming;
|
|
5
|
+
if (!template.usesGlobalTransition) {
|
|
6
|
+
if (timing !== void 0) {
|
|
7
|
+
throw new Error(`${template.id}.transitionTiming requires usesGlobalTransition: true`);
|
|
8
|
+
}
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (!timing || typeof timing !== "object" || Array.isArray(timing)) {
|
|
12
|
+
throw new Error(`${template.id}.transitionTiming is required when usesGlobalTransition is true`);
|
|
13
|
+
}
|
|
14
|
+
for (const key of Object.keys(timing)) {
|
|
15
|
+
if (!TRANSITION_TIMING_KEYS.has(key)) throw new Error(`${template.id}.transitionTiming.${key} is not supported`);
|
|
16
|
+
}
|
|
17
|
+
const { entryReadyProgress, holdProgress } = timing;
|
|
18
|
+
if (!Number.isFinite(entryReadyProgress) || !Number.isFinite(holdProgress) || entryReadyProgress < 0 || holdProgress > 1 || entryReadyProgress >= holdProgress) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`${template.id}.transitionTiming must satisfy 0 <= entryReadyProgress < holdProgress <= 1`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
assertTemplateTransitionMetadata
|
|
27
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// src/visual-system/catalog/value-validation.ts
|
|
2
|
+
var STRING_FORMATS = /* @__PURE__ */ new Set(["uri", "supplied-image", "grounded-quote", "stock-media-keyword", "emoji"]);
|
|
3
|
+
function isTemplatePropertyFormatSupported(format, type) {
|
|
4
|
+
return format === "grounded-stat" ? type === "number" || type === "integer" : STRING_FORMATS.has(format) && type === "string";
|
|
5
|
+
}
|
|
6
|
+
function validUrl(value) {
|
|
7
|
+
try {
|
|
8
|
+
return ["https:", "http:", "data:"].includes(new URL(value).protocol);
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function validSingleEmoji(value) {
|
|
14
|
+
const text = value.trim();
|
|
15
|
+
const graphemes = Array.from(new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(text));
|
|
16
|
+
return graphemes.length === 1 && /\p{Extended_Pictographic}|\p{Regional_Indicator}|\u20e3/u.test(text);
|
|
17
|
+
}
|
|
18
|
+
function validateTemplateSchemaValue(schema, value, path, options = {}) {
|
|
19
|
+
const fail = (message) => {
|
|
20
|
+
throw new Error(`${options.errorPrefix ?? ""}${path} ${message}`);
|
|
21
|
+
};
|
|
22
|
+
if (schema.enum && (typeof value !== "string" || !schema.enum.includes(value))) fail("must match an allowed value");
|
|
23
|
+
if (schema.type === "string") {
|
|
24
|
+
if (typeof value !== "string") fail("must be string");
|
|
25
|
+
const text = value;
|
|
26
|
+
if (schema.minLength != null && text.length < schema.minLength) fail(`must contain at least ${schema.minLength} characters`);
|
|
27
|
+
if (schema.maxLength != null && text.length > schema.maxLength) fail(`must contain at most ${schema.maxLength} characters`);
|
|
28
|
+
if (schema.format === "uri" || schema.format === "supplied-image") {
|
|
29
|
+
const emptySentinel = options.allowEmptyOptionalMedia && options.optional && text === "";
|
|
30
|
+
if (!emptySentinel && !validUrl(text)) fail("must be a valid URL");
|
|
31
|
+
}
|
|
32
|
+
if (schema.format === "emoji" && !validSingleEmoji(text)) fail("must be a single emoji");
|
|
33
|
+
} else if (schema.type === "number" || schema.type === "integer") {
|
|
34
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
35
|
+
fail(`must be ${options.includeTypeArticle ? "a " : ""}${schema.type}`);
|
|
36
|
+
}
|
|
37
|
+
const number = value;
|
|
38
|
+
if (schema.type === "integer" && !Number.isInteger(number)) fail("must be integer");
|
|
39
|
+
if (schema.minimum != null && number < schema.minimum) fail(`must be at least ${schema.minimum}`);
|
|
40
|
+
if (schema.maximum != null && number > schema.maximum) fail(`must be at most ${schema.maximum}`);
|
|
41
|
+
} else if (schema.type === "boolean") {
|
|
42
|
+
if (typeof value !== "boolean") fail("must be boolean");
|
|
43
|
+
} else if (schema.type === "array") {
|
|
44
|
+
if (!Array.isArray(value)) fail("must be array");
|
|
45
|
+
const items = value;
|
|
46
|
+
if (schema.minItems === schema.maxItems && schema.minItems != null && items.length !== schema.minItems) {
|
|
47
|
+
fail(`must contain exactly ${schema.minItems} items`);
|
|
48
|
+
}
|
|
49
|
+
if (schema.minItems != null && items.length < schema.minItems) fail(`must contain at least ${schema.minItems} items`);
|
|
50
|
+
if (schema.maxItems != null && items.length > schema.maxItems) fail(`must contain at most ${schema.maxItems} items`);
|
|
51
|
+
if (schema.items) items.forEach((item, index) => validateTemplateSchemaValue(schema.items, item, `${path}.${index}`, {
|
|
52
|
+
...options,
|
|
53
|
+
optional: false
|
|
54
|
+
}));
|
|
55
|
+
} else if (schema.type === "object") {
|
|
56
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) fail("must be object");
|
|
57
|
+
const object = value;
|
|
58
|
+
for (const required2 of schema.required ?? []) {
|
|
59
|
+
if (!Object.hasOwn(object, required2)) fail(`requires ${required2}`);
|
|
60
|
+
}
|
|
61
|
+
const properties = schema.properties ?? {};
|
|
62
|
+
if (schema.additionalProperties === false) {
|
|
63
|
+
for (const name of Object.keys(object)) {
|
|
64
|
+
if (!Object.hasOwn(properties, name)) {
|
|
65
|
+
throw new Error(`${options.errorPrefix ?? ""}${path}.${name} is not declared`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const required = new Set(schema.required ?? []);
|
|
70
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
71
|
+
if (Object.hasOwn(object, name)) validateTemplateSchemaValue(property, object[name], `${path}.${name}`, {
|
|
72
|
+
...options,
|
|
73
|
+
allowEmptyOptionalMedia: property.default === "",
|
|
74
|
+
optional: !required.has(name)
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export {
|
|
81
|
+
isTemplatePropertyFormatSupported,
|
|
82
|
+
validateTemplateSchemaValue
|
|
83
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/visual-system/catalog/schema.ts
|
|
2
|
+
function cloneJson(value) {
|
|
3
|
+
return JSON.parse(JSON.stringify(value));
|
|
4
|
+
}
|
|
5
|
+
function getTemplateDefaults(schema) {
|
|
6
|
+
return Object.fromEntries(Object.entries(schema.properties).filter(([, property]) => property.default !== void 0).map(([name, property]) => [name, cloneJson(property.default)]));
|
|
7
|
+
}
|
|
8
|
+
function isMeaningfullyPresentTemplateValue(value) {
|
|
9
|
+
if (value == null) return false;
|
|
10
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
11
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
12
|
+
if (typeof value === "object") return Object.keys(value).length > 0;
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
function schemaProperties(schema) {
|
|
16
|
+
return [
|
|
17
|
+
schema,
|
|
18
|
+
...Object.values(schema.properties ?? {}).flatMap(schemaProperties),
|
|
19
|
+
...schema.items ? schemaProperties(schema.items) : []
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
function getTemplateSchemaGates(schema) {
|
|
23
|
+
const requiredAnyOf = schema["x-vanillasky"]?.requiredAnyOf ?? [];
|
|
24
|
+
for (const [groupIndex, group] of requiredAnyOf.entries()) {
|
|
25
|
+
if (!Array.isArray(group) || group.length === 0) {
|
|
26
|
+
throw new Error(`Template schema x-vanillasky.requiredAnyOf[${groupIndex}] must name at least one field`);
|
|
27
|
+
}
|
|
28
|
+
for (const field of group) {
|
|
29
|
+
if (typeof field !== "string" || !Object.hasOwn(schema.properties, field)) {
|
|
30
|
+
throw new Error(`Template schema x-vanillasky.requiredAnyOf[${groupIndex}] field ${String(field)} is not declared`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const properties = Object.values(schema.properties).flatMap(schemaProperties);
|
|
35
|
+
return {
|
|
36
|
+
requiresStat: schema["x-vanillasky"]?.requiresStat === true || properties.some(({ format }) => format === "grounded-stat"),
|
|
37
|
+
requiresQuote: properties.some(({ format }) => format === "grounded-quote"),
|
|
38
|
+
requiresScreenshot: properties.some(({ format }) => format === "supplied-image"),
|
|
39
|
+
allowsStockMedia: schema["x-vanillasky"]?.allowsStockMedia === true || properties.some(({ format }) => format === "stock-media-keyword"),
|
|
40
|
+
requiredAnyOf
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function summaryType(property) {
|
|
44
|
+
if (property.format === "uri" || property.format === "supplied-image") return "media";
|
|
45
|
+
if (property.enum) return "enum";
|
|
46
|
+
if (property.type === "number" || property.type === "integer") return "number";
|
|
47
|
+
if (property.type === "boolean") return "boolean";
|
|
48
|
+
if (property.type === "array" && property.items?.type === "object") return "data-points";
|
|
49
|
+
if (property.type === "array") return "string-array";
|
|
50
|
+
return "string";
|
|
51
|
+
}
|
|
52
|
+
function summarizeTemplateVariables(schema) {
|
|
53
|
+
const required = new Set(schema.required ?? []);
|
|
54
|
+
return Object.fromEntries(Object.entries(schema.properties).map(([name, property]) => [name, {
|
|
55
|
+
type: summaryType(property),
|
|
56
|
+
...property.title == null ? {} : { title: property.title },
|
|
57
|
+
...property.description == null ? {} : { description: property.description },
|
|
58
|
+
...property.default === void 0 ? {} : { default: cloneJson(property.default) },
|
|
59
|
+
...property.examples == null ? {} : { examples: cloneJson(property.examples) },
|
|
60
|
+
...property.enum == null ? {} : { options: [...property.enum] },
|
|
61
|
+
required: required.has(name),
|
|
62
|
+
...property.minItems == null ? {} : { minItems: property.minItems },
|
|
63
|
+
...property.maxItems == null ? {} : { maxItems: property.maxItems }
|
|
64
|
+
}]));
|
|
65
|
+
}
|
|
66
|
+
function templateVariableNotation(property, required) {
|
|
67
|
+
const type = summaryType(property);
|
|
68
|
+
const options = property.enum?.length ? `(${property.enum.join("|")})` : "";
|
|
69
|
+
const itemCount = property.minItems === property.maxItems && property.minItems != null ? `[${property.minItems}]` : property.minItems != null || property.maxItems != null ? `[${property.minItems ?? 0}..${property.maxItems ?? "\u221E"}]` : "";
|
|
70
|
+
const textProperty = property.type === "array" ? property.items : property;
|
|
71
|
+
const characterCount = textProperty?.type === "string" && (textProperty.minLength != null || textProperty.maxLength != null) ? `{${textProperty.minLength ?? 0}..${textProperty.maxLength ?? "\u221E"}}` : "";
|
|
72
|
+
return `${type}${itemCount}${characterCount}${options}${required ? "!" : ""}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
getTemplateDefaults,
|
|
77
|
+
isMeaningfullyPresentTemplateValue,
|
|
78
|
+
getTemplateSchemaGates,
|
|
79
|
+
summarizeTemplateVariables,
|
|
80
|
+
templateVariableNotation
|
|
81
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BUILTIN_TEMPLATE_CATALOG
|
|
3
|
+
} from "./chunk-CHLGW22F.js";
|
|
4
|
+
import {
|
|
5
|
+
assertTemplateTransitionMetadata
|
|
6
|
+
} from "./chunk-2E6T633S.js";
|
|
7
|
+
|
|
8
|
+
// src/visual-system/catalog/server-kit.ts
|
|
9
|
+
function createServerTemplateRegistry(options) {
|
|
10
|
+
const byId = /* @__PURE__ */ new Map();
|
|
11
|
+
for (const template of options.templates) {
|
|
12
|
+
assertTemplateTransitionMetadata(template);
|
|
13
|
+
if (byId.has(template.id)) throw new Error(`Duplicate template id: ${template.id}`);
|
|
14
|
+
byId.set(template.id, template);
|
|
15
|
+
}
|
|
16
|
+
const templates = Object.freeze([...options.templates]);
|
|
17
|
+
const capabilities = { templates: templates.map(({ id }) => id) };
|
|
18
|
+
return Object.freeze({
|
|
19
|
+
capabilities,
|
|
20
|
+
getTemplateMetadata: (id) => byId.get(id),
|
|
21
|
+
listTemplateMetadata: () => [...templates]
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function overlayServerTemplateRegistry(defaults, customer) {
|
|
25
|
+
const defaultTemplates = defaults.listTemplateMetadata();
|
|
26
|
+
const customerTemplates = customer.listTemplateMetadata();
|
|
27
|
+
const customerMetadata = new Map(customerTemplates.map((template) => [template.id, template]));
|
|
28
|
+
const defaultIds = new Set(defaultTemplates.map(({ id }) => id));
|
|
29
|
+
return createServerTemplateRegistry({
|
|
30
|
+
templates: [
|
|
31
|
+
...defaultTemplates.map((template) => customerMetadata.get(template.id) ?? template),
|
|
32
|
+
...customerTemplates.filter(({ id }) => !defaultIds.has(id))
|
|
33
|
+
]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/visual-system/catalog/builtin-server.ts
|
|
38
|
+
var BUILTIN_SERVER_TEMPLATE_KIT = createServerTemplateRegistry({
|
|
39
|
+
templates: BUILTIN_TEMPLATE_CATALOG
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
createServerTemplateRegistry,
|
|
44
|
+
overlayServerTemplateRegistry,
|
|
45
|
+
BUILTIN_SERVER_TEMPLATE_KIT
|
|
46
|
+
};
|