@revideo/create 0.5.10-alpha.1094 → 0.5.10

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 (62) hide show
  1. package/examples/avatar-with-background/package.json +8 -8
  2. package/examples/avatar-with-background/src/scenes/example.tsx +1 -0
  3. package/examples/default/package.json +8 -8
  4. package/examples/github-stars-celebration/package.json +7 -7
  5. package/examples/github-stars-celebration/src/scenes/example.tsx +2 -2
  6. package/examples/google-cloud-run-parallelized/render-orchestrator/package.json +21 -21
  7. package/examples/google-cloud-run-parallelized/render-worker/package.json +8 -8
  8. package/examples/marketing-templates/marketing-template/package.json +7 -7
  9. package/examples/marketing-templates/multiple-videos-in-one/package.json +7 -7
  10. package/examples/minimal-drag-drop/README.md +27 -0
  11. package/examples/minimal-drag-drop/next/app/api/render/route.ts +22 -0
  12. package/examples/minimal-drag-drop/next/app/globals.css +3 -0
  13. package/examples/minimal-drag-drop/next/app/layout.tsx +25 -0
  14. package/examples/minimal-drag-drop/next/app/page.tsx +277 -0
  15. package/examples/minimal-drag-drop/next/next-env.d.ts +5 -0
  16. package/examples/minimal-drag-drop/next/next.config.mjs +4 -0
  17. package/examples/minimal-drag-drop/next/package-lock.json +5577 -0
  18. package/examples/minimal-drag-drop/next/package.json +35 -0
  19. package/examples/minimal-drag-drop/next/postcss.config.js +6 -0
  20. package/examples/minimal-drag-drop/next/public/favicon.ico +0 -0
  21. package/examples/minimal-drag-drop/next/public/next.svg +1 -0
  22. package/examples/minimal-drag-drop/next/public/vercel.svg +1 -0
  23. package/examples/minimal-drag-drop/next/tailwind.config.ts +30 -0
  24. package/examples/minimal-drag-drop/next/tsconfig.json +26 -0
  25. package/examples/minimal-drag-drop/next/utils/parse.ts +35 -0
  26. package/examples/minimal-drag-drop/revideo/package-lock.json +3771 -0
  27. package/examples/minimal-drag-drop/revideo/package.json +24 -0
  28. package/examples/minimal-drag-drop/revideo/src/global.css +1 -0
  29. package/examples/minimal-drag-drop/revideo/src/project.meta +28 -0
  30. package/examples/minimal-drag-drop/revideo/src/project.ts +9 -0
  31. package/examples/minimal-drag-drop/revideo/src/render.ts +15 -0
  32. package/examples/minimal-drag-drop/revideo/src/revideo.d.ts +1 -0
  33. package/examples/minimal-drag-drop/revideo/src/scenes/example.meta +5 -0
  34. package/examples/minimal-drag-drop/revideo/src/scenes/example.tsx +44 -0
  35. package/examples/minimal-drag-drop/revideo/tsconfig.json +9 -0
  36. package/examples/minimal-drag-drop/revideo/vite.config.ts +6 -0
  37. package/examples/parallelized-aws-lambda/revideo-project/package.json +8 -11
  38. package/examples/parallelized-aws-lambda/revideo-project/src/lambda.ts +15 -2
  39. package/examples/parallelized-aws-lambda/revideo-project/src/render.ts +2 -1
  40. package/examples/parallelized-aws-lambda/revideo-project/src/scenes/example.tsx +1 -0
  41. package/examples/reddit-post-video/package.json +7 -7
  42. package/examples/reddit-post-video/src/project.meta +2 -2
  43. package/examples/rive-explanation-video/package.json +8 -8
  44. package/examples/saas-template/next/package.json +33 -33
  45. package/examples/saas-template/revideo/package.json +23 -23
  46. package/examples/stitching-videos/package.json +6 -6
  47. package/examples/stitching-videos/src/render.ts +2 -1
  48. package/examples/three-js-example/package-lock.json +41 -41
  49. package/examples/three-js-example/package.json +7 -7
  50. package/examples/youtube-shorts/README.md +7 -7
  51. package/examples/youtube-shorts/package-lock.json +332 -365
  52. package/examples/youtube-shorts/package.json +8 -8
  53. package/examples/youtube-shorts/src/get-assets.ts +34 -0
  54. package/examples/youtube-shorts/src/render.ts +4 -35
  55. package/examples/youtube-shorts/src/scenes/example.tsx +25 -12
  56. package/index.js +8 -1
  57. package/package.json +9 -9
  58. package/examples/avatar-with-background/package-lock.json +0 -3223
  59. package/examples/marketing-templates/marketing-template/package-lock.json +0 -3824
  60. package/examples/marketing-templates/multiple-videos-in-one/package-lock.json +0 -3824
  61. package/examples/rive-explanation-video/package-lock.json +0 -3706
  62. package/examples/stitching-videos/package-lock.json +0 -2801
