@vanillaskyai/video 0.10.2 → 0.10.4
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 +17 -0
- package/PUBLIC-API.md +43 -31
- package/README.md +18 -9
- package/dist/check-runtime.js +2 -2
- package/dist/{chunk-KHLO5OHT.js → chunk-G5TYLTL5.js} +1 -1
- package/dist/{chunk-77VY4O7A.js → chunk-O6FBIB4L.js} +58 -10
- package/dist/{chunk-TVIU23OM.js → chunk-OOBT4X46.js} +13 -3
- package/dist/{chunk-5C6HZNHY.js → chunk-QZAZT44G.js} +3 -2
- package/dist/{chunk-6KZGF63O.js → chunk-WZFLEPLM.js} +58 -35
- package/dist/{chunk-VQH3JTQC.js → chunk-Z2ZI5G7O.js} +1 -1
- package/dist/{cinema-media-APDJS7SC.js → cinema-media-3SIGVVCY.js} +4 -4
- package/dist/{comparison-ZVE2DE7U.js → comparison-VDGRWPB5.js} +4 -4
- package/dist/{editorial-timeline-YIXXVJUV.js → editorial-timeline-QJU4JJWB.js} +4 -4
- package/dist/{key-figure-I2C37FWV.js → key-figure-YUAQW6OE.js} +4 -4
- package/dist/{mobile-message-JSGBU6EX.js → mobile-message-ZMMTUDUQ.js} +4 -4
- package/dist/{quote-XH54G3FS.js → quote-BB6UJT6L.js} +4 -4
- package/dist/react.d.ts +3 -1
- package/dist/react.js +94 -84
- package/dist/{scene-video-backdrop-PCTARAAJ.js → scene-video-backdrop-4EZVVQY7.js} +2 -2
- package/dist/server.d.ts +15 -3
- package/dist/server.js +416 -275
- package/dist/{types-CdBmNTGD.d.ts → types-BqB8zC9u.d.ts} +1 -1
- package/docs/agent-integration.md +5 -4
- package/docs/concepts.md +2 -2
- package/docs/development.md +23 -0
- package/docs/getting-started.md +7 -5
- package/docs/media-and-audio.md +37 -18
- package/docs/performance.md +21 -10
- package/docs/production.md +11 -10
- package/docs/prompt-and-input.md +24 -15
- package/docs/provider-integration.md +11 -13
- package/docs/reference/protocol.md +7 -1
- package/docs/reference/provider-adapters.md +7 -4
- package/docs/testing.md +7 -2
- package/package.json +5 -2
- package/registry/items/backgrounds.json +2 -2
- package/registry/items/cinemaMedia.json +1 -1
- package/registry/items/comparison.json +1 -1
- package/registry/items/editorialTimeline.json +1 -1
- package/registry/items/keyFigure.json +1 -1
- package/registry/items/mobileMessage.json +1 -1
- package/registry/items/quote.json +1 -1
- package/starters/video-chat/README.md +25 -26
- package/starters/video-chat/package.json +1 -1
- package/starters/video-chat/providers/video.ts +10 -6
- package/starters/video-chat/server.ts +1 -1
- package/starters/video-chat/stock.ts +59 -31
- package/styles/video-chat.css +6 -61
|
@@ -42,7 +42,7 @@ export const handleVideoChat = createVideoChatHandler({
|
|
|
42
42
|
return text;
|
|
43
43
|
},
|
|
44
44
|
searchMedia: process.env.PEXELS_API_KEY
|
|
45
|
-
? (query, { orientation, signal
|
|
45
|
+
? (query, { orientation, signal }) => findStockFootage(query, orientation, signal)
|
|
46
46
|
: undefined,
|
|
47
47
|
...providers,
|
|
48
48
|
welcome: {
|
|
@@ -1,38 +1,66 @@
|
|
|
1
1
|
import type { VideoOrientation } from "@vanillaskyai/video";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
description: string;
|
|
8
|
-
reviewedAt: string;
|
|
9
|
-
media: { url: string; type: "video" | "image"; posterUrl?: string };
|
|
3
|
+
interface StockVideo {
|
|
4
|
+
url: string;
|
|
5
|
+
type: "video";
|
|
6
|
+
posterUrl?: string;
|
|
10
7
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
|
|
8
|
+
interface PexelsVideo {
|
|
9
|
+
url?: string;
|
|
10
|
+
image?: string;
|
|
11
|
+
video_files?: { link?: string; width?: number; height?: number; file_type?: string }[];
|
|
12
|
+
}
|
|
13
|
+
const cache = new Map<string, { expires: number; media: StockVideo | null }>();
|
|
14
|
+
const words = (value: string): string[] => value.toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
|
|
15
|
+
function pexelsUrl(value: unknown): value is string {
|
|
16
|
+
if (typeof value !== "string") return false;
|
|
17
|
+
try {
|
|
18
|
+
const url = new URL(value);
|
|
19
|
+
return url.protocol === "https:" && !url.username && !url.password && !url.port
|
|
20
|
+
&& (url.hostname === "pexels.com" || url.hostname.endsWith(".pexels.com"));
|
|
21
|
+
} catch { return false; }
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
)
|
|
24
|
+
/** Full catalog search. Metadata establishes subject relevance, not factual proof.
|
|
25
|
+
* Applications using this adapter must display a prominent link to Pexels.
|
|
26
|
+
* https://www.pexels.com/api/documentation/#guidelines
|
|
27
|
+
*/
|
|
28
|
+
export async function findStockFootage(query: string, orientation: VideoOrientation, signal: AbortSignal) {
|
|
29
|
+
signal.throwIfAborted();
|
|
30
|
+
const normalized = query.trim().toLowerCase().replace(/\s+/g, " ");
|
|
31
|
+
const tokens = words(normalized);
|
|
32
|
+
const key = `${orientation}:${normalized}`;
|
|
33
|
+
const apiKey = process.env.PEXELS_API_KEY;
|
|
34
|
+
if (!apiKey || !tokens.length || normalized.length > 80 || tokens.length > 8) return null;
|
|
35
|
+
const existing = cache.get(key);
|
|
36
|
+
if (existing && existing.expires > Date.now()) return existing.media;
|
|
37
|
+
const url = new URL("https://api.pexels.com/v1/videos/search");
|
|
38
|
+
url.search = new URLSearchParams({ query: normalized, orientation, per_page: "12", size: "medium" }).toString();
|
|
39
|
+
const response = await fetch(url, { headers: { Authorization: apiKey }, signal });
|
|
40
|
+
signal.throwIfAborted();
|
|
41
|
+
if (!response.ok) return null;
|
|
42
|
+
const result = await response.json() as { videos?: PexelsVideo[] };
|
|
31
43
|
signal.throwIfAborted();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
let selected: StockVideo | null = null, bestScore = 0;
|
|
45
|
+
for (const video of (Array.isArray(result.videos) ? result.videos : []).slice(0, 12)) {
|
|
46
|
+
if (!pexelsUrl(video.url)) continue;
|
|
47
|
+
const subject = words(new URL(video.url).pathname);
|
|
48
|
+
const matches = tokens.filter(token => subject.includes(token)).length;
|
|
49
|
+
// Require a majority of the literal query, never accept search rank alone.
|
|
50
|
+
if (matches < Math.ceil(tokens.length * 0.6)) continue;
|
|
51
|
+
const files = (Array.isArray(video.video_files) ? video.video_files : []).filter(file =>
|
|
52
|
+
file.file_type === "video/mp4" && pexelsUrl(file.link)
|
|
53
|
+
&& Number.isFinite(file.width) && Number.isFinite(file.height)
|
|
54
|
+
&& Math.min(file.width!, file.height!) >= 360
|
|
55
|
+
&& (orientation === "portrait" ? file.height! > file.width! : file.width! >= file.height!),
|
|
56
|
+
).sort((a, b) => Math.abs(Math.max(a.width!, a.height!) - 1280) - Math.abs(Math.max(b.width!, b.height!) - 1280));
|
|
57
|
+
const file = files[0];
|
|
58
|
+
if (!file || matches <= bestScore) continue;
|
|
59
|
+
bestScore = matches;
|
|
60
|
+
selected = { url: file.link!, type: "video", ...(pexelsUrl(video.image) ? { posterUrl: video.image } : {}) };
|
|
61
|
+
}
|
|
62
|
+
// Bounded process-local cache; no request signal or credentials are retained.
|
|
63
|
+
if (cache.size >= 128) cache.delete(cache.keys().next().value!);
|
|
64
|
+
cache.set(key, { expires: Date.now() + (selected ? 24 * 60 * 60_000 : 60_000), media: selected });
|
|
65
|
+
return selected;
|
|
38
66
|
}
|
package/styles/video-chat.css
CHANGED
|
@@ -193,59 +193,6 @@
|
|
|
193
193
|
|
|
194
194
|
.vanillasky-video-chat .stage :where(video, canvas, img) { max-width: 100%; max-height: 100%; object-fit: contain; }
|
|
195
195
|
|
|
196
|
-
.vanillasky-video-chat .ground {
|
|
197
|
-
position: absolute;
|
|
198
|
-
inset: -30%;
|
|
199
|
-
background: radial-gradient(closest-side, oklch(1 0 0 / 28%), transparent 70%);
|
|
200
|
-
animation: vanillasky-video-chat-drift 9s ease-in-out infinite alternate;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
@keyframes vanillasky-video-chat-drift {
|
|
204
|
-
from { transform: translate3d(-12%, -8%, 0) scale(1); }
|
|
205
|
-
to { transform: translate3d(14%, 10%, 0) scale(1.25); }
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.vanillasky-video-chat .asked {
|
|
209
|
-
margin: 0 auto;
|
|
210
|
-
padding: 0 1.5rem;
|
|
211
|
-
display: grid;
|
|
212
|
-
gap: 1.1rem;
|
|
213
|
-
justify-items: center;
|
|
214
|
-
align-content: center;
|
|
215
|
-
text-align: center;
|
|
216
|
-
color: var(--vs-on-media);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.vanillasky-video-chat .asked-prompt {
|
|
220
|
-
margin: 0;
|
|
221
|
-
max-width: 26ch;
|
|
222
|
-
font-size: var(--vs-text-display);
|
|
223
|
-
line-height: var(--vs-lead-display);
|
|
224
|
-
letter-spacing: var(--vs-track-display);
|
|
225
|
-
font-weight: 700;
|
|
226
|
-
text-wrap: balance;
|
|
227
|
-
animation: vanillasky-video-chat-step-in 620ms ease both;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.vanillasky-video-chat .asked-step {
|
|
231
|
-
margin: 0;
|
|
232
|
-
font-size: var(--vs-text-subhead);
|
|
233
|
-
line-height: var(--vs-lead-subhead);
|
|
234
|
-
letter-spacing: var(--vs-track-subhead);
|
|
235
|
-
font-weight: 500;
|
|
236
|
-
background: linear-gradient(100deg, oklch(1 0 0 / 45%) 20%, oklch(1 0 0) 42%, oklch(1 0 0 / 45%) 64%);
|
|
237
|
-
background-size: 220% 100%;
|
|
238
|
-
-webkit-background-clip: text;
|
|
239
|
-
background-clip: text;
|
|
240
|
-
color: transparent;
|
|
241
|
-
animation: vanillasky-video-chat-sweep 2.4s linear infinite, vanillasky-video-chat-step-in 520ms ease both;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
@keyframes vanillasky-video-chat-step-in {
|
|
245
|
-
from { opacity: 0; transform: translateY(6px); }
|
|
246
|
-
to { opacity: 1; transform: none; }
|
|
247
|
-
}
|
|
248
|
-
|
|
249
196
|
.vanillasky-video-chat .welcome { position: absolute; inset: 0; overflow: hidden; }
|
|
250
197
|
|
|
251
198
|
.vanillasky-video-chat .frame-media { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
|
|
@@ -463,7 +410,6 @@
|
|
|
463
410
|
animation: vanillasky-video-chat-fade-in 320ms ease both;
|
|
464
411
|
}
|
|
465
412
|
|
|
466
|
-
@keyframes vanillasky-video-chat-sweep { from { background-position: 140% 0; } to { background-position: -40% 0; } }
|
|
467
413
|
|
|
468
414
|
@keyframes vanillasky-video-chat-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
469
415
|
|
|
@@ -573,7 +519,6 @@
|
|
|
573
519
|
animation-iteration-count: 1 !important;
|
|
574
520
|
transition-duration: 0.01ms !important;
|
|
575
521
|
}
|
|
576
|
-
.vanillasky-video-chat .asked-step { color: var(--vs-on-media); -webkit-text-fill-color: currentColor; }
|
|
577
522
|
}
|
|
578
523
|
|
|
579
524
|
.vanillasky-video-chat {
|
|
@@ -1053,12 +998,7 @@
|
|
|
1053
998
|
@media (max-width: 700px) {
|
|
1054
999
|
.vanillasky-video-chat .sheet-popover { max-height: calc(100% - 102px); }
|
|
1055
1000
|
}
|
|
1056
|
-
|
|
1057
|
-
position: absolute;
|
|
1058
|
-
inset: 0;
|
|
1059
|
-
background: linear-gradient(180deg, rgb(8 11 22 / 12%), rgb(8 11 22 / 42%));
|
|
1060
|
-
pointer-events: none;
|
|
1061
|
-
}
|
|
1001
|
+
|
|
1062
1002
|
.vanillasky-video-chat .ending { pointer-events: none; }
|
|
1063
1003
|
.vanillasky-video-chat .ending .cards { pointer-events: auto; }
|
|
1064
1004
|
|
|
@@ -1138,3 +1078,8 @@
|
|
|
1138
1078
|
backdrop-filter: none;
|
|
1139
1079
|
-webkit-backdrop-filter: none;
|
|
1140
1080
|
}
|
|
1081
|
+
|
|
1082
|
+
.vanillasky-video-chat .opening-chapter { animation: vanillasky-chapter-enter 450ms ease-out both; }
|
|
1083
|
+
.vanillasky-video-chat .media-credit { color: var(--vs-fg); font-size: 11px; margin-left: 12px; text-underline-offset: 3px; }
|
|
1084
|
+
@keyframes vanillasky-chapter-enter { from { opacity: 0; } to { opacity: 1; } }
|
|
1085
|
+
@media (prefers-reduced-motion: reduce) { .vanillasky-video-chat .opening-chapter { animation: none; } }
|