loopwind 0.19.0 → 0.20.1

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 (52) hide show
  1. package/README.md +83 -0
  2. package/dist/commands/preview.d.ts.map +1 -1
  3. package/dist/commands/preview.js +2 -1
  4. package/dist/commands/preview.js.map +1 -1
  5. package/dist/commands/render.d.ts.map +1 -1
  6. package/dist/commands/render.js +2 -0
  7. package/dist/commands/render.js.map +1 -1
  8. package/dist/default-templates/AGENTS.md +54 -0
  9. package/dist/lib/renderer.d.ts.map +1 -1
  10. package/dist/lib/renderer.js +3 -0
  11. package/dist/lib/renderer.js.map +1 -1
  12. package/dist/lib/tailwind-browser.d.ts.map +1 -1
  13. package/dist/lib/tailwind-browser.js +169 -6
  14. package/dist/lib/tailwind-browser.js.map +1 -1
  15. package/dist/lib/tailwind.d.ts.map +1 -1
  16. package/dist/lib/tailwind.js +178 -7
  17. package/dist/lib/tailwind.js.map +1 -1
  18. package/dist/lib/video-preview.d.ts +1 -1
  19. package/dist/lib/video-preview.d.ts.map +1 -1
  20. package/dist/lib/video-preview.js +266 -249
  21. package/dist/lib/video-preview.js.map +1 -1
  22. package/dist/lib/video-renderer.d.ts +2 -0
  23. package/dist/lib/video-renderer.d.ts.map +1 -1
  24. package/dist/lib/video-renderer.js +4 -4
  25. package/dist/lib/video-renderer.js.map +1 -1
  26. package/dist/sdk/index.d.ts +2 -0
  27. package/dist/sdk/index.d.ts.map +1 -1
  28. package/dist/sdk/index.js +1 -0
  29. package/dist/sdk/index.js.map +1 -1
  30. package/dist/sdk/preview.d.ts +65 -0
  31. package/dist/sdk/preview.d.ts.map +1 -0
  32. package/dist/sdk/preview.js +262 -0
  33. package/dist/sdk/preview.js.map +1 -0
  34. package/examples/nextjs-template-import.ts +2 -2
  35. package/examples/sdk-video-preview.tsx +120 -0
  36. package/package.json +1 -1
  37. package/render-examples-600x400.mjs +161 -0
  38. package/render-spring-variants-fixed.mjs +60 -0
  39. package/render-staggered-text.mjs +56 -0
  40. package/test-sdk-config.mjs +138 -81
  41. package/test-static-debug.tsx +19 -0
  42. package/test-templates/test-sdk.mjs +46 -22
  43. package/test-video-props.json +3 -0
  44. package/website/DEPLOYMENT.md +1 -0
  45. package/website/OG_IMAGES.md +1 -0
  46. package/website/astro.config.mjs +18 -2
  47. package/website/package-lock.json +2866 -7080
  48. package/website/package.json +1 -2
  49. package/website/public/.gitkeep +1 -0
  50. package/website/templates/og-image.tsx +20 -21
  51. package/website/test-playground.mjs +45 -0
  52. package/output/sdk-static.jpg +0 -0
@@ -1,168 +1,92 @@
1
1
  import express from 'express';
2
- import { createServer as createViteServer } from 'vite';
3
2
  import path from 'path';
4
- import fs from 'fs/promises';
5
3
  import { fileURLToPath } from 'url';
6
- import { loadTailwindConfig } from './tailwind-config-loader.js';
7
- import { renderVideo } from './video-renderer.js';
8
- import { clearTemplateCache } from './renderer.js';
4
+ import { renderToSVG, clearTemplateCache } from './renderer.js';
5
+ import { loadConfig } from './config.js';
6
+ import chalk from 'chalk';
9
7
  const __filename = fileURLToPath(import.meta.url);
10
8
  const __dirname = path.dirname(__filename);