@@ -10,18 +10,18 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@deepgram/sdk": "^3.3.0",
13
- "@revideo/2d": "0.4.5",
14
- "@revideo/cli": "^0.4.6",
15
- "@revideo/core": "0.4.5",
16
- "@revideo/ffmpeg": "0.4.5",
17
- "@revideo/renderer": "0.4.5",
18
- "@revideo/vite-plugin": "0.4.5",
13
+ "@revideo/2d": "0.5.10",
14
+ "@revideo/cli": "0.5.10",
15
+ "@revideo/core": "0.5.10",
16
+ "@revideo/ffmpeg": "0.5.10",
17
+ "@revideo/renderer": "0.5.10",
18
+ "@revideo/vite-plugin": "0.5.10",
19
19
  "dotenv": "^16.4.5",
20
20
  "openai": "^4.39.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@revideo/ui": "0.4.5",
23
+ "@revideo/ui": "0.5.10",
24
24
  "typescript": "5.5.4",
25
25
  "vite": "^4.5"
26
26
  }
27
- }
27
+ }
@@ -17,6 +17,7 @@ export default makeScene2D(function* (view) {
17
17
  play={true}
18
18
  width={'100%'}
19
19
  ref={avatarRef}
20
+ decoder={"slow"} // we need to use the slow decoder to support transparency
20
21
  />
21
22
  </>,
22
23
  );
@@ -9,16 +9,16 @@
9
9
  "render": "tsc && node dist/render.js"
10
10
  },
11
11
  "dependencies": {
12
- "@revideo/core": "^0.5.9",
13
- "@revideo/2d": "^0.5.9",
14
- "@revideo/renderer": "^0.5.9",
15
- "@revideo/vite-plugin": "^0.5.9",
16
- "@revideo/ffmpeg": "^0.5.9"
12
+ "@revideo/core": "0.5.10",
13
+ "@revideo/2d": "0.5.10",
14
+ "@revideo/renderer": "0.5.10",
15
+ "@revideo/vite-plugin": "0.5.10",
16
+ "@revideo/ffmpeg": "0.5.10"
17
17
  },
18
18
  "devDependencies": {
19
- "@revideo/ui": "^0.5.9",
20
- "@revideo/cli": "^0.5.9",
19
+ "@revideo/ui": "0.5.10",
20
+ "@revideo/cli": "0.5.10",
21
21
  "typescript": "5.5.4",
22
22
  "vite": "^4.5"
23
23
  }
24
- }
24
+ }
@@ -9,15 +9,15 @@
9
9
  "render": "tsc && node dist/render.js"
10
10
  },
