vueseq 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="./vueseq.svg" alt="VueSeq Logo" width="200">
2
+ <img src="./vueseq.svg" alt="VueSeq Logo" >
3
3
  </p>
4
4
 
5
5
  # VueSeq
@@ -26,6 +26,32 @@ https://github.com/user-attachments/assets/84d01c02-4b4f-4d86-879e-720a7e367967
26
26
 
27
27
  *This video was rendered with VueSeq from [examples/HelloWorld.vue](./examples/HelloWorld.vue)*
28
28
 
29
+ ## What Can You Build?
30
+
31
+ **Your app. As a video. Using your actual components.**
32
+
33
+ Install VueSeq into your Vue project and create stunning videos that perfectly match your brand—because they *are* your brand. No recreating UIs in After Effects. No screen recording artifacts. Just your components, animated with GSAP, rendered to pixel-perfect video.
34
+
35
+ ### 🎬 App Showcases & Demos
36
+ Create promotional videos using your real UI components. Product tours, feature walkthroughs, "What's New in v2.0" announcements—all with your exact design system, your exact colors, your exact components.
37
+
38
+ ### 📱 Social Media Content
39
+ Generate short-form videos for Twitter, LinkedIn, Instagram. Show off a feature in 15 seconds. Your followers see your actual product, not a mockup.
40
+
41
+ ### 📚 Documentation & Tutorials
42
+ Animate how your features work. Show state transitions, user flows, component interactions. Embed videos in your docs that never go stale—regenerate them when your UI changes.
43
+
44
+ ### 📊 Data Visualizations
45
+ Animated charts, dashboards, infographics. Watch your bar charts grow, your line graphs draw, your data come alive.
46
+
47
+ ### 🎨 Design System Demos
48
+ Showcase your component library in motion. Let designers and developers *see* how components animate, transition, and interact.
49
+
50
+ ---
51
+
52
+ **The idea is simple:** If you can build it in Vue, you can render it to video. One command. Deterministic output. Every time.
53
+
54
+
29
55
  ## Philosophy
30
56
 
31
57
  VueSeq is intentionally minimal. We bundle only the essentials: **Vue**, **GSAP**, **Playwright**, and **Vite**.