11
9
  export async function startVideoPreview(options) {
12
- const { templateName, templatePath, props, meta, port, open } = options;
10
+ const { templateName, templatePath, props, meta, port, open, debug } = options;
13
11
  const app = express();
14
- // Try to load loopwind.json by searching up from template directory
15
- let loopwindConfig = null;
12
+ const loopwindConfig = await loadConfig();
16
13
  const templateDir = path.dirname(templatePath);
17
- // Search up from template directory to find _loopwind/loopwind.json
18
- let searchDir = templateDir;
19
- while (searchDir !== path.dirname(searchDir)) { // Stop at root
20
- const loopwindJsonPath = path.join(searchDir, '_loopwind', 'loopwind.json');
21
- try {
22
- const loopwindContent = await fs.readFile(loopwindJsonPath, 'utf-8');
23
- loopwindConfig = JSON.parse(loopwindContent);
24
- break;
25
- }
26
- catch {
27
- // Not found here, try parent
28
- searchDir = path.dirname(searchDir);
29
- }
30
- }
31
- // Also try current working directory as fallback
32
- if (!loopwindConfig) {
33
- try {
34
- const cwdLoopwindPath = path.join(process.cwd(), '_loopwind', 'loopwind.json');
35
- const loopwindContent = await fs.readFile(cwdLoopwindPath, 'utf-8');
36
- loopwindConfig = JSON.parse(loopwindContent);
37
- }
38
- catch {
39
- // loopwind.json doesn't exist or couldn't be read - that's fine
14
+ // Pre-render all frames as SVG
15
+ console.log(chalk.dim('Pre-rendering video frames...'));
16
+ const { fps, duration } = meta.video;
17
+ const totalFrames = Math.floor(fps * duration);
18
+ const frames = [];
19
+ const startTime = Date.now();
20
+ for (let frame = 0; frame < totalFrames; frame++) {
21
+ const progress = frame / totalFrames;
22
+ const svg = await renderToSVG(templateName, {
23
+ ...props,
24
+ frame,
25
+ progress,
26
+ }, { config: loopwindConfig, debug });
27
+ frames.push(svg);
28
+ // Show progress every 30 frames
29
+ if (frame % 30 === 0 || frame === totalFrames - 1) {
30
+ const percent = Math.round((frame / totalFrames) * 100);
31
+ process.stdout.write(`\r${chalk.dim(` Rendering frames... ${percent}% (${frame + 1}/${totalFrames})`)}`);
40
32
  }
41
33
  }
42
- // Load Tailwind config for custom colors and theme values
43
- let tailwindTheme = null;
44
- try {
45
- const tailwindConfig = await loadTailwindConfig();
46
- if (tailwindConfig?.theme) {
47
- // Extract only the serializable theme parts
48
- tailwindTheme = {
49
- theme: {
50
- colors: tailwindConfig.theme.colors,
51
- spacing: tailwindConfig.theme.spacing,
52
- fontSize: tailwindConfig.theme.fontSize,
53
- borderRadius: tailwindConfig.theme.borderRadius,
54
- extend: tailwindConfig.theme.extend ? {
55
- colors: tailwindConfig.theme.extend.colors,
56
- spacing: tailwindConfig.theme.extend.spacing,
57
- fontSize: tailwindConfig.theme.extend.fontSize,
58
- borderRadius: tailwindConfig.theme.extend.borderRadius,
59
- } : undefined
60
- }
61
- };
34
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
35
+ console.log(`\n${chalk.green('✓')} ${chalk.dim(`Rendered ${totalFrames} frames in ${elapsed}s`)}\n`);
36
+ // Serve individual frame as SVG
37
+ app.get('/api/frame/:frameNumber', (req, res) => {
38
+ const frameNum = parseInt(req.params.frameNumber);
39
+ if (frameNum < 0 || frameNum >= frames.length) {
40
+ res.status(404).send('Frame not found');
41
+ return;
62
42
  }
63
- }
64
- catch {
65
- // Tailwind config couldn't be loaded - that's fine
66
- }
67
- // Generate @font-face CSS for custom fonts from loopwind.json
68
- let fontFaceCSS = '';
69
- const fontFilePaths = {};
70
- let defaultFontFamily = "'Noto Sans', system-ui, sans-serif"; // Match Satori's default
71
- if (loopwindConfig?.fonts) {
72
- for (const [key, value] of Object.entries(loopwindConfig.fonts || {})) {
73
- if (!value || typeof value !== 'object')
74
- continue;
75
- const fontDef = value;
76
- if (fontDef.files && fontDef.files.length > 0 && fontDef.family && fontDef.family[0]) {
77
- const fontName = fontDef.family[0];
78
- // Use the sans font as the default body font
79
- if (key === 'sans') {
80
- defaultFontFamily = fontDef.family.map(f => f.includes(' ') ? `'${f}'` : f).join(', ');
81
- }
82
- for (const fileDef of fontDef.files) {
83
- // Resolve font path relative to CWD
84
- const fontPath = path.resolve(fileDef.path);
85
- const fontFileName = path.basename(fontPath);
86
- const fontExt = path.extname(fontPath).toLowerCase();
87
- // Map extension to format
88
- const formatMap = {
89
- '.woff2': 'woff2',
90
- '.woff': 'woff',
91
- '.ttf': 'truetype',
92
- '.otf': 'opentype',
93
- };
94
- const format = formatMap[fontExt] || 'truetype';
95
- // Store path for serving
96
- fontFilePaths[fontFileName] = fontPath;
97
- fontFaceCSS += `
98
- @font-face {
99
- font-family: '${fontName}';
100
- src: url('/fonts/${fontFileName}') format('${format}');
101
- font-weight: ${fileDef.weight || 400};
102
- font-style: ${fileDef.style || 'normal'};
103
- }`;
43
+ res.setHeader('Content-Type', 'image/svg+xml');
44
+ res.setHeader('Cache-Control', 'no-cache');
45
+ res.send(frames[frameNum]);
46
+ });
47
+ // Serve template directory for local assets
48
+ app.use('/template', express.static(templateDir));
49
+ // Regenerate frames endpoint (for HMR-like behavior)
50
+ app.get('/api/regenerate', async (req, res) => {
51
+ try {
52
+ console.log(chalk.blue('\n📝 Regenerating frames...'));
53
+ // Clear caches
54
+ clearTemplateCache();
55
+ frames.length = 0;
56
+ // Re-render all frames
57
+ const startTime = Date.now();
58
+ for (let frame = 0; frame < totalFrames; frame++) {
59
+ const progress = frame / totalFrames;
60
+ const svg = await renderToSVG(templateName, {
61
+ ...props,
62
+ frame,
63
+ progress,
64
+ }, { config: loopwindConfig, debug });
65
+ frames.push(svg);
66
+ if (frame % 30 === 0 || frame === totalFrames - 1) {
67
+ const percent = Math.round((frame / totalFrames) * 100);
68
+ process.stdout.write(`\r${chalk.dim(` Rendering frames... ${percent}% (${frame + 1}/${totalFrames})`)}`);
104
69
  }
105
70
  }
71
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
72
+ console.log(`\n${chalk.green('✓')} ${chalk.dim(`Regenerated ${totalFrames} frames in ${elapsed}s`)}\n`);
73
+ res.json({ success: true, totalFrames });
106
74
  }
107
- }
108
- // Create temporary directory for generated files
109
- const tempDir = path.join(__dirname, '../../.video-preview');
110
- await fs.mkdir(tempDir, { recursive: true });
111
- // Get the dist directory for imports
112
- // __dirname at runtime is dist/lib (when running from compiled package)
113
- // We use the compiled JS files which Vite can still HMR
114
- const distLibDir = path.join(__dirname);
115
- // Generate the entry file
116
- const entryContent = `
117
- import React from 'react';
118
- import { createRoot } from 'react-dom/client';
119
- import { VideoPlayer } from '${distLibDir}/video-player.js';
120
- import { tw } from '${distLibDir}/tailwind-browser.js';
121
- import Template, { meta } from '${templatePath}';
122
-
123
- const props = ${JSON.stringify(props)};
124
-
125
- // Tailwind config theme (loaded server-side and serialized)
126
- const tailwindConfig = ${JSON.stringify(tailwindTheme)};
127
-
128
- // loopwind.json config (loaded server-side and embedded)
129
- const loopwindConfig = ${JSON.stringify(loopwindConfig)};
130
-
131
- function App() {
132
- return (
133
- <VideoPlayer
134
- Template={Template}
135
- meta={meta}
136
- props={props}
137
- tailwindConfig={tailwindConfig}
138
- loopwindConfig={loopwindConfig}
139
- />
140
- );
141
- }
142
-
143
- const container = document.getElementById('root');
144
- const root = createRoot(container);
145
- root.render(<App />);
146
-
147
- // Hot module replacement
148
- if (import.meta.hot) {
149
- import.meta.hot.accept();
150
- }
151
- `;
152
- const entryPath = path.join(tempDir, 'entry.tsx');
153
- await fs.writeFile(entryPath, entryContent);
154
- // Generate index.html
155
- const htmlContent = `
75
+ catch (error) {
76
+ console.error(chalk.red('✖ Regeneration error:'), error.message);
77
+ res.status(500).json({ error: error.message });
78
+ }
79
+ });
80
+ // Serve index.html for root
81
+ app.get('/', (req, res) => {
82
+ const html = `
156
83
  <!DOCTYPE html>
157
84
  <html lang="en">
158
85
  <head>
159
86
  <meta charset="UTF-8">
160
87
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
161
88
  <title>Video Preview: ${templateName}</title>
162
- <style>${fontFaceCSS}
163
- html {
164
- font-size: 16px; /* Match Satori's default rem base */
165
- }
89
+ <style>
166
90
  * {
167
91
  margin: 0;
168
92
  padding: 0;
@@ -170,7 +94,7 @@ if (import.meta.hot) {
170
94
  }
171
95
  body {
172
96
  overflow: hidden;
173
- font-family: ${defaultFontFamily};
97
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
174
98
  }
175
99
  /* Custom range slider styling */
176
100
  input[type="range"] {
@@ -208,105 +132,206 @@ if (import.meta.hot) {
208
132
  </head>
209
133
  <body>
210
134
  <div id="root"></div>
211
- <script type="module" src="/entry.tsx"></script>
212
- </body>
213
- </html>
214
- `;
215
- const htmlPath = path.join(tempDir, 'index.html');
216
- await fs.writeFile(htmlPath, htmlContent);
217
- // Dynamically import the React plugin
218
- // @ts-expect-error - moduleResolution incompatibility with @vitejs/plugin-react types
219
- const { default: react } = await import('@vitejs/plugin-react');
220
- // Create Vite server
221
- const vite = await createViteServer({
222
- root: tempDir,
223
- server: {
224
- middlewareMode: true,
225
- hmr: true,
226
- },
227
- plugins: [react()],
228
- define: {
229
- 'process.env.NODE_ENV': JSON.stringify('development'),
230
- 'process.env': JSON.stringify({}),
231
- },
232
- resolve: {
233
- alias: {
234
- // Ensure proper resolution of our library files
235
- }
236
- },
237
- optimizeDeps: {
238
- include: ['react', 'react-dom'],
239
- // Don't pre-bundle our local files
240
- exclude: ['video-player']
241
- },
242
- build: {
243
- rollupOptions: {
244
- input: entryPath
245
- }
135
+ <script type="module">
136
+ const meta = ${JSON.stringify(meta)};
137
+ const totalFrames = ${totalFrames};
138
+
139
+ // Mount the simplified video player
140
+ const root = document.getElementById('root');
141
+
142
+ let frame = 0;
143
+ let isPlaying = true;
144
+ let isLooping = true;
145
+ let animationId = null;
146
+ let lastTime = performance.now();
147
+
148
+ const { fps, duration } = meta.video;
149
+ const durationMs = duration * 1000;
150
+
151
+ // Create UI
152
+ root.innerHTML = \`
153
+ <div style="display: flex; flex-direction: column; height: 100vh; background: #0a0a0a; color: #ffffff;">
154
+ <!-- Header -->
155
+ <div style="background: #18181b; border-bottom: 1px solid #27272a; padding: 1rem 2rem; display: flex; justify-content: space-between; align-items: center;">
156
+ <h1 style="font-size: 1.25rem; font-weight: 600; margin: 0;">loopwind preview: ${templateName}</h1>
157
+ <div style="display: flex; gap: 0.5rem; align-items: center;">
158
+ <button id="regenerate-btn" style="background: #3b82f6; border: none; color: white; cursor: pointer; padding: 0.5rem 1rem; border-radius: 0.375rem; font-size: 0.875rem; font-weight: 500;">
159
+ Regenerate
160
+ </button>
161
+ <span style="background: #7c3aed; color: white; padding: 0.25rem 0.75rem; border-radius: 0.375rem; font-size: 0.875rem; font-weight: 500;">VIDEO</span>
162
+ <span style="background: #27272a; color: #a1a1aa; padding: 0.25rem 0.75rem; border-radius: 0.375rem; font-size: 0.75rem; font-family: monospace;">
163
+ \${meta.size.width}×\${meta.size.height} @ \${fps}fps
164
+ </span>
165
+ </div>
166
+ </div>
167
+
168
+ <!-- Canvas -->
169
+ <div id="canvas-container" style="flex: 1; display: flex; align-items: center; justify-content: center; padding: 2rem; overflow: hidden;">
170
+ <div id="video-wrapper" style="box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5); border-radius: 0.5rem; overflow: hidden; border: 1px solid #3f3f46; max-width: 100%; max-height: 100%;">
171
+ <img id="frame-img" style="display: block; width: 100%; height: 100%; object-fit: contain;" width="\${meta.size.width}" height="\${meta.size.height}" />
172
+ </div>
173
+ </div>
174
+
175
+ <!-- Controls -->
176
+ <div style="background: #18181b; border-top: 1px solid #27272a; padding: 1rem 2rem;">
177
+ <div style="margin-bottom: 0.75rem;">
178
+ <input type="range" id="scrubber" min="0" max="100" step="0.1" value="0" style="width: 100%; height: 6px; border-radius: 3px; cursor: pointer; background: linear-gradient(to right, #7c3aed 0%, #3f3f46 0%);" />
179
+ </div>
180
+
181
+ <div style="display: flex; align-items: center; justify-content: space-between;">
182
+ <div style="display: flex; align-items: center; gap: 0.5rem;">
183
+ <button id="restart-btn" style="background: transparent; border: none; color: #a1a1aa; cursor: pointer; padding: 0.5rem; font-size: 1rem;">⏮</button>
184
+ <button id="step-back-btn" style="background: transparent; border: none; color: #a1a1aa; cursor: pointer; padding: 0.5rem; font-size: 1rem;">⏪</button>
185
+ <button id="play-pause-btn" style="background: #7c3aed; border: none; color: white; cursor: pointer; padding: 0.75rem 1rem; border-radius: 0.375rem; font-size: 1rem; min-width: 3rem;">⏸</button>
186
+ <button id="step-forward-btn" style="background: transparent; border: none; color: #a1a1aa; cursor: pointer; padding: 0.5rem; font-size: 1rem;">⏩</button>
187
+ <button id="loop-btn" style="background: #3f3f46; border: 1px solid #3f3f46; color: #ffffff; cursor: pointer; padding: 0.5rem 0.75rem; border-radius: 0.375rem; font-size: 0.75rem; margin-left: 0.5rem;">🔁</button>
188
+ </div>
189
+
190
+ <div style="font-family: monospace; font-size: 0.875rem; color: #a1a1aa;">
191
+ <span id="current-time" style="color: #ffffff;">0ms</span>
192
+ /
193
+ <span>\${Math.floor(durationMs)}ms</span>
194
+ <span style="margin-left: 1rem; color: #71717a;">Frame <span id="current-frame">0</span> / \${totalFrames}</span>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ </div>
199
+ \`;
200
+
201
+ // Get elements
202
+ const frameImg = document.getElementById('frame-img');
203
+ const scrubber = document.getElementById('scrubber');
204
+ const playPauseBtn = document.getElementById('play-pause-btn');
205
+ const restartBtn = document.getElementById('restart-btn');
206
+ const stepBackBtn = document.getElementById('step-back-btn');
207
+ const stepForwardBtn = document.getElementById('step-forward-btn');
208
+ const loopBtn = document.getElementById('loop-btn');
209
+ const regenerateBtn = document.getElementById('regenerate-btn');
210
+ const currentTimeEl = document.getElementById('current-time');
211
+ const currentFrameEl = document.getElementById('current-frame');
212
+
213
+ // Update frame
214
+ function updateFrame() {
215
+ const frameNum = Math.floor(frame);
216
+ frameImg.src = \`/api/frame/\${frameNum}?t=\${Date.now()}\`;
217
+
218
+ const currentMs = (frame / totalFrames) * durationMs;
219
+ const progress = (frame / totalFrames) * 100;
220
+
221
+ currentTimeEl.textContent = \`\${Math.floor(currentMs)}ms\`;
222
+ currentFrameEl.textContent = frameNum;
223
+ scrubber.value = progress;
224
+ scrubber.style.background = \`linear-gradient(to right, #7c3aed \${progress}%, #3f3f46 \${progress}%)\`;
225
+ }
226
+
227
+ // Animation loop
228
+ function animate(time) {
229
+ const delta = time - lastTime;
230
+ lastTime = time;
231
+
232
+ frame += (delta / 1000) * fps;
233
+
234
+ if (frame >= totalFrames) {
235
+ if (isLooping) {
236
+ frame = frame % totalFrames;
237
+ } else {
238
+ frame = totalFrames - 1;
239
+ isPlaying = false;
240
+ playPauseBtn.textContent = '▶';
246
241
  }
242
+ }
243
+
244
+ updateFrame();
245
+
246
+ if (isPlaying) {
247
+ animationId = requestAnimationFrame(animate);
248
+ }
249
+ }
250
+
251
+ // Start animation
252
+ updateFrame();
253
+ animationId = requestAnimationFrame(animate);
254
+
255
+ // Controls
256
+ scrubber.addEventListener('input', (e) => {
257
+ const value = parseFloat(e.target.value);
258
+ frame = (value / 100) * totalFrames;
259
+ updateFrame();
247
260
  });
248
- // Serve template directory for local assets (videos, images)
249
- app.use('/template', express.static(templateDir));
250
- // Serve font files from loopwind.json
251
- app.get('/fonts/:filename', async (req, res) => {
252
- const filename = req.params.filename;
253
- const fontPath = fontFilePaths[filename];
254
- if (!fontPath) {
255
- res.status(404).send('Font not found');
256
- return;
257
- }
258
- try {
259
- const fontData = await fs.readFile(fontPath);
260
- const ext = path.extname(filename).toLowerCase();
261
- const mimeTypes = {
262
- '.woff2': 'font/woff2',
263
- '.woff': 'font/woff',
264
- '.ttf': 'font/ttf',
265
- '.otf': 'font/otf',
266
- };
267
- res.setHeader('Content-Type', mimeTypes[ext] || 'application/octet-stream');
268
- res.send(fontData);
269
- }
270
- catch {
271
- res.status(404).send('Font not found');
272
- }
261
+
262
+ playPauseBtn.addEventListener('click', () => {
263
+ if (!isPlaying && frame >= totalFrames - 1) {
264
+ frame = 0;
265
+ }
266
+ isPlaying = !isPlaying;
267
+ playPauseBtn.textContent = isPlaying ? '⏸' : '▶';
268
+ if (isPlaying) {
269
+ lastTime = performance.now();
270
+ animationId = requestAnimationFrame(animate);
271
+ } else if (animationId) {
272
+ cancelAnimationFrame(animationId);
273
+ }
273
274
  });
274
- // API endpoint for SDK-based video rendering
275
- app.get('/api/render-video', async (req, res) => {
276
- try {
277
- console.log('Starting video render...');
278
- // Clear template cache to pick up latest changes
279
- clearTemplateCache();
280
- // Create temp output path
281
- const outputPath = path.join(tempDir, `render-${Date.now()}.mp4`);
282
- await renderVideo(templateName, props, outputPath);
283
- console.log('Video render complete');
284
- // Read the rendered video
285
- const videoBuffer = await fs.readFile(outputPath);
286
- // Clean up temp file
287
- await fs.unlink(outputPath).catch(() => { });
288
- res.setHeader('Content-Type', 'video/mp4');
289
- res.setHeader('Cache-Control', 'no-cache');
290
- res.send(videoBuffer);
291
- }
292
- catch (error) {
293
- console.error('Render error:', error.message);
294
- res.status(500).json({ error: error.message });
295
- }
275
+
276
+ restartBtn.addEventListener('click', () => {
277
+ frame = 0;
278
+ isPlaying = true;
279
+ playPauseBtn.textContent = '⏸';
280
+ lastTime = performance.now();
281
+ updateFrame();
282
+ if (!animationId) {
283
+ animationId = requestAnimationFrame(animate);
284
+ }
296
285
  });
297
- // Use Vite's connect instance as middleware
298
- app.use(vite.middlewares);
299
- // Serve index.html for root
300
- app.get('/', async (req, res) => {
301
- try {
302
- let html = await fs.readFile(htmlPath, 'utf-8');
303
- html = await vite.transformIndexHtml(req.url, html);
304
- res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
305
- }
306
- catch (e) {
307
- vite.ssrFixStacktrace(e);
308
- res.status(500).end(e.message);
286
+
287
+ stepBackBtn.addEventListener('click', () => {
288
+ isPlaying = false;
289
+ playPauseBtn.textContent = '';
290
+ if (animationId) cancelAnimationFrame(animationId);
291
+ frame = Math.max(0, frame - 1);
292
+ updateFrame();
293
+ });
294
+
295
+ stepForwardBtn.addEventListener('click', () => {
296
+ isPlaying = false;
297
+ playPauseBtn.textContent = '▶';
298
+ if (animationId) cancelAnimationFrame(animationId);
299
+ frame = Math.min(totalFrames - 1, frame + 1);
300
+ updateFrame();
301
+ });
302
+
303
+ loopBtn.addEventListener('click', () => {
304
+ isLooping = !isLooping;
305
+ loopBtn.style.background = isLooping ? '#3f3f46' : 'transparent';
306
+ loopBtn.style.color = isLooping ? '#ffffff' : '#71717a';
307
+ });
308
+
309
+ regenerateBtn.addEventListener('click', async () => {
310
+ regenerateBtn.disabled = true;
311
+ regenerateBtn.textContent = 'Regenerating...';
312
+ regenerateBtn.style.opacity = '0.5';
313
+
314
+ try {
315
+ const response = await fetch('/api/regenerate');
316
+ if (response.ok) {
317
+ // Reload the page to get fresh frames
318
+ window.location.reload();
319
+ } else {
320
+ alert('Regeneration failed');
309
321
  }
322
+ } catch (error) {
323
+ alert('Regeneration failed: ' + error.message);
324
+ }
325
+
326
+ regenerateBtn.disabled = false;
327
+ regenerateBtn.textContent = 'Regenerate';
328
+ regenerateBtn.style.opacity = '1';
329
+ });
330
+ </script>
331
+ </body>
332
+ </html>
333
+ `;
334
+ res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
310
335
  });
311
336
  // Start server
312
337
  const server = app.listen(port, () => {
@@ -321,16 +346,8 @@ if (import.meta.hot) {
321
346
  });
322
347
  // Cleanup function
323
348
  const cleanup = async () => {
324
- await vite.close();
325
349
  server.close();
326
- // Clean up temp files
327
- try {
328
- await fs.rm(tempDir, { recursive: true });
329
- }
330
- catch {
331
- // Ignore cleanup errors
332
- }
333
350
  };
334
- return { server, vite, cleanup };
351
+ return { server, cleanup };
335
352
  }
336
353
  //# sourceMappingURL=video-preview.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"video-preview.js","sourceRoot":"","sources":["../../src/lib/video-preview.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"video-preview.js","sourceRoot":"","sources":["../../src/lib/video-preview.ts"],"names":[],"mappings":"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"}
@@ -30,6 +30,7 @@ export declare function renderVideo(templateName: string, props: TemplateProps,
30
30
  quality?: number;
31
31
  onFrameProgress?: (frame: number, total: number, phase?: 'svg' | 'encode') => void;
32
32
  config?: any;
33
+ debug?: boolean;
33
34
  }): Promise<void>;
34
35
  /**
35
36
  * Render a video template to GIF using gifenc (pure JS, no ffmpeg required)
@@ -37,5 +38,6 @@ export declare function renderVideo(templateName: string, props: TemplateProps,
37
38
  export declare function renderVideoToGif(templateName: string, props: TemplateProps, outputPath: string, options?: {
38
39
  onFrameProgress?: (frame: number, total: number, phase?: 'svg' | 'encode') => void;
39
40
  config?: any;
41
+ debug?: boolean;
40
42
  }): Promise<void>;
41
43
  //# sourceMappingURL=video-renderer.d.ts.map