11
11
  "dependencies": {
12
- "@revideo/core": "^0.4.5",
13
- "@revideo/2d": "^0.4.5",
14
- "@revideo/renderer": "^0.4.5",
15
- "@revideo/vite-plugin": "^0.4.5",
16
- "@revideo/ffmpeg": "^0.4.5"
12
+ "@revideo/core": "0.5.10",
13
+ "@revideo/2d": "0.5.10",
14
+ "@revideo/renderer": "0.5.10",
15
+ "@revideo/vite-plugin": "0.5.10",
16
+ "@revideo/ffmpeg": "0.5.10"
17
17
  },
18
18
  "devDependencies": {
19
- "@revideo/ui": "^0.4.5",
20
- "@revideo/cli": "^0.4.5",
19
+ "@revideo/ui": "0.5.10",
20
+ "@revideo/cli": "0.5.10",
21
21
  "typescript": "5.5.4",
22
22
  "vite": "^4.5"
23
23
  }
@@ -29,13 +29,13 @@ export default makeScene2D(function* (view) {
29
29
 
30
30
  yield* rocketRef().position([1000, -600], 2, easeInCubic);
31
31
 
32
- view.add(
32
+ yield view.add(
33
33
  <Txt fontFamily={"Lexend"} fill="white" ref={txtRef} textAlign={"center"} fontSize={60}></Txt>
34
34
  )
35
35
 
36
36
  yield* txtRef().text("Thank you for 1,000 Github stars!", 2);
37
37
 
38
- view.add(
38
+ yield view.add(
39
39
  <Img
40
40
  width={'1%'}
41
41
  ref={logoRef}
@@ -1,22 +1,22 @@
1
1
  {
2
- "name": "google-cloud-run",
3
- "private": true,
4
- "version": "0.0.0",
5
- "scripts": {
6
- "start": "tsc && node dist/index.js"
7
- },
8
- "dependencies": {
9
- "@google-cloud/storage": "^7.11.0",
10
- "@revideo/2d": "0.4.6",
11
- "@revideo/core": "0.4.6",
12
- "@revideo/ffmpeg": "0.4.6",
13
- "express": "^4.19.2",
14
- "uuid": "^9.0.1"
15
- },
16
- "devDependencies": {
17
- "@types/express": "^4.17.21",
18
- "@types/uuid": "^9.0.8",
19
- "typescript": "^5.4.5",
20
- "vite": "^4.5"
21
- }
22
- }
2
+ "name": "google-cloud-run",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "scripts": {
6
+ "start": "tsc && node dist/index.js"
7
+ },
8
+ "dependencies": {
9
+ "@google-cloud/storage": "^7.11.0",
10
+ "@revideo/2d": "0.5.10",
11
+ "@revideo/core": "0.5.10",
12
+ "@revideo/ffmpeg": "0.5.10",
13
+ "express": "^4.19.2",
14
+ "uuid": "^9.0.1"
15
+ },
16
+ "devDependencies": {
17
+ "@types/express": "^4.17.21",
18
+ "@types/uuid": "^9.0.8",
19
+ "typescript": "^5.4.5",
20
+ "vite": "^4.5"
21
+ }
22
+ }
@@ -12,13 +12,13 @@
12
12
  "@ffprobe-installer/ffprobe": "^2.1.2",
13
13
  "@google-cloud/functions-framework": "^3.4.0",
14
14
  "@google-cloud/storage": "^7.11.0",
15
- "@revideo/2d": "0.4.6",
16
- "@revideo/cli": "^0.4.6",
17
- "@revideo/core": "0.4.6",
18
- "@revideo/ffmpeg": "0.4.6",
19
- "@revideo/renderer": "0.4.6",
20
- "@revideo/ui": "0.4.6",
21
- "@revideo/vite-plugin": "0.4.6",
15
+ "@revideo/2d": "0.5.10",
16
+ "@revideo/cli": "0.5.10",
17
+ "@revideo/core": "0.5.10",
18
+ "@revideo/ffmpeg": "0.5.10",
19
+ "@revideo/renderer": "0.5.10",
20
+ "@revideo/ui": "0.5.10",
21
+ "@revideo/vite-plugin": "0.5.10",
22
22
  "@types/express": "^4.17.21",
23
23
  "@types/uuid": "^9.0.8",
24
24
  "express": "^4.19.2",
@@ -27,4 +27,4 @@
27
27
  "uuid": "^9.0.1",
28
28
  "vite": "^4.5"
29
29
  }
30
- }
30
+ }
@@ -9,15 +9,15 @@
9
9
  "render": "tsc && node dist/render.js"
10
10
  },
