@sansavision/create-vidra-app 0.1.7-alpha.0 → 0.1.9-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/package.json +2 -2
- package/template/README.md +16 -16
- package/template/index.html +91 -183
- package/template/package.json +0 -1
- package/template/src/video.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sansavision/create-vidra-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9-alpha.0",
|
|
4
4
|
"description": "Scaffold a new Vidra video project — SDK, Player, Web Capture ready to go",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-vidra-app": "./index.js"
|
|
@@ -23,4 +23,4 @@
|
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
25
25
|
}
|
|
26
|
-
}
|
|
26
|
+
}
|
package/template/README.md
CHANGED
|
@@ -6,42 +6,42 @@ A video project powered by [Vidra](https://github.com/Sansa-Organisation/vidra).
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install
|
|
9
|
-
npm run dev # Open browser preview
|
|
10
|
-
npm run build:video #
|
|
9
|
+
npm run dev # Open browser — shows SDK output preview + live web scene
|
|
10
|
+
npm run build:video # Build the SDK project and output JSON IR to stdout
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Render to MP4
|
|
14
14
|
|
|
15
|
+
Use the Vidra CLI with the VidraScript file:
|
|
15
16
|
```bash
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
npx @sansavision/vidra render video.vidra -o output.mp4
|
|
18
|
+
```
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
Or open the visual editor:
|
|
21
|
+
```bash
|
|
22
|
+
npx @sansavision/vidra editor video.vidra
|
|
21
23
|
```
|
|
22
24
|
|
|
23
25
|
## Project Structure
|
|
24
26
|
|
|
25
27
|
```
|
|
26
|
-
├── index.html ←
|
|
28
|
+
├── index.html ← Dev page: SDK output preview + embedded web scene
|
|
27
29
|
├── src/
|
|
28
|
-
│ └── video.js ← SDK-based video builder
|
|
30
|
+
│ └── video.js ← SDK-based video builder (outputs JSON IR)
|
|
29
31
|
├── web/
|
|
30
|
-
│ └── chart.html ← Web scene
|
|
31
|
-
├── video.vidra ← VidraScript DSL
|
|
32
|
+
│ └── chart.html ← Web scene using @sansavision/vidra-web-capture
|
|
33
|
+
├── video.vidra ← VidraScript DSL (render with CLI)
|
|
32
34
|
└── package.json
|
|
33
35
|
```
|
|
34
36
|
|
|
35
37
|
## Packages Used
|
|
36
38
|
|
|
37
|
-
| Package | Purpose |
|
|
38
|
-
|
|
39
|
-
| `@sansavision/vidra-sdk` | Build video projects programmatically |
|
|
40
|
-
| `@sansavision/vidra-
|
|
41
|
-
| `@sansavision/vidra-web-capture` | Bridge for web scenes |
|
|
39
|
+
| Package | Where | Purpose |
|
|
40
|
+
|---------|-------|---------|
|
|
41
|
+
| `@sansavision/vidra-sdk` | `src/video.js`, `index.html` | Build video projects programmatically |
|
|
42
|
+
| `@sansavision/vidra-web-capture` | `web/chart.html` | Bridge for web scenes to sync with video timeline |
|
|
42
43
|
|
|
43
44
|
## Learn More
|
|
44
45
|
|
|
45
46
|
- [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
47
|
- [Web Scenes Guide](https://github.com/Sansa-Organisation/vidra/blob/main/docs/web-scenes.md)
|
package/template/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>{{PROJECT_NAME}} —
|
|
6
|
+
<title>{{PROJECT_NAME}} — Vidra Project</title>
|
|
7
7
|
<style>
|
|
8
8
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
9
|
body {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
margin-bottom: 8px;
|
|
23
23
|
background: linear-gradient(135deg, #58a6ff, #d2a8ff);
|
|
24
24
|
-webkit-background-clip: text;
|
|
25
|
+
background-clip: text;
|
|
25
26
|
-webkit-text-fill-color: transparent;
|
|
26
27
|
}
|
|
27
28
|
.subtitle {
|
|
@@ -29,56 +30,12 @@
|
|
|
29
30
|
font-size: 14px;
|
|
30
31
|
margin-bottom: 32px;
|
|
31
32
|
}
|
|
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
33
|
.info-grid {
|
|
77
34
|
display: grid;
|
|
78
|
-
grid-template-columns: repeat(
|
|
35
|
+
grid-template-columns: repeat(2, 1fr);
|
|
79
36
|
gap: 16px;
|
|
80
|
-
margin-top:
|
|
81
|
-
max-width:
|
|
37
|
+
margin-top: 16px;
|
|
38
|
+
max-width: 700px;
|
|
82
39
|
width: 100%;
|
|
83
40
|
}
|
|
84
41
|
.info-card {
|
|
@@ -106,157 +63,108 @@
|
|
|
106
63
|
font-size: 13px;
|
|
107
64
|
color: #58a6ff;
|
|
108
65
|
}
|
|
66
|
+
.web-scene-wrapper {
|
|
67
|
+
border-radius: 12px;
|
|
68
|
+
overflow: hidden;
|
|
69
|
+
box-shadow: 0 0 0 1px rgba(240,246,252,0.1), 0 16px 48px rgba(0,0,0,0.4);
|
|
70
|
+
max-width: 700px;
|
|
71
|
+
width: 100%;
|
|
72
|
+
margin-top: 24px;
|
|
73
|
+
}
|
|
74
|
+
.web-scene-wrapper h3 {
|
|
75
|
+
font-size: 13px;
|
|
76
|
+
color: #8b949e;
|
|
77
|
+
font-weight: 500;
|
|
78
|
+
text-transform: uppercase;
|
|
79
|
+
letter-spacing: 0.5px;
|
|
80
|
+
padding: 12px 16px;
|
|
81
|
+
background: #161b22;
|
|
82
|
+
border-bottom: 1px solid #30363d;
|
|
83
|
+
}
|
|
84
|
+
iframe {
|
|
85
|
+
width: 100%;
|
|
86
|
+
height: 400px;
|
|
87
|
+
border: none;
|
|
88
|
+
background: #000;
|
|
89
|
+
}
|
|
90
|
+
.status {
|
|
91
|
+
margin-top: 24px;
|
|
92
|
+
padding: 16px 24px;
|
|
93
|
+
background: #161b22;
|
|
94
|
+
border: 1px solid #30363d;
|
|
95
|
+
border-radius: 10px;
|
|
96
|
+
max-width: 700px;
|
|
97
|
+
width: 100%;
|
|
98
|
+
}
|
|
99
|
+
.status h3 {
|
|
100
|
+
font-size: 13px;
|
|
101
|
+
color: #8b949e;
|
|
102
|
+
font-weight: 500;
|
|
103
|
+
text-transform: uppercase;
|
|
104
|
+
letter-spacing: 0.5px;
|
|
105
|
+
margin-bottom: 10px;
|
|
106
|
+
}
|
|
107
|
+
.status pre {
|
|
108
|
+
font-family: 'SF Mono', 'JetBrains Mono', monospace;
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
color: #58a6ff;
|
|
111
|
+
white-space: pre-wrap;
|
|
112
|
+
line-height: 1.6;
|
|
113
|
+
}
|
|
109
114
|
</style>
|
|
110
115
|
</head>
|
|
111
116
|
<body>
|
|
112
117
|
<h1>🎬 {{PROJECT_NAME}}</h1>
|
|
113
|
-
<p class="subtitle">Vidra Video Project
|
|
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>
|
|
118
|
+
<p class="subtitle">Vidra Video Project</p>
|
|
125
119
|
|
|
126
120
|
<div class="info-grid">
|
|
127
121
|
<div class="info-card">
|
|
128
|
-
<h3>
|
|
129
|
-
<p>
|
|
122
|
+
<h3>Build Video IR</h3>
|
|
123
|
+
<p>Run <code>npm run build:video</code> to build the SDK project in <code>src/video.js</code> into Vidra JSON IR.</p>
|
|
130
124
|
</div>
|
|
131
125
|
<div class="info-card">
|
|
132
|
-
<h3>
|
|
133
|
-
<p>
|
|
126
|
+
<h3>Render to MP4</h3>
|
|
127
|
+
<p>Run <code>npx @sansavision/vidra render video.vidra -o output.mp4</code> to render the VidraScript project.</p>
|
|
134
128
|
</div>
|
|
135
129
|
<div class="info-card">
|
|
136
|
-
<h3>
|
|
137
|
-
<p>Run <code>vidra
|
|
130
|
+
<h3>Visual Editor</h3>
|
|
131
|
+
<p>Run <code>npx @sansavision/vidra editor video.vidra</code> to open the browser-based visual editor.</p>
|
|
132
|
+
</div>
|
|
133
|
+
<div class="info-card">
|
|
134
|
+
<h3>Web Scene</h3>
|
|
135
|
+
<p>The <code>web/chart.html</code> file uses <code>@sansavision/vidra-web-capture</code> to sync with the video timeline.</p>
|
|
138
136
|
</div>
|
|
139
137
|
</div>
|
|
140
138
|
|
|
141
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
};
|
|
139
|
+
<div class="web-scene-wrapper">
|
|
140
|
+
<h3>Live Web Scene Preview — web/chart.html</h3>
|
|
141
|
+
<iframe src="web/chart.html"></iframe>
|
|
142
|
+
</div>
|
|
254
143
|
|
|
255
|
-
|
|
256
|
-
|
|
144
|
+
<div class="status" id="status">
|
|
145
|
+
<h3>SDK Output Preview</h3>
|
|
146
|
+
<pre id="output">Loading SDK project...</pre>
|
|
147
|
+
</div>
|
|
257
148
|
|
|
258
|
-
|
|
259
|
-
|
|
149
|
+
<script type="module">
|
|
150
|
+
// Import the real SDK and build the project to show the IR output
|
|
151
|
+
import { Project, Scene, Layer, Easing } from "@sansavision/vidra-sdk";
|
|
152
|
+
|
|
153
|
+
const project = new Project({ width: 1920, height: 1080, fps: 30 })
|
|
154
|
+
.addScene(
|
|
155
|
+
new Scene("intro", 4)
|
|
156
|
+
.addLayer(new Layer("bg").solid("#0d1117"))
|
|
157
|
+
.addLayer(
|
|
158
|
+
new Layer("title")
|
|
159
|
+
.text("Welcome to {{PROJECT_NAME}}", "Inter", 72, "#e6edf3")
|
|
160
|
+
.position(960, 480)
|
|
161
|
+
.animate("opacity", 0, 1, 0.8, Easing.EaseOut)
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const ir = project.build();
|
|
166
|
+
const output = document.getElementById("output");
|
|
167
|
+
output.textContent = JSON.stringify(ir, null, 2).slice(0, 600) + "\n...";
|
|
260
168
|
</script>
|
|
261
169
|
</body>
|
|
262
170
|
</html>
|
package/template/package.json
CHANGED
package/template/src/video.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// This file uses the Vidra SDK to build a video project programmatically.
|
|
4
4
|
// Run it with: npm run build:video
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
6
|
+
// It outputs the Vidra IR as JSON to stdout. To render to video,
|
|
7
|
+
// use the VidraScript file instead: npx @sansavision/vidra render video.vidra -o output.mp4
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
Project,
|