@vanillaskyai/video 0.8.8 → 0.9.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 (175) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/PUBLIC-API.md +5 -8
  3. package/dist/{builtin-server-CUYPIDE7.js → builtin-server-HW6HGXCZ.js} +2 -2
  4. package/dist/{catalog-types-BIhSpOWK.d.ts → catalog-types-WTbLP6Jh.d.ts} +5 -1
  5. package/dist/chapter-title-YRLIQY3V.js +7 -0
  6. package/dist/check-runtime.js +6 -15
  7. package/dist/{chunk-2USVB3CY.js → chunk-3ZTEFJIR.js} +285 -178
  8. package/dist/chunk-4YM2M62S.js +13 -0
  9. package/dist/chunk-77VY4O7A.js +390 -0
  10. package/dist/{chunk-MMUXVA47.js → chunk-7M56IUUX.js} +9 -8
  11. package/dist/chunk-7NZOMTAL.js +63 -0
  12. package/dist/{chunk-2DOLF4QN.js → chunk-AUPC6MDK.js} +12 -14
  13. package/dist/{chunk-352ZZCET.js → chunk-EGVQODKU.js} +2 -2
  14. package/dist/chunk-EWWCQFTI.js +18 -0
  15. package/dist/{chunk-MXZSDGZQ.js → chunk-G7IZBBWF.js} +14 -9
  16. package/dist/{chunk-KT3M7KDL.js → chunk-HKUI5BTB.js} +3 -3
  17. package/dist/{chunk-LMG3GHLL.js → chunk-IQMYK5DX.js} +29 -3
  18. package/dist/chunk-LH4BPALA.js +564 -0
  19. package/dist/{chunk-OWBT45EN.js → chunk-MQILEEXC.js} +1 -1
  20. package/dist/chunk-MXEYPK7M.js +552 -0
  21. package/dist/{chunk-AITKH6QT.js → chunk-OR52UMSF.js} +13 -39
  22. package/dist/{chunk-5SLENAJW.js → chunk-PAJ44TFW.js} +1 -2
  23. package/dist/{chunk-L7QGGKJ2.js → chunk-PDQFQIQW.js} +1 -1
  24. package/dist/{chunk-WJ6FLKNC.js → chunk-R3XAOMKP.js} +14 -0
  25. package/dist/{chunk-2OKB27TG.js → chunk-RXTN2CW6.js} +2 -2
  26. package/dist/{chunk-XWWLKRNU.js → chunk-TVIU23OM.js} +38 -49
  27. package/dist/{chunk-224QNWRA.js → chunk-UDIODEDK.js} +1 -5
  28. package/dist/{chunk-3VRMKFM7.js → chunk-ZDJNFA52.js} +11 -24
  29. package/dist/cinema-media-YQHSIFDA.js +11 -0
  30. package/dist/cli.js +18 -9
  31. package/dist/comparison-RZMMHZR2.js +27 -0
  32. package/dist/compose-video-TBFTONUC.js +12 -0
  33. package/dist/editorial-timeline-OZX7OS6G.js +36 -0
  34. package/dist/{events-BQ6z264n.d.ts → events-B4YCc4vc.d.ts} +1 -1
  35. package/dist/focus-cards-TJ72BT7U.js +20 -0
  36. package/dist/index.d.ts +4 -6
  37. package/dist/index.js +3 -7
  38. package/dist/key-figure-GYENXKH3.js +20 -0
  39. package/dist/{kit-tQnCPMOM.d.ts → kit-DrRpdn0p.d.ts} +2 -2
  40. package/dist/mobile-message-UZUPIKHD.js +49 -0
  41. package/dist/{preload-media-LJXWKTWG.js → preload-media-CFTZIMZL.js} +1 -1
  42. package/dist/quote-EW5HQA6H.js +23 -0
  43. package/dist/react.d.ts +21 -11
  44. package/dist/react.js +590 -833
  45. package/dist/scene-video-backdrop-PCTARAAJ.js +7 -0
  46. package/dist/server.d.ts +7 -8
  47. package/dist/server.js +91 -74
  48. package/dist/{system-prompt-RRXIWDDD.js → system-prompt-SYRT5DWM.js} +1 -1
  49. package/dist/template-catalog.d.ts +257 -16
  50. package/dist/template-catalog.js +1 -1
  51. package/dist/templates.d.ts +5 -5
  52. package/dist/test.d.ts +3 -3
  53. package/dist/test.js +18 -18
  54. package/dist/text-stream-LD364KBI.js +9 -0
  55. package/dist/{types-DWFO6qca.d.ts → types-CdBmNTGD.d.ts} +1 -1
  56. package/dist/{types-CEh9eUFu.d.ts → types-DdZw4GRQ.d.ts} +17 -48
  57. package/dist/validate-4OUD2CLX.js +10 -0
  58. package/docs/architecture.md +1 -1
  59. package/docs/concepts.md +1 -1
  60. package/docs/custom-templates.md +13 -28
  61. package/docs/customization.md +11 -59
  62. package/docs/getting-started.md +2 -2
  63. package/docs/immersive-interface.md +3 -4
  64. package/docs/maintainers/cinematic-migration.md +34 -0
  65. package/docs/media-and-audio.md +14 -14
  66. package/docs/motion-and-effects.md +6 -14
  67. package/docs/persistence.md +3 -3
  68. package/docs/production.md +11 -9
  69. package/docs/provider-integration.md +9 -16
  70. package/docs/reference/design-system.html +1 -1
  71. package/docs/reference/protocol.md +14 -4
  72. package/docs/testing.md +1 -1
  73. package/package.json +6 -3
  74. package/registry/items/backgrounds.json +1 -1
  75. package/registry/items/chapterTitle.json +80 -0
  76. package/registry/items/cinemaMedia.json +136 -0
  77. package/registry/items/comparison.json +101 -0
  78. package/registry/items/editorialTimeline.json +105 -0
  79. package/registry/items/focusCards.json +90 -0
  80. package/registry/items/keyFigure.json +97 -0
  81. package/registry/items/mobileMessage.json +153 -0
  82. package/registry/items/quote.json +94 -0
  83. package/registry/items/template-context.json +1 -1
  84. package/registry/items/theme.json +1 -1
  85. package/starters/video-chat/README.md +24 -26
  86. package/starters/video-chat/package.json +1 -1
  87. package/starters/video-chat/providers/video.ts +6 -5
  88. package/starters/video-chat/stock.ts +35 -151
  89. package/styles/fonts/OFL.txt +93 -0
  90. package/styles/fonts/manifest.json +66 -0
  91. package/styles/fonts/roboto-v51-cyrillic-ext.woff2 +0 -0
  92. package/styles/fonts/roboto-v51-cyrillic.woff2 +0 -0
  93. package/styles/fonts/roboto-v51-greek-ext.woff2 +0 -0
  94. package/styles/fonts/roboto-v51-greek.woff2 +0 -0
  95. package/styles/fonts/roboto-v51-latin-ext.woff2 +0 -0
  96. package/styles/fonts/roboto-v51-latin.woff2 +0 -0
  97. package/styles/fonts/roboto-v51-math.woff2 +0 -0
  98. package/styles/fonts/roboto-v51-symbols.woff2 +0 -0
  99. package/styles/fonts/roboto-v51-vietnamese.woff2 +0 -0
  100. package/styles/fonts/roboto.css +91 -0
  101. package/styles/video-chat.css +30 -22
  102. package/dist/bg-confetti-HITCGLGD.js +0 -89
  103. package/dist/bg-emoji-VU4UZYZN.js +0 -150
  104. package/dist/bg-media-L34PDQXJ.js +0 -106
  105. package/dist/brand-message-FUJ4STFO.js +0 -256
  106. package/dist/chart-bar-EGPHJATL.js +0 -310
  107. package/dist/chart-counter-5FFWQNZO.js +0 -306
  108. package/dist/chart-progress-ring-JMXQLMLC.js +0 -302
  109. package/dist/chunk-4UNKZEAT.js +0 -3575
  110. package/dist/chunk-5CDAM24P.js +0 -31
  111. package/dist/chunk-AOWSXU2K.js +0 -18
  112. package/dist/chunk-CJ7F2CE6.js +0 -104
  113. package/dist/chunk-FRN6WKHA.js +0 -185
  114. package/dist/chunk-G5JPHBX7.js +0 -336
  115. package/dist/chunk-L5BTTCZV.js +0 -97
  116. package/dist/chunk-P22HHFDK.js +0 -43
  117. package/dist/chunk-PP3MCPN4.js +0 -1998
  118. package/dist/chunk-QAAO7OAY.js +0 -19
  119. package/dist/chunk-S7HFLYNN.js +0 -35
  120. package/dist/chunk-W3VDIBGR.js +0 -217
  121. package/dist/chunk-XGRA2MUV.js +0 -242
  122. package/dist/chunk-XTOXFMLM.js +0 -105
  123. package/dist/chunk-YJJC4N4D.js +0 -761
  124. package/dist/compose-video-2NFH4DZY.js +0 -13
  125. package/dist/cta-logo-YR7LYUR5.js +0 -212
  126. package/dist/cta-media-DQJBKUO2.js +0 -221
  127. package/dist/incoming-call-54KQ7O5A.js +0 -323
  128. package/dist/infographic-before-after-ROS52GOW.js +0 -460
  129. package/dist/infographic-feature-list-E7MGUDAN.js +0 -272
  130. package/dist/infographic-problem-solution-3S6NGO5N.js +0 -343
  131. package/dist/infographic-stat-row-SYB7AB7H.js +0 -247
  132. package/dist/infographic-steps-KCE6JWE2.js +0 -406
  133. package/dist/prompt-input-MHX4O42G.js +0 -239
  134. package/dist/reaction-SLEGR3BE.js +0 -122
  135. package/dist/scene-video-backdrop-XFPX4O3I.js +0 -10
  136. package/dist/showcase-code-2HLAVKBM.js +0 -357
  137. package/dist/showcase-phone-Q4HEO4LH.js +0 -278
  138. package/dist/showcase-terminal-DL45LUFG.js +0 -312
  139. package/dist/showcase-web-2QZO73FZ.js +0 -396
  140. package/dist/social-conversation-75SD2637.js +0 -695
  141. package/dist/social-milestone-GEFUQSBF.js +0 -296
  142. package/dist/social-notification-FJ5VHVLK.js +0 -247
  143. package/dist/social-review-stack-KXQCKFRP.js +0 -242
  144. package/dist/social-testimonial-JZ7CHC7S.js +0 -287
  145. package/dist/social-tweet-UCDXM25F.js +0 -298
  146. package/dist/text-stream-54LXABJW.js +0 -10
  147. package/dist/validate-T7GBU2YF.js +0 -8
  148. package/registry/items/barChart.json +0 -245
  149. package/registry/items/beforeAfter.json +0 -180
  150. package/registry/items/bigNumber.json +0 -232
  151. package/registry/items/brandMessage.json +0 -164
  152. package/registry/items/cardList.json +0 -214
  153. package/registry/items/chatMessenger.json +0 -151
  154. package/registry/items/chatWhatsapp.json +0 -151
  155. package/registry/items/codeEditor.json +0 -191
  156. package/registry/items/confetti.json +0 -107
  157. package/registry/items/ctaLogo.json +0 -167
  158. package/registry/items/ctaMedia.json +0 -200
  159. package/registry/items/emojiBurst.json +0 -101
  160. package/registry/items/incomingCall.json +0 -177
  161. package/registry/items/media.json +0 -187
  162. package/registry/items/milestone.json +0 -212
  163. package/registry/items/notification.json +0 -189
  164. package/registry/items/phoneMockup.json +0 -261
  165. package/registry/items/problemSolution.json +0 -193
  166. package/registry/items/progressRing.json +0 -206
  167. package/registry/items/promptInput.json +0 -155
  168. package/registry/items/reaction.json +0 -176
  169. package/registry/items/reviewStack.json +0 -231
  170. package/registry/items/steps.json +0 -215
  171. package/registry/items/terminal.json +0 -219
  172. package/registry/items/testimonial.json +0 -171
  173. package/registry/items/tripleStats.json +0 -235
  174. package/registry/items/tweet.json +0 -210
  175. package/registry/items/webMockup.json +0 -280
