@sansavision/create-vidra-app 0.1.7-alpha.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/index.js ADDED
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ─── create-vidra-app ───────────────────────────────────────────────
4
+ // Scaffolds a new Vidra project with SDK, Player, and Web Capture
5
+ // ready to go.
6
+ //
7
+ // Usage:
8
+ // npx @sansavision/create-vidra-app my-video
9
+ // cd my-video && npm install && npm run dev
10
+
11
+ import { mkdirSync, writeFileSync, readFileSync, readdirSync, statSync, copyFileSync } from "fs";
12
+ import { join, dirname, resolve, basename } from "path";
13
+ import { fileURLToPath } from "url";
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+
18
+ const args = process.argv.slice(2);
19
+ const projectName = args[0];
20
+
21
+ if (!projectName) {
22
+ console.log(`
23
+ \x1b[1m\x1b[35m🎬 create-vidra-app\x1b[0m
24
+
25
+ Scaffold a new Vidra video project.
26
+
27
+ \x1b[1mUsage:\x1b[0m
28
+ npx @sansavision/create-vidra-app \x1b[36m<project-name>\x1b[0m
29
+
30
+ \x1b[1mExample:\x1b[0m
31
+ npx @sansavision/create-vidra-app my-video
32
+ cd my-video
33
+ npm install
34
+ npm run dev
35
+ `);
36
+ process.exit(1);
37
+ }
38
+
39
+ const targetDir = resolve(process.cwd(), projectName);
40
+ const templateDir = join(__dirname, "template");
41
+ const baseName = basename(targetDir);
42
+
43
+ console.log();
44
+ console.log(` \x1b[1m\x1b[35m🎬 Creating Vidra project:\x1b[0m ${projectName}`);
45
+ console.log();
46
+
47
+ // Recursively copy template directory
48
+ function copyDir(src, dest) {
49
+ mkdirSync(dest, { recursive: true });
50
+ for (const entry of readdirSync(src)) {
51
+ const srcPath = join(src, entry);
52
+ const destPath = join(dest, entry);
53
+ if (statSync(srcPath).isDirectory()) {
54
+ copyDir(srcPath, destPath);
55
+ } else {
56
+ let content = readFileSync(srcPath, "utf-8");
57
+ // Replace template variables
58
+ content = content.replace(/\{\{PROJECT_NAME\}\}/g, baseName);
59
+ writeFileSync(destPath, content);
60
+ }
61
+ }
62
+ }
63
+
64
+ copyDir(templateDir, targetDir);
65
+
66
+ console.log(` \x1b[32m✓\x1b[0m Project scaffolded at \x1b[1m${targetDir}\x1b[0m`);
67
+ console.log();
68
+ console.log(` \x1b[1mNext steps:\x1b[0m`);
69
+ console.log();
70
+ console.log(` cd ${projectName}`);
71
+ console.log(` npm install`);
72
+ console.log(` npm run dev \x1b[2m# Live browser preview\x1b[0m`);
73
+ console.log(` npm run build:video \x1b[2m# Generate project IR JSON\x1b[0m`);
74
+ console.log();
75
+ console.log(` \x1b[2mTo render to MP4 (requires Vidra CLI):\x1b[0m`);
76
+ console.log(` vidra render video.vidra -o output.mp4`);
77
+ console.log();
78
+ console.log(` \x1b[35m📖 Docs:\x1b[0m https://github.com/Sansa-Organisation/vidra`);
79
+ console.log();
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@sansavision/create-vidra-app",
3
+ "version": "0.1.7-alpha.0",
4
+ "description": "Scaffold a new Vidra video project — SDK, Player, Web Capture ready to go",
5
+ "bin": {
6
+ "create-vidra-app": "./index.js"
7
+ },
8
+ "type": "module",
9
+ "files": [
10
+ "index.js",
11
+ "template/**/*"
12
+ ],
13
+ "keywords": [
14
+ "vidra",
15
+ "video",
16
+ "create",
17
+ "scaffold",
18
+ "motion-graphics",
19
+ "programmatic-video"
20
+ ],
21
+ "author": "Sansa Vision",
22
+ "license": "MIT",
23
+ "publishConfig": {
24
+ "access": "public"
25
+ }
26
+ }
@@ -0,0 +1,47 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ A video project powered by [Vidra](https://github.com/Sansa-Organisation/vidra).
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npm install
9
+ npm run dev # Open browser preview
10
+ npm run build:video # Generate project IR JSON
11
+ ```
12
+
13
+ ## Render to MP4
14
+
15
+ ```bash
16
+ # Using the Vidra CLI:
17
+ vidra render video.vidra -o output.mp4
18
+
19
+ # Or use the visual editor:
20
+ vidra editor video.vidra
21
+ ```
22
+
23
+ ## Project Structure
24
+
25
+ ```
26
+ ├── index.html ← Browser preview (Vite dev server)
27
+ ├── src/
28
+ │ └── video.js ← SDK-based video builder
29
+ ├── web/
30
+ │ └── chart.html ← Web scene with capture bridge
31
+ ├── video.vidra ← VidraScript DSL version
32
+ └── package.json
33
+ ```
34
+
35
+ ## Packages Used
36
+
37
+ | Package | Purpose |
38
+ |---------|---------|
39
+ | `@sansavision/vidra-sdk` | Build video projects programmatically |
40
+ | `@sansavision/vidra-player` | WASM-powered browser renderer |
41
+ | `@sansavision/vidra-web-capture` | Bridge for web scenes |
42
+
43
+ ## Learn More
44
+
45
+ - [Vidra Documentation](https://github.com/Sansa-Organisation/vidra/tree/main/docs)
46
+ - [VidraScript Reference](https://github.com/Sansa-Organisation/vidra/blob/main/docs/vidrascript.md)
47
+ - [Web Scenes Guide](https://github.com/Sansa-Organisation/vidra/blob/main/docs/web-scenes.md)
@@ -0,0 +1,262 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{PROJECT_NAME}} — Preview</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body {
10
+ min-height: 100vh;
11
+ background: #0d1117;
12
+ color: #e6edf3;
13
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
14
+ display: flex;
15
+ flex-direction: column;
16
+ align-items: center;
17
+ padding: 40px 20px;
18
+ }
19
+ h1 {
20
+ font-size: 28px;
21
+ font-weight: 700;
22
+ margin-bottom: 8px;
23
+ background: linear-gradient(135deg, #58a6ff, #d2a8ff);
24
+ -webkit-background-clip: text;
25
+ -webkit-text-fill-color: transparent;
26
+ }
27
+ .subtitle {
28
+ color: #8b949e;
29
+ font-size: 14px;
30
+ margin-bottom: 32px;
31
+ }
32
+ .canvas-wrapper {
33
+ position: relative;
34
+ border-radius: 12px;
35
+ overflow: hidden;
36
+ box-shadow: 0 0 0 1px rgba(240,246,252,0.1), 0 16px 48px rgba(0,0,0,0.4);
37
+ }
38
+ canvas {
39
+ display: block;
40
+ background: #000;
41
+ }
42
+ .controls {
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 12px;
46
+ margin-top: 20px;
47
+ padding: 12px 20px;
48
+ background: #161b22;
49
+ border-radius: 10px;
50
+ border: 1px solid #30363d;
51
+ }
52
+ button {
53
+ background: #238636;
54
+ color: white;
55
+ border: none;
56
+ border-radius: 6px;
57
+ padding: 8px 20px;
58
+ font-size: 14px;
59
+ font-weight: 600;
60
+ cursor: pointer;
61
+ transition: background 0.15s;
62
+ }
63
+ button:hover { background: #2ea043; }
64
+ button.secondary {
65
+ background: #21262d;
66
+ border: 1px solid #30363d;
67
+ }
68
+ button.secondary:hover { background: #30363d; }
69
+ .frame-display {
70
+ font-family: 'SF Mono', 'JetBrains Mono', monospace;
71
+ font-size: 13px;
72
+ color: #8b949e;
73
+ min-width: 100px;
74
+ text-align: center;
75
+ }
76
+ .info-grid {
77
+ display: grid;
78
+ grid-template-columns: repeat(3, 1fr);
79
+ gap: 16px;
80
+ margin-top: 24px;
81
+ max-width: 800px;
82
+ width: 100%;
83
+ }
84
+ .info-card {
85
+ background: #161b22;
86
+ border: 1px solid #30363d;
87
+ border-radius: 10px;
88
+ padding: 20px;
89
+ }
90
+ .info-card h3 {
91
+ font-size: 13px;
92
+ color: #8b949e;
93
+ font-weight: 500;
94
+ margin-bottom: 8px;
95
+ text-transform: uppercase;
96
+ letter-spacing: 0.5px;
97
+ }
98
+ .info-card p {
99
+ font-size: 15px;
100
+ line-height: 1.5;
101
+ }
102
+ .info-card code {
103
+ background: #0d1117;
104
+ padding: 2px 6px;
105
+ border-radius: 4px;
106
+ font-size: 13px;
107
+ color: #58a6ff;
108
+ }
109
+ </style>
110
+ </head>
111
+ <body>
112
+ <h1>🎬 {{PROJECT_NAME}}</h1>
113
+ <p class="subtitle">Vidra Video Project — Browser Preview</p>
114
+
115
+ <div class="canvas-wrapper">
116
+ <canvas id="preview" width="960" height="540"></canvas>
117
+ </div>
118
+
119
+ <div class="controls">
120
+ <button id="playBtn" onclick="togglePlay()">▶ Play</button>
121
+ <button class="secondary" onclick="seekStart()">⏮ Start</button>
122
+ <div class="frame-display" id="frameDisplay">Frame 0 / 0</div>
123
+ <button class="secondary" onclick="seekEnd()">⏭ End</button>
124
+ </div>
125
+
126
+ <div class="info-grid">
127
+ <div class="info-card">
128
+ <h3>SDK Video</h3>
129
+ <p>Edit <code>src/video.js</code> to build your video programmatically using the Vidra SDK.</p>
130
+ </div>
131
+ <div class="info-card">
132
+ <h3>Web Scene</h3>
133
+ <p>Edit <code>web/chart.html</code> to create interactive web scenes using the capture bridge.</p>
134
+ </div>
135
+ <div class="info-card">
136
+ <h3>Render</h3>
137
+ <p>Run <code>vidra render video.vidra</code> to export your project as an MP4 file.</p>
138
+ </div>
139
+ </div>
140
+
141
+ <script type="module">
142
+ // This preview page demonstrates how @sansavision/vidra-player
143
+ // would render a project in the browser. For now, it shows a
144
+ // placeholder with the project structure info.
145
+
146
+ const canvas = document.getElementById('preview');
147
+ const ctx = canvas.getContext('2d');
148
+ const frameDisplay = document.getElementById('frameDisplay');
149
+ const playBtn = document.getElementById('playBtn');
150
+
151
+ const FPS = 30;
152
+ const TOTAL_FRAMES = 330; // 11 seconds at 30fps
153
+ let currentFrame = 0;
154
+ let playing = false;
155
+ let lastTime = 0;
156
+
157
+ function drawFrame(frame) {
158
+ const time = frame / FPS;
159
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
160
+
161
+ // Background
162
+ ctx.fillStyle = '#0d1117';
163
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
164
+
165
+ if (time < 4) {
166
+ // Scene 1: Intro
167
+ const t = Math.min(time / 0.8, 1);
168
+ const eased = 1 - Math.pow(1 - t, 3);
169
+
170
+ ctx.globalAlpha = eased;
171
+ ctx.fillStyle = '#e6edf3';
172
+ ctx.font = 'bold 36px Inter, system-ui';
173
+ ctx.textAlign = 'center';
174
+ ctx.fillText('Welcome to {{PROJECT_NAME}}', canvas.width / 2, canvas.height / 2 - 20);
175
+
176
+ const t2 = Math.max(0, Math.min((time - 0.3) / 0.8, 1));
177
+ ctx.globalAlpha = 1 - Math.pow(1 - t2, 3);
178
+ ctx.fillStyle = '#8b949e';
179
+ ctx.font = '16px Inter, system-ui';
180
+ ctx.fillText('Built with Vidra SDK', canvas.width / 2, canvas.height / 2 + 30);
181
+
182
+ // Accent line
183
+ const t3 = Math.max(0, Math.min((time - 0.6) / 0.6, 1));
184
+ ctx.globalAlpha = 1 - Math.pow(1 - t3, 3);
185
+ const lineWidth = t3 * 100;
186
+ ctx.fillStyle = '#58a6ff';
187
+ ctx.fillRect(canvas.width / 2 - lineWidth, canvas.height / 2 + 60, lineWidth * 2, 2);
188
+
189
+ } else if (time < 8) {
190
+ // Scene 2: Content
191
+ const st = time - 4;
192
+ ctx.fillStyle = '#161b22';
193
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
194
+
195
+ const t1 = Math.min(st / 0.6, 1);
196
+ ctx.globalAlpha = 1 - Math.pow(1 - t1, 3);
197
+ ctx.fillStyle = '#e6edf3';
198
+ ctx.font = 'bold 28px Inter, system-ui';
199
+ ctx.textAlign = 'center';
200
+ ctx.fillText('Programmatic Video', canvas.width / 2, 150);
201
+
202
+ const bullets = ['✦ TypeScript SDK', '✦ GPU-accelerated rendering', '✦ Web scenes (React, D3, Three.js)'];
203
+ bullets.forEach((txt, i) => {
204
+ const bt = Math.max(0, Math.min((st - 0.2 - i * 0.2) / 0.5, 1));
205
+ ctx.globalAlpha = 1 - Math.pow(1 - bt, 3);
206
+ ctx.fillStyle = '#58a6ff';
207
+ ctx.font = '18px Inter, system-ui';
208
+ ctx.fillText(txt, canvas.width / 2, 230 + i * 50);
209
+ });
210
+
211
+ } else {
212
+ // Scene 3: Outro
213
+ const st = time - 8;
214
+ ctx.globalAlpha = Math.min(st / 0.8, 1);
215
+ ctx.fillStyle = '#ffffff';
216
+ ctx.font = 'bold 24px Inter, system-ui';
217
+ ctx.textAlign = 'center';
218
+ ctx.fillText('Start building with Vidra', canvas.width / 2, canvas.height / 2 - 10);
219
+
220
+ const t2 = Math.max(0, Math.min((st - 0.4) / 0.6, 1));
221
+ ctx.globalAlpha = t2;
222
+ ctx.fillStyle = '#8b949e';
223
+ ctx.font = '12px Inter, system-ui';
224
+ ctx.fillText('github.com/Sansa-Organisation/vidra', canvas.width / 2, canvas.height / 2 + 30);
225
+ }
226
+
227
+ ctx.globalAlpha = 1;
228
+ frameDisplay.textContent = `Frame ${frame} / ${TOTAL_FRAMES}`;
229
+ }
230
+
231
+ function animate(timestamp) {
232
+ if (!playing) return;
233
+ if (!lastTime) lastTime = timestamp;
234
+ const delta = timestamp - lastTime;
235
+ if (delta >= 1000 / FPS) {
236
+ lastTime = timestamp;
237
+ currentFrame++;
238
+ if (currentFrame >= TOTAL_FRAMES) {
239
+ currentFrame = 0;
240
+ }
241
+ drawFrame(currentFrame);
242
+ }
243
+ requestAnimationFrame(animate);
244
+ }
245
+
246
+ window.togglePlay = () => {
247
+ playing = !playing;
248
+ playBtn.textContent = playing ? '⏸ Pause' : '▶ Play';
249
+ if (playing) {
250
+ lastTime = 0;
251
+ requestAnimationFrame(animate);
252
+ }
253
+ };
254
+
255
+ window.seekStart = () => { currentFrame = 0; drawFrame(0); };
256
+ window.seekEnd = () => { currentFrame = TOTAL_FRAMES - 1; drawFrame(currentFrame); };
257
+
258
+ // Initial render
259
+ drawFrame(0);
260
+ </script>
261
+ </body>
262
+ </html>
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "npx vite",
8
+ "build": "npx vite build",
9
+ "build:video": "node src/video.js",
10
+ "preview": "npx vite preview"
11
+ },
12
+ "dependencies": {
13
+ "@sansavision/vidra-sdk": "^0.1.7-alpha.0",
14
+ "@sansavision/vidra-player": "^0.1.7-alpha.0",
15
+ "@sansavision/vidra-web-capture": "^0.1.7-alpha.0"
16
+ },
17
+ "devDependencies": {
18
+ "vite": "^7.3.1"
19
+ }
20
+ }
@@ -0,0 +1,111 @@
1
+ // ─── {{PROJECT_NAME}} — Video Project (SDK) ────────────────────────
2
+ //
3
+ // This file uses the Vidra SDK to build a video project programmatically.
4
+ // Run it with: npm run build:video
5
+ //
6
+ // The output is a JSON IR that can be rendered by the Vidra CLI:
7
+ // vidra render --ir output.json -o video.mp4
8
+
9
+ import {
10
+ Project,
11
+ Scene,
12
+ Layer,
13
+ Easing,
14
+ hex,
15
+ } from "@sansavision/vidra-sdk";
16
+
17
+ // ── Build the project ───────────────────────────────────────────────
18
+
19
+ const project = new Project({ width: 1920, height: 1080, fps: 30 })
20
+ // Scene 1: Intro
21
+ .addScene(
22
+ new Scene("intro", 4)
23
+ // Dark gradient background
24
+ .addLayer(new Layer("bg").solid("#0d1117"))
25
+
26
+ // Main title — fades in and slides up
27
+ .addLayer(
28
+ new Layer("title")
29
+ .text("Welcome to {{PROJECT_NAME}}", "Inter", 72, "#e6edf3")
30
+ .position(960, 480)
31
+ .animate("opacity", 0, 1, 0.8, Easing.EaseOut)
32
+ )
33
+
34
+ // Subtitle
35
+ .addLayer(
36
+ new Layer("subtitle")
37
+ .text("Built with Vidra SDK", "Inter", 32, "#8b949e")
38
+ .position(960, 560)
39
+ .animate("opacity", 0, 1, 0.8, Easing.EaseOut, 0.3)
40
+ )
41
+
42
+ // Decorative shape
43
+ .addLayer(
44
+ new Layer("accent")
45
+ .shape("rect", { width: 200, height: 4, radius: 2, fill: "#58a6ff" })
46
+ .position(960, 620)
47
+ .animate("opacity", 0, 1, 0.6, Easing.EaseOut, 0.6)
48
+ )
49
+ )
50
+
51
+ // Scene 2: Content
52
+ .addScene(
53
+ new Scene("content", 4)
54
+ .addLayer(new Layer("bg2").solid("#161b22"))
55
+
56
+ .addLayer(
57
+ new Layer("heading")
58
+ .text("Programmatic Video", "Inter", 56, "#e6edf3")
59
+ .position(960, 300)
60
+ .animate("opacity", 0, 1, 0.6, Easing.EaseOut)
61
+ )
62
+
63
+ .addLayer(
64
+ new Layer("bullet1")
65
+ .text("✦ TypeScript SDK", "Inter", 36, "#58a6ff")
66
+ .position(960, 450)
67
+ .animate("opacity", 0, 1, 0.5, Easing.EaseOut, 0.2)
68
+ )
69
+
70
+ .addLayer(
71
+ new Layer("bullet2")
72
+ .text("✦ GPU-accelerated rendering", "Inter", 36, "#58a6ff")
73
+ .position(960, 520)
74
+ .animate("opacity", 0, 1, 0.5, Easing.EaseOut, 0.4)
75
+ )
76
+
77
+ .addLayer(
78
+ new Layer("bullet3")
79
+ .text("✦ Web scenes (React, D3, Three.js)", "Inter", 36, "#58a6ff")
80
+ .position(960, 590)
81
+ .animate("opacity", 0, 1, 0.5, Easing.EaseOut, 0.6)
82
+ )
83
+ )
84
+
85
+ // Scene 3: Outro
86
+ .addScene(
87
+ new Scene("outro", 3)
88
+ .addLayer(new Layer("bg3").solid("#0d1117"))
89
+
90
+ .addLayer(
91
+ new Layer("cta")
92
+ .text("Start building with Vidra", "Inter", 48, "#ffffff")
93
+ .position(960, 500)
94
+ .animate("opacity", 0, 1, 0.8, Easing.EaseOut)
95
+ )
96
+
97
+ .addLayer(
98
+ new Layer("url")
99
+ .text("github.com/Sansa-Organisation/vidra", "Inter", 24, "#8b949e")
100
+ .position(960, 580)
101
+ .animate("opacity", 0, 1, 0.6, Easing.EaseOut, 0.4)
102
+ )
103
+ );
104
+
105
+ // ── Output ──────────────────────────────────────────────────────────
106
+
107
+ const ir = project.build();
108
+ const json = JSON.stringify(ir, null, 2);
109
+
110
+ // Write to stdout (pipe to file: node src/video.js > project.json)
111
+ console.log(json);
@@ -0,0 +1,81 @@
1
+ // ─── {{PROJECT_NAME}} — VidraScript ─────────────────────────────────
2
+ //
3
+ // This is the same project defined in src/video.js, but written in
4
+ // VidraScript DSL. Render it with the Vidra CLI:
5
+ //
6
+ // vidra render video.vidra -o output.mp4
7
+ // vidra dev video.vidra # live preview with hot reload
8
+
9
+ project(1920, 1080, 30) {
10
+ scene("intro", 4s) {
11
+ layer("bg") {
12
+ solid(#0d1117)
13
+ }
14
+
15
+ layer("title") {
16
+ text("Welcome to {{PROJECT_NAME}}", font: "Inter", size: 72, color: #e6edf3)
17
+ position(960, 480)
18
+ animation(opacity, from: 0, to: 1, duration: 0.8s, easing: easeOut)
19
+ }
20
+
21
+ layer("subtitle") {
22
+ text("Built with Vidra SDK", font: "Inter", size: 32, color: #8b949e)
23
+ position(960, 560)
24
+ animation(opacity, from: 0, to: 1, duration: 0.8s, easing: easeOut)
25
+ }
26
+
27
+ layer("accent") {
28
+ shape(rect, width: 200, height: 4, radius: 2, fill: #58a6ff)
29
+ position(960, 620)
30
+ animation(opacity, from: 0, to: 1, duration: 0.6s, easing: easeOut)
31
+ }
32
+ }
33
+
34
+ scene("content", 4s) {
35
+ layer("bg2") {
36
+ solid(#161b22)
37
+ }
38
+
39
+ layer("heading") {
40
+ text("Programmatic Video", font: "Inter", size: 56, color: #e6edf3)
41
+ position(960, 300)
42
+ animation(opacity, from: 0, to: 1, duration: 0.6s, easing: easeOut)
43
+ }
44
+
45
+ layer("bullet1") {
46
+ text("TypeScript SDK", font: "Inter", size: 36, color: #58a6ff)
47
+ position(960, 450)
48
+ animation(opacity, from: 0, to: 1, duration: 0.5s, easing: easeOut)
49
+ }
50
+
51
+ layer("bullet2") {
52
+ text("GPU-accelerated rendering", font: "Inter", size: 36, color: #58a6ff)
53
+ position(960, 520)
54
+ animation(opacity, from: 0, to: 1, duration: 0.5s, easing: easeOut)
55
+ }
56
+
57
+ layer("bullet3") {
58
+ text("Web scenes with React & D3", font: "Inter", size: 36, color: #58a6ff)
59
+ position(960, 590)
60
+ animation(opacity, from: 0, to: 1, duration: 0.5s, easing: easeOut)
61
+ }
62
+ }
63
+
64
+ scene("outro", 3s) {
65
+ layer("bg3") {
66
+ solid(#0d1117)
67
+ }
68
+
69
+ layer("cta") {
70
+ text("Start building with Vidra", font: "Inter", size: 48, color: #ffffff)
71
+ position(960, 500)
72
+ animation(opacity, from: 0, to: 1, duration: 0.8s, easing: easeOut)
73
+ }
74
+
75
+ layer("url") {
76
+ text("github.com/Sansa-Organisation/vidra", font: "Inter", size: 24, color: #8b949e)
77
+ position(960, 580)
78
+ animation(opacity, from: 0, to: 1, duration: 0.6s, easing: easeOut)
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,95 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>{{PROJECT_NAME}} — Web Scene</title>
6
+ <style>
7
+ * { margin: 0; padding: 0; box-sizing: border-box; }
8
+ body {
9
+ width: 100vw; height: 100vh;
10
+ background: transparent;
11
+ display: flex; align-items: center; justify-content: center;
12
+ font-family: 'Inter', system-ui, sans-serif;
13
+ overflow: hidden;
14
+ }
15
+ .container {
16
+ text-align: center;
17
+ color: white;
18
+ }
19
+ .bar-chart {
20
+ display: flex;
21
+ align-items: flex-end;
22
+ justify-content: center;
23
+ gap: 12px;
24
+ height: 300px;
25
+ margin-top: 40px;
26
+ }
27
+ .bar {
28
+ width: 60px;
29
+ border-radius: 8px 8px 0 0;
30
+ transition: height 0.3s ease;
31
+ }
32
+ .label {
33
+ color: #8b949e;
34
+ font-size: 14px;
35
+ margin-top: 8px;
36
+ }
37
+ h2 {
38
+ font-size: 28px;
39
+ font-weight: 600;
40
+ color: #e6edf3;
41
+ }
42
+ </style>
43
+ </head>
44
+ <body>
45
+ <div class="container">
46
+ <h2>Revenue by Quarter</h2>
47
+ <div class="bar-chart" id="chart"></div>
48
+ </div>
49
+
50
+ <script type="module">
51
+ import { VidraCapture } from '@sansavision/vidra-web-capture';
52
+
53
+ const capture = new VidraCapture();
54
+ const data = [
55
+ { label: 'Q1', value: 45, color: '#58a6ff' },
56
+ { label: 'Q2', value: 72, color: '#3fb950' },
57
+ { label: 'Q3', value: 58, color: '#d2a8ff' },
58
+ { label: 'Q4', value: 91, color: '#f0883e' },
59
+ ];
60
+
61
+ const chart = document.getElementById('chart');
62
+
63
+ // Create bars
64
+ data.forEach(d => {
65
+ const col = document.createElement('div');
66
+ col.innerHTML = `
67
+ <div class="bar" style="background: ${d.color}; height: 0px;"></div>
68
+ <div class="label">${d.label}</div>
69
+ `;
70
+ chart.appendChild(col);
71
+ });
72
+
73
+ const bars = chart.querySelectorAll('.bar');
74
+
75
+ function render() {
76
+ const { time } = capture.getState();
77
+
78
+ // Animate bars growing over 2 seconds
79
+ const progress = Math.min(time / 2, 1);
80
+ const eased = 1 - Math.pow(1 - progress, 3); // ease-out cubic
81
+
82
+ data.forEach((d, i) => {
83
+ const delay = i * 0.15;
84
+ const t = Math.max(0, Math.min((time - delay) / 1.5, 1));
85
+ const barEased = 1 - Math.pow(1 - t, 3);
86
+ bars[i].style.height = `${barEased * d.value * 3}px`;
87
+ });
88
+
89
+ requestAnimationFrame(render);
90
+ }
91
+
92
+ render();
93
+ </script>
94
+ </body>
95
+ </html>