@t3lnet/sceneforge 1.0.18 → 1.0.20

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.
@@ -111,8 +111,10 @@ async function buildConcatWithIntroOutro(stepFiles, demoDir, introPath, outroPat
111
111
 
112
112
  const concatInputs = inputPaths.map(({ index }) => `[${index}:v:0][${index}:a:0]`).join("");
113
113
  const scaleFilter = buildScaleFilter(outputDimensions);
114
- const scaleChain = scaleFilter ? `,${scaleFilter}` : "";
115
- const filterGraph = `${concatInputs}concat=n=${inputPaths.length}:v=1:a=1${scaleChain}[outv][outa]`;
114
+ // If scaling, we need to output concat to temp labels, then apply scale to video only
115
+ const filterGraph = scaleFilter
116
+ ? `${concatInputs}concat=n=${inputPaths.length}:v=1:a=1[tmpv][outa];[tmpv]${scaleFilter}[outv]`
117
+ : `${concatInputs}concat=n=${inputPaths.length}:v=1:a=1[outv][outa]`;
116
118
  const encodingArgs = getVideoEncodingArgs(qualityOptions);
117
119
 
118
120
  await runFFmpeg([
@@ -337,8 +339,10 @@ async function concatDemo(demoName, paths, options = {}) {
337
339
  const inputArgs = stepFiles.flatMap((file) => ["-i", path.join(demoDir, file)]);
338
340
  const concatInputs = stepFiles.map((_, index) => `[${index}:v:0][${index}:a:0]`).join("");
339
341
  const scaleFilter = buildScaleFilter(outputDimensions);
340
- const scaleChain = scaleFilter ? `,${scaleFilter}` : "";
341
- const filterGraph = `${concatInputs}concat=n=${stepFiles.length}:v=1:a=1${scaleChain}[outv][outa]`;
342
+ // If scaling, we need to output concat to temp labels, then apply scale to video only
343
+ const filterGraph = scaleFilter
344
+ ? `${concatInputs}concat=n=${stepFiles.length}:v=1:a=1[tmpv][outa];[tmpv]${scaleFilter}[outv]`
345
+ : `${concatInputs}concat=n=${stepFiles.length}:v=1:a=1[outv][outa]`;
342
346
  const encodingArgs = getVideoEncodingArgs(qualityOptions);
343
347
 
344
348
  await runFFmpeg([
@@ -19,7 +19,6 @@ import {
19
19
  parseViewportArgs,
20
20
  parseDeviceScaleFactor,
21
21
  getViewportHelpText,
22
- logViewportSettings,
23
22
  } from "../utils/dimensions.js";
24
23
 
25
24
  function printHelp() {
@@ -213,17 +212,37 @@ export async function runRecordDemoCommand(argv) {
213
212
  await ensureDir(outputPaths.outputDir);
214
213
  await ensureDir(outputPaths.videosDir);
215
214
 
216
- const viewport = parseViewportArgs(args, getFlagValue);
217
- const deviceScaleFactor = parseDeviceScaleFactor(args, getFlagValue);
218
-
219
- logViewportSettings(viewport, deviceScaleFactor, "[record]");
220
-
221
- // Calculate video recording size (viewport * scale factor for high-DPI capture)
215
+ const requestedViewport = parseViewportArgs(args, getFlagValue);
216
+ const zoomFactor = parseDeviceScaleFactor(args, getFlagValue);
217
+
218
+ // To achieve "zoom", we use a smaller viewport but record at the requested resolution.
219
+ // This makes content appear larger (zoomed in) in the final video.
220
+ // Example: --viewport 1080p --zoom 150 means:
221
+ // - User wants 1920x1080 output video
222
+ // - Content should appear 1.5x larger (zoomed)
223
+ // - So we set viewport to 1280x720 (1920/1.5 x 1080/1.5)
224
+ // - Record at 1920x1080
225
+ const viewport = zoomFactor !== 1
226
+ ? {
227
+ width: Math.round(requestedViewport.width / zoomFactor),
228
+ height: Math.round(requestedViewport.height / zoomFactor),
229
+ }
230
+ : requestedViewport;
231
+
232
+ // Recording size is the requested viewport (the final video dimensions)
222
233
  const recordVideoSize = {
223
- width: Math.round(viewport.width * deviceScaleFactor),
224
- height: Math.round(viewport.height * deviceScaleFactor),
234
+ width: requestedViewport.width,
235
+ height: requestedViewport.height,
225
236
  };
226
237
 
238
+ // Log what's happening
239
+ if (zoomFactor !== 1) {
240
+ console.log(`[record] Viewport: ${viewport.width}x${viewport.height} (zoomed ${Math.round(zoomFactor * 100)}%)`);
241
+ console.log(`[record] Output video: ${recordVideoSize.width}x${recordVideoSize.height}`);
242
+ } else {
243
+ console.log(`[record] Viewport: ${viewport.width}x${viewport.height}`);
244
+ }
245
+
227
246
  const recordDir = path.join(outputPaths.videosDir, ".recordings", definition.name);
228
247
  if (!noVideo) {
229
248
  await ensureDir(recordDir);
@@ -236,7 +255,8 @@ export async function runRecordDemoCommand(argv) {
236
255
 
237
256
  const context = await browser.newContext({
238
257
  viewport,
239
- deviceScaleFactor,
258
+ // Note: we achieve "zoom" by using a smaller viewport, not deviceScaleFactor
259
+ // deviceScaleFactor only affects pixel density, not content size
240
260
  recordVideo: noVideo ? undefined : { dir: recordDir, size: recordVideoSize },
241
261
  storageState: storageState ? toAbsolute(rootDir, storageState) : undefined,
242
262
  locale: locale || undefined,
@@ -188,13 +188,15 @@ export function getScaleFilterArgs(dimensions) {
188
188
  export function getViewportHelpText() {
189
189
  return `
190
190
  Viewport Options (Recording):
191
- --viewport <WxH|preset> Browser viewport size (default: 1440x900)
191
+ --viewport <WxH|preset> Target video resolution (default: 1440x900)
192
192
  Presets: 720p, 1080p, 1440p, 4k
193
193
  Example: --viewport 1920x1080 or --viewport 1080p
194
- --width <px> Viewport width (overrides --viewport)
195
- --height <px> Viewport height (overrides --viewport)
196
- --zoom <percent> Browser zoom level: 100, 150, 200 (default: 100)
197
- --device-scale-factor <n> Device pixel ratio: 1, 1.5, 2 (alternative to --zoom)`;
194
+ --width <px> Video width (overrides --viewport)
195
+ --height <px> Video height (overrides --viewport)
196
+ --zoom <percent> Content zoom level: 100, 150, 200 (default: 100)
197
+ Makes UI elements appear larger in the video
198
+ Example: --viewport 1080p --zoom 150 records at
199
+ 1920x1080 with content appearing 1.5x larger`;
198
200
  }
199
201
 
200
202
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@t3lnet/sceneforge",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "SceneForge runner and generation utilities for YAML-driven demos",
5
5
  "license": "MIT",
6
6
  "author": "T3LNET",