11
11
  "dependencies": {
12
- "@revideo/core": "0.4.5",
13
- "@revideo/2d": "0.4.5",
14
- "@revideo/renderer": "0.4.5",
15
- "@revideo/vite-plugin": "0.4.5",
16
- "@revideo/ffmpeg": "0.4.5"
12
+ "@revideo/core": "0.5.10",
13
+ "@revideo/2d": "0.5.10",
14
+ "@revideo/renderer": "0.5.10",
15
+ "@revideo/vite-plugin": "0.5.10",
16
+ "@revideo/ffmpeg": "0.5.10"
17
17
  },
18
18
  "devDependencies": {
19
- "@revideo/ui": "0.4.5",
20
- "@revideo/cli": "^0.4.5",
19
+ "@revideo/ui": "0.5.10",
20
+ "@revideo/cli": "0.5.10",
21
21
  "typescript": "5.5.4",
22
22
  "vite": "^4.5"
23
23
  }
@@ -9,15 +9,15 @@
9
9
  "render": "tsc && node dist/render.js"
10
10
  },
11
11
  "dependencies": {
12
- "@revideo/core": "0.4.5",
13
- "@revideo/2d": "0.4.5",
14
- "@revideo/renderer": "0.4.5",
15
- "@revideo/vite-plugin": "0.4.5",
16
- "@revideo/ffmpeg": "0.4.5"
12
+ "@revideo/core": "0.5.10",
13
+ "@revideo/2d": "0.5.10",
14
+ "@revideo/renderer": "0.5.10",
15
+ "@revideo/vite-plugin": "0.5.10",
16
+ "@revideo/ffmpeg": "0.5.10"
17
17
  },
18
18
  "devDependencies": {
19
- "@revideo/ui": "0.4.5",
20
- "@revideo/cli": "^0.4.5",
19
+ "@revideo/ui": "0.5.10",
20
+ "@revideo/cli": "0.5.10",
21
21
  "typescript": "5.5.4",
22
22
  "vite": "^4.5"
23
23
  }