@@ -1,19 +0,0 @@
1
- // src/visual-system/scene-templates/celebration-particle-timing.ts
2
- var SECOND_WAVE_INTERVAL = 4;
3
- var SECOND_WAVE_START = 0.33;
4
- var SECOND_WAVE_DURATION = 1;
5
- var EXIT_FADE_START = 0.76;
6
- var EXIT_FADE_END = 0.85;
7
- function getCelebrationParticleTiming(sceneProgress, particleIndex, durationFactor) {
8
- if (sceneProgress >= EXIT_FADE_END) return null;
9
- const isSecondWave = particleIndex % SECOND_WAVE_INTERVAL === 0;
10
- if (isSecondWave && sceneProgress <= SECOND_WAVE_START) return null;
11
- const waveProgress = isSecondWave ? (sceneProgress - SECOND_WAVE_START) / SECOND_WAVE_DURATION : sceneProgress;
12
- const progress = Math.min(1, Math.max(0, waveProgress / durationFactor));
13
- const opacity = sceneProgress <= EXIT_FADE_START ? 1 : (EXIT_FADE_END - sceneProgress) / (EXIT_FADE_END - EXIT_FADE_START);
14
- return { progress, opacity };
15
- }
16
-
17
- export {
18
- getCelebrationParticleTiming
19
- };
@@ -1,35 +0,0 @@
1
- // src/visual-system/scene-templates/text-utils.ts
2
- function stripPipe(text) {
3
- const idx = text.indexOf("|");
4
- return idx === -1 ? text : text.slice(0, idx);
5
- }
6
- function getResponsiveFontSize(text, width, role = "headline", height) {
7
- const s = height ? Math.min(width, height) / 1080 : width / 1080;
8
- const words = text.split(/\s+/).filter(Boolean).length;
9
- const chars = text.length;
10
- if (role === "subtitle") {
11
- if (chars > 60) return 24 * s;
12
- if (chars > 30) return 28 * s;
13
- return 32 * s;
14
- }
15
- if (words <= 2 && chars <= 12) return Math.max(36 * s, 96 * s);
16
- if (words <= 3 && chars <= 20) return Math.max(36 * s, 80 * s);
17
- if (words <= 5 && chars <= 35) return Math.max(36 * s, 64 * s);
18
- if (words <= 8 && chars <= 60) return Math.max(36 * s, 48 * s);
19
- return 36 * s;
20
- }
21
- function fitTextSize(text, baseFontSize, maxWidth, opts) {
22
- if (!text) return baseFontSize;
23
- const minScale = opts?.minScale ?? 0.5;
24
- const charWidthRatio = opts?.charWidthRatio ?? 0.55;
25
- const estWidth = text.length * baseFontSize * charWidthRatio;
26
- if (estWidth <= maxWidth) return baseFontSize;
27
- const scaled = maxWidth / estWidth * baseFontSize;
28
- return Math.max(scaled, baseFontSize * minScale);
29
- }
30
-
31
- export {
32
- stripPipe,
33
- getResponsiveFontSize,
34
- fitTextSize
35
- };
@@ -1,217 +0,0 @@
1
- import {
2
- interpolate
3
- } from "./chunk-XTOXFMLM.js";
4
- import {
5
- darken,
6
- resolveTokens
7
- } from "./chunk-XWWLKRNU.js";
8
-
9
- // src/visual-system/theme/colors.ts
10
- function withOpacity(hex, opacity) {
11
- const match = hex.match(/^#([0-9a-f]{3,8})$/i);
12
- if (!match) return hex;
13
- const h = match[1];
14
- let r;
15
- let g;
16
- let b;
17
- if (h.length === 3) {
18
- r = parseInt(h[0] + h[0], 16);
19
- g = parseInt(h[1] + h[1], 16);
20
- b = parseInt(h[2] + h[2], 16);
21
- } else if (h.length >= 6) {
22
- r = parseInt(h.slice(0, 2), 16);
23
- g = parseInt(h.slice(2, 4), 16);
24
- b = parseInt(h.slice(4, 6), 16);
25
- } else {
26
- return hex;
27
- }
28
- return `rgba(${r},${g},${b},${opacity})`;
29
- }
30
- function orderDarkToLight(a, b) {
31
- const luminance = (hex) => {
32
- const match = hex.match(/^#([0-9a-f]{6})$/i);
33
- if (!match) return 0;
34
- const h = match[1];
35
- return 0.299 * parseInt(h.slice(0, 2), 16) + 0.587 * parseInt(h.slice(2, 4), 16) + 0.114 * parseInt(h.slice(4, 6), 16);
36
- };
37
- return luminance(a) <= luminance(b) ? [a, b] : [b, a];
38
- }
39
- function relativeLuminance(hex) {
40
- const match = hex.match(/^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i);
41
- if (!match) return void 0;
42
- const value = match[1].length === 3 ? match[1].split("").map((character) => character + character).join("") : match[1].slice(0, 6);
43
- const channel = (offset) => {
44
- const encoded = Number.parseInt(value.slice(offset, offset + 2), 16) / 255;
45
- return encoded <= 0.04045 ? encoded / 12.92 : ((encoded + 0.055) / 1.055) ** 2.4;
46
- };
47
- return 0.2126 * channel(0) + 0.7152 * channel(2) + 0.0722 * channel(4);
48
- }
49
- function contrastRatio(first, second) {
50
- const firstLuminance = relativeLuminance(first);
51
- const secondLuminance = relativeLuminance(second);
52
- if (firstLuminance == null || secondLuminance == null) return 0;
53
- return (Math.max(firstLuminance, secondLuminance) + 0.05) / (Math.min(firstLuminance, secondLuminance) + 0.05);
54
- }
55
- function accessibleTextColor(background, preferred) {
56
- if (preferred && contrastRatio(background, preferred) >= 4.5) return preferred;
57
- return contrastRatio(background, "#FFFFFF") >= contrastRatio(background, "#000000") ? "#FFFFFF" : "#000000";
58
- }
59
- function autoTextColor(background) {
60
- return accessibleTextColor(background);
61
- }
62
-
63
- // src/visual-system/scene-templates/color-utils.ts
64
- import React from "react";
65
- var TOP_TEXT_AREA_RATIO = 0.32;
66
- function gradientBackground({
67
- colorA,
68
- colorB,
69
- solidBg,
70
- progress,
71
- sceneDuration,
72
- seed,
73
- family
74
- }) {
75
- if (solidBg) return solidBg;
76
- const normalizedProgress = Math.max(0, Math.min(1, progress));
77
- const atBoundary = normalizedProgress === 0 || normalizedProgress === 1;
78
- const easedLoop = normalizedProgress * normalizedProgress * (3 - 2 * normalizedProgress);
79
- const seedEnvelope = atBoundary ? 0 : Math.sin(normalizedProgress * Math.PI) ** 2;
80
- void sceneDuration;
81
- const seededRandom = (s) => {
82
- const x = Math.sin(s * 12.9898 + s * 78.233) * 43758.5453;
83
- return x - Math.floor(x);
84
- };
85
- const phase = seededRandom(seed) * Math.PI * 2 * seedEnvelope;
86
- const t = (atBoundary ? 0 : easedLoop * Math.PI * 2) + phase;
87
- const [topColor, bottomColor] = orderDarkToLight(colorA, colorB);
88
- if (family === "wash") {
89
- const shift = Math.sin(t) * 6;
90
- return `linear-gradient(${175 + shift}deg, ${topColor} 0%, ${bottomColor} 100%)`;
91
- }
92
- if (family === "spotlight") {
93
- const x = 50 + Math.sin(t) * 8;
94
- const y = 28 + Math.cos(t + 1) * 5;
95
- return `
96
- radial-gradient(ellipse 70% 55% at ${x}% ${y}%, ${colorA}66 0%, ${colorA}1a 45%, transparent 72%),
97
- linear-gradient(180deg, ${darken(topColor, 0.55)} 0%, ${darken(bottomColor, 0.7)} 100%)
98
- `;
99
- }
100
- const x1 = 25 + Math.sin(t) * 25;
101
- const y1 = 30 + Math.cos(t + 1) * 25;
102
- const x2 = 75 + Math.sin(t + 2) * 25;
103
- const y2 = 70 + Math.cos(t + 3.5) * 25;
104
- return `
105
- radial-gradient(ellipse 60% 60% at ${x1}% ${y1}%, ${colorA}4d 0%, transparent 65%),
106
- radial-gradient(ellipse 60% 60% at ${x2}% ${y2}%, ${colorB}4d 0%, transparent 65%),
107
- linear-gradient(180deg, ${topColor} 0%, ${bottomColor} 100%)
108
- `;
109
- }
110
- var BrandGradientOverlay = ({ style: globalStyle, progress, sceneDuration, seed }) => {
111
- const tokens = resolveTokens(globalStyle);
112
- const [first, second] = tokens.background.type === "gradient" ? tokens.background.colors : [tokens.background.color, tokens.background.color];
113
- const solidBg = tokens.background.type === "solid" ? tokens.background.color : void 0;
114
- return React.createElement("div", {
115
- "data-brand-gradient": "true",
116
- style: {
117
- position: "absolute",
118
- inset: 0,
119
- background: gradientBackground({ colorA: first, colorB: second, solidBg, progress, sceneDuration, seed, family: tokens.preset.background }),
120
- pointerEvents: "none"
121
- }
122
- });
123
- };
124
-
125
- // src/visual-system/scene-templates/background-effect.ts
126
- var DIRECTIONS = ["right", "left", "up", "down"];
127
- function getKenBurnsTransform(progress, direction) {
128
- const scale = interpolate(progress, [0, 1], [1.05, 1.15]);
129
- const mainAxis = direction === "right" || direction === "left";
130
- const sign = direction === "right" || direction === "down" ? 1 : -1;
131
- const primary = interpolate(progress, [0, 1], [-2 * sign, 2 * sign]);
132
- const secondary = interpolate(progress, [0, 1], [-0.5, 0.5]);
133
- const x = mainAxis ? primary : secondary;
134
- const y = mainAxis ? secondary : primary;
135
- return {
136
- transform: `scale(${scale}) translate(${x}%, ${y}%)`,
137
- transformOrigin: "center"
138
- };
139
- }
140
- function getDriftTransform(progress, direction) {
141
- const horizontal = direction === "right" || direction === "left";
142
- const sign = direction === "right" || direction === "down" ? 1 : -1;
143
- const offset = interpolate(progress, [0, 1], [-2 * sign, 2 * sign]);
144
- return {
145
- transform: horizontal ? `scale(1.1) translateX(${offset}%)` : `scale(1.1) translateY(${offset}%)`,
146
- transformOrigin: "center"
147
- };
148
- }
149
- function getBackgroundTransform(effect, progress, beatIntensity = 0, sceneIndex = 0) {
150
- const dir = DIRECTIONS[sceneIndex % DIRECTIONS.length];
151
- const resolved = effect ?? "static";
152
- switch (resolved) {
153
- case "slow-zoom-in":
154
- return {
155
- transform: `scale(${interpolate(progress, [0, 1], [1, 1.12])})`,
156
- transformOrigin: "center"
157
- };
158
- case "slow-zoom-out":
159
- return {
160
- transform: `scale(${interpolate(progress, [0, 1], [1.12, 1])})`,
161
- transformOrigin: "center"
162
- };
163
- case "ken-burns":
164
- return getKenBurnsTransform(progress, dir);
165
- case "drift":
166
- return getDriftTransform(progress, dir);
167
- case "pulse": {
168
- const base = 1 + beatIntensity * 0.04;
169
- const breathe = 1 + Math.sin(progress * Math.PI * 2) * 0.02;
170
- return {
171
- transform: `scale(${base * breathe})`,
172
- transformOrigin: "center"
173
- };
174
- }
175
- case "breathe": {
176
- const s = 1 + Math.sin(progress * Math.PI) * 0.03;
177
- const y = Math.sin(progress * Math.PI) * 0.5;
178
- return {
179
- transform: `scale(${s}) translateY(${y}%)`,
180
- transformOrigin: "center"
181
- };
182
- }
183
- case "slow-tilt": {
184
- const angle = interpolate(progress, [0, 1], [0, 2.5]);
185
- return {
186
- transform: `scale(1.08) rotate(${angle}deg)`,
187
- transformOrigin: "center"
188
- };
189
- }
190
- case "camera-shake": {
191
- const t = progress * 40;
192
- const sx = Math.sin(t * 2.3) * 0.4 + Math.sin(t * 5.7) * 0.2;
193
- const sy = Math.cos(t * 3.1) * 0.3 + Math.cos(t * 4.9) * 0.15;
194
- const sr = Math.sin(t * 1.7) * 0.3;
195
- return {
196
- transform: `scale(1.05) translate(${sx}%, ${sy}%) rotate(${sr}deg)`,
197
- transformOrigin: "center"
198
- };
199
- }
200
- case "static":
201
- default:
202
- return {
203
- transform: "none",
204
- transformOrigin: "center"
205
- };
206
- }
207
- }
208
-
209
- export {
210
- withOpacity,
211
- accessibleTextColor,
212
- autoTextColor,
213
- TOP_TEXT_AREA_RATIO,
214
- gradientBackground,
215
- BrandGradientOverlay,
216
- getBackgroundTransform
217
- };
@@ -1,242 +0,0 @@
1
- // src/protocol/background.ts
2
- var BACKGROUND_PRESETS = {
3
- cosmic: { type: "gradient", colors: ["#8711C1", "#2167E3"] },
4
- horizon: { type: "gradient", colors: ["#5967C4", "#133A94"] },
5
- twilight: { type: "gradient", colors: ["#0C1740", "#3D1B66"] },
6
- meadow: { type: "gradient", colors: ["#348756", "#54B6CA"] },
7
- velvet: { type: "gradient", colors: ["#76030F", "#121B67"] },
8
- flamingo: { type: "gradient", colors: ["#C72D50", "#3E3B92"] },
9
- peach: { type: "gradient", colors: ["#B45A4A", "#AD336D"] },
10
- saffron: { type: "gradient", colors: ["#F3696E", "#F8A902"] },
11
- black: { type: "solid", color: "#000000" },
12
- midnight: { type: "solid", color: "#070B20" },
13
- aubergine: { type: "solid", color: "#170A2E" },
14
- coal: { type: "solid", color: "#0A0A0A" },
15
- navy: { type: "solid", color: "#0A2240" }
16
- };
17
- var DEFAULT_BACKGROUND_PRESET = "cosmic";
18
- var DEFAULT_COLORS = {
19
- primary: "#00E5A0",
20
- secondary: "#006BE5",
21
- foreground: "#FFFFFF",
22
- surface: "#0A0A14",
23
- surfaceElevated: "#14152A",
24
- muted: "#A7A6B0"
25
- };
26
- var HEX_COLOR = /^#[0-9a-f]{6}$/i;
27
- var MINIMUM_TEXT_CONTRAST = 4.5;
28
- function normalizeColor(value, path) {
29
- if (!HEX_COLOR.test(value)) throw new Error(`${path} must be a hex color`);
30
- return value.toUpperCase();
31
- }
32
- function resolveBackground(input) {
33
- if (input == null || typeof input === "string") {
34
- const preset = BACKGROUND_PRESETS[input ?? DEFAULT_BACKGROUND_PRESET];
35
- return preset.type === "solid" ? { type: "solid", color: preset.color } : { type: "gradient", colors: [...preset.colors] };
36
- }
37
- if ("color" in input) {
38
- return { type: "solid", color: normalizeColor(input.color, "brand.background.color") };
39
- }
40
- return {
41
- type: "gradient",
42
- colors: [
43
- normalizeColor(input.colors[0], "brand.background.colors[0]"),
44
- normalizeColor(input.colors[1], "brand.background.colors[1]")
45
- ]
46
- };
47
- }
48
- function channel(value) {
49
- const normalized = value / 255;
50
- return normalized <= 0.04045 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
51
- }
52
- function luminance(color) {
53
- return 0.2126 * channel(Number.parseInt(color.slice(1, 3), 16)) + 0.7152 * channel(Number.parseInt(color.slice(3, 5), 16)) + 0.0722 * channel(Number.parseInt(color.slice(5, 7), 16));
54
- }
55
- function gradientLuminanceAt(colors, progress) {
56
- const channels = colors.map((color) => [1, 3, 5].map((offset) => Number.parseInt(color.slice(offset, offset + 2), 16)));
57
- return 0.2126 * channel(channels[0][0] + (channels[1][0] - channels[0][0]) * progress) + 0.7152 * channel(channels[0][1] + (channels[1][1] - channels[0][1]) * progress) + 0.0722 * channel(channels[0][2] + (channels[1][2] - channels[0][2]) * progress);
58
- }
59
- function backgroundLuminanceRange(background) {
60
- if (background.type === "solid") {
61
- const value = luminance(background.color);
62
- return [value, value];
63
- }
64
- const start = luminance(background.colors[0]);
65
- const end = luminance(background.colors[1]);
66
- let left = 0;
67
- let right = 1;
68
- for (let iteration = 0; iteration < 64; iteration += 1) {
69
- const first = left + (right - left) / 3;
70
- const second = right - (right - left) / 3;
71
- if (gradientLuminanceAt(background.colors, first) <= gradientLuminanceAt(background.colors, second)) {
72
- right = second;
73
- } else {
74
- left = first;
75
- }
76
- }
77
- const minimum = Math.min(start, end, gradientLuminanceAt(background.colors, (left + right) / 2));
78
- return [minimum, Math.max(start, end)];
79
- }
80
- function minimumBackgroundContrast(background, foreground) {
81
- const foregroundLuminance = luminance(foreground);
82
- const [minimum, maximum] = backgroundLuminanceRange(background);
83
- if (foregroundLuminance < minimum) {
84
- return (minimum + 0.05) / (foregroundLuminance + 0.05);
85
- }
86
- if (foregroundLuminance > maximum) {
87
- return (foregroundLuminance + 0.05) / (maximum + 0.05);
88
- }
89
- return 1;
90
- }
91
- function resolveForeground(input, background) {
92
- const supplied = input?.colors?.foreground;
93
- if (supplied != null) return normalizeColor(supplied, "brand.colors.foreground");
94
- const candidates = ["#FFFFFF", "#000000"];
95
- const foreground = candidates.map((color) => ({ color, contrast: minimumBackgroundContrast(background, color) })).sort((left, right) => right.contrast - left.contrast)[0];
96
- if (foreground.contrast < MINIMUM_TEXT_CONTRAST) {
97
- throw new Error(
98
- "brand.colors.foreground is required because neither black nor white contrasts with the entire background; choose a background with one consistently readable foreground"
99
- );
100
- }
101
- return foreground.color;
102
- }
103
- function validateResolvedBrandContrast(brand, path) {
104
- const contrast = minimumBackgroundContrast(brand.background, brand.colors.foreground);
105
- if (contrast < MINIMUM_TEXT_CONTRAST) {
106
- throw new Error(
107
- `${path}.colors.foreground must have at least 4.5:1 contrast across ${path}.background (minimum ${contrast.toFixed(2)}:1); choose a different foreground or background)`
108
- );
109
- }
110
- }
111
- function resolveVideoBrand(input) {
112
- validateBrandInput(input, "brand");
113
- const background = resolveBackground(input?.background);
114
- const colors = Object.fromEntries(Object.entries({
115
- ...DEFAULT_COLORS,
116
- ...input?.colors,
117
- foreground: resolveForeground(input, background)
118
- }).map(([name, value]) => [name, normalizeColor(value, `brand.colors.${name}`)]));
119
- const brand = {
120
- ...input?.name?.trim() ? { name: input.name.trim() } : {},
121
- ...input?.logoUrl?.trim() ? { logoUrl: input.logoUrl.trim() } : {},
122
- font: input?.font?.trim() || "Inter",
123
- scriptFont: input?.scriptFont?.trim() || "Caveat",
124
- background,
125
- colors
126
- };
127
- validateResolvedBrandContrast(brand, "brand");
128
- return brand;
129
- }
130
- function isBackgroundPreset(value) {
131
- return Object.prototype.hasOwnProperty.call(BACKGROUND_PRESETS, value);
132
- }
133
- function validateBrandBackgroundInput(value, path) {
134
- if (typeof value === "string") {
135
- if (!isBackgroundPreset(value)) throw new Error(`${path} preset is unsupported`);
136
- return;
137
- }
138
- if (!value || typeof value !== "object" || Array.isArray(value)) {
139
- throw new Error(`${path} must be a preset or custom color object`);
140
- }
141
- const background = value;
142
- const keys = Object.keys(background);
143
- if (keys.length !== 1 || keys[0] !== "color" && keys[0] !== "colors") {
144
- throw new Error(`${path} must contain either color or colors`);
145
- }
146
- if ("color" in background) {
147
- if (typeof background.color !== "string") throw new Error(`${path}.color must be a hex color`);
148
- normalizeColor(background.color, `${path}.color`);
149
- return;
150
- }
151
- if (!Array.isArray(background.colors) || background.colors.length !== 2 || background.colors.some((color) => typeof color !== "string")) {
152
- throw new Error(`${path}.colors must contain two colors`);
153
- }
154
- background.colors.forEach((color, index) => normalizeColor(color, `${path}.colors[${index}]`));
155
- }
156
- function validateBrandInput(value, path) {
157
- if (value == null) return;
158
- if (typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} must be an object`);
159
- const input = value;
160
- const allowed = /* @__PURE__ */ new Set(["name", "logoUrl", "font", "scriptFont", "background", "colors"]);
161
- for (const key of Object.keys(input)) {
162
- if (!allowed.has(key)) throw new Error(`${path} contains unsupported field ${key}`);
163
- }
164
- for (const key of ["name", "logoUrl", "font", "scriptFont"]) {
165
- if (input[key] != null && (typeof input[key] !== "string" || !input[key].trim())) {
166
- throw new Error(`${path}.${key} must be a non-empty string`);
167
- }
168
- }
169
- if (input.background != null) validateBrandBackgroundInput(input.background, `${path}.background`);
170
- if (input.colors != null) {
171
- if (typeof input.colors !== "object" || Array.isArray(input.colors)) {
172
- throw new Error(`${path}.colors must be an object`);
173
- }
174
- const colors = input.colors;
175
- const colorNames = new Set(Object.keys(DEFAULT_COLORS));
176
- for (const [name, color] of Object.entries(colors)) {
177
- if (!colorNames.has(name)) throw new Error(`${path}.colors contains unsupported field ${name}`);
178
- if (typeof color !== "string") throw new Error(`${path}.colors.${name} must be a hex color`);
179
- normalizeColor(color, `${path}.colors.${name}`);
180
- }
181
- }
182
- }
183
- function validateVideoBrand(value, path) {
184
- if (!value || typeof value !== "object" || Array.isArray(value)) {
185
- throw new Error(`${path} must be an object`);
186
- }
187
- const brand = value;
188
- const allowed = /* @__PURE__ */ new Set(["name", "logoUrl", "font", "scriptFont", "background", "colors"]);
189
- for (const key of Object.keys(brand)) {
190
- if (!allowed.has(key)) throw new Error(`${path} contains unsupported field ${key}`);
191
- }
192
- for (const key of ["font", "scriptFont"]) {
193
- if (typeof brand[key] !== "string" || !brand[key].trim()) {
194
- throw new Error(`${path}.${key} must be a non-empty string`);
195
- }
196
- }
197
- for (const key of ["name", "logoUrl"]) {
198
- if (brand[key] != null && (typeof brand[key] !== "string" || !brand[key].trim())) {
199
- throw new Error(`${path}.${key} must be a non-empty string`);
200
- }
201
- }
202
- if (!brand.background || typeof brand.background !== "object" || Array.isArray(brand.background)) {
203
- throw new Error(`${path}.background must be an object`);
204
- }
205
- const background = brand.background;
206
- if (background.type === "solid") {
207
- if (Object.keys(background).some((key) => key !== "type" && key !== "color")) {
208
- throw new Error(`${path}.background contains unsupported fields`);
209
- }
210
- if (typeof background.color !== "string") throw new Error(`${path}.background.color must be a hex color`);
211
- normalizeColor(background.color, `${path}.background.color`);
212
- } else if (background.type === "gradient") {
213
- if (Object.keys(background).some((key) => key !== "type" && key !== "colors")) {
214
- throw new Error(`${path}.background contains unsupported fields`);
215
- }
216
- if (!Array.isArray(background.colors) || background.colors.length !== 2 || background.colors.some((color) => typeof color !== "string")) {
217
- throw new Error(`${path}.background.colors must contain two colors`);
218
- }
219
- background.colors.forEach((color, index) => normalizeColor(color, `${path}.background.colors[${index}]`));
220
- } else {
221
- throw new Error(`${path}.background.type is unsupported`);
222
- }
223
- if (!brand.colors || typeof brand.colors !== "object" || Array.isArray(brand.colors)) {
224
- throw new Error(`${path}.colors must be an object`);
225
- }
226
- const colors = brand.colors;
227
- const requiredColors = Object.keys(DEFAULT_COLORS);
228
- for (const name of requiredColors) {
229
- if (typeof colors[name] !== "string") throw new Error(`${path}.colors.${name} must be a hex color`);
230
- normalizeColor(colors[name], `${path}.colors.${name}`);
231
- }
232
- for (const name of Object.keys(colors)) {
233
- if (!requiredColors.includes(name)) throw new Error(`${path}.colors contains unsupported field ${name}`);
234
- }
235
- validateResolvedBrandContrast(brand, path);
236
- }
237
-
238
- export {
239
- resolveVideoBrand,
240
- validateBrandInput,
241
- validateVideoBrand
242
- };
@@ -1,105 +0,0 @@
1
- // src/visual-system/motion/curves.ts
2
- function cubicBezier(x1, y1, x2, y2) {
3
- const cx = 3 * x1;
4
- const bx = 3 * (x2 - x1) - cx;
5
- const ax = 1 - cx - bx;
6
- const cy = 3 * y1;
7
- const by = 3 * (y2 - y1) - cy;
8
- const ay = 1 - cy - by;
9
- const sampleX = (t) => ((ax * t + bx) * t + cx) * t;
10
- const sampleY = (t) => ((ay * t + by) * t + cy) * t;
11
- const sampleDX = (t) => (3 * ax * t + 2 * bx) * t + cx;
12
- return (x) => {
13
- if (x <= 0) return 0;
14
- if (x >= 1) return 1;
15
- let t = x;
16
- for (let i = 0; i < 8; i++) {
17
- const err = sampleX(t) - x;
18
- if (Math.abs(err) < 1e-6) return sampleY(t);
19
- const d = sampleDX(t);
20
- if (Math.abs(d) < 1e-6) break;
21
- t -= err / d;
22
- }
23
- let lo = 0;
24
- let hi = 1;
25
- t = x;
26
- for (let i = 0; i < 24; i++) {
27
- const v = sampleX(t);
28
- if (Math.abs(v - x) < 1e-6) break;
29
- if (v < x) lo = t;
30
- else hi = t;
31
- t = (lo + hi) / 2;
32
- }
33
- return sampleY(t);
34
- };
35
- }
36
- var Easing = {
37
- /** Easing.bezier(0.16, 1, 0.3, 1) — the canonical curve syntax from docs/motion-library.md. */
38
- bezier: cubicBezier,
39
- // Curves
40
- linear: (t) => t,
41
- quad: (t) => t * t,
42
- cubic: (t) => t * t * t,
43
- sin: (t) => 1 - Math.cos(t * Math.PI / 2),
44
- exp: (t) => t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),
45
- circle: (t) => 1 - Math.sqrt(1 - t * t),
46
- // Directional modifiers
47
- in: (fn) => fn,
48
- out: (fn) => (t) => 1 - fn(1 - t),
49
- inOut: (fn) => (t) => t < 0.5 ? fn(t * 2) / 2 : 1 - fn((1 - t) * 2) / 2
50
- };
51
- function interpolate(value, inputRange, outputRange, options) {
52
- const { easing, extrapolateLeft = "extend", extrapolateRight = "extend" } = options ?? {};
53
- let i = 0;
54
- for (; i < inputRange.length - 2; i++) {
55
- if (value < inputRange[i + 1]) break;
56
- }
57
- const inputMin = inputRange[i];
58
- const inputMax = inputRange[i + 1];
59
- const outputMin = outputRange[i];
60
- const outputMax = outputRange[i + 1];
61
- let t = inputMax === inputMin ? 0 : (value - inputMin) / (inputMax - inputMin);
62
- if (t < 0 && extrapolateLeft === "clamp") t = 0;
63
- if (t > 1 && extrapolateRight === "clamp") t = 1;
64
- if (easing && t >= 0 && t <= 1) {
65
- t = easing(t);
66
- }
67
- return outputMin + t * (outputMax - outputMin);
68
- }
69
- var SPRING_SMOOTH = { damping: 26, stiffness: 170 };
70
- var SPRING_SNAPPY = { damping: 20, stiffness: 300 };
71
- var SPRING_BOUNCY = { damping: 10, stiffness: 180 };
72
- var SPRING_CRISP = { damping: 14, stiffness: 320 };
73
- function spring(progress, config) {
74
- if (progress <= 0) return 0;
75
- if (progress >= 1) {
76
- const { damping: damping2 = 26 } = config ?? {};
77
- if (damping2 >= 20) return 1;
78
- }
79
- const { damping = 26, stiffness = 170, mass = 1 } = config ?? {};
80
- const omega = Math.sqrt(stiffness / mass);
81
- const zeta = damping / (2 * Math.sqrt(stiffness * mass));
82
- const t = progress * 3.5;
83
- let value;
84
- if (zeta < 1) {
85
- const omegaD = omega * Math.sqrt(1 - zeta * zeta);
86
- value = 1 - Math.exp(-zeta * omega * t) * (Math.cos(omegaD * t) + zeta * omega / omegaD * Math.sin(omegaD * t));
87
- } else if (zeta === 1) {
88
- value = 1 - Math.exp(-omega * t) * (1 + omega * t);
89
- } else {
90
- const s1 = -omega * (zeta + Math.sqrt(zeta * zeta - 1));
91
- const s2 = -omega * (zeta - Math.sqrt(zeta * zeta - 1));
92
- value = 1 + (s1 * Math.exp(s2 * t) - s2 * Math.exp(s1 * t)) / (s2 - s1);
93
- }
94
- return value;
95
- }
96
-
97
- export {
98
- Easing,
99
- interpolate,
100
- SPRING_SMOOTH,
101
- SPRING_SNAPPY,
102
- SPRING_BOUNCY,
103
- SPRING_CRISP,
104
- spring
105
- };