@@ -133,10 +159,10 @@ span {
133
159
  ### 2. Render to Video
134
160
 
135
161
  ```bash
136
- npx vueseq MyVideo.vue -d 4 -o hello.mp4
162
+ npx vueseq examples/HelloWorld.vue -o examples/hello.mp4
137
163
  ```
138
164
 
139
- That's it! Your video will be rendered at 1920x1080, 30fps.
165
+ That's it! Duration is auto-detected from your timeline. Your video will be rendered at 1920x1080, 30fps.
140
166
 
141
167
  ## CLI Options
142
168
 
@@ -145,7 +171,7 @@ vueseq <Video.vue> [options]
145
171
 
146
172
  Options:
147
173
  -o, --output Output file (default: ./output.mp4)
148
- -d, --duration Duration in seconds (required)
174
+ -d, --duration Duration in seconds (auto-detected from timeline if not specified)
149
175
  -f, --fps Frames per second (default: 30)
150
176
  -w, --width Video width in pixels (default: 1920)
151
177
  -H, --height Video height in pixels (default: 1080)
@@ -155,14 +181,20 @@ Options:
155
181
  ### Examples
156
182
 
157
183
  ```bash
158
- # Basic render
159
- vueseq Intro.vue -d 5 -o intro.mp4
184
+ # Simple example (auto-detects duration)
185
+ npx vueseq examples/HelloWorld.vue -o examples/hello.mp4
186
+
187
+ # Multi-scene showcase
188
+ npx vueseq examples/Showcase.vue -o examples/showcase.mp4
189
+
190
+ # Override duration (partial render)
191
+ npx vueseq examples/Showcase.vue -d 10 -o examples/partial.mp4
160
192
 
161
193
  # 4K at 60fps
162
- vueseq Intro.vue -d 10 -f 60 -w 3840 -H 2160 -o intro-4k.mp4
194
+ npx vueseq examples/HelloWorld.vue -f 60 -w 3840 -H 2160 -o examples/hello-4k.mp4
163
195
 
164
196
  # Square for social media
165
- vueseq Story.vue -d 15 -w 1080 -H 1080 -o story.mp4
197
+ npx vueseq examples/HelloWorld.vue -w 1080 -H 1080 -o examples/hello-square.mp4
166
198
  ```
167
199
 
168
200
  ## Programmatic API
package/bin/cli.js CHANGED
@@ -26,15 +26,15 @@ USAGE:
26
26
 
27
27
  OPTIONS:
28
28
  -o, --output Output file (default: ./output.mp4)
29
- -d, --duration Duration in seconds (required)
29
+ -d, --duration Duration in seconds (auto-detected if not specified)
30
30
  -f, --fps Frames per second (default: 30)
31
31
  -w, --width Video width in pixels (default: 1920)
32
32
  -H, --height Video height in pixels (default: 1080)
33
33
  --help Show this help message
34
34
 
35
35
  EXAMPLE:
36
- vueseq MyAnimation.vue -d 5 -o my-video.mp4
37
- vueseq Intro.vue -d 10 -f 60 -w 3840 -H 2160 -o intro-4k.mp4
36
+ npx vueseq examples/HelloWorld.vue -o examples/hello.mp4
37
+ npx vueseq examples/Showcase.vue -w 1280 -H 720 -f 60 -o examples/showcase.mp4
38
38
  `)
39
39
  }
40
40
 
@@ -76,16 +76,18 @@ if (extname(inputPath) !== '.vue') {
76
76
  process.exit(1)
77
77
  }
78
78
 
79
- // Validate duration
80
- if (!values.duration) {
81
- console.error('Error: Duration is required. Use -d or --duration to specify duration in seconds.')
82
- process.exit(1)
83
- }
79
+ // Parse duration if provided
80
+ let duration = null
81
+ let durationAuto = false
84
82
 
85
- const duration = parseFloat(values.duration)
86
- if (isNaN(duration) || duration <= 0) {
87
- console.error('Error: Duration must be a positive number')
88
- process.exit(1)
83
+ if (values.duration) {
84
+ duration = parseFloat(values.duration)
85
+ if (isNaN(duration) || duration <= 0) {
86
+ console.error('Error: Duration must be a positive number')
87
+ process.exit(1)
88
+ }
89
+ } else {
90
+ durationAuto = true
89
91
  }
90
92
 
91
93
  // Parse numeric options
@@ -104,13 +106,25 @@ if (isNaN(width) || width <= 0 || isNaN(height) || height <= 0) {
104
106
  }
105
107
 
106
108
  // Import renderer and start rendering
107
- console.log(`\nVueSeq - Rendering ${input}`)
108
- console.log(` Duration: ${duration}s at ${fps}fps (${Math.ceil(duration * fps)} frames)`)
109
- console.log(` Resolution: ${width}x${height}`)
110
- console.log(` Output: ${values.output}\n`)
111
-
112
109
  try {
113
110
  const { renderToMp4 } = await import('../src/renderer/encode.js')
111
+ const { getTimelineDuration } = await import('../src/renderer/render.js')
112
+
113
+ // Auto-detect duration if not provided
114
+ if (durationAuto) {
115
+ console.log(`\nVueSeq - Detecting timeline duration...`)
116
+ duration = await getTimelineDuration({ input: inputPath, width, height })
117
+ if (!duration || duration <= 0) {
118
+ console.error('Error: Could not auto-detect duration. Use -d to specify manually.')
119
+ process.exit(1)
120
+ }
121
+ console.log(` Auto-detected: ${duration.toFixed(2)}s`)
122
+ }
123
+
124
+ console.log(`\nVueSeq - Rendering ${input}`)
125
+ console.log(` Duration: ${duration}s at ${fps}fps (${Math.ceil(duration * fps)} frames)${durationAuto ? ' (auto)' : ''}`)
126
+ console.log(` Resolution: ${width}x${height}`)
127
+ console.log(` Output: ${values.output}\n`)
114
128
 
115
129
  const startTime = Date.now()
116
130
  let lastLoggedPercent = -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueseq",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A minimal, deterministic video renderer for Vue 3 + GSAP",
5
5
  "type": "module",
6
6
  "bin": {
@@ -51,4 +51,4 @@
51
51
  "engines": {
52
52
  "node": ">=18.0.0"
53
53
  }
54
- }
54
+ }
@@ -13,19 +13,56 @@ import { createVideoServer } from '../bundler/vite.js'
13
13
  import { mkdir } from 'fs/promises'
14
14
  import { join } from 'path'
15
15
 
16
+ /**
17
+ * Get the timeline duration from a Vue component
18
+ * @param {Object} options
19
+ * @param {string} options.input - Absolute path to the Video.vue component
20
+ * @param {number} [options.width=1920] - Video width in pixels
21
+ * @param {number} [options.height=1080] - Video height in pixels
22
+ * @returns {Promise<number|null>} Duration in seconds, or null if not detectable
23
+ */
24
+ export async function getTimelineDuration(options) {
25
+ const { input, width = 1920, height = 1080 } = options
26
+
27
+ const { url, cleanup } = await createVideoServer({ input, width, height })
28
+
29
+ const browser = await chromium.launch({ headless: true })
30
+ const context = await browser.newContext({
31
+ viewport: { width, height },
32
+ deviceScaleFactor: 1
33
+ })
34
+ const page = await context.newPage()
35
+
36
+ try {
37
+ await page.goto(url, { waitUntil: 'networkidle' })
38
+ await page.waitForFunction(
39
+ () => window.__VUESEQ_READY__ === true,
40
+ { timeout: 30000 }
41
+ )
42
+ // Give Vue/GSAP a moment to set up timelines
43
+ await page.waitForTimeout(100)
44
+
45
+ const duration = await page.evaluate(() => window.__VUESEQ_GET_DURATION__?.())
46
+ return duration
47
+ } finally {
48
+ await browser.close()
49
+ await cleanup()
50
+ }
51
+ }
52
+
16
53
  /**
17
54
  * Render frames from a Vue component
18
55
  * @param {Object} options
19
56
  * @param {string} options.input - Absolute path to the Video.vue component
20
57
  * @param {number} [options.fps=30] - Frames per second
21
- * @param {number} options.duration - Duration in seconds
58
+ * @param {number} [options.duration] - Duration in seconds (auto-detected if not provided)
22
59
  * @param {number} [options.width=1920] - Video width in pixels
23
60
  * @param {number} [options.height=1080] - Video height in pixels
24
61
  * @param {function} [options.onProgress] - Progress callback
25
62
  * @returns {Promise<{framesDir: string, totalFrames: number, cleanup: () => Promise<void>}>}
26
63
  */
27
64
  export async function renderFrames(options) {
28
- const {
65
+ let {
29
66
  input,
30
67
  fps = 30,
31
68
  duration,
@@ -34,8 +71,12 @@ export async function renderFrames(options) {
34
71
  onProgress
35
72
  } = options
36
73
 
37
- if (!duration || duration <= 0) {
38
- throw new Error('Duration must be a positive number (in seconds)')
74
+ // Auto-detect duration if not provided
75
+ if (!duration) {
76
+ duration = await getTimelineDuration({ input, width, height })
77
+ if (!duration || duration <= 0) {
78
+ throw new Error('Could not auto-detect duration. Specify -d/--duration manually.')
79
+ }
39
80
  }
40
81
 
41
82
  const totalFrames = Math.ceil(duration * fps)
@@ -35,7 +35,17 @@ window.__VUESEQ_SET_CONFIG__ = (config) => {
35
35
  window.__VUESEQ_CONFIG__ = config
36
36
  }
37
37
 
38
- // 6. Mark as ready after a microtask to ensure Vue is mounted
38
+ // 6. Expose timeline duration for auto-detection
39
+ window.__VUESEQ_GET_DURATION__ = () => {
40
+ const duration = gsap.globalTimeline.duration()
41
+ // Return null for infinite timelines (repeat: -1)
42
+ if (duration === Infinity || duration > 3600) {
43
+ return null
44
+ }
45
+ return duration
46
+ }
47
+
48
+ // 7. Mark as ready after a microtask to ensure Vue is mounted
39
49
  queueMicrotask(() => {
40
50
  window.__VUESEQ_READY__ = true
41
51
  })