@@ -0,0 +1,27 @@
1
+ # Minimal Drag-and-Drop Example
2
+
3
+ This is an adapted version of the [Revideo](https://github.com/redotvideo/revideo) [NextJS SaaS template](https://github.com/redotvideo/examples/saas-template), demonstrating how you can build drag-and-drop editing functionality with Revideo.
4
+
5
+ ## Getting Started
6
+
7
+ First, install the dependencies both in the next/ and the revideo/ directories:
8
+
9
+ ```bash
10
+ (cd next && npm install) &&
11
+ (cd revideo && npm install)
12
+ ```
13
+
14
+ Then, serve the Revideo project using the CLI:
15
+
16
+ ```bash
17
+ (cd revideo && npx revideo serve)
18
+ ```
19
+
20
+ Now you can start your NextJS app using the following command:
21
+
22
+ ```bash
23
+ (cd next && npm run dev)
24
+ ```
25
+
26
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
27
+ result. Happy developing!
@@ -0,0 +1,22 @@
1
+ const RENDER_URL = 'http://localhost:4000/render';
2
+
3
+ async function getResponse(body: string) {
4
+ return await fetch(RENDER_URL, {
5
+ method: 'POST',
6
+ headers: {
7
+ 'Content-Type': 'application/json',
8
+ },
9
+ body,
10
+ });
11
+ }
12
+
13
+ export async function POST(request: Request) {
14
+ const body = await request.json();
15
+
16
+ const response = await getResponse(JSON.stringify(body));
17
+ if (!response.ok) {
18
+ return new Response('Failed to render', {status: 500});
19
+ }
20
+
21
+ return new Response(response.body, {status: 200});
22
+ }
@@ -0,0 +1,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
@@ -0,0 +1,25 @@
1
+ import type {Metadata} from 'next';
2
+ import {Inter} from 'next/font/google';
3
+ import './globals.css';
4
+
5
+ const inter = Inter({subsets: ['latin']});
6
+
7
+ export const metadata: Metadata = {
8
+ title: 'Create Next App',
9
+ description: 'Generated by create next app',
10
+ };
11
+
12
+ export default function RootLayout({
13
+ children,
14
+ }: Readonly<{
15
+ children: React.ReactNode;
16
+ }>) {
17
+ return (
18
+ <html lang="en">
19
+ <head>
20
+ <link rel="stylesheet" href="http://localhost:4000/player/project.css" />
21
+ </head>
22
+ <body className={inter.className}>{children}</body>
23
+ </html>
24
+ );
25
+ }
@@ -0,0 +1,277 @@
1
+ 'use client';
2
+
3
+ import {Player} from '@revideo/player-react';
4
+ import {useState, useEffect} from 'react';
5
+ import {LoaderCircle} from 'lucide-react';
6
+ import { parseStream } from '../utils/parse';
7
+ import {Player as CorePlayer} from '@revideo/core';
8
+ import {Scene2D, Shape} from '@revideo/2d';
9
+
10
+
11
+ function Button({
12
+ children,
13
+ loading,
14
+ onClick,
15
+ }: {
16
+ children: React.ReactNode;
17
+ loading: boolean;
18
+ onClick: () => void;
19
+ }) {
20
+ return (
21
+ <button
22
+ className="text-sm flex items-center gap-x-2 rounded-md p-2 bg-gray-200 text-gray-700 hover:bg-gray-300"
23
+ onClick={() => onClick()}
24
+ disabled={loading}
25
+ >
26
+ {loading && <LoaderCircle className="animate-spin h-4 w-4 text-gray-700" />}
27
+ {children}
28
+ </button>
29
+ );
30
+ }
31
+
32
+ export default function Home() {
33
+ const [nodes, setNodes] = useState<Map<string, any>>(new Map([
34
+ ['txt', { text: "Text 1", x: 0, y: 0 }]
35
+ ]));
36
+ const [draggingNode, setDraggingNode] = useState<Shape | null>(null);
37
+ const [dragOffset, setDragOffset] = useState<{ x: number, y: number } | null>(null);
38
+ const [playerRect, setPlayerRect] = useState<DOMRect | null>(null);
39
+ const [corePlayer, setCorePlayer] = useState<CorePlayer | null>(null);
40
+ const [transformMatrix, setTransformMatrix] = useState<DOMMatrix | null>(null);
41
+
42
+ const [renderLoading, setRenderLoading] = useState(false);
43
+ const [progress, setProgress] = useState(0);
44
+ const [downloadUrl, setDownloadUrl] = useState<string | null>(null);
45
+
46
+ const [playing, setPlaying] = useState<boolean>(false);
47
+ const [dimensions, setDimensions] = useState<{x: number, y: number}>({ x: 1920, y: 1080 });
48
+ const [selectedNode, setSelectedNode] = useState<{
49
+ key: string;
50
+ x: number;
51
+ y: number;
52
+ width: number;
53
+ height: number;
54
+ } | null>(null);
55
+
56
+ useEffect(() => {
57
+ if (playerRect) {
58
+ setTransformMatrix(createTransformationMatrix(playerRect, dimensions));
59
+ }
60
+ }, [playerRect, dimensions]);
61
+
62
+ const handlePlayerReady = (player: CorePlayer) => {
63
+ setCorePlayer(player);
64
+ };
65
+
66
+ const addTextNode = () => {
67
+ setNodes(prevNodes => {
68
+ const newNodes = new Map(prevNodes);
69
+ const newKey = `txt_${newNodes.size+1}`;
70
+ newNodes.set(newKey, { text: `Text ${newNodes.size+1}`, x: 0, y: 0 });
71
+ return newNodes;
72
+ });
73
+ };
74
+
75
+ const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
76
+ if (playerRect && transformMatrix) {
77
+ const { x: playerX, y: playerY } = transformPoint(
78
+ event.clientX,
79
+ event.clientY,
80
+ transformMatrix
81
+ );
82
+
83
+ let hitNode = (corePlayer?.playback.currentScene as Scene2D).getNodeByPosition(playerX, playerY) as Shape;
84
+ while (hitNode) {
85
+ // We want to avoid getting TxtLeaf nodes, but only their parent Txt nodes
86
+ if (nodes.has(hitNode.key)) {
87
+ setDraggingNode(hitNode);
88
+ const node = nodes.get(hitNode.key);
89
+ setDragOffset({ x: playerX - node.x, y: playerY - node.y });
90
+
91
+ const position = hitNode.position();
92
+ const { x, y } = transformPoint(position.x+dimensions.x/2, position.y+dimensions.y/2, transformMatrix.inverse());
93
+
94
+ setSelectedNode({
95
+ key: hitNode.key,
96
+ x: x,
97
+ y: y,
98
+ width: selectedNode?.width || hitNode.width() / transformMatrix.a,
99
+ height: selectedNode?.height || hitNode.height() / transformMatrix.d
100
+ });
101
+ return;
102
+ }
103
+ hitNode = hitNode.parent() as Shape;
104
+ }
105
+ setSelectedNode(null);
106
+ };
107
+ }
108
+
109
+ const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
110
+ if (draggingNode && playerRect && transformMatrix) {
111
+ const { x: playerX, y: playerY } = transformPoint(
112
+ event.clientX,
113
+ event.clientY,
114
+ transformMatrix
115
+ );
116
+
117
+ setNodes(prevNodes => {
118
+ const newNodes = new Map(prevNodes);
119
+ const node = newNodes.get(draggingNode.key);
120
+ if (node && dragOffset) {
121
+ node.x = playerX - dragOffset.x;
122
+ node.y = playerY - dragOffset.y;
123
+
124
+ const { x, y } = transformPoint(node.x+dimensions.x/2, node.y+dimensions.y/2, transformMatrix.inverse());
125
+ setSelectedNode({
126
+ key: draggingNode.key,
127
+ x: x,
128
+ y: y,
129
+ width: selectedNode?.width || draggingNode.width() / transformMatrix.a,
130
+ height: selectedNode?.height || draggingNode.height() / transformMatrix.d,
131
+ });
132
+
133
+ }
134
+ return newNodes;
135
+ });
136
+ }
137
+ };
138
+
139
+ function createTransformationMatrix(
140
+ playerRect: DOMRect,
141
+ dimensions: { x: number; y: number }
142
+ ): DOMMatrix {
143
+ const scaleX = dimensions.x / playerRect.width;
144
+ const scaleY = dimensions.y / playerRect.height;
145
+
146
+ const matrix = new DOMMatrix();
147
+ matrix.scaleSelf(scaleX, scaleY);
148
+ matrix.translateSelf(-playerRect.left, -playerRect.top);
149
+
150
+ return matrix;
151
+ }
152
+
153
+ function transformPoint(
154
+ x: number,
155
+ y: number,
156
+ matrix: DOMMatrix
157
+ ): { x: number; y: number } {
158
+ const point = new DOMPoint(x, y);
159
+ const transformedPoint = point.matrixTransform(matrix);
160
+ return { x: transformedPoint.x, y: transformedPoint.y };
161
+ }
162
+
163
+ /**
164
+ * Render the video.
165
+ */
166
+ async function render() {
167
+ setRenderLoading(true);
168
+ const res = await fetch('/api/render', {
169
+ method: 'POST',
170
+ headers: {
171
+ 'Content-Type': 'application/json',
172
+ },
173
+ body: JSON.stringify({
174
+ variables: {
175
+ nodes: Object.fromEntries(nodes)
176
+ },
177
+ streamProgress: true,
178
+ }),
179
+ }).catch((e) => console.log(e));
180
+
181
+ if (!res) {
182
+ alert('Failed to render video.');
183
+ return;
184
+ }
185
+
186
+ const downloadUrl = await parseStream(res.body!.getReader(), (p) => setProgress(p));
187
+ setRenderLoading(false);
188
+ setDownloadUrl(downloadUrl);
189
+ }
190
+
191
+ return (
192
+ <>
193
+ <div className="m-auto p-12 max-w-6xl flex flex-col gap-y-4">
194
+ <div
195
+ className="rounded-lg overflow-hidden w-[100%] py-20 bg-gray-100 flex items-center justify-center"
196
+ onPointerDown={handlePointerDown}
197
+ onPointerMove={handlePointerMove}
198
+ onPointerUp={() => {
199
+ setDraggingNode(null);
200
+ setDragOffset(null);
201
+ }}
202
+ >
203
+ <div className='w-[80%] h-[80%]'>
204
+ <Player
205
+ src="http://localhost:4000/player/"
206
+ controls={false}
207
+ playing={playing}
208
+ width={dimensions.x}
209
+ height={dimensions.y}
210
+ variables={{
211
+ nodes: Object.fromEntries(nodes),
212
+ }}
213
+ onPlayerReady={handlePlayerReady}
214
+ onPlayerResize={(rect: DOMRect) => {
215
+ setPlayerRect(rect);
216
+ }}
217
+ />
218
+ {(selectedNode && playerRect) && (
219
+ <div
220
+ className="absolute border-2 border-[#5bbad5] pointer-events-none"
221
+ style={{
222
+ left: `${selectedNode.x-selectedNode.width/2}px`,
223
+ top: `${selectedNode.y-selectedNode.height/2}px`,
224
+ width: `${selectedNode.width}px`,
225
+ height: `${selectedNode.height}px`,
226
+ }}
227
+ />
228
+ )}
229
+
230
+ </div>
231
+ </div>
232
+ <div className="flex gap-x-4">
233
+ {/* Progress bar */}
234
+ <div className="text-sm flex-1 bg-gray-100 rounded-md overflow-hidden">
235
+ <div
236
+ className="text-gray-600 bg-gray-400 h-full flex items-center px-4 transition-all transition-200"
237
+ style={{
238
+ width: `${Math.round(progress * 100)}%`,
239
+ }}
240
+ >
241
+ {Math.round(progress * 100)}%
242
+ </div>
243
+ </div>
244
+ {downloadUrl ? (
245
+ <a
246
+ href={downloadUrl}
247
+ download
248
+ className="text-sm flex items-center gap-x-2 rounded-md p-2 bg-green-200 text-gray-700 hover:bg-gray-300"
249
+ >
250
+ Download video
251
+ </a>
252
+ ) : (
253
+ <>
254
+ <Button
255
+ loading={false}
256
+ onClick={() => {
257
+ setPlaying(!playing);
258
+ setSelectedNode(null);
259
+ }}
260
+ >
261
+ {playing ? "Pause" : "Play"}
262
+ </Button>
263
+
264
+ <Button onClick={addTextNode} loading={false}>
265
+ Add Text
266
+ </Button>
267
+
268
+ <Button onClick={() => render()} loading={renderLoading}>
269
+ Render video
270
+ </Button>
271
+ </>
272
+ )}
273
+ </div>
274
+ </div>
275
+ </>
276
+ );
277
+ }
@@ -0,0 +1,5 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // NOTE: This file should not be edited
5
+ // see https://nextjs.org/docs/basic-features/typescript for more information.
@@ -0,0 +1,4 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {};
3
+
4
+ export default nextConfig;