@remotion/web-renderer 4.0.447 → 4.0.449
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/dist/audio.d.ts +3 -2
- package/dist/drawing/clip-path.d.ts +43 -0
- package/dist/drawing/handle-filter.d.ts +1 -0
- package/dist/esm/index.mjs +247 -11
- package/dist/render-media-on-web.d.ts +1 -0
- package/package.json +10 -7
package/dist/audio.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { TRenderAsset } from 'remotion';
|
|
2
|
-
export declare const onlyInlineAudio: ({ assets, fps, timestamp, }: {
|
|
2
|
+
export declare const onlyInlineAudio: ({ assets, fps, timestamp, sampleRate, }: {
|
|
3
3
|
assets: TRenderAsset[];
|
|
4
4
|
fps: number;
|
|
5
5
|
timestamp: number;
|
|
6
|
-
|
|
6
|
+
sampleRate: number;
|
|
7
|
+
}) => AudioData;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
type ClipPathNone = {
|
|
2
|
+
type: 'none';
|
|
3
|
+
};
|
|
4
|
+
type ClipPathPolygon = {
|
|
5
|
+
type: 'polygon';
|
|
6
|
+
points: {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
}[];
|
|
10
|
+
};
|
|
11
|
+
type ClipPathPath = {
|
|
12
|
+
type: 'path';
|
|
13
|
+
d: string;
|
|
14
|
+
fillRule: CanvasFillRule;
|
|
15
|
+
};
|
|
16
|
+
type ClipPathCircle = {
|
|
17
|
+
type: 'circle';
|
|
18
|
+
radius: number;
|
|
19
|
+
cx: number;
|
|
20
|
+
cy: number;
|
|
21
|
+
};
|
|
22
|
+
type ClipPathEllipse = {
|
|
23
|
+
type: 'ellipse';
|
|
24
|
+
rx: number;
|
|
25
|
+
ry: number;
|
|
26
|
+
cx: number;
|
|
27
|
+
cy: number;
|
|
28
|
+
};
|
|
29
|
+
type ClipPathInset = {
|
|
30
|
+
type: 'inset';
|
|
31
|
+
top: number;
|
|
32
|
+
right: number;
|
|
33
|
+
bottom: number;
|
|
34
|
+
left: number;
|
|
35
|
+
};
|
|
36
|
+
type ParsedClipPath = ClipPathNone | ClipPathPolygon | ClipPathPath | ClipPathCircle | ClipPathEllipse | ClipPathInset;
|
|
37
|
+
export declare function parseClipPath(clipPath: string, rect: DOMRect): ParsedClipPath;
|
|
38
|
+
export declare function setClipPath({ ctx, clipPath, rect }: {
|
|
39
|
+
ctx: OffscreenCanvasRenderingContext2D;
|
|
40
|
+
clipPath: string;
|
|
41
|
+
rect: DOMRect;
|
|
42
|
+
}): () => void;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getPrecomposeRectForFilter: (element: HTMLElement | SVGElement) => DOMRect;
|
package/dist/esm/index.mjs
CHANGED
|
@@ -670,8 +670,10 @@ var handleArtifacts = () => {
|
|
|
670
670
|
|
|
671
671
|
// src/audio.ts
|
|
672
672
|
var TARGET_NUMBER_OF_CHANNELS = 2;
|
|
673
|
-
var TARGET_SAMPLE_RATE = 48000;
|
|
674
673
|
function mixAudio(waves, length) {
|
|
674
|
+
if (waves.length === 0) {
|
|
675
|
+
return new Int16Array(length);
|
|
676
|
+
}
|
|
675
677
|
if (waves.length === 1 && waves[0].length === length) {
|
|
676
678
|
return waves[0];
|
|
677
679
|
}
|
|
@@ -691,13 +693,11 @@ function mixAudio(waves, length) {
|
|
|
691
693
|
var onlyInlineAudio = ({
|
|
692
694
|
assets,
|
|
693
695
|
fps,
|
|
694
|
-
timestamp
|
|
696
|
+
timestamp,
|
|
697
|
+
sampleRate
|
|
695
698
|
}) => {
|
|
696
699
|
const inlineAudio = assets.filter((asset) => asset.type === "inline-audio");
|
|
697
|
-
|
|
698
|
-
return null;
|
|
699
|
-
}
|
|
700
|
-
const expectedLength = Math.round(TARGET_NUMBER_OF_CHANNELS * TARGET_SAMPLE_RATE / fps);
|
|
700
|
+
const expectedLength = Math.round(TARGET_NUMBER_OF_CHANNELS * sampleRate / fps);
|
|
701
701
|
for (const asset of inlineAudio) {
|
|
702
702
|
if (asset.toneFrequency !== 1) {
|
|
703
703
|
throw new Error("Setting the toneFrequency is not supported yet in web rendering.");
|
|
@@ -709,7 +709,7 @@ var onlyInlineAudio = ({
|
|
|
709
709
|
format: "s16",
|
|
710
710
|
numberOfChannels: TARGET_NUMBER_OF_CHANNELS,
|
|
711
711
|
numberOfFrames: expectedLength / TARGET_NUMBER_OF_CHANNELS,
|
|
712
|
-
sampleRate
|
|
712
|
+
sampleRate,
|
|
713
713
|
timestamp
|
|
714
714
|
});
|
|
715
715
|
};
|
|
@@ -1107,7 +1107,8 @@ function createScaffold({
|
|
|
1107
1107
|
defaultOutName: defaultOutName ?? null,
|
|
1108
1108
|
defaultVideoImageFormat: null,
|
|
1109
1109
|
defaultPixelFormat: null,
|
|
1110
|
-
defaultProResProfile: null
|
|
1110
|
+
defaultProResProfile: null,
|
|
1111
|
+
defaultSampleRate: null
|
|
1111
1112
|
},
|
|
1112
1113
|
folders: []
|
|
1113
1114
|
},
|
|
@@ -2392,6 +2393,233 @@ function setBorderRadius({
|
|
|
2392
2393
|
};
|
|
2393
2394
|
}
|
|
2394
2395
|
|
|
2396
|
+
// src/drawing/clip-path.ts
|
|
2397
|
+
function resolveLength(value, reference) {
|
|
2398
|
+
value = value.trim();
|
|
2399
|
+
if (value.endsWith("%")) {
|
|
2400
|
+
return parseFloat(value) / 100 * reference;
|
|
2401
|
+
}
|
|
2402
|
+
if (value.endsWith("px")) {
|
|
2403
|
+
return parseFloat(value);
|
|
2404
|
+
}
|
|
2405
|
+
return parseFloat(value);
|
|
2406
|
+
}
|
|
2407
|
+
function parsePosition(parts, width, height) {
|
|
2408
|
+
if (parts.length === 0) {
|
|
2409
|
+
return { x: width / 2, y: height / 2 };
|
|
2410
|
+
}
|
|
2411
|
+
if (parts.length === 1) {
|
|
2412
|
+
return { x: resolveLength(parts[0], width), y: height / 2 };
|
|
2413
|
+
}
|
|
2414
|
+
return {
|
|
2415
|
+
x: resolveLength(parts[0], width),
|
|
2416
|
+
y: resolveLength(parts[1], height)
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
function parsePolygon(args, rect) {
|
|
2420
|
+
const pointStrings = args.split(",");
|
|
2421
|
+
const points = pointStrings.map((pointStr) => {
|
|
2422
|
+
const coords = pointStr.trim().split(/\s+/);
|
|
2423
|
+
return {
|
|
2424
|
+
x: resolveLength(coords[0], rect.width) + rect.left,
|
|
2425
|
+
y: resolveLength(coords[1], rect.height) + rect.top
|
|
2426
|
+
};
|
|
2427
|
+
});
|
|
2428
|
+
return { type: "polygon", points };
|
|
2429
|
+
}
|
|
2430
|
+
function parsePath(args) {
|
|
2431
|
+
const match = args.match(/^(?:(nonzero|evenodd)\s*,\s*)?["'](.+)["']$/);
|
|
2432
|
+
if (!match) {
|
|
2433
|
+
return {
|
|
2434
|
+
type: "path",
|
|
2435
|
+
d: args.replace(/["']/g, ""),
|
|
2436
|
+
fillRule: "nonzero"
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
const fillRule = match[1] === "evenodd" ? "evenodd" : "nonzero";
|
|
2440
|
+
return { type: "path", d: match[2], fillRule };
|
|
2441
|
+
}
|
|
2442
|
+
function parseCircle(args, rect) {
|
|
2443
|
+
const atIndex = args.indexOf(" at ");
|
|
2444
|
+
let radiusStr;
|
|
2445
|
+
let positionParts;
|
|
2446
|
+
if (atIndex !== -1) {
|
|
2447
|
+
radiusStr = args.slice(0, atIndex).trim();
|
|
2448
|
+
positionParts = args.slice(atIndex + 4).trim().split(/\s+/);
|
|
2449
|
+
} else {
|
|
2450
|
+
radiusStr = args.trim();
|
|
2451
|
+
positionParts = [];
|
|
2452
|
+
}
|
|
2453
|
+
const closestSide = Math.min(rect.width, rect.height) / 2;
|
|
2454
|
+
const farthestSide = Math.max(rect.width, rect.height) / 2;
|
|
2455
|
+
let radius;
|
|
2456
|
+
if (radiusStr === "closest-side" || radiusStr === "") {
|
|
2457
|
+
radius = closestSide;
|
|
2458
|
+
} else if (radiusStr === "farthest-side") {
|
|
2459
|
+
radius = farthestSide;
|
|
2460
|
+
} else {
|
|
2461
|
+
const refSize = Math.sqrt(rect.width * rect.width + rect.height * rect.height) / Math.SQRT2;
|
|
2462
|
+
radius = resolveLength(radiusStr, refSize);
|
|
2463
|
+
}
|
|
2464
|
+
const position = parsePosition(positionParts, rect.width, rect.height);
|
|
2465
|
+
return {
|
|
2466
|
+
type: "circle",
|
|
2467
|
+
radius,
|
|
2468
|
+
cx: position.x + rect.left,
|
|
2469
|
+
cy: position.y + rect.top
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
function parseEllipse(args, rect) {
|
|
2473
|
+
const atIndex = args.indexOf(" at ");
|
|
2474
|
+
let radiiStr;
|
|
2475
|
+
let positionParts;
|
|
2476
|
+
if (atIndex !== -1) {
|
|
2477
|
+
radiiStr = args.slice(0, atIndex).trim();
|
|
2478
|
+
positionParts = args.slice(atIndex + 4).trim().split(/\s+/);
|
|
2479
|
+
} else {
|
|
2480
|
+
radiiStr = args.trim();
|
|
2481
|
+
positionParts = [];
|
|
2482
|
+
}
|
|
2483
|
+
const radiiParts = radiiStr.split(/\s+/);
|
|
2484
|
+
let rx;
|
|
2485
|
+
let ry;
|
|
2486
|
+
if (radiiParts.length >= 2) {
|
|
2487
|
+
rx = resolveLength(radiiParts[0], rect.width);
|
|
2488
|
+
ry = resolveLength(radiiParts[1], rect.height);
|
|
2489
|
+
} else {
|
|
2490
|
+
rx = rect.width / 2;
|
|
2491
|
+
ry = rect.height / 2;
|
|
2492
|
+
}
|
|
2493
|
+
const position = parsePosition(positionParts, rect.width, rect.height);
|
|
2494
|
+
return {
|
|
2495
|
+
type: "ellipse",
|
|
2496
|
+
rx,
|
|
2497
|
+
ry,
|
|
2498
|
+
cx: position.x + rect.left,
|
|
2499
|
+
cy: position.y + rect.top
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
function parseInset(args, rect) {
|
|
2503
|
+
const [insetPart] = args.split(/\s+round\s+/);
|
|
2504
|
+
const parts = insetPart.split(/\s+/);
|
|
2505
|
+
let top;
|
|
2506
|
+
let right;
|
|
2507
|
+
let bottom;
|
|
2508
|
+
let left;
|
|
2509
|
+
if (parts.length === 1) {
|
|
2510
|
+
const val = resolveLength(parts[0], rect.height);
|
|
2511
|
+
top = val;
|
|
2512
|
+
right = val;
|
|
2513
|
+
bottom = val;
|
|
2514
|
+
left = val;
|
|
2515
|
+
} else if (parts.length === 2) {
|
|
2516
|
+
top = resolveLength(parts[0], rect.height);
|
|
2517
|
+
bottom = resolveLength(parts[0], rect.height);
|
|
2518
|
+
right = resolveLength(parts[1], rect.width);
|
|
2519
|
+
left = resolveLength(parts[1], rect.width);
|
|
2520
|
+
} else if (parts.length === 3) {
|
|
2521
|
+
top = resolveLength(parts[0], rect.height);
|
|
2522
|
+
right = resolveLength(parts[1], rect.width);
|
|
2523
|
+
left = resolveLength(parts[1], rect.width);
|
|
2524
|
+
bottom = resolveLength(parts[2], rect.height);
|
|
2525
|
+
} else {
|
|
2526
|
+
top = resolveLength(parts[0], rect.height);
|
|
2527
|
+
right = resolveLength(parts[1], rect.width);
|
|
2528
|
+
bottom = resolveLength(parts[2], rect.height);
|
|
2529
|
+
left = resolveLength(parts[3], rect.width);
|
|
2530
|
+
}
|
|
2531
|
+
return { type: "inset", top, right, bottom, left };
|
|
2532
|
+
}
|
|
2533
|
+
function parseClipPath(clipPath, rect) {
|
|
2534
|
+
if (clipPath === "none" || clipPath === "") {
|
|
2535
|
+
return { type: "none" };
|
|
2536
|
+
}
|
|
2537
|
+
const polygonMatch = clipPath.match(/^polygon\((.+)\)$/);
|
|
2538
|
+
if (polygonMatch) {
|
|
2539
|
+
return parsePolygon(polygonMatch[1], rect);
|
|
2540
|
+
}
|
|
2541
|
+
const pathMatch = clipPath.match(/^path\((.+)\)$/);
|
|
2542
|
+
if (pathMatch) {
|
|
2543
|
+
return parsePath(pathMatch[1]);
|
|
2544
|
+
}
|
|
2545
|
+
const circleMatch = clipPath.match(/^circle\((.+)\)$/);
|
|
2546
|
+
if (circleMatch) {
|
|
2547
|
+
return parseCircle(circleMatch[1], rect);
|
|
2548
|
+
}
|
|
2549
|
+
const ellipseMatch = clipPath.match(/^ellipse\((.+)\)$/);
|
|
2550
|
+
if (ellipseMatch) {
|
|
2551
|
+
return parseEllipse(ellipseMatch[1], rect);
|
|
2552
|
+
}
|
|
2553
|
+
const insetMatch = clipPath.match(/^inset\((.+)\)$/);
|
|
2554
|
+
if (insetMatch) {
|
|
2555
|
+
return parseInset(insetMatch[1], rect);
|
|
2556
|
+
}
|
|
2557
|
+
return { type: "none" };
|
|
2558
|
+
}
|
|
2559
|
+
function setClipPath({
|
|
2560
|
+
ctx,
|
|
2561
|
+
clipPath,
|
|
2562
|
+
rect
|
|
2563
|
+
}) {
|
|
2564
|
+
const parsed = parseClipPath(clipPath, rect);
|
|
2565
|
+
if (parsed.type === "none") {
|
|
2566
|
+
return () => {};
|
|
2567
|
+
}
|
|
2568
|
+
ctx.save();
|
|
2569
|
+
switch (parsed.type) {
|
|
2570
|
+
case "polygon": {
|
|
2571
|
+
ctx.beginPath();
|
|
2572
|
+
for (let i = 0;i < parsed.points.length; i++) {
|
|
2573
|
+
const point = parsed.points[i];
|
|
2574
|
+
if (i === 0) {
|
|
2575
|
+
ctx.moveTo(point.x, point.y);
|
|
2576
|
+
} else {
|
|
2577
|
+
ctx.lineTo(point.x, point.y);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
ctx.closePath();
|
|
2581
|
+
ctx.clip();
|
|
2582
|
+
break;
|
|
2583
|
+
}
|
|
2584
|
+
case "path": {
|
|
2585
|
+
const path2d = new Path2D;
|
|
2586
|
+
const offsetMatrix = new DOMMatrix().translate(rect.left, rect.top);
|
|
2587
|
+
path2d.addPath(new Path2D(parsed.d), offsetMatrix);
|
|
2588
|
+
ctx.clip(path2d, parsed.fillRule);
|
|
2589
|
+
break;
|
|
2590
|
+
}
|
|
2591
|
+
case "circle": {
|
|
2592
|
+
ctx.beginPath();
|
|
2593
|
+
ctx.arc(parsed.cx, parsed.cy, parsed.radius, 0, Math.PI * 2);
|
|
2594
|
+
ctx.closePath();
|
|
2595
|
+
ctx.clip();
|
|
2596
|
+
break;
|
|
2597
|
+
}
|
|
2598
|
+
case "ellipse": {
|
|
2599
|
+
ctx.beginPath();
|
|
2600
|
+
ctx.ellipse(parsed.cx, parsed.cy, parsed.rx, parsed.ry, 0, 0, Math.PI * 2);
|
|
2601
|
+
ctx.closePath();
|
|
2602
|
+
ctx.clip();
|
|
2603
|
+
break;
|
|
2604
|
+
}
|
|
2605
|
+
case "inset": {
|
|
2606
|
+
const x = rect.left + parsed.left;
|
|
2607
|
+
const y = rect.top + parsed.top;
|
|
2608
|
+
const w = rect.width - parsed.left - parsed.right;
|
|
2609
|
+
const h = rect.height - parsed.top - parsed.bottom;
|
|
2610
|
+
ctx.beginPath();
|
|
2611
|
+
ctx.rect(x, y, w, h);
|
|
2612
|
+
ctx.clip();
|
|
2613
|
+
break;
|
|
2614
|
+
}
|
|
2615
|
+
default:
|
|
2616
|
+
break;
|
|
2617
|
+
}
|
|
2618
|
+
return () => {
|
|
2619
|
+
ctx.restore();
|
|
2620
|
+
};
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2395
2623
|
// src/drawing/get-background-fill.ts
|
|
2396
2624
|
var isColorTransparent = (color) => {
|
|
2397
2625
|
return color === "transparent" || color.startsWith("rgba") && (color.endsWith(", 0)") || color.endsWith(",0"));
|
|
@@ -3108,6 +3336,11 @@ var drawElement = async ({
|
|
|
3108
3336
|
parentRect,
|
|
3109
3337
|
scale
|
|
3110
3338
|
});
|
|
3339
|
+
const finishClipPath = setClipPath({
|
|
3340
|
+
ctx: context,
|
|
3341
|
+
clipPath: computedStyle.clipPath,
|
|
3342
|
+
rect
|
|
3343
|
+
});
|
|
3111
3344
|
const finishOpacity = setOpacity({
|
|
3112
3345
|
ctx: context,
|
|
3113
3346
|
opacity
|
|
@@ -3172,6 +3405,7 @@ var drawElement = async ({
|
|
|
3172
3405
|
cleanupAfterChildren: () => {
|
|
3173
3406
|
finishFilter();
|
|
3174
3407
|
finishOpacity();
|
|
3408
|
+
finishClipPath();
|
|
3175
3409
|
finishOverflowHidden();
|
|
3176
3410
|
}
|
|
3177
3411
|
};
|
|
@@ -4274,7 +4508,8 @@ var internalRenderMediaOnWeb = async ({
|
|
|
4274
4508
|
muted,
|
|
4275
4509
|
scale,
|
|
4276
4510
|
isProduction,
|
|
4277
|
-
allowHtmlInCanvas
|
|
4511
|
+
allowHtmlInCanvas,
|
|
4512
|
+
sampleRate
|
|
4278
4513
|
}) => {
|
|
4279
4514
|
let __stack2 = [];
|
|
4280
4515
|
try {
|
|
@@ -4553,7 +4788,7 @@ var internalRenderMediaOnWeb = async ({
|
|
|
4553
4788
|
if (signal?.aborted) {
|
|
4554
4789
|
throw new Error("renderMediaOnWeb() was cancelled");
|
|
4555
4790
|
}
|
|
4556
|
-
const audio = muted ? null : onlyInlineAudio({ assets, fps: resolved.fps, timestamp });
|
|
4791
|
+
const audio = muted ? null : onlyInlineAudio({ assets, fps: resolved.fps, timestamp, sampleRate });
|
|
4557
4792
|
internalState.addAudioMixingTime(performance.now() - audioCombineStart);
|
|
4558
4793
|
const addSampleStart = performance.now();
|
|
4559
4794
|
const encodingPromises = [];
|
|
@@ -4667,7 +4902,8 @@ var renderMediaOnWeb = (options) => {
|
|
|
4667
4902
|
muted: options.muted ?? false,
|
|
4668
4903
|
scale: options.scale ?? 1,
|
|
4669
4904
|
isProduction: options.isProduction ?? true,
|
|
4670
|
-
allowHtmlInCanvas: options.allowHtmlInCanvas ?? false
|
|
4905
|
+
allowHtmlInCanvas: options.allowHtmlInCanvas ?? false,
|
|
4906
|
+
sampleRate: options.sampleRate ?? 48000
|
|
4671
4907
|
}));
|
|
4672
4908
|
return onlyOneRenderAtATimeQueue.ref;
|
|
4673
4909
|
};
|
|
@@ -66,6 +66,7 @@ type OptionalRenderMediaOnWebOptions<Schema extends $ZodObject> = {
|
|
|
66
66
|
muted: boolean;
|
|
67
67
|
scale: number;
|
|
68
68
|
allowHtmlInCanvas: boolean;
|
|
69
|
+
sampleRate: number;
|
|
69
70
|
};
|
|
70
71
|
export type RenderMediaOnWebOptions<Schema extends $ZodObject, Props extends Record<string, unknown>> = MandatoryRenderMediaOnWebOptions<Schema, Props> & Partial<OptionalRenderMediaOnWebOptions<Schema>> & InputPropsIfHasProps<Schema, Props>;
|
|
71
72
|
export declare const renderMediaOnWeb: <Schema extends $ZodObject<Readonly<Readonly<{
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/web-renderer"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/web-renderer",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.449",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"scripts": {
|
|
@@ -22,16 +22,19 @@
|
|
|
22
22
|
"@mediabunny/mp3-encoder": "1.39.2",
|
|
23
23
|
"@mediabunny/aac-encoder": "1.39.2",
|
|
24
24
|
"@mediabunny/flac-encoder": "1.39.2",
|
|
25
|
-
"@remotion/licensing": "4.0.
|
|
26
|
-
"remotion": "4.0.
|
|
25
|
+
"@remotion/licensing": "4.0.448",
|
|
26
|
+
"remotion": "4.0.448",
|
|
27
27
|
"mediabunny": "1.39.2"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@react-three/fiber": "9.2.0",
|
|
31
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
32
|
-
"@remotion/
|
|
33
|
-
"@remotion/
|
|
34
|
-
"@remotion/
|
|
31
|
+
"@remotion/eslint-config-internal": "4.0.448",
|
|
32
|
+
"@remotion/paths": "4.0.448",
|
|
33
|
+
"@remotion/player": "4.0.448",
|
|
34
|
+
"@remotion/media": "4.0.448",
|
|
35
|
+
"@remotion/shapes": "4.0.448",
|
|
36
|
+
"@remotion/three": "4.0.448",
|
|
37
|
+
"@remotion/transitions": "4.0.448",
|
|
35
38
|
"@types/three": "0.170.0",
|
|
36
39
|
"@typescript/native-preview": "7.0.0-dev.20260217.1",
|
|
37
40
|
"@vitejs/plugin-react": "4.3.4",
|