pro-visu 0.3.0 → 0.3.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.
- package/dist/cli/index.js +83 -5
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/index.ts","../../src/version.ts","../../src/cli/update-check.ts","../../src/cli/commands/init.ts","../../src/utils/paths.ts","../../src/utils/fs.ts","../../src/utils/logger.ts","../../src/browser-install/ensure-chromium.ts","../../src/media/ensure-ffmpeg.ts","../../src/media/ffmpeg.ts","../../src/config/defaults.ts","../../src/config/json-schema.ts","../../src/config/schema.ts","../../src/generators/scroll-reel/index.ts","../../src/generators/scroll-reel/options.ts","../../src/generators/scroll-reel/capture.ts","../../src/generators/scroll-reel/scroll.ts","../../src/media/frame-capture.ts","../../src/generators/scroll-reel/clean-capture.ts","../../src/generators/scroll-reel/annotations.ts","../../src/generators/scroll-reel/timeline.ts","../../src/generators/scroll-reel/capture-frames.ts","../../src/generators/scroll-reel/variants.ts","../../src/generators/scroll-reel/outputs.ts","../../src/generators/scroll-reel/cards.ts","../../src/utils/hash.ts","../../src/generators/scroll-reel/interaction.ts","../../src/generators/require-url.ts","../../src/generators/screenshots/index.ts","../../src/generators/screenshots/options.ts","../../src/utils/concurrency.ts","../../src/generators/screenshots/capture.ts","../../src/generators/wall/options.ts","../../src/generators/scene/scene-options.ts","../../src/generators/specimen/options.ts","../../src/generators/scene/index.ts","../../src/scene/serve.ts","../../src/scene/capture-realtime.ts","../../src/scene/capture-frames.ts","../../src/generators/wall/index.ts","../../src/generators/specimen/index.ts","../../src/generators/palette/index.ts","../../src/generators/palette/options.ts","../../src/generators/palette/color.ts","../../src/generators/palette/html.ts","../../src/generators/palette-reel/options.ts","../../src/generators/palette-reel/index.ts","../../src/generators/image/index.ts","../../src/generators/image/options.ts","../../src/generators/registry.ts","../../src/cli/commands/schema.ts","../../src/config/load.ts","../../src/config/resolve-targets.ts","../../src/cli/interrupt.ts","../../src/cli/reexec.ts","../../src/cli/commands/generate.ts","../../src/cli/run-state.ts","../../src/server/manage-server.ts","../../src/pipeline/runner.ts","../../src/pipeline/browser.ts","../../src/pipeline/context.ts","../../src/pipeline/graph.ts","../../src/pipeline/cache.ts","../../src/manifest/manifest.ts","../../src/manifest/schema.ts","../../src/cli/dashboard/index.tsx","../../src/cli/dashboard/store.ts","../../src/cli/dashboard/Dashboard.tsx","../../src/pipeline/reporter.ts","../../src/cli/dashboard/view-model.ts","../../src/cli/dashboard/Summary.tsx","../../src/utils/format.ts","../../src/cli/ui.ts","../../src/cli/commands/list.ts","../../src/cli/commands/reset.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { cac } from \"cac\";\nimport { TOOL_VERSION } from \"@/version\";\nimport { checkForUpdates } from \"@/cli/update-check\";\nimport { runInit } from \"@/cli/commands/init\";\nimport { runGenerate } from \"@/cli/commands/generate\";\nimport { runList } from \"@/cli/commands/list\";\nimport { runReset } from \"@/cli/commands/reset\";\nimport { runSchema } from \"@/cli/commands/schema\";\n\nconst cli = cac(\"pro-visu\");\n\ncli\n .command(\"init\", \"Scaffold config, gitignore the output dir, and ensure a browser\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .option(\"--no-script\", \"Do not add a package.json script\")\n .option(\"--skip-browser\", \"Do not install Chromium\")\n .option(\"--json\", \"Scaffold a dependency-free JSON config + JSON Schema (for npx / global use)\")\n .action(runInit);\n\ncli\n .command(\"generate\", \"Generate showcase assets defined in your config\")\n .alias(\"gen\")\n .option(\"--config <path>\", \"Path to a config file\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .option(\"--asset <name>\", \"Only generate these assets (repeatable)\")\n .option(\"--concurrency <n>\", \"Override parallelism\")\n .option(\"--skip-browser\", \"Skip the Chromium check/install\")\n .option(\"--skip-server\", \"Skip the managed server (use an already-running site)\")\n .option(\"--skip-build\", \"Skip the server build step (fast iteration when the site is unchanged)\")\n .option(\"--draft\", \"Draft quality: faster, lower-fidelity renders for iteration\")\n .option(\"--cache\", \"Skip assets whose inputs+options are unchanged\")\n .option(\"--verbose\", \"Verbose (debug) logging\")\n .action(runGenerate);\n\ncli\n .command(\"list\", \"List assets recorded in the manifest\")\n .alias(\"ls\")\n .option(\"--config <path>\", \"Path to a config file\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(runList);\n\ncli\n .command(\"schema\", \"Write a JSON Schema for pro-visu.config.json (editor autocomplete)\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .option(\"--out <path>\", \"Output path (default pro-visu.schema.json)\")\n .action(runSchema);\n\ncli\n .command(\"reset\", \"Clean up orphaned processes/temp from an interrupted run\")\n .option(\"--config <path>\", \"Path to a config file\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .option(\"--force\", \"Clean up even if a run still looks active\")\n .action(runReset);\n\ncli.help();\ncli.version(TOOL_VERSION);\n\nconst parsed = cli.parse(process.argv, { run: false });\n\nasync function main(): Promise<void> {\n // Fire-and-forget: arms a deferred \"newer version on npm\" notice (best-effort, non-blocking).\n checkForUpdates();\n if (!cli.matchedCommand) {\n // --help / --version were already handled by parse(); otherwise show help.\n if (!parsed.options.help && !parsed.options.version) cli.outputHelp();\n return;\n }\n await cli.runMatchedCommand();\n}\n\nmain().catch((err: unknown) => {\n process.exitCode = 1;\n console.error(err instanceof Error ? (err.stack ?? err.message) : String(err));\n});\n","// Replaced at build time by tsup `define`. Falls back for tests / ts-node runs.\ndeclare const __TOOL_VERSION__: string | undefined;\n\nexport const TOOL_VERSION: string =\n typeof __TOOL_VERSION__ !== \"undefined\" ? __TOOL_VERSION__ : \"0.0.0-dev\";\n","import updateNotifier from \"update-notifier\";\nimport { TOOL_VERSION } from \"@/version\";\n\nconst PKG_NAME = \"pro-visu\";\n\n/**\n * Best-effort \"a newer version is on npm\" notice.\n *\n * update-notifier does the registry lookup in a detached, unref'd background process and persists\n * the result, so this call never blocks, slows, or fails a command — it just reads the previous\n * run's cached result and, if a newer version exists, prints a notice when the process exits.\n *\n * It self-suppresses where a notice would be unwelcome: in CI, in non-interactive (non-TTY) output,\n * when `NO_UPDATE_NOTIFIER` is set, with the `--no-update-notifier` flag, under `NODE_ENV=test`, or\n * when the user has opted out via configstore. We additionally skip the unpublished dev build, which\n * has no meaningful version to compare against.\n */\nexport function checkForUpdates(version: string = TOOL_VERSION): void {\n if (version === \"0.0.0-dev\") return;\n try {\n updateNotifier({\n pkg: { name: PKG_NAME, version },\n // Most people run `pro-visu` from a package.json script — surface the notice there too.\n shouldNotifyInNpmScript: true,\n }).notify({\n // Defer so the notice prints after the command finishes (the last thing on screen).\n defer: true,\n // {updateCommand} is install-mode aware (update-notifier detects global vs local), so the\n // suggested command is right whether the tool is a dev-dep or a global install.\n message: \"Update available {currentVersion} → {latestVersion}\\nRun {updateCommand} to update\",\n });\n } catch {\n // An update check must never break the CLI.\n }\n}\n","import path from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { resolveCwd } from \"@/utils/paths\";\nimport { ensureDir, ensureGitignoreEntry, pathExists } from \"@/utils/fs\";\nimport { createLogger } from \"@/utils/logger\";\nimport { ensureChromium } from \"@/browser-install/ensure-chromium\";\nimport { ensureFfmpeg } from \"@/media/ensure-ffmpeg\";\nimport { DEFAULT_OUTDIR } from \"@/config/defaults\";\nimport { serializeConfigJsonSchema } from \"@/config/json-schema\";\nimport { DEFAULT_SCHEMA_FILE } from \"@/cli/commands/schema\";\n\nconst CONFIG_FILES = [\n \"pro-visu.config.ts\",\n \"pro-visu.config.js\",\n \"pro-visu.config.mjs\",\n \"pro-visu.config.cjs\",\n \"pro-visu.config.json\",\n \".pro-visurc\",\n \".pro-visurc.json\",\n];\n\nconst CONFIG_TEMPLATE = `import { defineConfig } from \"pro-visu\";\n\n// URL the capture assets point at. The optional managed server (below) binds this port.\nconst URL = \"http://localhost:3101\";\n\nexport default defineConfig({\n settings: {\n outDir: \"pro-visu\",\n concurrency: 2,\n // Uses Playwright's managed Chromium by default. Set channel: \"chrome\" to use your\n // installed Chrome, or args: [\"--no-sandbox\"] on CI.\n browser: { headless: true },\n // Optional: let the tool build + start your site, wait for it, capture, then stop it.\n // 'port' defaults to 3101 (off the common 3000 dev port); your command must bind it.\n // server: {\n // build: \"npm run build\",\n // command: \"npm start -- -p 3101\", // e.g. Next: \"npx next start -p 3101\"\n // port: 3101,\n // },\n defaults: {\n // Keyed by generator id. Merged underneath each asset's own options.\n \"scroll-reel\": { width: 1440, height: 900, fps: 30 },\n },\n },\n assets: [\n {\n name: \"home-reel\",\n url: URL,\n generator: \"scroll-reel\",\n // options: { duration: 7000, waitForSelector: \"main\" },\n },\n // A looping type-specimen from a font file (no URL needed):\n // {\n // name: \"font-specimen\",\n // generator: \"specimen\",\n // options: { font: \"public/fonts/YourFont.woff2\", name: \"Your Font\", template: \"sweep\" },\n // },\n ],\n});\n`;\n\n// A dependency-free JSON config + a sibling JSON Schema (for editor autocomplete). Use this when\n// running the tool via npx / a global install rather than as a project dev-dependency.\nconst JSON_CONFIG_TEMPLATE = `{\n \"$schema\": \"./${DEFAULT_SCHEMA_FILE}\",\n \"settings\": {\n \"outDir\": \"pro-visu\",\n \"concurrency\": 2,\n \"browser\": { \"headless\": true },\n \"defaults\": {\n \"scroll-reel\": { \"width\": 1440, \"height\": 900, \"fps\": 30 }\n }\n },\n \"assets\": [\n { \"name\": \"home-reel\", \"url\": \"http://localhost:3101\", \"generator\": \"scroll-reel\" }\n ]\n}\n`;\n\nexport interface InitOptions {\n cwd?: string;\n /** --no-script sets this to false. */\n script?: boolean;\n skipBrowser?: boolean;\n /** Scaffold a JSON config + JSON Schema instead of a TS `defineConfig` file. */\n json?: boolean;\n}\n\nexport async function runInit(options: InitOptions = {}): Promise<void> {\n const cwd = resolveCwd(options.cwd);\n const logger = createLogger(\"info\");\n let createdSomething = false;\n const configFile = options.json ? \"pro-visu.config.json\" : \"pro-visu.config.ts\";\n\n // 1. Config file\n const existingConfig = findExistingConfig(cwd);\n if (existingConfig) {\n logger.info(`config exists (${existingConfig}) — leaving it untouched`);\n } else if (options.json) {\n await writeFile(path.join(cwd, configFile), JSON_CONFIG_TEMPLATE, \"utf8\");\n logger.success(`created ${configFile}`);\n // Materialize the matching JSON Schema so the JSON config gets editor autocomplete + validation\n // with no dependency on this package — it works the same whether run via npx, global, or a dep.\n await writeFile(path.join(cwd, DEFAULT_SCHEMA_FILE), serializeConfigJsonSchema(), \"utf8\");\n logger.success(`created ${DEFAULT_SCHEMA_FILE}`);\n createdSomething = true;\n } else {\n await writeFile(path.join(cwd, configFile), CONFIG_TEMPLATE, \"utf8\");\n logger.success(`created ${configFile}`);\n createdSomething = true;\n }\n\n // 2. Output directory\n const outDirAbs = path.join(cwd, DEFAULT_OUTDIR);\n if (await pathExists(outDirAbs)) {\n logger.info(`${DEFAULT_OUTDIR}/ exists`);\n } else {\n await ensureDir(outDirAbs);\n logger.success(`created ${DEFAULT_OUTDIR}/`);\n createdSomething = true;\n }\n\n // 3. .gitignore\n const gitignore = await ensureGitignoreEntry(cwd, `${DEFAULT_OUTDIR}/`);\n if (gitignore.changed) {\n logger.success(`added ${DEFAULT_OUTDIR}/ to .gitignore`);\n createdSomething = true;\n } else {\n logger.info(`.gitignore already ignores ${DEFAULT_OUTDIR}/`);\n }\n\n // 4. package.json script (opt-out via --no-script)\n if (options.script !== false) {\n const result = await addPackageScript(cwd);\n if (result === \"added\") {\n logger.success('added \"pro-visu\" script to package.json');\n createdSomething = true;\n } else if (result === \"exists\") {\n logger.info('\"pro-visu\" script already present');\n } else {\n logger.info(\"no package.json found — skipped script wiring\");\n }\n }\n\n // 5. Browser + ffmpeg\n if (!options.skipBrowser) {\n try {\n await ensureChromium({ logger });\n } catch (err) {\n logger.warn(`Could not install Chromium now: ${(err as Error).message}`);\n logger.warn(\"It will be installed on first `pro-visu generate`.\");\n }\n try {\n await ensureFfmpeg({ logger });\n } catch (err) {\n logger.warn(`Could not fetch ffmpeg now: ${(err as Error).message}`);\n logger.warn(\"It will be fetched on first `pro-visu generate`.\");\n }\n }\n\n logger.log(\"\");\n logger.info(\n createdSomething\n ? `Next: edit ${configFile}, start your site (or use a deployed URL), then run \\`pro-visu generate\\`.`\n : `Already initialized. Edit ${configFile}, then run \\`pro-visu generate\\`.`,\n );\n}\n\nfunction findExistingConfig(cwd: string): string | undefined {\n for (const file of CONFIG_FILES) {\n if (existsSync(path.join(cwd, file))) return file;\n }\n const pkgPath = path.join(cwd, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as Record<string, unknown>;\n if (pkg && typeof pkg === \"object\" && \"pro-visu\" in pkg) {\n return 'package.json \"pro-visu\" key';\n }\n } catch {\n // ignore unparseable package.json\n }\n }\n return undefined;\n}\n\nasync function addPackageScript(cwd: string): Promise<\"added\" | \"exists\" | \"none\"> {\n const pkgPath = path.join(cwd, \"package.json\");\n if (!existsSync(pkgPath)) return \"none\";\n\n const raw = await readFile(pkgPath, \"utf8\");\n let pkg: { scripts?: Record<string, string> } & Record<string, unknown>;\n try {\n pkg = JSON.parse(raw);\n } catch {\n return \"none\";\n }\n\n pkg.scripts ??= {};\n if (pkg.scripts[\"pro-visu\"]) return \"exists\";\n pkg.scripts[\"pro-visu\"] = \"pro-visu generate\";\n await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`, \"utf8\");\n return \"added\";\n}\n","import path from \"node:path\";\n\n/** Resolve a working directory to an absolute path (defaults to process.cwd()). */\nexport function resolveCwd(cwd?: string): string {\n return path.resolve(cwd ?? process.cwd());\n}\n\n/** Resolve the output directory (e.g. `pro-visu/`) against the consuming repo root. */\nexport function resolveOutDir(cwd: string, outDir: string): string {\n return path.resolve(cwd, outDir);\n}\n\n/** Per-generator subdirectory inside the output dir, e.g. `pro-visu/scroll-reel`. */\nexport function generatorDir(outDir: string, generatorId: string): string {\n return path.join(outDir, generatorId);\n}\n\n/** Normalize a path to forward slashes (for stable, cross-platform manifest entries). */\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** Path of `to` relative to `from`, normalized to forward slashes. */\nexport function relPosix(from: string, to: string): string {\n return toPosix(path.relative(from, to));\n}\n\n/** Turn an asset name into a safe filename slug. */\nexport function slugify(name: string): string {\n return (\n name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"asset\"\n );\n}\n","import { access, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport path from \"node:path\";\n\n/** mkdir -p */\nexport async function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n\n/** Does a path exist? */\nexport async function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/** rm -rf (never throws if missing). */\nexport async function removeDir(dir: string): Promise<void> {\n await rm(dir, { recursive: true, force: true });\n}\n\nexport interface GitignoreResult {\n /** Whether the file was modified. */\n changed: boolean;\n /** Whether a new .gitignore was created. */\n created: boolean;\n}\n\n/**\n * Idempotently ensure `entry` is present in the repo's `.gitignore`, appended under a\n * labelled comment block. Treats trailing-slash variants as equal so we never duplicate.\n */\nexport async function ensureGitignoreEntry(\n repoRoot: string,\n entry: string,\n label = \"pro-visu\",\n): Promise<GitignoreResult> {\n const file = path.join(repoRoot, \".gitignore\");\n const exists = await pathExists(file);\n const content = exists ? await readFile(file, \"utf8\") : \"\";\n const normalizedEntry = entry.replace(/\\/+$/, \"\");\n\n const already = content\n .split(/\\r?\\n/)\n .map((line) => line.trim().replace(/\\/+$/, \"\"))\n .some((line) => line === normalizedEntry);\n if (already) return { changed: false, created: false };\n\n let next = content;\n if (next.length > 0 && !next.endsWith(\"\\n\")) next += \"\\n\";\n if (next.length > 0) next += \"\\n\";\n next += `# ${label}\\n${entry}\\n`;\n\n await writeFile(file, next, \"utf8\");\n return { changed: true, created: !exists };\n}\n","import { createConsola } from \"consola\";\nimport type { ConsolaInstance } from \"consola\";\nimport type { LogLevel } from \"@/config/schema\";\n\n/** Map our friendly log levels onto consola's numeric levels. */\nconst LEVEL_MAP: Record<LogLevel, number> = {\n silent: Number.NEGATIVE_INFINITY,\n error: 0,\n warn: 1,\n info: 3,\n debug: 4,\n};\n\nexport type Logger = ConsolaInstance;\n\nexport function createLogger(level: LogLevel = \"info\"): Logger {\n return createConsola({ level: LEVEL_MAP[level] });\n}\n\n/** A destination for tagged log entries (the live job tracker implements this). */\nexport interface LogSink {\n /** Consume a tagged log line. Return true if handled (suppress normal printing). */\n route(tag: string, type: string, message: string): boolean;\n}\n\n/**\n * A logger that diverts entries to a sink: *tagged* entries (e.g.\n * `logger.withTag(\"home-reel\").info(…)`) feed each asset's progress into the live dashboard, while\n * untagged entries are committed above it — so the live renderer owns the terminal and stray logs\n * never corrupt its output. Anything the sink declines (e.g. after teardown) prints normally.\n */\nexport function createReportingLogger(level: LogLevel, sink: LogSink): Logger {\n const passthrough = createConsola({ level: LEVEL_MAP[level] });\n return createConsola({\n level: LEVEL_MAP[level],\n reporters: [\n {\n log(logObj) {\n const tag = typeof logObj.tag === \"string\" ? logObj.tag : \"\";\n const args = (logObj.args ?? []) as unknown[];\n const message = args.map((a) => (typeof a === \"string\" ? a : String(a))).join(\" \");\n if (sink.route(tag, logObj.type, message)) return;\n const fn = (passthrough as unknown as Record<string, unknown>)[logObj.type];\n const emit =\n typeof fn === \"function\"\n ? (fn as (...a: unknown[]) => void)\n : (passthrough.log as unknown as (...a: unknown[]) => void);\n emit.call(passthrough, ...args);\n },\n },\n ],\n });\n}\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { chromium } from \"playwright-core\";\nimport type { Logger } from \"@/utils/logger\";\n\nconst require = createRequire(import.meta.url);\n\n/** Is a managed Chromium present on disk for playwright-core? */\nfunction isChromiumInstalled(): boolean {\n try {\n const exe = chromium.executablePath();\n return !!exe && existsSync(exe);\n } catch {\n return false;\n }\n}\n\n/** Locate playwright-core's CLI so we can drive its browser installer. */\nfunction resolveCliPath(): string {\n try {\n return require.resolve(\"playwright-core/cli.js\");\n } catch {\n const pkgJson = require.resolve(\"playwright-core/package.json\");\n return path.join(path.dirname(pkgJson), \"cli.js\");\n }\n}\n\nexport interface EnsureChromiumOptions {\n logger: Logger;\n /** When true, only report status; never install. */\n checkOnly?: boolean;\n}\n\n/**\n * Ensure a Chromium build is available. playwright-core ships no browser at install time,\n * so we fetch it on demand (cached in the shared PLAYWRIGHT_BROWSERS_PATH). Returns whether\n * Chromium is present afterward.\n */\nexport async function ensureChromium(opts: EnsureChromiumOptions): Promise<boolean> {\n if (isChromiumInstalled()) return true;\n if (opts.checkOnly) return false;\n\n opts.logger.info(\"Installing Chromium for Playwright (one-time, ~100–150 MB)…\");\n const cli = resolveCliPath();\n await new Promise<void>((resolve, reject) => {\n const child = spawn(process.execPath, [cli, \"install\", \"chromium\"], {\n stdio: \"inherit\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) =>\n code === 0\n ? resolve()\n : reject(new Error(`Chromium install failed (exit code ${code}).`)),\n );\n });\n opts.logger.success(\"Chromium installed.\");\n return true;\n}\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { rm } from \"node:fs/promises\";\nimport { spawn } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { ffmpegPath } from \"@/media/ffmpeg\";\nimport type { Logger } from \"@/utils/logger\";\n\nconst require = createRequire(import.meta.url);\n\n/** Does the bundled ffmpeg binary exist AND actually execute on this platform? */\nasync function ffmpegWorks(): Promise<boolean> {\n let bin: string;\n try {\n bin = ffmpegPath();\n } catch {\n return false;\n }\n if (!existsSync(bin)) return false;\n return await new Promise<boolean>((resolve) => {\n // A corrupt / wrong-platform binary fails here — on Windows spawn() throws synchronously\n // (spawn UNKNOWN/EFTYPE); on POSIX it emits an async 'error'. Handle both.\n let child: ReturnType<typeof spawn>;\n try {\n child = spawn(bin, [\"-version\"], { stdio: \"ignore\" });\n } catch {\n resolve(false);\n return;\n }\n child.on(\"error\", () => resolve(false));\n child.on(\"close\", (code) => resolve(code === 0));\n });\n}\n\n/** Locate ffmpeg-static's bundled downloader script. */\nfunction resolveInstallScript(): string | null {\n try {\n return require.resolve(\"ffmpeg-static/install.js\");\n } catch {\n try {\n const pkg = require.resolve(\"ffmpeg-static/package.json\");\n return path.join(path.dirname(pkg), \"install.js\");\n } catch {\n return null;\n }\n }\n}\n\nexport interface EnsureFfmpegOptions {\n logger: Logger;\n /** When true, only report status; never download. */\n checkOnly?: boolean;\n}\n\n/**\n * Ensure a working ffmpeg binary. ffmpeg-static fetches its binary via a postinstall script,\n * which pnpm/npm block unless the consumer approves build scripts — leaving the binary\n * missing or corrupt so it fails to spawn (EFTYPE). We self-heal by running ffmpeg-static's\n * own downloader on demand (mirroring how we fetch Chromium), so consumers never have to\n * approve build scripts or install ffmpeg by hand.\n */\nexport async function ensureFfmpeg(opts: EnsureFfmpegOptions): Promise<boolean> {\n if (await ffmpegWorks()) return true;\n if (opts.checkOnly) return false;\n\n const installScript = resolveInstallScript();\n if (!installScript) {\n opts.logger.error(\"ffmpeg-static is not installed; cannot fetch an ffmpeg binary.\");\n return false;\n }\n\n opts.logger.info(\"Fetching ffmpeg (one-time, ~80 MB)…\");\n // ffmpeg-static's installer treats any existing file as already-done, so clear a corrupt\n // binary first to force a clean re-download.\n try {\n await rm(ffmpegPath(), { force: true });\n } catch {\n /* path may not resolve yet — the download recreates it */\n }\n await new Promise<void>((resolve, reject) => {\n const child = spawn(process.execPath, [installScript], {\n cwd: path.dirname(installScript),\n stdio: \"inherit\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) =>\n code === 0\n ? resolve()\n : reject(new Error(`ffmpeg download failed (exit code ${code}).`)),\n );\n });\n\n if (!(await ffmpegWorks())) {\n opts.logger.error(\"ffmpeg was downloaded but still won't run on this platform.\");\n return false;\n }\n opts.logger.success(\"ffmpeg ready.\");\n return true;\n}\n","import path from \"node:path\";\nimport { spawn } from \"node:child_process\";\nimport { rm, writeFile } from \"node:fs/promises\";\nimport ffmpegStatic from \"ffmpeg-static\";\nimport { ensureDir } from \"@/utils/fs\";\nimport type { Logger } from \"@/utils/logger\";\n\nexport interface TranscodeArgs {\n inputPath: string;\n outputPath: string;\n fps: number;\n width: number;\n height: number;\n crf: number;\n /** x264 speed/size tradeoff; \"ultrafast\" for draft, \"medium\" (default) for final. */\n preset?: string;\n /** Seconds to trim off the head of the input (e.g. the blank navigation lead of a recording). */\n startOffsetSeconds?: number;\n /** Clamp the output to exactly this many seconds (after any head trim) so length is deterministic. */\n durationSeconds?: number;\n /** Crop the source to this box (CSS px) before scaling — used for element-focused clips. */\n crop?: { x: number; y: number; width: number; height: number };\n}\n\n/**\n * Color correctness: every encode converts to and TAGS bt709 limited (tv) range — the standard for\n * web/HD video. Untagged output forces players to guess the matrix (bt601 vs bt709), which shifts\n * colors subtly; explicit conversion + tagging keeps brand colors exact everywhere.\n *\n * `SCALE_COLOR` goes on the scale filter (which performs the conversion); `format=yuv420p` must\n * directly follow scale in the chain so scale itself does the RGB→YUV conversion with the declared\n * matrix (for RGB inputs like PNG frames/overlays, a later auto-inserted converter would otherwise\n * use an unspecified matrix). `COLOR_TAGS` writes the matching metadata into the x264 VUI/container.\n */\nconst SCALE_COLOR = \"out_color_matrix=bt709:out_range=tv\";\nconst COLOR_TAGS = [\n \"-colorspace\",\n \"bt709\",\n \"-color_primaries\",\n \"bt709\",\n \"-color_trc\",\n \"bt709\",\n \"-color_range\",\n \"tv\",\n];\n\n/** Absolute path to the bundled ffmpeg binary. */\nexport function ffmpegPath(): string {\n const p = ffmpegStatic as unknown as string | null;\n if (!p) {\n throw new Error(\"ffmpeg-static did not provide a binary for this platform.\");\n }\n return p;\n}\n\n/**\n * Build the ffmpeg argument vector to re-encode the recording into a web-friendly\n * h264 mp4 at a fixed fps. Pure — unit-tested.\n */\nexport function buildTranscodeArgs(args: TranscodeArgs): string[] {\n // Input-side seek (`-ss` before `-i`) skips the blank navigation/readiness lead Playwright records\n // before playback starts, so the mp4 opens on the first real frame instead of a blank one.\n const seek =\n args.startOffsetSeconds && args.startOffsetSeconds > 0\n ? [\"-ss\", args.startOffsetSeconds.toFixed(3)]\n : [];\n // Output-side limit (after the head seek) so the clip is exactly `durationSeconds` long.\n const limit =\n args.durationSeconds && args.durationSeconds > 0\n ? [\"-t\", args.durationSeconds.toFixed(3)]\n : [];\n const crop = args.crop\n ? `crop=${args.crop.width}:${args.crop.height}:${args.crop.x}:${args.crop.y},`\n : \"\";\n return [\n \"-y\",\n ...seek,\n \"-i\",\n args.inputPath,\n \"-vf\",\n `${crop}scale=${args.width}:${args.height}:flags=lanczos:${SCALE_COLOR},format=yuv420p,fps=${args.fps}`,\n \"-c:v\",\n \"libx264\",\n \"-preset\",\n args.preset ?? \"medium\",\n \"-crf\",\n String(args.crf),\n \"-pix_fmt\",\n \"yuv420p\",\n ...COLOR_TAGS,\n \"-movflags\",\n \"+faststart\",\n \"-an\",\n ...limit,\n args.outputPath,\n ];\n}\n\n/** Read a video's pixel dimensions by parsing ffmpeg's stream info (no ffprobe needed). */\nexport async function probeVideoDimensions(\n file: string,\n): Promise<{ width: number; height: number } | null> {\n return new Promise((resolve) => {\n const child = spawn(ffmpegPath(), [\"-hide_banner\", \"-i\", file], {\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n });\n let stderr = \"\";\n child.stderr.on(\"data\", (chunk: Buffer) => (stderr += chunk.toString()));\n child.on(\"error\", () => resolve(null));\n // ffmpeg exits non-zero with no output file; the info we want is already on stderr. Anchor to\n // the video stream line so a \"1920x1080\"-looking tag on another line can't be picked up first.\n child.on(\"close\", () => {\n const videoLine = stderr.split(\"\\n\").find((l) => l.includes(\"Video:\"));\n const match = videoLine ? /,\\s(\\d+)x(\\d+)[\\s,]/.exec(videoLine) : null;\n resolve(match ? { width: Number(match[1]), height: Number(match[2]) } : null);\n });\n });\n}\n\n/** Read a video's duration in ms by parsing ffmpeg's stream info (no ffprobe needed). */\nexport async function probeVideoDurationMs(file: string): Promise<number | null> {\n return new Promise((resolve) => {\n const child = spawn(ffmpegPath(), [\"-hide_banner\", \"-i\", file], {\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n });\n let stderr = \"\";\n child.stderr.on(\"data\", (chunk: Buffer) => (stderr += chunk.toString()));\n child.on(\"error\", () => resolve(null));\n child.on(\"close\", () => {\n const m = /Duration:\\s*(\\d+):(\\d+):(\\d+)\\.(\\d+)/.exec(stderr);\n if (!m) return resolve(null);\n const frac = Number(`0.${m[4]}`);\n resolve((Number(m[1]) * 3600 + Number(m[2]) * 60 + Number(m[3]) + frac) * 1000);\n });\n });\n}\n\nexport interface FramePipeArgs {\n fps: number;\n width: number;\n height: number;\n crf: number;\n outPath: string;\n /** x264 speed/size tradeoff; \"ultrafast\" for draft, \"medium\" for final. */\n preset?: string;\n /** Piped frame format; \"png\" is the lossless (slower) path. Default \"jpeg\". */\n inputFormat?: \"jpeg\" | \"png\";\n}\n\n/**\n * ffmpeg argv to encode a stream of image frames (image2pipe on stdin) into an mp4. Pure —\n * unit-tested. Frames are scaled to the output size so we can screenshot at a higher device\n * scale and downsample for crispness. The input codec is set explicitly (mjpeg/png) rather than\n * relying on pipe content probing, so behavior is deterministic.\n */\nexport function buildFramePipeArgs(a: FramePipeArgs): string[] {\n return [\n \"-y\",\n \"-f\",\n \"image2pipe\",\n \"-framerate\",\n String(a.fps),\n \"-c:v\",\n a.inputFormat === \"png\" ? \"png\" : \"mjpeg\",\n \"-i\",\n \"pipe:0\",\n \"-vf\",\n `scale=${a.width}:${a.height}:flags=lanczos:${SCALE_COLOR},format=yuv420p`,\n \"-r\",\n String(a.fps),\n \"-c:v\",\n \"libx264\",\n \"-preset\",\n a.preset ?? \"medium\",\n \"-crf\",\n String(a.crf),\n \"-pix_fmt\",\n \"yuv420p\",\n ...COLOR_TAGS,\n \"-movflags\",\n \"+faststart\",\n \"-an\",\n a.outPath,\n ];\n}\n\nexport interface FrameEncoder {\n /** Push one encoded frame (JPEG buffer), respecting backpressure. */\n write(frame: Buffer): Promise<void>;\n /** Flush + wait for ffmpeg to finish writing the mp4. */\n done(): Promise<void>;\n}\n\n/** Spawn an ffmpeg that consumes piped JPEG frames and writes an mp4 — no frames hit disk. */\nexport function startFrameEncoder(a: FramePipeArgs, logger?: Logger, signal?: AbortSignal): FrameEncoder {\n const child = spawn(ffmpegPath(), buildFramePipeArgs(a), {\n stdio: [\"pipe\", \"ignore\", \"pipe\"],\n signal, // a cancelled run kills the encoder instead of waiting for it to drain\n });\n let stderr = \"\";\n let failed: Error | null = null;\n child.stderr.on(\"data\", (d: Buffer) => {\n stderr += d.toString();\n logger?.debug(d.toString().trim());\n });\n child.on(\"error\", (e) => {\n failed = e;\n });\n const stdin = child.stdin;\n\n return {\n write: (frame) =>\n new Promise<void>((resolve, reject) => {\n if (failed) return reject(failed);\n const flushed = stdin.write(frame, (err) => {\n if (err) reject(err);\n });\n if (flushed) resolve();\n else stdin.once(\"drain\", resolve);\n }),\n done: () =>\n new Promise<void>((resolve, reject) => {\n stdin.end();\n child.on(\"close\", (code) =>\n code === 0\n ? resolve()\n : reject(new Error(`ffmpeg frame encode failed (${code}):\\n${stderr.slice(-2000)}`)),\n );\n }),\n };\n}\n\n/** ffmpeg argv to losslessly concat mp4 segments (same codec params) via the concat demuxer. */\nexport function buildConcatArgs(listFile: string, outPath: string): string[] {\n return [\n \"-y\",\n \"-f\",\n \"concat\",\n \"-safe\",\n \"0\",\n \"-i\",\n listFile,\n \"-c\",\n \"copy\",\n \"-movflags\",\n \"+faststart\",\n outPath,\n ];\n}\n\n/** Concatenate mp4 segments into one file (stream copy — no re-encode). */\nexport async function concatMp4(\n segments: string[],\n outPath: string,\n logger?: Logger,\n signal?: AbortSignal,\n): Promise<void> {\n await ensureDir(path.dirname(outPath));\n // concat demuxer wants forward slashes and single-quoted paths.\n const listFile = `${outPath}.concat.txt`;\n const list = segments.map((s) => `file '${s.replace(/\\\\/g, \"/\")}'`).join(\"\\n\");\n await writeFile(listFile, `${list}\\n`, \"utf8\");\n try {\n await runFfmpeg(buildConcatArgs(listFile, outPath), logger, signal);\n } finally {\n await rm(listFile, { force: true });\n }\n}\n\n/** Run ffmpeg with an explicit argv, rejecting on a non-zero exit (or when `signal` aborts). */\nexport async function runFfmpeg(argv: string[], logger?: Logger, signal?: AbortSignal): Promise<void> {\n const bin = ffmpegPath();\n await new Promise<void>((resolve, reject) => {\n const child = spawn(bin, argv, { stdio: [\"ignore\", \"ignore\", \"pipe\"], signal });\n let stderr = \"\";\n child.stderr.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString();\n stderr += text;\n logger?.debug(text.trim());\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`ffmpeg exited with code ${code}:\\n${stderr.slice(-2000)}`));\n });\n });\n}\n\n/** Re-encode the recorded webm into an mp4 at outputPath. */\nexport async function transcodeToMp4(\n args: TranscodeArgs & { logger?: Logger; signal?: AbortSignal },\n): Promise<void> {\n await ensureDir(path.dirname(args.outputPath));\n await runFfmpeg(buildTranscodeArgs(args), args.logger, args.signal);\n}\n\n// --- output transforms: aspect reframing + alternate formats (gif / webp / poster) ---\n\nexport type AspectPreset = \"16:9\" | \"9:16\" | \"1:1\";\n\n/** Pure: resolve an aspect preset (or explicit size) to concrete output pixel dimensions. */\nexport function aspectTarget(\n aspect: AspectPreset | { width: number; height: number },\n): { width: number; height: number } {\n if (typeof aspect === \"object\") return { width: aspect.width, height: aspect.height };\n switch (aspect) {\n case \"9:16\":\n return { width: 1080, height: 1920 };\n case \"1:1\":\n return { width: 1080, height: 1080 };\n case \"16:9\":\n default:\n return { width: 1920, height: 1080 };\n }\n}\n\nexport interface AspectArgs {\n inputPath: string;\n outputPath: string;\n width: number;\n height: number;\n /** \"cover\" scales to fill + center-crops; \"contain\" scales to fit + pads. */\n fit: \"cover\" | \"contain\";\n padColor: string;\n fps: number;\n crf: number;\n preset?: string;\n}\n\n/** ffmpeg argv to reframe a video to a target aspect (h264, bt709 preserved). Pure — unit-tested. */\nexport function buildAspectArgs(a: AspectArgs): string[] {\n const vf =\n a.fit === \"contain\"\n ? `scale=${a.width}:${a.height}:force_original_aspect_ratio=decrease:flags=lanczos:${SCALE_COLOR},` +\n `pad=${a.width}:${a.height}:(ow-iw)/2:(oh-ih)/2:${a.padColor},format=yuv420p`\n : `scale=${a.width}:${a.height}:force_original_aspect_ratio=increase:flags=lanczos:${SCALE_COLOR},` +\n `crop=${a.width}:${a.height},format=yuv420p`;\n return [\n \"-y\",\n \"-i\",\n a.inputPath,\n \"-vf\",\n vf,\n \"-r\",\n String(a.fps),\n \"-c:v\",\n \"libx264\",\n \"-preset\",\n a.preset ?? \"medium\",\n \"-crf\",\n String(a.crf),\n \"-pix_fmt\",\n \"yuv420p\",\n ...COLOR_TAGS,\n \"-movflags\",\n \"+faststart\",\n \"-an\",\n a.outputPath,\n ];\n}\n\nexport interface GifArgs {\n inputPath: string;\n outputPath: string;\n fps: number;\n /** Optional output width (height auto, keeps aspect). */\n width?: number;\n}\n\n/** ffmpeg argv to encode an optimized GIF (per-clip palette). Pure — unit-tested. */\nexport function buildGifArgs(a: GifArgs): string[] {\n const scale = a.width ? `,scale=${a.width}:-1:flags=lanczos` : \"\";\n const filter =\n `[0:v] fps=${a.fps}${scale},split [s0][s1];` +\n `[s0] palettegen=stats_mode=diff [p];[s1][p] paletteuse=dither=bayer:bayer_scale=5`;\n return [\"-y\", \"-i\", a.inputPath, \"-filter_complex\", filter, a.outputPath];\n}\n\nexport interface WebpArgs {\n inputPath: string;\n outputPath: string;\n fps: number;\n /** 0–100 (libwebp q:v). */\n quality: number;\n}\n\n/** ffmpeg argv to encode an animated WebP. Pure — unit-tested. */\nexport function buildWebpArgs(a: WebpArgs): string[] {\n return [\n \"-y\",\n \"-i\",\n a.inputPath,\n \"-vcodec\",\n \"libwebp\",\n \"-filter:v\",\n `fps=${a.fps}`,\n \"-lossless\",\n \"0\",\n \"-q:v\",\n String(a.quality),\n \"-loop\",\n \"0\",\n \"-an\",\n \"-vsync\",\n \"0\",\n a.outputPath,\n ];\n}\n\nexport interface PosterArgs {\n inputPath: string;\n outputPath: string;\n /** Seek to this time (seconds) before grabbing one frame. */\n atSeconds: number;\n}\n\n/** ffmpeg argv to grab a single still frame (poster/thumbnail). Pure — unit-tested. */\nexport function buildPosterArgs(a: PosterArgs): string[] {\n const seek = a.atSeconds > 0 ? [\"-ss\", a.atSeconds.toFixed(3)] : [];\n return [\"-y\", ...seek, \"-i\", a.inputPath, \"-frames:v\", \"1\", a.outputPath];\n}\n\nexport interface StillSegmentArgs {\n pngPath: string;\n outPath: string;\n seconds: number;\n fps: number;\n width: number;\n height: number;\n /** Fade-from-black duration at the start (s); 0 to disable. */\n fadeInSec: number;\n /** Fade-to-black duration at the end (s); 0 to disable. */\n fadeOutSec: number;\n crf: number;\n preset?: string;\n}\n\n/**\n * ffmpeg argv to turn a still PNG into a fixed-length mp4 segment with optional fade in/out — used for\n * intro/outro cards. Same h264 / bt709 / yuv420p settings as the main clip so segments concat cleanly.\n * Pure — unit-tested.\n */\nexport function buildStillSegmentArgs(a: StillSegmentArgs): string[] {\n const filters = [`scale=${a.width}:${a.height}:flags=lanczos:${SCALE_COLOR}`, \"format=yuv420p\"];\n if (a.fadeInSec > 0) filters.push(`fade=t=in:st=0:d=${a.fadeInSec.toFixed(3)}`);\n if (a.fadeOutSec > 0) {\n filters.push(`fade=t=out:st=${Math.max(0, a.seconds - a.fadeOutSec).toFixed(3)}:d=${a.fadeOutSec.toFixed(3)}`);\n }\n return [\n \"-y\",\n \"-loop\",\n \"1\",\n \"-t\",\n a.seconds.toFixed(3),\n \"-i\",\n a.pngPath,\n \"-vf\",\n filters.join(\",\"),\n \"-r\",\n String(a.fps),\n \"-c:v\",\n \"libx264\",\n \"-preset\",\n a.preset ?? \"medium\",\n \"-crf\",\n String(a.crf),\n \"-pix_fmt\",\n \"yuv420p\",\n ...COLOR_TAGS,\n \"-movflags\",\n \"+faststart\",\n \"-an\",\n a.outPath,\n ];\n}\n","/** The config base name c12 discovers: pro-visu.config.*, .pro-visurc, package.json \"pro-visu\". */\nexport const CONFIG_NAME = \"pro-visu\";\nconst RC_FILE = \".pro-visurc\";\n\nexport const DEFAULT_OUTDIR = \"pro-visu\";\nconst DEFAULT_CONCURRENCY = 2;\n","import { zodToJsonSchema } from \"zod-to-json-schema\";\nimport type { ZodTypeAny } from \"zod\";\nimport { settingsSchema } from \"@/config/schema\";\nimport { listGenerators } from \"@/generators/registry\";\n\ntype JsonObject = Record<string, unknown>;\n\n/** Convert a zod schema to an inlined (no $ref) JSON Schema fragment, without its own $schema tag. */\nfunction toJson(schema: ZodTypeAny): JsonObject {\n const out = zodToJsonSchema(schema, { $refStrategy: \"none\" }) as JsonObject;\n // Strip the per-fragment $schema tag — we only want one at the document root.\n delete out.$schema;\n return out;\n}\n\n/**\n * Build a draft-07 JSON Schema for a `pro-visu.config.json`, assembled from the live, registered\n * schemas. A JSON config that references it via `$schema` then gets the same editor autocomplete +\n * validation as the typed `defineConfig` path — with no project dependency on this package.\n *\n * Per-generator `options` autocomplete comes from a discriminated union: once `generator` is set,\n * the matching `allOf` branch refines `options` to that generator's own schema. Because it's built\n * from the registry at runtime, `pro-visu init --json` / `pro-visu schema` materialize a schema that\n * always matches the installed tool version (works offline and in any install mode).\n */\nexport function generateConfigJsonSchema(): JsonObject {\n const generators = listGenerators();\n\n const optionBranches = generators.map((gen) => ({\n if: { properties: { generator: { const: gen.id } } },\n then: { properties: { options: toJson(gen.optionsSchema) } },\n }));\n\n const assetItem: JsonObject = {\n type: \"object\",\n required: [\"name\", \"generator\"],\n additionalProperties: false,\n properties: {\n name: {\n type: \"string\",\n minLength: 1,\n description: \"Unique id — also the output filename and manifest key.\",\n },\n url: {\n type: \"string\",\n description:\n \"Absolute http(s) URL, or a /path resolved against the managed server. Omit for local generators (wall, specimen, palette, image).\",\n },\n generator: {\n description: \"Which generator produces this asset.\",\n enum: generators.map((g) => g.id),\n },\n options: {\n type: \"object\",\n description: \"Generator-specific options — autocompletes once `generator` is set.\",\n },\n inputs: {\n type: \"object\",\n additionalProperties: { type: \"string\" },\n description: \"Other assets this one consumes, as { slotName: assetName }. Producers run first.\",\n },\n },\n allOf: optionBranches,\n };\n\n return {\n $schema: \"http://json-schema.org/draft-07/schema#\",\n title: \"pro-visu config\",\n description: \"Configuration for the pro-visu CLI (pro-visu.config.json).\",\n type: \"object\",\n required: [\"assets\"],\n additionalProperties: false,\n properties: {\n // The config file itself may carry a $schema pointer; the runtime validator ignores it.\n $schema: { type: \"string\" },\n settings: toJson(settingsSchema),\n assets: {\n type: \"array\",\n minItems: 1,\n description: \"What to generate (at least one).\",\n items: assetItem,\n },\n },\n };\n}\n\n/** Serialize the JSON Schema document for writing to disk. */\nexport function serializeConfigJsonSchema(): string {\n return `${JSON.stringify(generateConfigJsonSchema(), null, 2)}\\n`;\n}\n","import { z } from \"zod\";\n\n/** Friendly log levels surfaced in config + CLI. */\nconst logLevelSchema = z.enum([\"silent\", \"error\", \"warn\", \"info\", \"debug\"]);\nexport type LogLevel = z.infer<typeof logLevelSchema>;\n\n/** Playwright launch controls, settable per-repo. */\nconst browserSettingsSchema = z\n .object({\n headless: z\n .boolean()\n .default(true)\n .describe(\"Run the browser without a visible window (default true). Set false to watch captures.\"),\n /** Browser channel, e.g. \"chrome\" or \"msedge\". Omit to use the managed Chromium. */\n channel: z\n .string()\n .optional()\n .describe('Browser channel, e.g. \"chrome\" or \"msedge\". Omit to use the managed Chromium.'),\n /** Absolute path to a browser executable (overrides channel + managed Chromium). */\n executablePath: z\n .string()\n .optional()\n .describe(\"Absolute path to a browser executable (overrides channel + managed Chromium).\"),\n /** Extra launch args, e.g. [\"--no-sandbox\"] on CI. */\n args: z\n .array(z.string())\n .default([])\n .describe('Extra launch args, e.g. [\"--no-sandbox\"] on CI.'),\n /** Launch timeout (ms). */\n timeout: z\n .number()\n .int()\n .nonnegative()\n .default(30_000)\n .describe(\"Browser launch timeout in ms (default 30000).\"),\n })\n .strict();\nexport type ResolvedBrowserSettings = z.infer<typeof browserSettingsSchema>;\n\n/**\n * Optional managed server. When set, `pro-visu generate` builds (if given), starts the\n * server, waits for it to respond, runs the capture, then shuts it down — so a project's\n * npm script can be just `pro-visu generate`.\n */\nexport const serverSettingsSchema = z\n .object({\n /**\n * Command that starts the server, run via the shell. The tool sets PORT/HOST in the\n * command's environment to the readiness port/host, so frameworks that honor PORT (Next,\n * Vite, …) bind it automatically — `command: \"next start\"` is enough. An explicit flag\n * (e.g. `next start -p 4000`) still wins.\n */\n command: z\n .string()\n .min(1)\n .describe(\"Command that starts the server, run via the shell. PORT/HOST are set in its env so PORT-honoring frameworks bind automatically.\"),\n /** Optional one-shot build to run first, e.g. \"next build\". */\n build: z\n .string()\n .min(1)\n .optional()\n .describe('Optional one-shot build to run first, e.g. \"next build\".'),\n /** Health-check URL polled until it responds. Defaults to http://127.0.0.1:<port>. */\n url: z\n .string()\n .url()\n .optional()\n .describe(\"Health-check URL polled until it responds. Defaults to http://127.0.0.1:<port>.\"),\n /**\n * Port the readiness check polls — also derives `url` when `url` is omitted, and is passed to\n * the command as the PORT env var so it binds the same port automatically. Defaults to 3101\n * (off the common 3000 dev port).\n */\n port: z\n .number()\n .int()\n .positive()\n .default(3101)\n .describe(\"Port the readiness check polls; also derives `url` and is passed as PORT so the server binds it. Defaults to 3101.\"),\n /** Working dir for build + command, relative to the config dir. Defaults to it. */\n cwd: z\n .string()\n .optional()\n .describe(\"Working dir for build + command, relative to the config dir. Defaults to it.\"),\n /** Max time to wait for the server to become reachable (ms). */\n readyTimeoutMs: z\n .number()\n .int()\n .positive()\n .default(120_000)\n .describe(\"Max time to wait for the server to become reachable, in ms (default 120000).\"),\n /** If a server is already reachable at the URL, use it as-is (don't start or stop one). */\n reuseExisting: z\n .boolean()\n .default(true)\n .describe(\"If a server is already reachable at the URL, use it as-is (don't start or stop one). Default true.\"),\n })\n .strict();\nexport type ResolvedServerSettings = z.infer<typeof serverSettingsSchema>;\n\n/** Repo-level CLI behavior (the `settings` block). */\nexport const settingsSchema = z.object({\n /** Output directory, relative to the repo root. */\n outDir: z\n .string()\n .min(1)\n .default(\"pro-visu\")\n .describe('Output directory for generated assets, relative to the repo root (default \"pro-visu\").'),\n /** How many assets to generate in parallel (shared browser, separate contexts). */\n concurrency: z\n .number()\n .int()\n .positive()\n .default(2)\n .describe(\"How many assets to generate in parallel, sharing one browser with separate contexts (default 2).\"),\n /**\n * Raise the Node heap (V8 old-space) to this many MB for the run. Heavy jobs — large frame-stepped\n * walls especially — can exceed Node's default ~4 GB limit and crash with \"JavaScript heap out of\n * memory\". When set above the current limit, the CLI re-execs itself with `--max-old-space-size`.\n * (This is the Node process heap, not the browser's — the browser manages its own memory.)\n */\n maxMemoryMB: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Raise the Node heap (V8 old-space) to this many MB to avoid out-of-memory on heavy jobs; re-execs with --max-old-space-size.\"),\n logLevel: logLevelSchema.default(\"info\").describe(\"CLI log verbosity (default \\\"info\\\").\"),\n browser: browserSettingsSchema.default({}).describe(\"Playwright launch controls.\"),\n /**\n * Per-generator option defaults, keyed by generator id, merged underneath each asset's\n * own `options`. Validated loosely here; each generator validates its own option shape.\n */\n defaults: z\n .record(z.string(), z.record(z.string(), z.unknown()))\n .default({})\n .describe(\"Per-generator option defaults, keyed by generator id, merged underneath each asset's own options.\"),\n /** Optional managed dev/prod server lifecycle (build → start → wait → … → stop). */\n server: serverSettingsSchema\n .optional()\n .describe(\"Build → start → wait → capture → stop a server automatically.\"),\n /** Render quality. \"draft\" lowers fps/scale and speeds the encoder for fast iteration. */\n quality: z\n .enum([\"draft\", \"final\"])\n .default(\"final\")\n .describe('Render quality; \"draft\" lowers fps/scale and speeds the encoder for fast iteration (default \"final\").'),\n /** Skip assets whose inputs+options+tool fingerprint is unchanged (opt-in; can be stale). */\n cache: z\n .boolean()\n .default(false)\n .describe(\"Skip assets whose inputs+options+tool fingerprint is unchanged (opt-in; can be stale). Default false.\"),\n});\nexport type ResolvedSettings = z.infer<typeof settingsSchema>;\n\n/** One thing to generate. Options are validated by the target generator at run time. */\nexport const assetSpecSchema = z\n .object({\n name: z.string().min(1),\n /**\n * Page to capture: an absolute `https://…` URL, or a path like `/shop` resolved against the\n * managed server's URL. Optional — with a managed server, a url-based asset that omits it\n * captures the server root; local generators (`scene`, `palette`) need no url.\n */\n url: z\n .string()\n .min(1)\n .refine((s) => /^https?:\\/\\//i.test(s) || s.startsWith(\"/\"), {\n message:\n 'url must be an absolute http(s) URL or a path starting with \"/\" (resolved against the managed server)',\n })\n .optional(),\n generator: z.string().min(1),\n options: z.record(z.string(), z.unknown()).default({}),\n /**\n * Other assets this one consumes, as `{ slotName: assetName }`. The producing assets run\n * first and their output files are exposed to this asset (e.g. a scene playing an earlier\n * recording). Forms a DAG; cycles are rejected.\n */\n inputs: z.record(z.string(), z.string()).default({}),\n })\n .strict();\nexport type ResolvedAssetSpec = z.infer<typeof assetSpecSchema>;\n\nexport const showcaseConfigSchema = z\n .object({\n settings: settingsSchema.default({}),\n assets: z.array(assetSpecSchema).min(1, \"Define at least one asset in `assets`.\"),\n })\n .superRefine((cfg, ctx) => {\n const seen = new Set<string>();\n for (const [i, asset] of cfg.assets.entries()) {\n if (seen.has(asset.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate asset name \"${asset.name}\" — names must be unique.`,\n path: [\"assets\", i, \"name\"],\n });\n }\n seen.add(asset.name);\n }\n });\nexport type ResolvedConfig = z.infer<typeof showcaseConfigSchema>;\n","import path from \"node:path\";\nimport { stat } from \"node:fs/promises\";\nimport {\n scrollReelOptionsSchema,\n type ResolvedScrollReelOptions,\n} from \"@/generators/scroll-reel/options\";\nimport { captureScrollWebm } from \"@/generators/scroll-reel/capture\";\nimport { captureScrollFrames } from \"@/generators/scroll-reel/capture-frames\";\nimport { scrollTimelineTotalMs } from \"@/generators/scroll-reel/timeline\";\nimport { buildVariants } from \"@/generators/scroll-reel/variants\";\nimport { produceOutputs } from \"@/generators/scroll-reel/outputs\";\nimport { captureFocusWebm, captureInteractionWebm } from \"@/generators/scroll-reel/interaction\";\nimport { requireUrl } from \"@/generators/require-url\";\nimport { concatMp4, transcodeToMp4 } from \"@/media/ffmpeg\";\nimport { autoWorkers } from \"@/media/frame-capture\";\nimport { sha256File } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const SCROLL_REEL_ID = \"scroll-reel\";\n\nasync function run(\n ctx: PipelineContext,\n options: ResolvedScrollReelOptions,\n): Promise<{ assets: AssetRecord[] }> {\n const url = requireUrl(ctx);\n const draft = ctx.quality === \"draft\";\n const preset = draft ? \"ultrafast\" : \"medium\";\n\n // Element-focused clip: realtime capture of one component, cropped to its box — single asset.\n if (options.focus) {\n const scheme =\n options.colorScheme === \"dark\" ? \"dark\" : options.colorScheme === \"light\" ? \"light\" : undefined;\n const fileName = options.fileName ?? `${slugify(ctx.target.name)}.mp4`;\n const outPath = ctx.resolveOutPath(fileName);\n ctx.logger.info(`recording ${url} (focus: ${options.focus.selector})`);\n const { webmPath, leadSeconds, durationSeconds, cropBox } = await captureFocusWebm({\n browser: ctx.browser,\n url,\n options,\n colorScheme: scheme,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n });\n ctx.logger.debug(\"transcoding to mp4 (cropped)\");\n await transcodeToMp4({\n inputPath: webmPath,\n outputPath: outPath,\n fps: options.fps,\n width: cropBox.width,\n height: cropBox.height,\n crf: options.crf,\n preset,\n startOffsetSeconds: leadSeconds,\n durationSeconds,\n crop: cropBox,\n logger: ctx.logger,\n signal: ctx.signal,\n });\n const [stats, contentHash] = await Promise.all([stat(outPath), sha256File(outPath)]);\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: SCROLL_REEL_ID,\n sourceUrl: url,\n file: ctx.toManifestPath(outPath),\n format: \"mp4\",\n width: cropBox.width,\n height: cropBox.height,\n durationMs: Math.round(durationSeconds * 1000),\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n }\n\n // Interaction: a scripted realtime \"tour\" with a synthetic cursor — single asset, always realtime.\n if (options.actions && options.actions.length > 0) {\n if (options.viewports?.length || options.colorScheme === \"both\") {\n ctx.logger.warn('viewports / colorScheme:\"both\" are not expanded for interaction reels');\n }\n if (options.intro || options.outro || options.annotations?.length) {\n ctx.logger.warn(\"intro/outro/annotations are not applied to interaction reels\");\n }\n const scheme =\n options.colorScheme === \"dark\" ? \"dark\" : options.colorScheme === \"light\" ? \"light\" : undefined;\n const fileName = options.fileName ?? `${slugify(ctx.target.name)}.mp4`;\n const outPath = ctx.resolveOutPath(fileName);\n ctx.logger.info(`recording ${url} (interaction, ${options.actions.length} action(s))`);\n const { webmPath, leadSeconds, durationSeconds } = await captureInteractionWebm({\n browser: ctx.browser,\n url,\n options,\n colorScheme: scheme,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n });\n ctx.logger.debug(\"transcoding to mp4\");\n await transcodeToMp4({\n inputPath: webmPath,\n outputPath: outPath,\n fps: options.fps,\n width: options.width,\n height: options.height,\n crf: options.crf,\n preset,\n startOffsetSeconds: leadSeconds,\n durationSeconds,\n logger: ctx.logger,\n signal: ctx.signal,\n });\n const [stats, contentHash] = await Promise.all([stat(outPath), sha256File(outPath)]);\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: SCROLL_REEL_ID,\n sourceUrl: url,\n file: ctx.toManifestPath(outPath),\n format: \"mp4\",\n width: options.width,\n height: options.height,\n durationMs: Math.round(durationSeconds * 1000),\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n }\n\n // Multi-page tour: capture each route as a frame-stepped segment, then concatenate into one reel.\n if (options.routes && options.routes.length > 0) {\n if (options.viewports?.length || options.colorScheme === \"both\") {\n ctx.logger.warn('viewports / colorScheme:\"both\" are not expanded for route tours');\n }\n const scheme =\n options.colorScheme === \"dark\" ? \"dark\" : options.colorScheme === \"light\" ? \"light\" : undefined;\n const workers = options.workers ?? autoWorkers();\n const segments: string[] = [];\n const routeCount = options.routes.length; // captured: narrowing doesn't persist into the progress closure\n let totalMs = 0;\n for (let i = 0; i < options.routes.length; i++) {\n const r = options.routes[i]!;\n const routeUrl = typeof r === \"string\" ? r : r.url;\n const routeOpts =\n typeof r === \"string\"\n ? { ...options, choreography: undefined, autoSections: undefined }\n : {\n ...options,\n choreography: r.choreography,\n autoSections: r.autoSections,\n duration: r.durationMs ?? options.duration,\n };\n const segPath = path.join(ctx.tmpDir, `${slugify(ctx.target.name)}-route-${i}.mp4`);\n ctx.logger.info(\n `recording route ${i + 1}/${options.routes.length}: ${routeUrl} (frame-stepped)`,\n );\n await captureScrollFrames({\n browser: ctx.browser,\n url: routeUrl,\n options: routeOpts,\n outPath: segPath,\n preset,\n workers,\n frameFormat: draft ? \"jpeg\" : options.frameFormat,\n jpegQuality: draft ? 70 : 90,\n settlePerFrame: options.settlePerFrame ?? !draft,\n settleMaxMs: options.settleMaxMs,\n colorScheme: scheme,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n // Weight each route's capture into one overall 0–1 across the whole tour.\n onProgress: ctx.progress ? (f) => ctx.progress?.((i + f) / routeCount) : undefined,\n signal: ctx.signal,\n });\n segments.push(segPath);\n totalMs += scrollTimelineTotalMs(routeOpts);\n }\n const tourMp4 = path.join(ctx.tmpDir, `${slugify(ctx.target.name)}-tour.mp4`);\n await concatMp4(segments, tourMp4, ctx.logger, ctx.signal);\n const baseName = (options.fileName ?? `${slugify(ctx.target.name)}.mp4`).replace(/\\.mp4$/i, \"\");\n const recs = await produceOutputs({\n ctx,\n generatorId: SCROLL_REEL_ID,\n sourceMp4: tourMp4,\n fileBase: baseName,\n assetId: ctx.target.name,\n sourceUrl: url,\n width: options.width,\n height: options.height,\n deviceScaleFactor: options.deviceScaleFactor,\n durationMs: totalMs,\n options,\n preset,\n });\n for (const r of recs) await ctx.writeAsset(r);\n return { assets: recs };\n }\n\n // Realtime: a single capture. Choreography / auto-sections / variants are frames-only.\n if (options.capture !== \"frames\") {\n if (options.choreography?.length || options.autoSections) {\n ctx.logger.warn('choreography/autoSections are ignored for capture:\"realtime\"');\n }\n if (options.viewports?.length || options.colorScheme === \"both\") {\n ctx.logger.warn('viewports / colorScheme:\"both\" are ignored for capture:\"realtime\"');\n }\n if (options.intro || options.outro || options.annotations?.length) {\n ctx.logger.warn('intro/outro/annotations are not applied to capture:\"realtime\"');\n }\n const fileName = options.fileName ?? `${slugify(ctx.target.name)}.mp4`;\n const outPath = ctx.resolveOutPath(fileName);\n const durationSeconds = (options.startDelayMs + options.duration + options.endDwellMs) / 1000;\n ctx.logger.info(`recording ${url} (realtime)`);\n const { webmPath, leadSeconds } = await captureScrollWebm({\n browser: ctx.browser,\n url,\n options,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n });\n ctx.logger.debug(\"transcoding to mp4\");\n await transcodeToMp4({\n inputPath: webmPath,\n outputPath: outPath,\n fps: options.fps,\n width: options.width,\n height: options.height,\n crf: options.crf,\n preset,\n // Drop the navigation + warm-up lead, then clamp to the intended length.\n startOffsetSeconds: leadSeconds,\n durationSeconds,\n logger: ctx.logger,\n signal: ctx.signal,\n });\n const [stats, contentHash] = await Promise.all([stat(outPath), sha256File(outPath)]);\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: SCROLL_REEL_ID,\n sourceUrl: url,\n file: ctx.toManifestPath(outPath),\n format: \"mp4\",\n width: options.width,\n height: options.height,\n durationMs: options.startDelayMs + options.duration + options.endDwellMs,\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n }\n\n // Frames: expand the viewport × color-scheme matrix; each variant is its own asset.\n const variants = buildVariants({\n width: options.width,\n height: options.height,\n deviceScaleFactor: options.deviceScaleFactor,\n viewports: options.viewports,\n colorScheme: options.colorScheme,\n });\n const baseName = (options.fileName ?? `${slugify(ctx.target.name)}.mp4`).replace(/\\.mp4$/i, \"\");\n const workers = options.workers ?? autoWorkers();\n const assets: AssetRecord[] = [];\n\n for (const v of variants) {\n const fileBase = v.suffix ? `${baseName}-${v.suffix}` : baseName;\n const assetId = v.suffix ? `${ctx.target.name}-${v.suffix}` : ctx.target.name;\n const captureMp4 = path.join(ctx.tmpDir, `${slugify(assetId)}-capture.mp4`);\n const vopts = {\n ...options,\n width: v.width,\n height: v.height,\n deviceScaleFactor: v.deviceScaleFactor,\n };\n const label = v.suffix ? ` [${v.suffix}]` : \"\";\n ctx.logger.info(`recording ${url}${label} (frame-stepped, ${workers} worker(s))`);\n await captureScrollFrames({\n browser: ctx.browser,\n url,\n options: vopts,\n outPath: captureMp4,\n preset,\n workers,\n // Draft always uses fast jpeg intermediates; final uses the configured format (png = lossless).\n frameFormat: draft ? \"jpeg\" : options.frameFormat,\n jpegQuality: draft ? 70 : 90,\n // Per-frame settling defaults on, off in draft for speed (override with the explicit option).\n settlePerFrame: options.settlePerFrame ?? !draft,\n settleMaxMs: options.settleMaxMs,\n colorScheme: v.colorScheme,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n onProgress: ctx.progress,\n signal: ctx.signal,\n });\n // Reframe to the requested aspect (if any) and emit each output format as its own asset.\n const recs = await produceOutputs({\n ctx,\n generatorId: SCROLL_REEL_ID,\n sourceMp4: captureMp4,\n fileBase,\n assetId,\n sourceUrl: url,\n width: v.width,\n height: v.height,\n deviceScaleFactor: v.deviceScaleFactor,\n durationMs: scrollTimelineTotalMs(vopts),\n options,\n preset,\n });\n for (const r of recs) await ctx.writeAsset(r);\n assets.push(...recs);\n }\n\n return { assets };\n}\n\nexport const scrollReelGenerator: Generator<ResolvedScrollReelOptions> = {\n id: SCROLL_REEL_ID,\n requiresUrl: true,\n optionsSchema: scrollReelOptionsSchema,\n run,\n};\n","import { z } from \"zod\";\n\nconst easingSchema = z.enum([\n \"linear\",\n \"easeInOutCubic\",\n \"easeInOutQuad\",\n \"easeOutCubic\",\n \"easeInOutSine\",\n \"easeInOutExpo\",\n \"easeOutQuint\",\n]);\nexport type Easing = z.infer<typeof easingSchema>;\n\n/** One choreographed scroll step (see `choreography` below). */\nconst choreographyStepSchema = z\n .object({\n /** Target: a 0..1 number, an \"NN%\" string, or a CSS selector to bring into view. */\n to: z\n .union([z.number(), z.string()])\n .describe('Target: a 0..1 number, an \"NN%\" string, or a CSS selector to bring into view.'),\n /** Travel time to this target (ms). Default 1200. */\n durationMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Travel time to this target (ms). Default 1200.\"),\n /** Hold time at this target after arriving (ms). Default 800. */\n holdMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Hold time at this target after arriving (ms). Default 800.\"),\n easing: easingSchema\n .optional()\n .describe('Easing for the travel to this target. Default \"easeInOutCubic\".'),\n })\n .strict();\n\n/** One step of a scripted interaction (see `actions` below). */\nconst interactionActionSchema = z\n .object({\n do: z\n .enum([\"move\", \"click\", \"hover\", \"type\", \"scrollTo\", \"wait\"])\n .describe(\"What this step does.\"),\n /** Target element for move/click/hover/type. */\n selector: z.string().optional().describe(\"Target element for move/click/hover/type.\"),\n /** Viewport-relative target for `move` without a selector (0..1). */\n x: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe(\"Viewport-relative X target for a selector-less `move` (0..1).\"),\n y: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe(\"Viewport-relative Y target for a selector-less `move` (0..1).\"),\n /** Text to type (for `type`). */\n text: z.string().optional().describe(\"Text to type (for `type`).\"),\n /** Scroll target for `scrollTo`: a 0..1 number, an \"NN%\" string, or a CSS selector. */\n to: z\n .union([z.number(), z.string()])\n .optional()\n .describe('Scroll target for `scrollTo`: a 0..1 number, an \"NN%\" string, or a CSS selector.'),\n /** Cursor travel / scroll animation time (ms). Default 700. */\n durationMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Cursor travel / scroll animation time (ms). Default 700.\"),\n /** Pause after the step (ms). Default 600. */\n holdMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Pause after the step (ms). Default 600.\"),\n })\n .strict();\n\n/** Tuning for auto-section choreography (see `autoSections` below). */\nconst autoSectionsSchema = z\n .object({\n /** Min element height (as a fraction of the viewport) to count as a section. Default 0.5. */\n minHeightFraction: z\n .number()\n .positive()\n .max(2)\n .optional()\n .describe(\n \"Min element height (as a fraction of the viewport) to count as a section. Default 0.5.\",\n ),\n /** Explicit section selector; overrides the heuristic. */\n selector: z\n .string()\n .optional()\n .describe(\"Explicit section selector; overrides the heuristic. Omit to auto-detect.\"),\n /** Hold at each detected section (ms). Default 700. */\n holdMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Hold at each detected section (ms). Default 700.\"),\n /** Total clip length (ms) split across detected sections. Default 12000. */\n durationMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Total clip length (ms) split across detected sections. Default 12000.\"),\n /** Cap on the number of sections. Default 8. */\n maxSections: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Cap on the number of sections. Default 8.\"),\n /** Distribute travel time by distance for uniform scroll speed. Default true. */\n constantVelocity: z\n .boolean()\n .optional()\n .describe(\"Distribute travel time by distance for uniform scroll speed. Default true.\"),\n })\n .strict();\n\n/** An intro/outro card (see `intro` / `outro`). */\nconst cardSchema = z\n .object({\n title: z.string().optional().describe(\"Card title (large). Omit for none.\"),\n subtitle: z\n .string()\n .optional()\n .describe(\"Card subtitle (small, under the title). Omit for none.\"),\n background: z.string().optional().describe(\"Card background color. Default black.\"),\n color: z.string().optional().describe(\"Card text color. Default white.\"),\n durationMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"How long the card holds (ms). Default 1500.\"),\n fadeMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Fade in/out length (ms). Default 400.\"),\n })\n .strict();\n\n/** A timed on-screen annotation (see `annotations`). */\nconst annotationSchema = z\n .object({\n /** Caption text shown while active. */\n text: z\n .string()\n .optional()\n .describe(\"Caption text shown while active. Omit for a ring/spotlight with no caption.\"),\n /** Selector to outline with a highlight ring. */\n ring: z.string().optional().describe(\"Selector to outline with a highlight ring.\"),\n /** Selector to spotlight (everything else dimmed). */\n spotlight: z.string().optional().describe(\"Selector to spotlight (everything else dimmed).\"),\n /** When it appears (clip time, ms). Default 0. */\n atMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"When it appears (clip time, ms). Default 0.\"),\n /** When it disappears (clip time, ms). Default = end. */\n untilMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"When it disappears (clip time, ms). Default = end of clip.\"),\n /** Caption placement. Default \"bottom\". */\n position: z\n .enum([\"top\", \"bottom\", \"center\"])\n .optional()\n .describe('Caption placement. Default \"bottom\".'),\n })\n .strict();\n\nexport const scrollReelOptionsSchema = z\n .object({\n /** Viewport + output width in CSS pixels. */\n width: z\n .number()\n .int()\n .positive()\n .default(1280)\n .describe(\"Viewport + output width in CSS px. Default 1280.\"),\n /** Viewport + output height in CSS pixels. */\n height: z\n .number()\n .int()\n .positive()\n .default(800)\n .describe(\"Viewport + output height in CSS px. Default 800.\"),\n /** Render scale (2 = retina-crisp capture, downscaled into the video). */\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(2)\n .describe(\"Render scale (2 = retina-crisp capture, downscaled into the video). Default 2.\"),\n /** Output frames per second (re-encoded from the recording). */\n fps: z\n .number()\n .int()\n .positive()\n .max(120)\n .default(30)\n .describe(\"Output frames per second (re-encoded from the recording). Default 30.\"),\n /** Time to scroll from top to bottom (ms). */\n duration: z\n .number()\n .int()\n .positive()\n .default(6000)\n .describe(\"Time to scroll from top to bottom (ms). Default 6000.\"),\n easing: easingSchema\n .default(\"easeInOutCubic\")\n .describe('Easing for the default top→bottom scroll. Default \"easeInOutCubic\".'),\n /** Dwell at the top before scrolling (ms). */\n startDelayMs: z\n .number()\n .int()\n .nonnegative()\n .default(500)\n .describe(\"Dwell at the top before scrolling (ms). Default 500.\"),\n /** Dwell at the bottom after scrolling (ms). */\n endDwellMs: z\n .number()\n .int()\n .nonnegative()\n .default(800)\n .describe(\"Dwell at the bottom after scrolling (ms). Default 800.\"),\n waitUntil: z\n .enum([\"load\", \"domcontentloaded\", \"networkidle\", \"commit\"])\n .default(\"networkidle\")\n .describe('Page-load milestone to wait for before recording. Default \"networkidle\".'),\n /** Optional element to wait for before recording (e.g. a hero section). */\n waitForSelector: z\n .string()\n .optional()\n .describe(\"Optional element to wait for before recording (e.g. a hero section). Omit to skip.\"),\n /** x264 quality, 0–51 (lower = better/larger). */\n crf: z\n .number()\n .int()\n .min(0)\n .max(51)\n .default(18)\n .describe(\"x264 quality, 0–51 (lower = better quality / larger file). Default 18.\"),\n /**\n * Capture strategy. \"frames\" (default) steps a virtual clock, sets the scroll position per frame and\n * screenshots — frame-accurate, crisp (supersampled) and reproducible. \"realtime\" records the live\n * browser session; use it only when the page's hero relies on time-based (not scroll-driven)\n * animation or autoplay video that should play during the capture.\n */\n capture: z\n .enum([\"frames\", \"realtime\"])\n .default(\"frames\")\n .describe(\n '\"frames\" (default) steps a virtual clock per frame — frame-accurate, crisp, reproducible; \"realtime\" records the live session for time-based hero animation or autoplay video.',\n ),\n /** Parallel render workers for \"frames\" (each its own browser context). Omit to auto-pick by cores. */\n workers: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\n 'Parallel render workers for \"frames\" (each its own browser context). Omit to auto-pick by cores.',\n ),\n /** Intermediate frame format for \"frames\"; \"png\" is lossless (slower), \"jpeg\" (default) is faster. */\n frameFormat: z\n .enum([\"jpeg\", \"png\"])\n .default(\"jpeg\")\n .describe(\n 'Intermediate frame format for \"frames\". \"jpeg\" (default) is faster; \"png\" is lossless.',\n ),\n\n /**\n * Choreographed scroll: an ordered list of steps instead of one top→bottom sweep. Each step scrolls\n * to a target (a 0..1 number, an \"NN%\" string, or a CSS selector to bring into view), then holds —\n * the \"pause on each section\" look. \"frames\" capture only (ignored by \"realtime\"). Omit for the\n * default single eased sweep. Clip length becomes startDelay + Σ(step travel + hold) + endDwell.\n */\n choreography: z\n .array(choreographyStepSchema)\n .optional()\n .describe(\n 'Choreographed scroll: an ordered list of steps instead of one top→bottom sweep (\"frames\" only). Omit for the default single eased sweep.',\n ),\n\n /**\n * Auto-choreograph: detect the page's sections and pan/hold through them automatically (no manual\n * selectors). `true` for defaults, or an object to tune. The clip is a fixed budget (`durationMs`,\n * default 12000) split across detected sections. \"frames\" capture only; ignored if `choreography`\n * is set.\n */\n autoSections: z\n .union([z.boolean(), autoSectionsSchema])\n .optional()\n .describe(\n 'Auto-choreograph: detect sections and pan/hold through them (\"frames\" only). `true` for defaults, or an object to tune. Ignored if `choreography` is set.',\n ),\n\n /** Loop style. \"boomerang\" plays the scroll forward then back within the clip for a seamless loop. */\n loop: z\n .enum([\"none\", \"boomerang\"])\n .default(\"none\")\n .describe(\n 'Loop style. \"boomerang\" plays the scroll forward then back for a seamless loop. Default \"none\".',\n ),\n\n /**\n * Ken Burns: a slow zoom over the clip (\"frames\" only). Scales the page toward an origin each frame\n * (folds automatically under a boomerang loop to stay seamless). May affect position:fixed elements —\n * pair with clean-capture if needed.\n */\n kenBurns: z\n .object({\n /** Start scale (1 = no zoom). Default 1. */\n scaleFrom: z\n .number()\n .positive()\n .optional()\n .describe(\"Start scale (1 = no zoom). Default 1.\"),\n /** End scale. Default 1.08. */\n scaleTo: z.number().positive().optional().describe(\"End scale. Default 1.08.\"),\n easing: easingSchema\n .optional()\n .describe('Easing for the zoom ramp. Default \"easeInOutCubic\".'),\n /** Zoom origin X within the viewport (0 = left, 1 = right). Default 0.5. */\n originX: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe(\"Zoom origin X within the viewport (0 = left, 1 = right). Default 0.5.\"),\n /** Zoom origin Y within the viewport (0 = top, 1 = bottom). Default 0.5. */\n originY: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe(\"Zoom origin Y within the viewport (0 = top, 1 = bottom). Default 0.5.\"),\n })\n .strict()\n .optional()\n .describe('Ken Burns slow zoom over the clip (\"frames\" only). Omit for no zoom.'),\n\n // --- interaction (scripted realtime \"tour\" with a synthetic cursor) ---\n /**\n * Drive a scripted interaction instead of an auto-scroll: a sequence of steps (move / click /\n * hover / type / scrollTo / wait) performed live with a visible cursor. Setting this records in\n * REALTIME (interactions and their animations are inherently time-based) and emits a single asset\n * (variants / aspect / extra outputs are skipped).\n */\n actions: z\n .array(interactionActionSchema)\n .optional()\n .describe(\n \"Drive a scripted interaction instead of an auto-scroll (move/click/hover/type/scrollTo/wait). Records in REALTIME and emits a single asset (variants/aspect/extra outputs skipped).\",\n ),\n /** The synthetic cursor shown during an interaction. */\n cursor: z\n .object({\n show: z\n .boolean()\n .optional()\n .describe(\"Show the cursor. Default true (when `actions` is set).\"),\n size: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Cursor size (px). Default 22.\"),\n color: z.string().optional().describe(\"Cursor color. Default white-with-shadow.\"),\n })\n .strict()\n .optional()\n .describe(\"The synthetic cursor shown during an interaction. Omit for the default cursor.\"),\n\n /**\n * Element-focused clip: scroll one component into view, optionally trigger it (`actions`), hold,\n * and crop the output to its box (+padding). Realtime; emits a single asset (variants / aspect /\n * outputs / cards are skipped).\n */\n focus: z\n .object({\n selector: z\n .string()\n .describe(\"Selector of the element to scroll into view and crop to.\"),\n /** Padding (px) around the element when cropping. Default 24. */\n padding: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Padding (px) around the element when cropping. Default 24.\"),\n /** Optional steps to trigger the component (e.g. open a dropdown) before holding. */\n actions: z\n .array(interactionActionSchema)\n .optional()\n .describe(\n \"Optional steps to trigger the component (e.g. open a dropdown) before holding.\",\n ),\n /** Time to dwell on the element after positioning/triggering (ms). Default 2000. */\n holdMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Time to dwell on the element after positioning / triggering (ms). Default 2000.\"),\n })\n .strict()\n .optional()\n .describe(\n \"Element-focused clip: scroll one component into view, optionally trigger it, hold, and crop the output to its box. Realtime; emits a single asset (variants/aspect/outputs/cards skipped).\",\n ),\n\n // --- variants (each emitted as its own asset; \"frames\" path) ---\n /** Force a color scheme. \"both\" emits a light AND a dark asset (<name>-light / <name>-dark). */\n colorScheme: z\n .enum([\"light\", \"dark\", \"both\"])\n .optional()\n .describe(\n 'Force a color scheme. \"both\" emits a light AND a dark asset (<name>-light / <name>-dark). Omit to leave as-is.',\n ),\n /** Add this class to <html> before capture (e.g. to trigger a CSS-class dark theme). */\n themeClass: z\n .string()\n .optional()\n .describe(\n \"Add this class to <html> before capture (e.g. to trigger a CSS-class dark theme). Omit for none.\",\n ),\n /** Capture the same reel at multiple viewports; each emits an asset (<name>-<viewport name>). */\n viewports: z\n .array(\n z\n .object({\n name: z\n .string()\n .describe(\"Name appended to the asset (<name>-<viewport name>).\"),\n width: z.number().int().positive().describe(\"Viewport width in CSS px.\"),\n height: z.number().int().positive().describe(\"Viewport height in CSS px.\"),\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .optional()\n .describe(\n \"Override the generator-level `deviceScaleFactor` for this viewport. Omit to inherit it.\",\n ),\n })\n .strict(),\n )\n .optional()\n .describe(\n \"Also capture the reel at these viewports; each emits an asset (<name>-<viewport name>).\",\n ),\n\n // --- multi-page tour (\"frames\" path) ---\n /**\n * Capture several routes and concatenate them into one reel. Each entry is a URL, or an object with\n * per-route `choreography` / `autoSections` / `durationMs`. Frame-stepped per route; emits a single\n * asset (variants are skipped; aspect/outputs apply to the final tour).\n */\n routes: z\n .array(\n z.union([\n z.string(),\n z\n .object({\n url: z\n .string()\n .describe('Route URL (absolute, or a \"/path\" against the managed server).'),\n choreography: z\n .array(choreographyStepSchema)\n .optional()\n .describe(\"Per-route choreographed scroll (overrides the tour default).\"),\n autoSections: z\n .union([z.boolean(), autoSectionsSchema])\n .optional()\n .describe(\"Per-route auto-section choreography.\"),\n durationMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"This route's slice of the tour (ms).\"),\n })\n .strict(),\n ]),\n )\n .optional()\n .describe(\n 'Capture several routes and concatenate them into one reel (\"frames\" path). Emits a single asset (variants skipped; aspect/outputs apply to the final tour).',\n ),\n\n // --- clean capture (suppress real-site noise; applied on the \"frames\" path) ---\n /** Hide elements matching these CSS selectors before capture (cookie banners, chat widgets, …). */\n hideSelectors: z\n .array(z.string())\n .default([])\n .describe(\n \"Hide elements matching these CSS selectors before capture (cookie banners, chat widgets, …). Default none.\",\n ),\n /** Extra CSS injected before capture (e.g. a brand backdrop, or hiding a sticky header). */\n injectCss: z\n .string()\n .optional()\n .describe(\n \"Extra CSS injected before capture (e.g. a brand backdrop, or hiding a sticky header). Omit for none.\",\n ),\n /** Click these selectors once after load to dismiss overlays (consent dialogs); best-effort. */\n clickSelectors: z\n .array(z.string())\n .default([])\n .describe(\n \"Click these selectors once after load to dismiss overlays (consent dialogs); best-effort. Default none.\",\n ),\n /** Hide scrollbars so they don't appear in the capture. */\n hideScrollbars: z\n .boolean()\n .default(true)\n .describe(\"Hide scrollbars so they don't appear in the capture. Default true.\"),\n /** Pause CSS animations/transitions for fully static, deterministic frames. */\n pauseAnimations: z\n .boolean()\n .default(false)\n .describe(\"Pause CSS animations/transitions for fully static, deterministic frames. Default false.\"),\n /** Freeze Date.now / performance.now / Math.random (seeded) so time/random content is stable. */\n freezeClock: z\n .boolean()\n .default(false)\n .describe(\n \"Freeze Date.now / performance.now / Math.random (seeded) so time/random content is stable. Default false.\",\n ),\n /** Abort common analytics/ads/session-replay requests during capture (cleaner, faster). */\n blockTrackers: z\n .boolean()\n .default(true)\n .describe(\n \"Abort common analytics/ads/session-replay requests during capture (cleaner, faster). Default true.\",\n ),\n /** Extra hostname substrings to block during capture. */\n blockHosts: z\n .array(z.string())\n .default([])\n .describe(\"Extra hostname substrings to block during capture. Default none.\"),\n /** Playwright resource types to block (e.g. \"media\", \"font\", \"image\"). */\n blockResourceTypes: z\n .array(z.string())\n .default([])\n .describe('Playwright resource types to block (e.g. \"media\", \"font\", \"image\"). Default none.'),\n\n // --- per-frame settling (\"frames\" path) ---\n /** Wait for fonts + in-view images before each frame's screenshot. Defaults on (off in draft). */\n settlePerFrame: z\n .boolean()\n .optional()\n .describe(\n \"Wait for fonts + in-view images before each frame's screenshot (\\\"frames\\\"). Defaults on (off in draft).\",\n ),\n /** Max time (ms) to wait per frame for settling before screenshotting anyway. */\n settleMaxMs: z\n .number()\n .int()\n .nonnegative()\n .default(250)\n .describe(\n \"Max time (ms) to wait per frame for settling before screenshotting anyway. Default 250.\",\n ),\n\n // --- output formats & reframing (\"frames\" path) ---\n /** Reframe the output to a target aspect: a preset (\"16:9\"|\"9:16\"|\"1:1\") or explicit {width,height}. */\n aspect: z\n .union([\n z.enum([\"16:9\", \"9:16\", \"1:1\"]),\n z.object({ width: z.number().int().positive(), height: z.number().int().positive() }).strict(),\n ])\n .optional()\n .describe(\n 'Reframe the output to a target aspect: a preset (\"16:9\"|\"9:16\"|\"1:1\") or explicit {width,height}. Omit to keep the capture aspect.',\n ),\n /** How to fit the capture into the aspect: \"cover\" (scale + center-crop) or \"contain\" (scale + pad). */\n fit: z\n .enum([\"cover\", \"contain\"])\n .default(\"cover\")\n .describe(\n 'How to fit the capture into `aspect`: \"cover\" (scale + center-crop) or \"contain\" (scale + pad). Default \"cover\".',\n ),\n /** Pad color used by \"contain\". */\n padColor: z\n .string()\n .default(\"#0b0b0f\")\n .describe('Pad color used by \"contain\". Default \"#0b0b0f\".'),\n /** Files to emit per variant; each becomes its own asset. */\n outputs: z\n .array(z.enum([\"mp4\", \"gif\", \"webp\", \"poster\"]))\n .default([\"mp4\"])\n .describe('Files to emit per variant; each becomes its own asset. Default [\"mp4\"].'),\n /** GIF / animated-WebP frame rate. Defaults to min(fps, 15). */\n gifFps: z\n .number()\n .int()\n .positive()\n .max(50)\n .optional()\n .describe(\"GIF / animated-WebP frame rate. Defaults to min(fps, 15).\"),\n /** Intro card shown before the reel (fades from black). Applies to frames / route tours. */\n intro: cardSchema\n .optional()\n .describe(\n \"Intro card shown before the reel (fades from black). Applies to frames / route tours. Omit for none.\",\n ),\n /** Outro / end card shown after the reel. */\n outro: cardSchema.optional().describe(\"Outro / end card shown after the reel. Omit for none.\"),\n /** Timed on-screen annotations (caption text, a highlight ring, or a spotlight on a selector). */\n annotations: z\n .array(annotationSchema)\n .optional()\n .describe(\n \"Timed on-screen annotations (caption text, a highlight ring, or a spotlight on a selector).\",\n ),\n\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName: z\n .string()\n .optional()\n .describe('Output filename; defaults to \"<slug(asset name)>.mp4\".'),\n })\n .strict();\n\n// ---------------------------------------------------------------------------\n// Author-facing input types (editor autocomplete + hover docs). JSDoc on the\n// zod schema above does NOT surface on hover through `z.input`, so the docs\n// live on these hand-written interfaces, kept in sync with the schema by the\n// Exact<> guard at the bottom — a drift is a compile error.\n// ---------------------------------------------------------------------------\n\n/** One choreographed scroll step. */\nexport interface ChoreographyStepInput {\n /** Target: a 0..1 number, an \"NN%\" string, or a CSS selector to bring into view. */\n to: number | string;\n /** Travel time to this target (ms). Default 1200. */\n durationMs?: number;\n /** Hold time at this target after arriving (ms). Default 800. */\n holdMs?: number;\n /** Easing for the travel to this target. Default \"easeInOutCubic\". */\n easing?: Easing;\n}\n\n/** One step of a scripted interaction (`actions` / `focus.actions`). */\nexport interface InteractionActionInput {\n /** What this step does. */\n do: \"move\" | \"click\" | \"hover\" | \"type\" | \"scrollTo\" | \"wait\";\n /** Target element for move/click/hover/type. */\n selector?: string;\n /** Viewport-relative X target for a selector-less `move` (0..1). */\n x?: number;\n /** Viewport-relative Y target for a selector-less `move` (0..1). */\n y?: number;\n /** Text to type (for `type`). */\n text?: string;\n /** Scroll target for `scrollTo`: a 0..1 number, an \"NN%\" string, or a CSS selector. */\n to?: number | string;\n /** Cursor travel / scroll animation time (ms). Default 700. */\n durationMs?: number;\n /** Pause after the step (ms). Default 600. */\n holdMs?: number;\n}\n\n/** Tuning for auto-section choreography (`autoSections`). */\nexport interface AutoSectionsInput {\n /** Min element height (as a fraction of the viewport) to count as a section. Default 0.5. */\n minHeightFraction?: number;\n /** Explicit section selector; overrides the heuristic. Omit to auto-detect. */\n selector?: string;\n /** Hold at each detected section (ms). Default 700. */\n holdMs?: number;\n /** Total clip length (ms) split across detected sections. Default 12000. */\n durationMs?: number;\n /** Cap on the number of sections. Default 8. */\n maxSections?: number;\n /** Distribute travel time by distance for uniform scroll speed. Default true. */\n constantVelocity?: boolean;\n}\n\n/** An intro / outro card (`intro` / `outro`). */\nexport interface CardInput {\n /** Card title (large). Omit for none. */\n title?: string;\n /** Card subtitle (small, under the title). Omit for none. */\n subtitle?: string;\n /** Card background color. Default black. */\n background?: string;\n /** Card text color. Default white. */\n color?: string;\n /** How long the card holds (ms). Default 1500. */\n durationMs?: number;\n /** Fade in/out length (ms). Default 400. */\n fadeMs?: number;\n}\n\n/** A timed on-screen annotation (`annotations`). */\nexport interface AnnotationInput {\n /** Caption text shown while active. Omit for a ring/spotlight with no caption. */\n text?: string;\n /** Selector to outline with a highlight ring. */\n ring?: string;\n /** Selector to spotlight (everything else dimmed). */\n spotlight?: string;\n /** When it appears (clip time, ms). Default 0. */\n atMs?: number;\n /** When it disappears (clip time, ms). Default = end of clip. */\n untilMs?: number;\n /** Caption placement. Default \"bottom\". */\n position?: \"top\" | \"bottom\" | \"center\";\n}\n\n/** Ken Burns slow-zoom config (\"frames\" capture only). */\nexport interface KenBurnsInput {\n /** Start scale (1 = no zoom). Default 1. */\n scaleFrom?: number;\n /** End scale. Default 1.08. */\n scaleTo?: number;\n /** Easing for the zoom ramp. Default \"easeInOutCubic\". */\n easing?: Easing;\n /** Zoom origin X within the viewport (0 = left, 1 = right). Default 0.5. */\n originX?: number;\n /** Zoom origin Y within the viewport (0 = top, 1 = bottom). Default 0.5. */\n originY?: number;\n}\n\n/** The synthetic cursor shown during an interaction. */\nexport interface CursorInput {\n /** Show the cursor. Default true (when `actions` is set). */\n show?: boolean;\n /** Cursor size (px). Default 22. */\n size?: number;\n /** Cursor color. Default white-with-shadow. */\n color?: string;\n}\n\n/** Element-focused clip config (`focus`). */\nexport interface FocusInput {\n /** Selector of the element to scroll into view and crop to. */\n selector: string;\n /** Padding (px) around the element when cropping. Default 24. */\n padding?: number;\n /** Optional steps to trigger the component (e.g. open a dropdown) before holding. */\n actions?: InteractionActionInput[];\n /** Time to dwell on the element after positioning / triggering (ms). Default 2000. */\n holdMs?: number;\n}\n\n/** One extra viewport to also capture the reel at (`viewports`). */\nexport interface ViewportInput {\n /** Name appended to the asset (<name>-<viewport name>). */\n name: string;\n /** Viewport width in CSS px. */\n width: number;\n /** Viewport height in CSS px. */\n height: number;\n /** Override the generator-level `deviceScaleFactor` for this viewport. Omit to inherit it. */\n deviceScaleFactor?: number;\n}\n\n/** One route in a multi-page tour: a URL string, or an object with per-route choreography. */\nexport type RouteInput =\n | string\n | {\n /** Route URL (absolute, or a \"/path\" against the managed server). */\n url: string;\n /** Per-route choreographed scroll (overrides the tour default). */\n choreography?: ChoreographyStepInput[];\n /** Per-route auto-section choreography. */\n autoSections?: boolean | AutoSectionsInput;\n /** This route's slice of the tour (ms). */\n durationMs?: number;\n };\n\n/** Target output aspect: a preset, or an explicit pixel box. */\nexport type AspectInput = \"16:9\" | \"9:16\" | \"1:1\" | { width: number; height: number };\n\n/**\n * Author-facing options for the `scroll-reel` generator — a video of a page. By default it eases a\n * single top→bottom scroll; the options below switch it into choreographed / auto-section / focus /\n * interaction / multi-route modes, add cards & annotations, clean up the page, and reframe / re-encode\n * the output. Everything is optional, with the defaults noted below.\n */\nexport interface ScrollReelOptionsInput {\n /** Viewport + output width in CSS px. Default 1280. */\n width?: number;\n /** Viewport + output height in CSS px. Default 800. */\n height?: number;\n /** Render scale (2 = retina-crisp capture, downscaled into the video). Default 2. */\n deviceScaleFactor?: number;\n /** Output frames per second (re-encoded from the recording). Default 30. */\n fps?: number;\n /** Time to scroll from top to bottom (ms). Default 6000. */\n duration?: number;\n /** Easing for the default top→bottom scroll. Default \"easeInOutCubic\". */\n easing?: Easing;\n /** Dwell at the top before scrolling (ms). Default 500. */\n startDelayMs?: number;\n /** Dwell at the bottom after scrolling (ms). Default 800. */\n endDwellMs?: number;\n /** Page-load milestone to wait for before recording. Default \"networkidle\". */\n waitUntil?: \"load\" | \"domcontentloaded\" | \"networkidle\" | \"commit\";\n /** Optional element to wait for before recording (e.g. a hero section). Omit to skip. */\n waitForSelector?: string;\n /** x264 quality, 0–51 (lower = better quality / larger file). Default 18. */\n crf?: number;\n /**\n * Capture strategy. \"frames\" (default) steps a virtual clock per frame — frame-accurate, crisp,\n * reproducible. \"realtime\" records the live session; use it only for time-based hero animation or\n * autoplay video. Default \"frames\".\n */\n capture?: \"frames\" | \"realtime\";\n /** Parallel render workers for \"frames\" (each its own browser context). Omit to auto-pick by cores. */\n workers?: number;\n /** Intermediate frame format for \"frames\". \"jpeg\" (default) is faster; \"png\" is lossless. Default \"jpeg\". */\n frameFormat?: \"jpeg\" | \"png\";\n /**\n * Choreographed scroll: an ordered list of steps instead of one top→bottom sweep (\"frames\" only).\n * Omit for the default single eased sweep.\n */\n choreography?: ChoreographyStepInput[];\n /**\n * Auto-choreograph: detect the page's sections and pan/hold through them (\"frames\" only). `true`\n * for defaults, or an object to tune. Ignored if `choreography` is set. Omit to disable.\n */\n autoSections?: boolean | AutoSectionsInput;\n /** Loop style. \"boomerang\" plays the scroll forward then back for a seamless loop. Default \"none\". */\n loop?: \"none\" | \"boomerang\";\n /** Ken Burns slow zoom over the clip (\"frames\" only). Omit for no zoom. */\n kenBurns?: KenBurnsInput;\n /**\n * Drive a scripted interaction instead of an auto-scroll (move/click/hover/type/scrollTo/wait).\n * Setting this records in REALTIME and emits a single asset (variants/aspect/extra outputs skipped).\n */\n actions?: InteractionActionInput[];\n /** The synthetic cursor shown during an interaction. Omit for the default cursor. */\n cursor?: CursorInput;\n /**\n * Element-focused clip: scroll one component into view, optionally trigger it, hold, and crop the\n * output to its box. Realtime; emits a single asset (variants/aspect/outputs/cards skipped).\n */\n focus?: FocusInput;\n /** Force a color scheme. \"both\" emits a light AND a dark asset (<name>-light / <name>-dark). Omit to leave as-is. */\n colorScheme?: \"light\" | \"dark\" | \"both\";\n /** Add this class to <html> before capture (e.g. to trigger a CSS-class dark theme). Omit for none. */\n themeClass?: string;\n /** Also capture the reel at these viewports; each emits an asset (<name>-<viewport name>). */\n viewports?: ViewportInput[];\n /**\n * Capture several routes and concatenate them into one reel (\"frames\" path). Emits a single asset\n * (variants skipped; aspect/outputs apply to the final tour).\n */\n routes?: RouteInput[];\n /** Hide elements matching these CSS selectors before capture (cookie banners, chat widgets, …). Default none. */\n hideSelectors?: string[];\n /** Extra CSS injected before capture (e.g. a brand backdrop, or hiding a sticky header). Omit for none. */\n injectCss?: string;\n /** Click these selectors once after load to dismiss overlays (consent dialogs); best-effort. Default none. */\n clickSelectors?: string[];\n /** Hide scrollbars so they don't appear in the capture. Default true. */\n hideScrollbars?: boolean;\n /** Pause CSS animations/transitions for fully static, deterministic frames. Default false. */\n pauseAnimations?: boolean;\n /** Freeze Date.now / performance.now / Math.random (seeded) so time/random content is stable. Default false. */\n freezeClock?: boolean;\n /** Abort common analytics/ads/session-replay requests during capture (cleaner, faster). Default true. */\n blockTrackers?: boolean;\n /** Extra hostname substrings to block during capture. Default none. */\n blockHosts?: string[];\n /** Playwright resource types to block (e.g. \"media\", \"font\", \"image\"). Default none. */\n blockResourceTypes?: string[];\n /** Wait for fonts + in-view images before each frame's screenshot (\"frames\"). Defaults on (off in draft). */\n settlePerFrame?: boolean;\n /** Max time (ms) to wait per frame for settling before screenshotting anyway. Default 250. */\n settleMaxMs?: number;\n /** Reframe the output to a target aspect: a preset (\"16:9\"|\"9:16\"|\"1:1\") or explicit {width,height}. Omit to keep the capture aspect. */\n aspect?: AspectInput;\n /** How to fit the capture into `aspect`: \"cover\" (scale + center-crop) or \"contain\" (scale + pad). Default \"cover\". */\n fit?: \"cover\" | \"contain\";\n /** Pad color used by \"contain\". Default \"#0b0b0f\". */\n padColor?: string;\n /** Files to emit per variant; each becomes its own asset. Default [\"mp4\"]. */\n outputs?: (\"mp4\" | \"gif\" | \"webp\" | \"poster\")[];\n /** GIF / animated-WebP frame rate. Defaults to min(fps, 15). */\n gifFps?: number;\n /** Intro card shown before the reel (fades from black). Applies to frames / route tours. Omit for none. */\n intro?: CardInput;\n /** Outro / end card shown after the reel. Omit for none. */\n outro?: CardInput;\n /** Timed on-screen annotations (caption text, a highlight ring, or a spotlight on a selector). */\n annotations?: AnnotationInput[];\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName?: string;\n}\n\n/** Author-facing input (documented for editor hover; the schema validates it at run time). */\nexport type ScrollReelOptions = ScrollReelOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedScrollReelOptions = z.infer<typeof scrollReelOptionsSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _scrollReelInputInSync: Exact<\n ScrollReelOptionsInput,\n z.input<typeof scrollReelOptionsSchema>\n> = true;\nvoid _scrollReelInputInSync;\n","import path from \"node:path\";\nimport { mkdtemp } from \"node:fs/promises\";\nimport type { Browser } from \"playwright-core\";\nimport type { Logger } from \"@/utils/logger\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { pageScroll, prepareScroll } from \"@/generators/scroll-reel/scroll\";\nimport type { ResolvedScrollReelOptions } from \"@/generators/scroll-reel/options\";\n\n/** Time at the bottom for lazy/below-the-fold content to load during the (trimmed) warm-up pass. */\nconst PREWARM_SETTLE_MS = 700;\n\nexport interface CaptureArgs {\n browser: Browser;\n url: string;\n options: ResolvedScrollReelOptions;\n /** Scratch root; a unique recording subdir is created inside. */\n tmpDir: string;\n logger: Logger;\n}\n\nexport interface CaptureResult {\n /** Absolute path to the recorded .webm. */\n webmPath: string;\n /** Seconds of navigation + warm-up before the animated scroll began — trim this off the head. */\n leadSeconds: number;\n}\n\n/**\n * Open a recording context, navigate, run the eased scroll, then close to finalize the\n * webm. Returns the recording path for downstream transcoding.\n */\nexport async function captureScrollWebm(args: CaptureArgs): Promise<CaptureResult> {\n const { browser, url, options, tmpDir, logger } = args;\n await ensureDir(tmpDir);\n const recordDir = await mkdtemp(path.join(tmpDir, \"rec-\"));\n\n const context = await browser.newContext({\n viewport: { width: options.width, height: options.height },\n deviceScaleFactor: options.deviceScaleFactor,\n recordVideo: {\n dir: recordDir,\n size: { width: options.width, height: options.height },\n },\n });\n const page = await context.newPage();\n const video = page.video();\n\n const recStart = Date.now(); // Playwright records the whole context lifetime\n let leadSeconds = 0;\n try {\n logger.debug(`navigating to ${url} (waitUntil=${options.waitUntil})`);\n await page.goto(url, { waitUntil: options.waitUntil });\n if (options.waitForSelector) {\n logger.debug(`waiting for selector ${options.waitForSelector}`);\n await page.waitForSelector(options.waitForSelector, { state: \"visible\" });\n }\n // Warm-up (trimmed from the head): load lazy content, fonts and images, settle at the top.\n await page.evaluate(prepareScroll, { settleMs: PREWARM_SETTLE_MS });\n // Everything above is blank/churn in the recording; the animated scroll starts now.\n leadSeconds = (Date.now() - recStart) / 1000;\n const distance = await page.evaluate(pageScroll, {\n durationMs: options.duration,\n easing: options.easing,\n startDelayMs: options.startDelayMs,\n endDwellMs: options.endDwellMs,\n });\n if (!distance) {\n logger.warn(\n \"page did not scroll — no scrollable content found (the reel will be static). Check the URL renders a taller-than-viewport page.\",\n );\n }\n } finally {\n // Closing the context flushes + finalizes the recording file.\n await context.close();\n }\n\n if (!video) {\n throw new Error(\"Playwright did not record a video (recordVideo inactive).\");\n }\n return { webmPath: await video.path(), leadSeconds };\n}\n","export type EasingName =\n | \"linear\"\n | \"easeInOutCubic\"\n | \"easeInOutQuad\"\n | \"easeOutCubic\"\n | \"easeInOutSine\"\n | \"easeInOutExpo\"\n | \"easeOutQuint\";\n\n/**\n * Pure easing functions, t in [0,1] -> [0,1]. Unit-tested on the Node side; the formula inside\n * `pageScroll` below MUST stay in sync with these.\n */\nexport const EASINGS: Record<EasingName, (t: number) => number> = {\n linear: (t) => t,\n easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2),\n easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2),\n easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),\n easeInOutSine: (t) => -(Math.cos(Math.PI * t) - 1) / 2,\n easeInOutExpo: (t) =>\n t === 0\n ? 0\n : t === 1\n ? 1\n : t < 0.5\n ? Math.pow(2, 20 * t - 10) / 2\n : (2 - Math.pow(2, -20 * t + 10)) / 2,\n easeOutQuint: (t) => 1 - Math.pow(1 - t, 5),\n};\n\nexport interface PageScrollArgs {\n durationMs: number;\n easing: EasingName;\n startDelayMs: number;\n endDwellMs: number;\n}\n\nexport interface PrepareScrollArgs {\n /** How long to wait at the bottom for lazy/below-the-fold content to load (ms). */\n settleMs: number;\n}\n\nexport interface SeekScrollArgs {\n /** Normalized scroll position to jump to (0 = top, 1 = bottom). */\n normalizedY: number;\n /** Optional Ken Burns zoom scale for this frame (omit for no zoom). */\n scale?: number;\n /** Zoom origin X within the viewport (0 = left, 1 = right). Default 0.5. */\n originX?: number;\n /** Zoom origin Y within the viewport (0 = top, 1 = bottom). Default 0.5. */\n originY?: number;\n}\n\nexport interface MeasureOffsetsArgs {\n /** CSS selectors to resolve to normalized scroll positions (0..1). */\n selectors: string[];\n}\n\nexport interface DetectSectionsArgs {\n /** Minimum element height as a fraction of the viewport to count as a section. */\n minHeightFraction: number;\n /** Explicit section selector; overrides the heuristic when set (non-null). */\n selector: string | null;\n /** Cap on the number of returned sections. */\n maxSections: number;\n}\n\n// Browser globals — declared loosely (no DOM lib in this Node project). The exported functions are\n// serialized and run inside the page via page.evaluate, so each must be fully self-contained (no\n// references to module-level bindings); the small scroll helpers are therefore inlined in both.\ndeclare const window: {\n scrollTo: (o: { top: number; left: number; behavior: string }) => void;\n innerHeight: number;\n innerWidth: number;\n pageYOffset: number;\n pageXOffset: number;\n};\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ndeclare const document: any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ndeclare const getComputedStyle: (el: any) => { overflowY: string };\ndeclare const requestAnimationFrame: (cb: (t: number) => void) => number;\ndeclare const setTimeout: (cb: () => void, ms: number) => unknown;\n\n/**\n * Runs INSIDE the page. Warms the page for capture: disables smooth scrolling, jumps to the bottom\n * (instantly) to trigger lazy-loaded / intersection-mounted content and below-the-fold image\n * requests, waits for those plus fonts and image decode, then returns to the top. Meant to run\n * *before* the recording's trim point so none of this churn is visible in the final clip.\n */\nexport async function prepareScroll(args: PrepareScrollArgs): Promise<void> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n forceInstant(target);\n\n const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(() => resolve(), ms));\n\n scrollTargetTo(target, maxScrollOf(target)); // jump to bottom to trigger lazy content\n await sleep(Math.max(0, args.settleMs));\n try {\n await (document.fonts?.ready ?? Promise.resolve());\n } catch {\n /* no font set */\n }\n try {\n const imgs = Array.from(document.images ?? []);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await Promise.all(imgs.map((im: any) => (im.decode ? im.decode().catch(() => {}) : null)));\n } catch {\n /* decode unsupported */\n }\n scrollTargetTo(target, 0); // back to the top for the animated pass\n await sleep(50);\n\n // --- inlined, self-contained scroll helpers (duplicated in pageScroll; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function scrollTargetTo(t: any, y: number): void {\n if (isDocTarget(t)) window.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else if (t.scrollTo) t.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else t.scrollTop = y;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function forceInstant(t: any): void {\n try {\n document.documentElement.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n try {\n if (t && t.style) t.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n }\n}\n\n/**\n * Runs INSIDE the page. Auto-detects the page's section boundaries as normalized scroll positions\n * (0..1), sorted ascending and deduped, with the bottom (1) always included. Uses an explicit selector\n * when given, else a heuristic (<section>, direct children of <main>, [data-section]) filtered to\n * elements at least `minHeightFraction` of the viewport tall. Returns [] for a non-scrollable page.\n * Self-contained (serialized via page.evaluate).\n */\nexport async function detectSectionOffsets(args: DetectSectionsArgs): Promise<number[]> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n const distance = maxScrollOf(target);\n if (distance <= 0) return [];\n const docTarget = isDocTarget(target);\n const vh = window.innerHeight || document.documentElement.clientHeight || 1;\n const containerTop = docTarget ? 0 : target.getBoundingClientRect().top;\n const curScroll = docTarget\n ? window.pageYOffset || document.documentElement.scrollTop || 0\n : target.scrollTop;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let els: any[] = [];\n try {\n els = Array.from(\n document.querySelectorAll(args.selector || \"section, main > *, [data-section]\"),\n );\n } catch {\n els = [];\n }\n const minH = vh * args.minHeightFraction;\n const offsets: number[] = [];\n for (const el of els) {\n let rect: { top: number; height: number };\n try {\n rect = el.getBoundingClientRect();\n } catch {\n continue;\n }\n // Apply the height filter only to the heuristic; trust an explicit selector verbatim.\n if (!args.selector && rect.height < minH) continue;\n const top = docTarget ? rect.top + curScroll : rect.top - containerTop + curScroll;\n const y = Math.max(0, Math.min(distance, top));\n offsets.push(y / distance);\n }\n offsets.sort((a, b) => a - b);\n const deduped: number[] = [];\n for (const o of offsets) {\n if (deduped.length === 0 || o - deduped[deduped.length - 1]! > 0.01) deduped.push(o);\n }\n // Always end the reel at the bottom.\n if (deduped.length === 0 || deduped[deduped.length - 1]! < 0.99) deduped.push(1);\n return deduped.slice(0, args.maxSections);\n\n // --- inlined, self-contained scroll helpers (duplicated elsewhere in this file; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n}\n\n/**\n * Runs INSIDE the page. Resolves CSS selectors to normalized scroll positions (0..1) on the real scroll\n * target — used by choreography to \"scroll to a section\". Returns null for a selector that matches no\n * element. Measured once after warm-up (positions are stable), so all workers agree. Self-contained.\n */\nexport async function measureNormalizedOffsets(\n args: MeasureOffsetsArgs,\n): Promise<Array<number | null>> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n const distance = maxScrollOf(target);\n const docTarget = isDocTarget(target);\n const containerTop = docTarget ? 0 : target.getBoundingClientRect().top;\n const curScroll = docTarget\n ? window.pageYOffset || document.documentElement.scrollTop || 0\n : target.scrollTop;\n return args.selectors.map((sel) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let el: any = null;\n try {\n el = document.querySelector(sel);\n } catch {\n el = null;\n }\n if (!el) return null;\n const rect = el.getBoundingClientRect();\n const offsetTop = docTarget ? rect.top + curScroll : rect.top - containerTop + curScroll;\n const y = Math.max(0, Math.min(distance, offsetTop));\n return distance > 0 ? y / distance : 0;\n });\n\n // --- inlined, self-contained scroll helpers (duplicated elsewhere in this file; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n}\n\n/**\n * Runs INSIDE the page. Waits for the CURRENT scroll position's in-view content to be ready — fonts\n * loaded and visible images decoded — then yields one animation frame. Bounded by the caller (a\n * Node-side timeout) so a stuck decode can't hang a frame. Self-contained (serialized via page.evaluate).\n */\nexport async function settleInView(): Promise<void> {\n try {\n await (document.fonts?.ready ?? Promise.resolve());\n } catch {\n /* no font set */\n }\n try {\n const vh = window.innerHeight || document.documentElement.clientHeight || 0;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const imgs = Array.from(document.images ?? []) as any[];\n const inView = imgs.filter((im) => {\n try {\n const r = im.getBoundingClientRect();\n return r.bottom > 0 && r.top < vh && r.width > 0 && r.height > 0;\n } catch {\n return false;\n }\n });\n await Promise.all(inView.map((im) => (im.decode ? im.decode().catch(() => {}) : null)));\n } catch {\n /* decode unsupported */\n }\n await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n}\n\n/**\n * Runs INSIDE the page. Jumps the real scroll target to a normalized position (0 = top, 1 = bottom)\n * instantly, then waits TWO animation frames so scroll-linked animations, IntersectionObserver reveals,\n * and `position: sticky` elements recompute and PAINT before the caller screenshots. This is the\n * frame-stepped counterpart to `pageScroll`: the capture layer calls it once per frame with the\n * timeline's position for that frame, so the resulting video is frame-accurate and reproducible. Must be\n * self-contained (serialized via page.evaluate); the scroll helpers are inlined as elsewhere in this file.\n */\nexport async function seekScrollTo(args: SeekScrollArgs): Promise<void> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n forceInstant(target);\n const distance = maxScrollOf(target);\n const clamped = args.normalizedY < 0 ? 0 : args.normalizedY > 1 ? 1 : args.normalizedY;\n scrollTargetTo(target, clamped * distance);\n // Ken Burns: scale the page toward a viewport-anchored origin (computed from the live scroll), so\n // the zoom centers on what's on screen. Only touched when a scale is supplied.\n if (typeof args.scale === \"number\") {\n try {\n const vw = window.innerWidth || document.documentElement.clientWidth || 0;\n const vh = window.innerHeight || document.documentElement.clientHeight || 0;\n const sx = window.pageXOffset || 0;\n const sy = window.pageYOffset || document.documentElement.scrollTop || 0;\n const ox = sx + (args.originX ?? 0.5) * vw;\n const oy = sy + (args.originY ?? 0.5) * vh;\n const body = document.body;\n if (body && body.style) {\n body.style.transformOrigin = `${ox}px ${oy}px`;\n body.style.transform = `scale(${args.scale})`;\n }\n } catch {\n /* ignore */\n }\n }\n // Two rAF ticks: let scroll handlers run (frame 1) and the resulting paint commit (frame 2).\n await new Promise<void>((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(() => resolve())),\n );\n\n // --- inlined, self-contained scroll helpers (duplicated in prepareScroll/pageScroll; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function scrollTargetTo(t: any, y: number): void {\n if (isDocTarget(t)) window.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else if (t.scrollTo) t.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else t.scrollTop = y;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function forceInstant(t: any): void {\n try {\n document.documentElement.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n try {\n if (t && t.style) t.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n }\n}\n\n/**\n * Runs INSIDE the page. Smoothly scrolls the real scroll target top -> bottom over `durationMs`\n * using requestAnimationFrame, with a dwell before and after. Forces instant per-frame positioning\n * so a site's `scroll-behavior: smooth` can't fight the animation, and targets the actual scroll\n * container (not just the window) so app-shell layouts scroll too. Returns the scrolled distance in\n * px (0 = nothing scrollable was found — the caller can warn).\n */\nexport async function pageScroll(args: PageScrollArgs): Promise<number> {\n const { durationMs, easing, startDelayMs, endDwellMs } = args;\n\n const ease = (t: number): number => {\n switch (easing) {\n case \"easeInOutCubic\":\n return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;\n case \"easeInOutQuad\":\n return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;\n case \"easeOutCubic\":\n return 1 - Math.pow(1 - t, 3);\n case \"easeInOutSine\":\n return -(Math.cos(Math.PI * t) - 1) / 2;\n case \"easeInOutExpo\":\n return t === 0\n ? 0\n : t === 1\n ? 1\n : t < 0.5\n ? Math.pow(2, 20 * t - 10) / 2\n : (2 - Math.pow(2, -20 * t + 10)) / 2;\n case \"easeOutQuint\":\n return 1 - Math.pow(1 - t, 5);\n default:\n return t;\n }\n };\n const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(() => resolve(), ms));\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n forceInstant(target);\n\n scrollTargetTo(target, 0);\n await sleep(startDelayMs);\n\n // Snapshot the scroll distance once so lazy content loading mid-scroll can't move the goalpost.\n const distance = maxScrollOf(target);\n if (distance > 0) {\n await new Promise<void>((resolve) => {\n let start: number | null = null;\n const step = (now: number): void => {\n if (start === null) start = now;\n const t = Math.min(1, (now - start) / durationMs);\n scrollTargetTo(target, ease(t) * distance);\n if (t < 1) requestAnimationFrame(step);\n else resolve();\n };\n requestAnimationFrame(step);\n });\n }\n\n await sleep(endDwellMs);\n return distance;\n\n // --- inlined, self-contained scroll helpers (duplicated in prepareScroll; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function scrollTargetTo(t: any, y: number): void {\n if (isDocTarget(t)) window.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else if (t.scrollTo) t.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else t.scrollTop = y;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function forceInstant(t: any): void {\n try {\n document.documentElement.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n try {\n if (t && t.style) t.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n }\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport type { Browser, Page } from \"playwright-core\";\nimport { concatMp4, startFrameEncoder } from \"@/media/ffmpeg\";\nimport { ensureDir } from \"@/utils/fs\";\nimport type { Logger } from \"@/utils/logger\";\n\n/**\n * Deterministic frame-stepped video capture, shared by every \"step a clock and screenshot\" path\n * (the `scene` subsystem and site recording). The only per-path differences are two callbacks:\n * - `prepare` — runs once per worker context (navigate, wait for readiness/warm-up); returns\n * opaque per-page state passed back to `seekToFrame`.\n * - `seekToFrame` — advance the page to clip-time `t` (seconds) before the screenshot.\n *\n * Everything else (frame timing, parallel workers, supersample-then-encode, lossless concat) is the\n * machine-independent core: frame N is a pure function of `t`, so splitting the range across worker\n * contexts and concatenating is safe. Frames never touch disk — they pipe straight into ffmpeg.\n */\nexport interface FrameStepArgs<S = unknown> {\n browser: Browser;\n /** Viewport + screenshot size; supersampled by `deviceScaleFactor`, downscaled in ffmpeg. */\n width: number;\n height: number;\n deviceScaleFactor: number;\n fps: number;\n durationSeconds: number;\n crf: number;\n /** Absolute mp4 path to write. */\n outPath: string;\n /** \"ultrafast\" in draft, \"medium\" otherwise. */\n preset?: string;\n /** Intermediate frame format; \"png\" is lossless into the encoder. Default \"jpeg\". */\n frameFormat?: \"jpeg\" | \"png\";\n /** JPEG quality for intermediate frames (perf vs fidelity; ignored for png). */\n jpegQuality?: number;\n /** Parallel render workers (each its own browser context). Default 1. */\n workers?: number;\n /** Scratch dir for per-worker segments. */\n tmpDir: string;\n logger: Logger;\n /** Runs once per worker context (after newContext/newPage). Returns opaque per-page state. */\n prepare: (page: Page, helpers: { logger: Logger }) => Promise<S>;\n /** Advance the page to clip-time `t` (seconds) before the screenshot. */\n seekToFrame: (page: Page, t: number, state: S) => Promise<void>;\n /** Called with fractional progress (0–1) as frames complete (aggregated across workers). */\n onProgress?: (fraction: number) => void;\n /** Cancels the capture: the frame loop stops at the next boundary and the encoder is killed. */\n signal?: AbortSignal;\n}\n\n/** Default parallel workers: about half the cores, capped at 6 (each is a browser context). */\nexport function autoWorkers(): number {\n const cores = os.cpus()?.length ?? 2;\n return Math.max(1, Math.min(6, Math.floor(cores / 2)));\n}\n\n/**\n * Pure: split `totalFrames` into at most `workers` contiguous, non-overlapping [start, end) ranges\n * that exactly tile [0, totalFrames). Unit-tested (the I/O loop around it is exercised elsewhere).\n */\nexport function planFrames(totalFrames: number, workers: number): Array<{ start: number; end: number }> {\n const w = Math.max(1, Math.min(workers, totalFrames));\n const chunk = Math.ceil(totalFrames / w);\n const ranges: Array<{ start: number; end: number }> = [];\n for (let i = 0; i < w; i++) {\n const start = i * chunk;\n const end = Math.min(totalFrames, start + chunk);\n if (start >= end) break;\n ranges.push({ start, end });\n }\n return ranges;\n}\n\ninterface ChunkArgs<S> {\n browser: Browser;\n width: number;\n height: number;\n deviceScaleFactor: number;\n fps: number;\n crf: number;\n preset?: string;\n frameFormat: \"jpeg\" | \"png\";\n jpegQuality: number;\n /** Seconds advanced per frame (duration / totalFrames) — see captureFramedVideo. */\n timeStep: number;\n /** Inclusive start frame, exclusive end frame. */\n frameStart: number;\n frameEnd: number;\n outPath: string;\n logger: Logger;\n prepare: (page: Page, helpers: { logger: Logger }) => Promise<S>;\n seekToFrame: (page: Page, t: number, state: S) => Promise<void>;\n /** Called once per encoded frame (for aggregated progress). */\n onFrame?: () => void;\n signal?: AbortSignal;\n}\n\n/** Render a contiguous frame range in its own browser context, encoding to one mp4 segment. */\nasync function renderChunk<S>(a: ChunkArgs<S>): Promise<void> {\n const context = await a.browser.newContext({\n viewport: { width: a.width, height: a.height },\n deviceScaleFactor: a.deviceScaleFactor,\n });\n const page = await context.newPage();\n page.on(\"console\", (m) => a.logger.debug(`[capture] ${m.text()}`));\n page.on(\"pageerror\", (e) => a.logger.debug(`[capture error] ${e.message}`));\n\n try {\n const state = await a.prepare(page, { logger: a.logger });\n a.signal?.throwIfAborted(); // bail before spawning the encoder if we were cancelled during prepare\n\n const encoder = startFrameEncoder(\n {\n fps: a.fps,\n width: a.width,\n height: a.height,\n crf: a.crf,\n outPath: a.outPath,\n preset: a.preset,\n inputFormat: a.frameFormat,\n },\n a.logger,\n a.signal,\n );\n // Playwright rejects `quality` for png screenshots, so only pass it on the jpeg path.\n const shotOptions =\n a.frameFormat === \"png\"\n ? ({ type: \"png\" } as const)\n : ({ type: \"jpeg\", quality: a.jpegQuality } as const);\n for (let frame = a.frameStart; frame < a.frameEnd; frame++) {\n a.signal?.throwIfAborted(); // a cancelled run stops here, not after every frame is rendered\n const t = frame * a.timeStep;\n await a.seekToFrame(page, t, state);\n const buf = await page.screenshot(shotOptions);\n await encoder.write(buf);\n a.onFrame?.();\n }\n await encoder.done();\n } finally {\n await context.close();\n }\n}\n\n/**\n * Step each frame to its exact time via `seekToFrame`, screenshot, and pipe straight into ffmpeg —\n * frame-accurate and machine-independent, no frames touch disk. With `workers > 1`, the frame range is\n * split into contiguous segments rendered in parallel contexts, then losslessly concatenated (the\n * determinism is what makes this safe).\n */\nexport async function captureFramedVideo<S>(args: FrameStepArgs<S>): Promise<void> {\n await ensureDir(path.dirname(args.outPath));\n const totalFrames = Math.max(1, Math.round(args.durationSeconds * args.fps));\n // Step time as duration/totalFrames (≈ 1/fps) so the N frames evenly tile [0, duration). The frame\n // that would sit at t=duration is the loop point — which equals t=0 for a seamless clip — so omitting\n // it (we render 0..N-1) makes the last→first wrap exactly one step: no seam hitch.\n const timeStep = args.durationSeconds / totalFrames;\n const frameFormat = args.frameFormat ?? \"jpeg\";\n const jpegQuality = args.jpegQuality ?? 90;\n const ranges = planFrames(totalFrames, Math.max(1, args.workers ?? 1));\n // Aggregate frame completions across parallel workers into a single 0–1 fraction.\n let completed = 0;\n const onFrame = args.onProgress\n ? () => {\n completed += 1;\n args.onProgress?.(completed / totalFrames);\n }\n : undefined;\n const common = {\n browser: args.browser,\n width: args.width,\n height: args.height,\n deviceScaleFactor: args.deviceScaleFactor,\n fps: args.fps,\n crf: args.crf,\n preset: args.preset,\n frameFormat,\n jpegQuality,\n timeStep,\n logger: args.logger,\n prepare: args.prepare,\n seekToFrame: args.seekToFrame,\n onFrame,\n signal: args.signal,\n };\n\n if (ranges.length === 1) {\n args.logger.debug(`stepping ${totalFrames} frames @ ${args.fps}fps`);\n await renderChunk({ ...common, frameStart: 0, frameEnd: totalFrames, outPath: args.outPath });\n return;\n }\n\n const segs = ranges.map((r, i) => ({\n ...r,\n seg: path.join(args.tmpDir, `seg-${i}-${path.basename(args.outPath)}`),\n }));\n args.logger.debug(`stepping ${totalFrames} frames across ${segs.length} workers`);\n await Promise.all(\n segs.map((r) =>\n renderChunk({ ...common, frameStart: r.start, frameEnd: r.end, outPath: r.seg }),\n ),\n );\n await concatMp4(\n segs.map((r) => r.seg),\n args.outPath,\n args.logger,\n args.signal,\n );\n}\n","import type { Page } from \"playwright-core\";\nimport type { ResolvedScrollReelOptions } from \"@/generators/scroll-reel/options\";\nimport type { Logger } from \"@/utils/logger\";\n\nexport interface CleanCssOptions {\n hideSelectors: string[];\n hideScrollbars: boolean;\n pauseAnimations: boolean;\n injectCss?: string;\n}\n\n/**\n * Pure: assemble the CSS injected to suppress capture noise. Returns \"\" when nothing is requested, so\n * callers can skip the (otherwise empty) style tag. Unit-tested.\n */\nexport function buildCleanCss(opts: CleanCssOptions): string {\n const parts: string[] = [];\n if (opts.hideSelectors.length > 0) {\n parts.push(`${opts.hideSelectors.join(\", \")} { display: none !important; }`);\n }\n if (opts.hideScrollbars) {\n parts.push(\n `::-webkit-scrollbar { width: 0 !important; height: 0 !important; display: none !important; }`,\n `html { scrollbar-width: none !important; }`,\n );\n }\n if (opts.pauseAnimations) {\n parts.push(\n `*, *::before, *::after { animation-play-state: paused !important; transition: none !important; }`,\n );\n }\n if (opts.injectCss && opts.injectCss.trim()) {\n parts.push(opts.injectCss);\n }\n return parts.join(\"\\n\");\n}\n\n/**\n * Runs in the page via addInitScript (before any page script, on every navigation): pin the wall clock\n * and randomness so time-/random-driven content renders identically across frames and runs. Best-effort\n * by design — it overrides `Date.now` / `performance.now` / `Math.random` but not the `Date` constructor\n * or the rAF timestamp, so it won't break apps that read `new Date()`; pair with `pauseAnimations` (CSS)\n * for visual stillness. Must be self-contained (serialized into the page).\n */\nfunction freezeClockScript(): void {\n try {\n const FIXED = 1700000000000; // a fixed epoch (2023-11-14) so the page never sees time advance\n Date.now = () => FIXED;\n } catch {\n /* ignore */\n }\n try {\n const perf = (globalThis as { performance?: { now: () => number } }).performance;\n if (perf) perf.now = () => 0;\n } catch {\n /* ignore */\n }\n try {\n let seed = 1234567;\n Math.random = () => {\n seed = (seed * 1103515245 + 12345) & 0x7fffffff;\n return seed / 0x7fffffff;\n };\n } catch {\n /* ignore */\n }\n}\n\n/** Common analytics / ads / tag-manager / session-replay hosts, blocked by default for clean captures. */\nexport const DEFAULT_TRACKER_HOSTS = [\n \"google-analytics.com\",\n \"googletagmanager.com\",\n \"googlesyndication.com\",\n \"doubleclick.net\",\n \"adservice.google.com\",\n \"connect.facebook.net\",\n \"facebook.com/tr\",\n \"hotjar.com\",\n \"hotjar.io\",\n \"segment.com\",\n \"segment.io\",\n \"mixpanel.com\",\n \"amplitude.com\",\n \"fullstory.com\",\n \"clarity.ms\",\n \"sentry.io\",\n \"intercom.io\",\n \"intercomcdn.com\",\n \"analytics.tiktok.com\",\n \"snap.licdn.com\",\n \"bat.bing.com\",\n];\n\n/** Pure: should a request be aborted, given the host denylist and blocked resource types? */\nexport function shouldBlockRequest(\n url: string,\n opts: { hosts: string[]; resourceTypes: string[]; resourceType: string },\n): boolean {\n if (opts.resourceTypes.includes(opts.resourceType)) return true;\n return opts.hosts.some((h) => url.includes(h));\n}\n\n/** Abort tracker/denylisted/blocked-type requests during capture (must run BEFORE `page.goto`). */\nexport async function installNetworkHygiene(\n page: Page,\n opts: ResolvedScrollReelOptions,\n): Promise<void> {\n const hosts = [...(opts.blockTrackers ? DEFAULT_TRACKER_HOSTS : []), ...opts.blockHosts];\n const resourceTypes = opts.blockResourceTypes;\n if (hosts.length === 0 && resourceTypes.length === 0) return;\n await page.route(\"**/*\", (route) => {\n const req = route.request();\n if (shouldBlockRequest(req.url(), { hosts, resourceTypes, resourceType: req.resourceType() })) {\n return route.abort();\n }\n return route.continue();\n });\n}\n\n/**\n * Runs in the page: pause and rewind all media so autoplay video/audio can't make frames depend on\n * wall-clock time (frame-stepping screenshots a static page). Self-contained (serialized into the page).\n */\nfunction pauseAllMedia(): void {\n const doc = (globalThis as { document?: { querySelectorAll(s: string): ArrayLike<unknown> } }).document;\n if (!doc) return;\n try {\n const media = doc.querySelectorAll(\"video, audio\");\n for (let i = 0; i < media.length; i++) {\n const m = media[i] as { autoplay?: boolean; pause?: () => void; currentTime?: number };\n try {\n m.autoplay = false;\n m.pause?.();\n if (typeof m.currentTime === \"number\") m.currentTime = 0;\n } catch {\n /* ignore */\n }\n }\n } catch {\n /* ignore */\n }\n}\n\n/** Runs in the page: add a theme class to <html> (e.g. to force a dark variant). Self-contained. */\nfunction applyThemeClass(cls: string): void {\n try {\n (\n globalThis as { document?: { documentElement?: { classList?: { add(c: string): void } } } }\n ).document?.documentElement?.classList?.add(cls);\n } catch {\n /* ignore */\n }\n}\n\n/** Install pre-navigation hooks (must run BEFORE `page.goto`). */\nexport async function installPreNav(page: Page, opts: ResolvedScrollReelOptions): Promise<void> {\n if (opts.freezeClock) {\n await page.addInitScript(freezeClockScript);\n }\n if (opts.themeClass) {\n await page.addInitScript(applyThemeClass, opts.themeClass);\n }\n}\n\n/** Apply post-navigation cleanup: inject suppression CSS, then dismiss overlays (best-effort). */\nexport async function applyPostNav(\n page: Page,\n opts: ResolvedScrollReelOptions,\n logger: Logger,\n): Promise<void> {\n const css = buildCleanCss({\n hideSelectors: opts.hideSelectors,\n hideScrollbars: opts.hideScrollbars,\n pauseAnimations: opts.pauseAnimations,\n injectCss: opts.injectCss,\n });\n if (css) {\n await page.addStyleTag({ content: css });\n }\n // Re-apply the theme class after the (possibly client-rendered) app has mounted.\n if (opts.themeClass) {\n await page.evaluate(applyThemeClass, opts.themeClass);\n }\n for (const selector of opts.clickSelectors) {\n try {\n await page.click(selector, { timeout: 1000 });\n logger.debug(`dismissed overlay via ${selector}`);\n } catch {\n // Not present / not clickable — fine, these are best-effort consent dismissals.\n }\n }\n // Pause media so autoplay video can't make frames depend on wall-clock time.\n await page.evaluate(pauseAllMedia);\n}\n","export interface Annotation {\n /** Caption text shown while active. */\n text?: string;\n /** Selector to outline with a highlight ring while active. */\n ring?: string;\n /** Selector to spotlight (everything else dimmed) while active. */\n spotlight?: string;\n /** When the annotation appears (clip time, ms). Default 0. */\n atMs?: number;\n /** When it disappears (clip time, ms). Default = end of clip. */\n untilMs?: number;\n /** Caption placement. Default \"bottom\". */\n position?: \"top\" | \"bottom\" | \"center\";\n}\n\nexport interface AnnotationState {\n caption?: { text: string; position: \"top\" | \"bottom\" | \"center\" };\n ringSelector?: string;\n spotlightSelector?: string;\n}\n\n/**\n * Pure: resolve the overlay state at clip-time `tMs`. The first active annotation of each kind\n * (caption / ring / spotlight) wins, so independent annotations can overlap. Unit-tested.\n */\nexport function annotationStateAt(\n annotations: Annotation[],\n tMs: number,\n totalMs: number,\n): AnnotationState {\n const state: AnnotationState = {};\n for (const a of annotations) {\n const start = a.atMs ?? 0;\n const end = a.untilMs ?? totalMs;\n if (tMs < start || tMs >= end) continue;\n if (a.text && !state.caption) state.caption = { text: a.text, position: a.position ?? \"bottom\" };\n if (a.ring && !state.ringSelector) state.ringSelector = a.ring;\n if (a.spotlight && !state.spotlightSelector) state.spotlightSelector = a.spotlight;\n }\n return state;\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Runs INSIDE the page. Builds the annotation overlay (caption box, highlight ring, spotlight dim) and\n * exposes `window.__scAnno.update(state)` to reposition it per frame. The layer is attached to <html>\n * (not <body>) so a Ken Burns body transform doesn't move it; element boxes are read via\n * getBoundingClientRect, which already reflects any transform. Self-contained (serialized into the page).\n */\nexport function installAnnotationRuntime(opts: { color: string }): void {\n const g = globalThis as any;\n const doc = g.document;\n if (!doc || !doc.documentElement) return;\n\n const layer = doc.createElement(\"div\");\n layer.style.cssText = \"position:fixed;inset:0;margin:0;pointer-events:none;z-index:2147483646\";\n\n const spot = doc.createElement(\"div\");\n spot.style.cssText =\n \"position:fixed;display:none;border-radius:12px;box-shadow:0 0 0 9999px rgba(0,0,0,0.62)\";\n\n const ring = doc.createElement(\"div\");\n ring.style.cssText =\n \"position:fixed;display:none;border:3px solid \" +\n opts.color +\n \";border-radius:12px;box-shadow:0 0 0 3px rgba(255,255,255,0.45)\";\n\n const cap = doc.createElement(\"div\");\n cap.style.cssText =\n \"position:fixed;left:50%;display:none;max-width:80%;background:rgba(11,11,15,0.85);color:#fff;\" +\n \"font:600 28px -apple-system,Segoe UI,Roboto,sans-serif;padding:14px 22px;border-radius:12px;text-align:center\";\n\n layer.appendChild(spot);\n layer.appendChild(ring);\n layer.appendChild(cap);\n doc.documentElement.appendChild(layer);\n\n const boxOf = (sel: string): { x: number; y: number; w: number; h: number } | null => {\n try {\n const el = doc.querySelector(sel);\n if (!el) return null;\n const r = el.getBoundingClientRect();\n return { x: r.left, y: r.top, w: r.width, h: r.height };\n } catch {\n return null;\n }\n };\n const place = (el: any, b: { x: number; y: number; w: number; h: number }, pad: number): void => {\n el.style.display = \"block\";\n el.style.left = b.x - pad + \"px\";\n el.style.top = b.y - pad + \"px\";\n el.style.width = b.w + 2 * pad + \"px\";\n el.style.height = b.h + 2 * pad + \"px\";\n };\n\n g.__scAnno = {\n update: (state: any): Promise<void> =>\n new Promise((resolve) => {\n const sb = state.spotlightSelector ? boxOf(state.spotlightSelector) : null;\n if (sb) place(spot, sb, 8);\n else spot.style.display = \"none\";\n\n const rb = state.ringSelector ? boxOf(state.ringSelector) : null;\n if (rb) place(ring, rb, 6);\n else ring.style.display = \"none\";\n\n if (state.caption && state.caption.text) {\n cap.style.display = \"block\";\n cap.textContent = state.caption.text;\n const pos = state.caption.position;\n if (pos === \"top\") {\n cap.style.top = \"6%\";\n cap.style.bottom = \"auto\";\n cap.style.transform = \"translateX(-50%)\";\n } else if (pos === \"center\") {\n cap.style.top = \"50%\";\n cap.style.bottom = \"auto\";\n cap.style.transform = \"translate(-50%,-50%)\";\n } else {\n cap.style.bottom = \"6%\";\n cap.style.top = \"auto\";\n cap.style.transform = \"translateX(-50%)\";\n }\n } else {\n cap.style.display = \"none\";\n }\n g.requestAnimationFrame(() => resolve());\n }),\n };\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n","import { EASINGS, type EasingName } from \"@/generators/scroll-reel/scroll\";\n\n/**\n * A pure, frame-accurate model of how a recording's scroll position moves over time. This is the\n * extensibility seam for cinematic features: the default is still a single eased top→bottom sweep with\n * start/end dwell, but the same `ScrollSegment[]` shape can express pause-on-section choreography,\n * multiple eased segments, and speed ramps without another capture-side refactor.\n *\n * Positions are NORMALIZED (0 = top, 1 = bottom). The actual scrollable distance is only known\n * in-page after warm-up, so the capture layer multiplies `scrollAt(t)` by the measured distance. Keeping\n * the model normalized is what makes it a pure Node function (unit-testable like the easings) and lets\n * parallel workers agree: each re-measures the same distance after the same deterministic warm-up.\n */\nexport interface ScrollSegment {\n /** Normalized scroll position at the segment's start (0..1). */\n fromY: number;\n /** Normalized scroll position at the segment's end (0..1). A hold has `fromY === toY`. */\n toY: number;\n /** Fraction of the whole clip this segment occupies (0..1). All fractions sum to 1. */\n durationFraction: number;\n easing: EasingName;\n}\n\nexport interface TimelineSpec {\n segments: ScrollSegment[];\n}\n\nexport interface ResolvedTimeline {\n /** Normalized scroll position (0..1) at clip-time `tSeconds`. */\n scrollAt: (tSeconds: number) => number;\n totalSeconds: number;\n}\n\nexport interface DefaultTimelineArgs {\n startDelayMs: number;\n durationMs: number;\n endDwellMs: number;\n easing: EasingName;\n}\n\n/**\n * The current scroll-reel behavior expressed as a timeline: hold at top, eased scroll 0→1, hold at\n * bottom. The dwells become HELD FRAMES — the frame-stepped equivalent of the realtime path's\n * `sleep(startDelayMs)` / `sleep(endDwellMs)` — so the clip is exactly `startDelay + duration + endDwell`\n * long, matching the manifest `durationMs`.\n */\nexport function defaultTimelineSpec(a: DefaultTimelineArgs): TimelineSpec {\n const total = a.startDelayMs + a.durationMs + a.endDwellMs;\n if (total <= 0) {\n return { segments: [{ fromY: 0, toY: 0, durationFraction: 1, easing: \"linear\" }] };\n }\n const segments: ScrollSegment[] = [];\n if (a.startDelayMs > 0) {\n segments.push({ fromY: 0, toY: 0, durationFraction: a.startDelayMs / total, easing: \"linear\" });\n }\n if (a.durationMs > 0) {\n segments.push({ fromY: 0, toY: 1, durationFraction: a.durationMs / total, easing: a.easing });\n }\n if (a.endDwellMs > 0) {\n segments.push({ fromY: 1, toY: 1, durationFraction: a.endDwellMs / total, easing: \"linear\" });\n }\n // All-zero durations are guarded above, but keep a defined fallback for safety.\n if (segments.length === 0) {\n segments.push({ fromY: 0, toY: 0, durationFraction: 1, easing: \"linear\" });\n }\n return { segments };\n}\n\n/**\n * Pure: the normalized scroll position at progress `p` (0..1 over the whole clip). Walks the segments,\n * applies the segment's easing to its local progress, and lerps `fromY → toY`. The last segment owns\n * `p === 1` so floating-point fraction sums can't fall through.\n */\nexport function normalizedScrollAt(spec: TimelineSpec, p: number): number {\n const clamped = p < 0 ? 0 : p > 1 ? 1 : p;\n let acc = 0;\n for (let i = 0; i < spec.segments.length; i++) {\n const seg = spec.segments[i];\n if (!seg) continue;\n const segEnd = acc + seg.durationFraction;\n const isLast = i === spec.segments.length - 1;\n if (clamped <= segEnd || isLast) {\n const local = seg.durationFraction > 0 ? (clamped - acc) / seg.durationFraction : 1;\n const lc = local < 0 ? 0 : local > 1 ? 1 : local;\n const eased = EASINGS[seg.easing](lc);\n return seg.fromY + (seg.toY - seg.fromY) * eased;\n }\n acc = segEnd;\n }\n const last = spec.segments[spec.segments.length - 1];\n return last ? last.toY : 0;\n}\n\n/** Bind a spec to a wall-clock length, yielding `scrollAt(tSeconds)`. */\nexport function resolveTimeline(spec: TimelineSpec, totalSeconds: number): ResolvedTimeline {\n return {\n totalSeconds,\n scrollAt: (tSeconds: number) =>\n normalizedScrollAt(spec, totalSeconds > 0 ? tSeconds / totalSeconds : 1),\n };\n}\n\nexport function clamp01(n: number): number {\n return n < 0 ? 0 : n > 1 ? 1 : n;\n}\n\n// --- choreography (pause-on-section / keyframe scrolling) ---\n\n/** Default per-step travel time when a choreography step omits `durationMs`. */\nexport const DEFAULT_STEP_DURATION_MS = 1200;\n/** Default per-step hold time when a choreography step omits `holdMs`. */\nexport const DEFAULT_STEP_HOLD_MS = 800;\n\n/** An authored choreography step (raw, before targets are resolved to a position). */\nexport interface ChoreographyStep {\n /** Where to scroll to: a normalized 0..1 number, an \"NN%\" string, or a CSS selector to bring into view. */\n to: number | string;\n /** Time to travel to this target (ms). Defaults to {@link DEFAULT_STEP_DURATION_MS}. */\n durationMs?: number;\n /** Time to hold at this target after arriving (ms). Defaults to {@link DEFAULT_STEP_HOLD_MS}. */\n holdMs?: number;\n easing?: EasingName;\n}\n\n/** A choreography step whose target has been resolved to a normalized position. */\nexport interface ResolvedChoreographyStep {\n toY: number;\n durationMs: number;\n holdMs: number;\n easing: EasingName;\n}\n\nexport interface ChoreographyTimelineArgs {\n startDelayMs: number;\n endDwellMs: number;\n steps: ResolvedChoreographyStep[];\n}\n\n/**\n * Build a timeline from resolved choreography: an initial hold at the top, then for each step a travel\n * segment (eased) to its target followed by an optional hold, then a final dwell. A hold is a segment\n * with `fromY === toY`, so `normalizedScrollAt` keeps it pinned — exactly the \"pause on a section\"\n * behavior. The fractions are derived from each segment's ms over the total, so the clip length matches\n * {@link scrollTimelineTotalMs} computed from the same step timings.\n */\nexport function choreographyTimelineSpec(a: ChoreographyTimelineArgs): TimelineSpec {\n let total = a.startDelayMs + a.endDwellMs;\n for (const s of a.steps) total += s.durationMs + s.holdMs;\n if (total <= 0) {\n return { segments: [{ fromY: 0, toY: 0, durationFraction: 1, easing: \"linear\" }] };\n }\n const segments: ScrollSegment[] = [];\n let fromY = 0;\n if (a.startDelayMs > 0) {\n segments.push({ fromY: 0, toY: 0, durationFraction: a.startDelayMs / total, easing: \"linear\" });\n }\n for (const s of a.steps) {\n if (s.durationMs > 0) {\n segments.push({ fromY, toY: s.toY, durationFraction: s.durationMs / total, easing: s.easing });\n }\n if (s.holdMs > 0) {\n segments.push({ fromY: s.toY, toY: s.toY, durationFraction: s.holdMs / total, easing: \"linear\" });\n }\n fromY = s.toY;\n }\n if (a.endDwellMs > 0) {\n segments.push({ fromY, toY: fromY, durationFraction: a.endDwellMs / total, easing: \"linear\" });\n }\n if (segments.length === 0) {\n segments.push({ fromY: 0, toY: fromY, durationFraction: 1, easing: \"linear\" });\n }\n return { segments };\n}\n\n// --- auto-section detection (auto-generated choreography) ---\n\n/** Default total clip length (ms) for an auto-section reel. */\nexport const DEFAULT_AUTO_DURATION_MS = 12000;\n/** Default hold (ms) at each detected section. */\nexport const DEFAULT_AUTO_HOLD_MS = 700;\n/** Default minimum element height (as a fraction of the viewport) to count as a section. */\nexport const DEFAULT_AUTO_MIN_HEIGHT_FRACTION = 0.5;\n/** Default cap on the number of detected sections (keeps clips a reasonable length). */\nexport const DEFAULT_AUTO_MAX_SECTIONS = 8;\n\nexport interface AutoSectionsConfig {\n minHeightFraction?: number;\n selector?: string;\n holdMs?: number;\n /** Total clip length in ms (the budget split across detected sections). */\n durationMs?: number;\n maxSections?: number;\n constantVelocity?: boolean;\n}\n\n/** The total clip length (ms) for an auto-section config — a fixed, page-independent budget. */\nexport function autoSectionsBudgetMs(cfg: boolean | AutoSectionsConfig): number {\n const c = typeof cfg === \"object\" ? cfg : {};\n return c.durationMs ?? DEFAULT_AUTO_DURATION_MS;\n}\n\nexport interface AutoSectionStepsArgs {\n /** Detected section positions, normalized 0..1, sorted ascending. */\n offsets: number[];\n /** Total clip budget in ms (== autoSectionsBudgetMs). */\n budgetMs: number;\n startDelayMs: number;\n endDwellMs: number;\n holdMs: number;\n /** Distribute travel time by distance so scroll speed is uniform. */\n constantVelocity: boolean;\n easing: EasingName;\n}\n\n/**\n * Pure: turn detected section offsets into choreography steps that fit exactly into `budgetMs`. The\n * start delay and end dwell are reserved; the remainder is split into a hold at each section plus travel\n * between them. With `constantVelocity`, travel time is proportional to the distance covered (uniform\n * scroll speed); otherwise it's split evenly. Holds are capped so travel always has time. Returns []\n * when there are no offsets (the caller falls back to a default sweep).\n */\nexport function autoSectionSteps(a: AutoSectionStepsArgs): ResolvedChoreographyStep[] {\n const n = a.offsets.length;\n if (n === 0) return [];\n const remaining = Math.max(0, a.budgetMs - a.startDelayMs - a.endDwellMs);\n let holdEach = a.holdMs;\n if (holdEach * n > remaining * 0.7) holdEach = (remaining * 0.7) / n;\n const travelTotal = Math.max(0, remaining - holdEach * n);\n\n let prev = 0;\n const dists = a.offsets.map((o) => {\n const d = Math.max(0, o - prev);\n prev = o;\n return d;\n });\n const sumD = dists.reduce((s, d) => s + d, 0);\n const useVelocity = a.constantVelocity && sumD > 0;\n\n const steps: ResolvedChoreographyStep[] = [];\n let usedTravel = 0;\n for (let i = 0; i < n; i++) {\n const isLast = i === n - 1;\n const weight = useVelocity ? dists[i]! / sumD : 1 / n;\n // Last step absorbs any rounding so travel sums exactly to travelTotal.\n const travel = isLast ? travelTotal - usedTravel : travelTotal * weight;\n usedTravel += travel;\n steps.push({\n toY: clamp01(a.offsets[i]!),\n durationMs: Math.max(0, travel),\n holdMs: holdEach,\n easing: a.easing,\n });\n }\n return steps;\n}\n\nexport interface ScrollTimelineTotalArgs {\n startDelayMs: number;\n duration: number;\n endDwellMs: number;\n choreography?: Array<{ durationMs?: number; holdMs?: number }>;\n autoSections?: boolean | AutoSectionsConfig;\n}\n\n/**\n * Pure: total clip length in ms. Explicit choreography wins; then auto-sections use their fixed budget;\n * otherwise the classic startDelay + duration + endDwell. The capture layer and the manifest's\n * `durationMs` both use this so they always agree.\n */\nexport function scrollTimelineTotalMs(o: ScrollTimelineTotalArgs): number {\n if (o.choreography && o.choreography.length > 0) {\n let total = o.startDelayMs + o.endDwellMs;\n for (const s of o.choreography) {\n total += (s.durationMs ?? DEFAULT_STEP_DURATION_MS) + (s.holdMs ?? DEFAULT_STEP_HOLD_MS);\n }\n return total;\n }\n if (o.autoSections) {\n return autoSectionsBudgetMs(o.autoSections);\n }\n return o.startDelayMs + o.duration + o.endDwellMs;\n}\n\n// --- loop / boomerang ---\n\n/**\n * Pure: mirror a spec so it plays forward then backward within the same total length — a seamless\n * loop (last frame ≈ first). Each segment's fraction is halved; the second half is the segments in\n * reverse with swapped endpoints. Exactly time-symmetric for symmetric easings (linear/easeInOut*);\n * for asymmetric easings the down/up acceleration differs slightly but the loop is still seamless.\n */\nexport function boomerangSpec(spec: TimelineSpec): TimelineSpec {\n const forward = spec.segments.map((s) => ({ ...s, durationFraction: s.durationFraction / 2 }));\n const backward = spec.segments\n .slice()\n .reverse()\n .map((s) => ({\n fromY: s.toY,\n toY: s.fromY,\n durationFraction: s.durationFraction / 2,\n easing: s.easing,\n }));\n return { segments: [...forward, ...backward] };\n}\n\n// --- Ken Burns (slow zoom) ---\n\n/** Fold progress into a tent: 0→0, 0.5→1, 1→0. Keeps a zoom seamless under a boomerang loop. */\nexport function foldProgress(p: number): number {\n const c = clamp01(p);\n return c < 0.5 ? c * 2 : (1 - c) * 2;\n}\n\nexport interface KenBurnsResolved {\n scaleFrom: number;\n scaleTo: number;\n easing: EasingName;\n}\n\n/** Pure: the zoom scale at normalized progress (0..1), eased from scaleFrom to scaleTo. */\nexport function kenBurnsScaleAt(progress: number, cfg: KenBurnsResolved): number {\n const eased = EASINGS[cfg.easing](clamp01(progress));\n return cfg.scaleFrom + (cfg.scaleTo - cfg.scaleFrom) * eased;\n}\n","import type { Browser, Page } from \"playwright-core\";\nimport { captureFramedVideo } from \"@/media/frame-capture\";\nimport {\n detectSectionOffsets,\n measureNormalizedOffsets,\n prepareScroll,\n seekScrollTo,\n settleInView,\n} from \"@/generators/scroll-reel/scroll\";\nimport {\n applyPostNav,\n installNetworkHygiene,\n installPreNav,\n} from \"@/generators/scroll-reel/clean-capture\";\nimport {\n annotationStateAt,\n installAnnotationRuntime,\n} from \"@/generators/scroll-reel/annotations\";\nimport {\n autoSectionSteps,\n autoSectionsBudgetMs,\n boomerangSpec,\n choreographyTimelineSpec,\n clamp01,\n defaultTimelineSpec,\n foldProgress,\n kenBurnsScaleAt,\n resolveTimeline,\n scrollTimelineTotalMs,\n DEFAULT_AUTO_HOLD_MS,\n DEFAULT_AUTO_MAX_SECTIONS,\n DEFAULT_AUTO_MIN_HEIGHT_FRACTION,\n DEFAULT_STEP_DURATION_MS,\n DEFAULT_STEP_HOLD_MS,\n type ResolvedChoreographyStep,\n type ResolvedTimeline,\n type TimelineSpec,\n} from \"@/generators/scroll-reel/timeline\";\nimport type { ResolvedScrollReelOptions } from \"@/generators/scroll-reel/options\";\nimport type { Logger } from \"@/utils/logger\";\n\n/** Time at the bottom for lazy/below-the-fold content to load during the (un-recorded) warm-up pass. */\nconst PREWARM_SETTLE_MS = 700;\n\nexport interface ScrollFramesArgs {\n browser: Browser;\n url: string;\n options: ResolvedScrollReelOptions;\n /** Absolute mp4 path to write. */\n outPath: string;\n preset?: string;\n workers: number;\n frameFormat: \"jpeg\" | \"png\";\n jpegQuality: number;\n /** Wait for fonts + in-view images before each frame (resolved: defaults on, off in draft). */\n settlePerFrame: boolean;\n /** Max ms to wait per frame for settling before screenshotting anyway. */\n settleMaxMs: number;\n /** Force a color scheme for this capture (emulated via prefers-color-scheme). */\n colorScheme?: \"light\" | \"dark\";\n tmpDir: string;\n logger: Logger;\n /** Fractional progress (0–1) as frames complete. */\n onProgress?: (fraction: number) => void;\n /** Cancels the capture mid-flight. */\n signal?: AbortSignal;\n}\n\n/**\n * Build the resolved scroll timeline for this capture. The default (no `choreography`) is a pure Node\n * computation; with choreography, selector targets are measured against the (warmed-up) page so the\n * positions are real and stable. Run once per worker inside `prepare`; since the measurements are\n * deterministic, every worker produces the same timeline.\n */\nasync function buildScrollTimeline(\n page: Page,\n options: ResolvedScrollReelOptions,\n totalSeconds: number,\n logger: Logger,\n): Promise<ResolvedTimeline> {\n const steps = options.choreography;\n // Apply the loop transform (boomerang mirrors the spec) before binding to wall-clock time.\n const finalize = (spec: TimelineSpec): ResolvedTimeline =>\n resolveTimeline(options.loop === \"boomerang\" ? boomerangSpec(spec) : spec, totalSeconds);\n\n // 1. Explicit choreography wins: resolve selector targets in one in-page pass (numbers/% in Node).\n if (steps && steps.length > 0) {\n const selectors = steps\n .map((s) => s.to)\n .filter((to): to is string => typeof to === \"string\" && !to.trim().endsWith(\"%\"));\n const measured =\n selectors.length > 0 ? await page.evaluate(measureNormalizedOffsets, { selectors }) : [];\n\n let mi = 0;\n let prevY = 0;\n const resolved: ResolvedChoreographyStep[] = steps.map((s) => {\n let toY: number;\n if (typeof s.to === \"number\") {\n toY = clamp01(s.to);\n } else if (s.to.trim().endsWith(\"%\")) {\n toY = clamp01(parseFloat(s.to) / 100);\n } else {\n const m = measured[mi++];\n if (m == null) {\n logger.warn(`choreography: selector \"${s.to}\" not found — holding position`);\n toY = prevY;\n } else {\n toY = clamp01(m);\n }\n }\n prevY = toY;\n return {\n toY,\n durationMs: s.durationMs ?? DEFAULT_STEP_DURATION_MS,\n holdMs: s.holdMs ?? DEFAULT_STEP_HOLD_MS,\n easing: s.easing ?? options.easing,\n };\n });\n\n return finalize(\n choreographyTimelineSpec({\n startDelayMs: options.startDelayMs,\n endDwellMs: options.endDwellMs,\n steps: resolved,\n }),\n );\n }\n\n // 2. Auto-sections: detect the page's sections and pan/hold through them within a fixed budget.\n if (options.autoSections) {\n const cfg = options.autoSections === true ? {} : options.autoSections;\n const offsets = await page.evaluate(detectSectionOffsets, {\n minHeightFraction: cfg.minHeightFraction ?? DEFAULT_AUTO_MIN_HEIGHT_FRACTION,\n selector: cfg.selector ?? null,\n maxSections: cfg.maxSections ?? DEFAULT_AUTO_MAX_SECTIONS,\n });\n const autoSteps = autoSectionSteps({\n offsets,\n budgetMs: autoSectionsBudgetMs(options.autoSections),\n startDelayMs: options.startDelayMs,\n endDwellMs: options.endDwellMs,\n holdMs: cfg.holdMs ?? DEFAULT_AUTO_HOLD_MS,\n constantVelocity: cfg.constantVelocity ?? true,\n easing: options.easing,\n });\n if (autoSteps.length > 0) {\n logger.debug(`autoSections: ${autoSteps.length} section(s) detected`);\n return finalize(\n choreographyTimelineSpec({\n startDelayMs: options.startDelayMs,\n endDwellMs: options.endDwellMs,\n steps: autoSteps,\n }),\n );\n }\n logger.warn(\"autoSections: no scrollable sections detected — using a default sweep\");\n }\n\n // 3. Default: a single eased top→bottom sweep.\n return finalize(\n defaultTimelineSpec({\n startDelayMs: options.startDelayMs,\n durationMs: options.duration,\n endDwellMs: options.endDwellMs,\n easing: options.easing,\n }),\n );\n}\n\n/**\n * Deterministic, frame-stepped recording of a real site. Each worker navigates + warms the page once\n * (reusing {@link prepareScroll} to trigger lazy content, fonts and image decode), applies clean-capture\n * suppression, builds the scroll timeline (resolving any choreography selectors against the page), then\n * for every frame sets the scroll position via {@link seekScrollTo}, optionally settles in-view content,\n * and screenshots — piped straight into ffmpeg by the shared {@link captureFramedVideo} harness. No\n * realtime recording, no dropped/jittered frames, no navigation lead to trim.\n */\nexport async function captureScrollFrames(a: ScrollFramesArgs): Promise<void> {\n const { options } = a;\n const totalSeconds = scrollTimelineTotalMs(options) / 1000;\n await captureFramedVideo<ResolvedTimeline>({\n browser: a.browser,\n width: options.width,\n height: options.height,\n deviceScaleFactor: options.deviceScaleFactor,\n fps: options.fps,\n durationSeconds: totalSeconds,\n crf: options.crf,\n outPath: a.outPath,\n preset: a.preset,\n frameFormat: a.frameFormat,\n jpegQuality: a.jpegQuality,\n workers: a.workers,\n tmpDir: a.tmpDir,\n logger: a.logger,\n onProgress: a.onProgress,\n signal: a.signal,\n prepare: async (page, { logger }) => {\n // Force the color scheme + block tracker requests before navigation so load-time media queries\n // and the network match the intended capture.\n if (a.colorScheme) await page.emulateMedia({ colorScheme: a.colorScheme });\n await installNetworkHygiene(page, options);\n // Pre-navigation hooks (e.g. freeze the clock, theme class) must be installed before page scripts.\n await installPreNav(page, options);\n logger.debug(`navigating to ${a.url} (waitUntil=${options.waitUntil})`);\n await page.goto(a.url, { waitUntil: options.waitUntil });\n if (options.waitForSelector) {\n logger.debug(`waiting for selector ${options.waitForSelector}`);\n await page.waitForSelector(options.waitForSelector, { state: \"visible\" });\n }\n // Suppress capture noise (hide banners/scrollbars, inject CSS, dismiss consent overlays).\n await applyPostNav(page, options, logger);\n // Warm-up (not recorded): load lazy content, fonts and images, then settle back at the top.\n await page.evaluate(prepareScroll, { settleMs: PREWARM_SETTLE_MS });\n if (options.annotations && options.annotations.length > 0) {\n await page.evaluate(installAnnotationRuntime, { color: \"#7c9cff\" });\n }\n // Resolve the timeline against the (now stable) page; deterministic across workers.\n return buildScrollTimeline(page, options, totalSeconds, logger);\n },\n seekToFrame: async (page, t, timeline) => {\n const kb = options.kenBurns;\n let scale: number | undefined;\n let originX: number | undefined;\n let originY: number | undefined;\n if (kb) {\n const p = totalSeconds > 0 ? t / totalSeconds : 1;\n // Fold the zoom progress under a boomerang loop so the zoom returns to its start (seamless).\n const zp = options.loop === \"boomerang\" ? foldProgress(p) : p;\n scale = kenBurnsScaleAt(zp, {\n scaleFrom: kb.scaleFrom ?? 1,\n scaleTo: kb.scaleTo ?? 1.08,\n easing: kb.easing ?? options.easing,\n });\n originX = kb.originX ?? 0.5;\n originY = kb.originY ?? 0.5;\n }\n await page.evaluate(seekScrollTo, {\n normalizedY: timeline.scrollAt(t),\n scale,\n originX,\n originY,\n });\n if (options.annotations && options.annotations.length > 0) {\n const state = annotationStateAt(options.annotations, t * 1000, totalSeconds * 1000);\n await page.evaluate(\n (s) =>\n (globalThis as { __scAnno?: { update(x: unknown): Promise<void> } }).__scAnno?.update(s),\n state,\n );\n }\n if (a.settlePerFrame) {\n // Bound the in-page settle Node-side so a stuck decode can't hang the frame.\n await Promise.race([\n page.evaluate(settleInView),\n new Promise<void>((resolve) => setTimeout(resolve, a.settleMaxMs)),\n ]);\n }\n },\n });\n}\n","/**\n * A capture \"variant\" is one cell of the viewport × color-scheme matrix. A scroll-reel asset with\n * `viewports` and/or `colorScheme: \"both\"` expands into several variants, each emitted as its own\n * asset (the `suffix` is appended to the base name). The common case — no viewports, no `\"both\"` —\n * yields a single variant with an empty suffix (the asset keeps its base name).\n */\nexport interface VariantViewport {\n name: string;\n width: number;\n height: number;\n deviceScaleFactor?: number;\n}\n\nexport interface CaptureVariant {\n width: number;\n height: number;\n deviceScaleFactor: number;\n colorScheme?: \"light\" | \"dark\";\n /** Appended to the base asset name / filename; \"\" for the single default variant. */\n suffix: string;\n}\n\nexport interface BuildVariantsArgs {\n width: number;\n height: number;\n deviceScaleFactor: number;\n viewports?: VariantViewport[];\n colorScheme?: \"light\" | \"dark\" | \"both\";\n}\n\n/** Pure: expand the viewport × color-scheme matrix into capture variants. Unit-tested. */\nexport function buildVariants(o: BuildVariantsArgs): CaptureVariant[] {\n const viewports: VariantViewport[] =\n o.viewports && o.viewports.length > 0\n ? o.viewports\n : [{ name: \"\", width: o.width, height: o.height, deviceScaleFactor: o.deviceScaleFactor }];\n\n const schemes: Array<\"light\" | \"dark\" | undefined> =\n o.colorScheme === \"both\"\n ? [\"light\", \"dark\"]\n : o.colorScheme === \"light\" || o.colorScheme === \"dark\"\n ? [o.colorScheme]\n : [undefined];\n const multiScheme = schemes.length > 1;\n\n const out: CaptureVariant[] = [];\n for (const vp of viewports) {\n for (const scheme of schemes) {\n const suffix = [vp.name, multiScheme ? (scheme ?? \"\") : \"\"].filter((p) => p).join(\"-\");\n out.push({\n width: vp.width,\n height: vp.height,\n deviceScaleFactor: vp.deviceScaleFactor ?? o.deviceScaleFactor,\n colorScheme: scheme,\n suffix,\n });\n }\n }\n return out;\n}\n","import path from \"node:path\";\nimport { copyFile, stat } from \"node:fs/promises\";\nimport {\n aspectTarget,\n buildAspectArgs,\n buildGifArgs,\n buildPosterArgs,\n buildStillSegmentArgs,\n buildWebpArgs,\n concatMp4,\n probeVideoDurationMs,\n runFfmpeg,\n} from \"@/media/ffmpeg\";\nimport {\n renderCard,\n DEFAULT_CARD_DURATION_MS,\n DEFAULT_CARD_FADE_MS,\n type CardSpec,\n} from \"@/generators/scroll-reel/cards\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { sha256File } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { ResolvedScrollReelOptions } from \"@/generators/scroll-reel/options\";\nimport type { PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport interface OutputJob {\n ctx: PipelineContext;\n generatorId: string;\n /** The captured mp4 (viewport-sized) in tmp. */\n sourceMp4: string;\n /** Filename base (no extension), including any variant suffix. */\n fileBase: string;\n /** Manifest asset id base, including any variant suffix. */\n assetId: string;\n sourceUrl: string;\n /** Capture dimensions (before any aspect reframe). */\n width: number;\n height: number;\n deviceScaleFactor: number;\n durationMs: number;\n options: ResolvedScrollReelOptions;\n preset: string;\n}\n\n// mp4 first so it stays the primary output (used when another asset references this one as an input).\nconst FORMAT_ORDER = [\"mp4\", \"gif\", \"webp\", \"poster\"] as const;\n\n/**\n * From a captured mp4, optionally reframe to a target aspect (shared by every output), then emit the\n * requested formats — mp4 / gif / webp / poster — each as its own manifest asset (id suffixed by format,\n * except mp4 which keeps the base id). The aspect reframe and alternate encodes are pure ffmpeg argv\n * builders in `@/media/ffmpeg`; this just orchestrates them and records the results.\n */\nexport async function produceOutputs(job: OutputJob): Promise<AssetRecord[]> {\n const { ctx, options } = job;\n const logger = ctx.logger;\n const signal = ctx.signal; // a cancel kills any in-progress reframe/encode here too\n\n // 1. Aspect reframe (once, shared by every output) — or use the capture as-is.\n let videoMp4 = job.sourceMp4;\n let outW = job.width;\n let outH = job.height;\n if (options.aspect) {\n const target = aspectTarget(options.aspect);\n outW = target.width;\n outH = target.height;\n const reframed = path.join(ctx.tmpDir, `${slugify(job.assetId)}-aspect.mp4`);\n await runFfmpeg(\n buildAspectArgs({\n inputPath: job.sourceMp4,\n outputPath: reframed,\n width: outW,\n height: outH,\n fit: options.fit,\n padColor: options.padColor,\n fps: options.fps,\n crf: options.crf,\n preset: job.preset,\n }),\n logger,\n signal,\n );\n videoMp4 = reframed;\n }\n\n // The poster always comes from the content clip (never an intro card's fade-from-black).\n const posterSource = videoMp4;\n\n // 2. Intro / outro cards: render at the output size and concatenate around the clip.\n let extraMs = 0;\n if (options.intro || options.outro) {\n const makeCard = async (spec: CardSpec, tag: string): Promise<string> => {\n const ms = spec.durationMs ?? DEFAULT_CARD_DURATION_MS;\n const fade = (spec.fadeMs ?? DEFAULT_CARD_FADE_MS) / 1000;\n const png = path.join(ctx.tmpDir, `${slugify(job.assetId)}-${tag}.png`);\n await renderCard(ctx.browser, {\n spec,\n width: outW,\n height: outH,\n scale: job.deviceScaleFactor,\n outPath: png,\n });\n const seg = path.join(ctx.tmpDir, `${slugify(job.assetId)}-${tag}.mp4`);\n await runFfmpeg(\n buildStillSegmentArgs({\n pngPath: png,\n outPath: seg,\n seconds: ms / 1000,\n fps: options.fps,\n width: outW,\n height: outH,\n fadeInSec: fade,\n fadeOutSec: fade,\n crf: options.crf,\n preset: job.preset,\n }),\n logger,\n signal,\n );\n extraMs += ms;\n return seg;\n };\n const segs: string[] = [];\n if (options.intro) segs.push(await makeCard(options.intro, \"intro\"));\n segs.push(videoMp4);\n if (options.outro) segs.push(await makeCard(options.outro, \"outro\"));\n if (segs.length > 1) {\n const stitched = path.join(ctx.tmpDir, `${slugify(job.assetId)}-carded.mp4`);\n await concatMp4(segs, stitched, logger, signal);\n videoMp4 = stitched;\n }\n }\n // Probe the real container duration (concat/mux can drift a little from the computed sum).\n const videoMs = (await probeVideoDurationMs(videoMp4)) ?? job.durationMs + extraMs;\n\n const gifFps = options.gifFps ?? Math.min(options.fps, 15);\n const want = new Set(options.outputs);\n const records: AssetRecord[] = [];\n\n for (const fmt of FORMAT_ORDER) {\n if (!want.has(fmt)) continue;\n const ext = fmt === \"poster\" ? \"png\" : fmt;\n const outPath = ctx.resolveOutPath(`${job.fileBase}.${ext}`);\n await ensureDir(path.dirname(outPath));\n\n if (fmt === \"mp4\") {\n await copyFile(videoMp4, outPath);\n } else if (fmt === \"gif\") {\n await runFfmpeg(buildGifArgs({ inputPath: videoMp4, outputPath: outPath, fps: gifFps }), logger, signal);\n } else if (fmt === \"webp\") {\n await runFfmpeg(\n buildWebpArgs({ inputPath: videoMp4, outputPath: outPath, fps: gifFps, quality: 75 }),\n logger,\n signal,\n );\n } else {\n await runFfmpeg(\n buildPosterArgs({ inputPath: posterSource, outputPath: outPath, atSeconds: 0 }),\n logger,\n signal,\n );\n }\n\n const [stats, contentHash] = await Promise.all([stat(outPath), sha256File(outPath)]);\n const id = fmt === \"mp4\" ? job.assetId : `${job.assetId}-${fmt}`;\n records.push({\n id,\n generator: job.generatorId,\n sourceUrl: job.sourceUrl,\n file: ctx.toManifestPath(outPath),\n format: ext,\n width: outW,\n height: outH,\n durationMs: fmt === \"poster\" ? undefined : videoMs,\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n });\n logger.success(`${id} → ${ctx.toManifestPath(outPath)}`);\n }\n\n return records;\n}\n","import type { Browser } from \"playwright-core\";\n\nexport interface CardSpec {\n title?: string;\n subtitle?: string;\n background?: string;\n color?: string;\n durationMs?: number;\n fadeMs?: number;\n}\n\n/** Default intro/outro card length (ms). */\nexport const DEFAULT_CARD_DURATION_MS = 1500;\n/** Default card fade in/out (ms). */\nexport const DEFAULT_CARD_FADE_MS = 400;\n\n/** Pure: escape text for safe interpolation into the card HTML. */\nexport function escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\n/** Pure: the HTML document for a card at a given size. */\nexport function cardHtml(spec: CardSpec, width: number, height: number): string {\n const bg = spec.background ?? \"#0b0b0f\";\n const color = spec.color ?? \"#ffffff\";\n const titleSize = Math.round(height * 0.08);\n const subSize = Math.round(height * 0.038);\n const gap = Math.round(height * 0.03);\n const title = spec.title\n ? `<div style=\"font-size:${titleSize}px;font-weight:700;line-height:1.1\">${escapeHtml(spec.title)}</div>`\n : \"\";\n const subtitle = spec.subtitle\n ? `<div style=\"font-size:${subSize}px;opacity:0.75;margin-top:${gap}px\">${escapeHtml(spec.subtitle)}</div>`\n : \"\";\n return (\n `<!doctype html><html><head><meta charset=\"utf-8\"></head>` +\n `<body style=\"margin:0;width:${width}px;height:${height}px;display:flex;flex-direction:column;` +\n `align-items:center;justify-content:center;background:${bg};color:${color};` +\n `font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;text-align:center;` +\n `padding:0 8%;box-sizing:border-box\">${title}${subtitle}</body></html>`\n );\n}\n\n/** Render a card to a PNG via Playwright (HTML → PNG), supersampled by `scale`. */\nexport async function renderCard(\n browser: Browser,\n args: { spec: CardSpec; width: number; height: number; scale: number; outPath: string },\n): Promise<void> {\n const context = await browser.newContext({\n viewport: { width: args.width, height: args.height },\n deviceScaleFactor: args.scale,\n });\n try {\n const page = await context.newPage();\n await page.setContent(cardHtml(args.spec, args.width, args.height), { waitUntil: \"load\" });\n try {\n await page.evaluate(\n () =>\n (globalThis as { document?: { fonts?: { ready?: Promise<unknown> } } }).document?.fonts?.ready,\n );\n } catch {\n /* no font set */\n }\n await page.screenshot({ path: args.outPath, type: \"png\" });\n } finally {\n await context.close();\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\n\n/** sha256 hex digest of a file's contents. */\nexport async function sha256File(file: string): Promise<string> {\n const buf = await readFile(file);\n return createHash(\"sha256\").update(buf).digest(\"hex\");\n}\n\n/** sha256 hex digest of an in-memory buffer. */\nexport function sha256Buffer(buf: Uint8Array): string {\n return createHash(\"sha256\").update(buf).digest(\"hex\");\n}\n","import path from \"node:path\";\nimport { mkdtemp } from \"node:fs/promises\";\nimport type { Browser, Page } from \"playwright-core\";\nimport { ensureDir } from \"@/utils/fs\";\nimport {\n applyPostNav,\n installNetworkHygiene,\n installPreNav,\n} from \"@/generators/scroll-reel/clean-capture\";\nimport type { ResolvedScrollReelOptions } from \"@/generators/scroll-reel/options\";\nimport type { Logger } from \"@/utils/logger\";\n\n/** Default cursor travel / scroll-animation time for a step that omits `durationMs`. */\nexport const DEFAULT_ACTION_DURATION_MS = 700;\n/** Default pause after a step that omits `holdMs`. */\nexport const DEFAULT_ACTION_HOLD_MS = 600;\n/** Default dwell on the focused element. */\nconst DEFAULT_FOCUS_HOLD_MS = 2000;\n/** Default padding around a focused element when cropping. */\nconst DEFAULT_FOCUS_PADDING = 24;\n\n/**\n * Pure: turn an element box (CSS px) + padding into an even-dimensioned crop rectangle clamped to the\n * viewport (h264 needs even width/height). Unit-tested.\n */\nexport function clampCrop(\n box: { x: number; y: number; w: number; h: number },\n padding: number,\n viewportWidth: number,\n viewportHeight: number,\n): { x: number; y: number; width: number; height: number } {\n let x = Math.floor(box.x - padding);\n let y = Math.floor(box.y - padding);\n let w = Math.ceil(box.w + padding * 2);\n let h = Math.ceil(box.h + padding * 2);\n if (x < 0) {\n w += x;\n x = 0;\n }\n if (y < 0) {\n h += y;\n y = 0;\n }\n if (x + w > viewportWidth) w = viewportWidth - x;\n if (y + h > viewportHeight) h = viewportHeight - y;\n w = Math.max(2, w - (w % 2));\n h = Math.max(2, h - (h % 2));\n x = Math.max(0, Math.min(x, viewportWidth - w));\n y = Math.max(0, Math.min(y, viewportHeight - h));\n return { x, y, width: w, height: h };\n}\n\ntype InteractionAction = NonNullable<ResolvedScrollReelOptions[\"actions\"]>[number];\n\n/** Pure: total interaction clip length in ms (start delay + each step's travel + hold + end dwell). */\nexport function interactionTotalMs(\n actions: Array<{ durationMs?: number; holdMs?: number }>,\n startDelayMs: number,\n endDwellMs: number,\n): number {\n let total = startDelayMs + endDwellMs;\n for (const a of actions) {\n total += (a.durationMs ?? DEFAULT_ACTION_DURATION_MS) + (a.holdMs ?? DEFAULT_ACTION_HOLD_MS);\n }\n return total;\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Runs INSIDE the page. Installs a synthetic cursor and a `__sc` driver with eased moves, smooth\n * scrolling and a click pulse. Self-contained (serialized via page.evaluate); all globals are read off\n * `globalThis` so it carries no outer references.\n */\nfunction installCursorRuntime(opts: { show: boolean; size: number; color: string }): void {\n const g = globalThis as any;\n const doc = g.document;\n if (!doc || !doc.body) return;\n\n let cur: any = null;\n if (opts.show) {\n cur = doc.createElement(\"div\");\n cur.id = \"__sc_cursor\";\n cur.style.cssText = [\n \"position:fixed\",\n \"left:50%\",\n \"top:50%\",\n \"width:\" + opts.size + \"px\",\n \"height:\" + opts.size + \"px\",\n \"margin-left:\" + -opts.size / 2 + \"px\",\n \"margin-top:\" + -opts.size / 2 + \"px\",\n \"border-radius:50%\",\n \"background:\" + opts.color,\n \"opacity:0.85\",\n \"pointer-events:none\",\n \"z-index:2147483647\",\n \"box-shadow:0 0 0 2px rgba(255,255,255,0.85)\",\n \"transition:transform 0.12s ease\",\n ].join(\";\");\n doc.body.appendChild(cur);\n }\n\n let curX = (g.innerWidth || 0) / 2;\n let curY = (g.innerHeight || 0) / 2;\n const setPos = (x: number, y: number): void => {\n curX = x;\n curY = y;\n if (cur) {\n cur.style.left = x + \"px\";\n cur.style.top = y + \"px\";\n }\n };\n const ease = (t: number): number => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2);\n const tween = (x1: number, y1: number, ms: number): Promise<void> =>\n new Promise((resolve) => {\n const x0 = curX;\n const y0 = curY;\n if (ms <= 0) {\n setPos(x1, y1);\n resolve();\n return;\n }\n let start: number | null = null;\n const step = (now: number): void => {\n if (start === null) start = now;\n const t = Math.min(1, (now - start) / ms);\n const e = ease(t);\n setPos(x0 + (x1 - x0) * e, y0 + (y1 - y0) * e);\n if (t < 1) g.requestAnimationFrame(step);\n else resolve();\n };\n g.requestAnimationFrame(step);\n });\n\n g.__sc = {\n moveTo: (x: number, y: number, ms: number): Promise<void> => tween(x, y, ms),\n moveToFraction: (fx: number, fy: number, ms: number): Promise<void> =>\n tween((g.innerWidth || 0) * fx, (g.innerHeight || 0) * fy, ms),\n moveToSelector: async (sel: string, ms: number): Promise<void> => {\n const el = doc.querySelector(sel);\n if (!el) return;\n try {\n el.scrollIntoView({ behavior: \"instant\", block: \"center\" });\n } catch {\n try {\n el.scrollIntoView();\n } catch {\n /* ignore */\n }\n }\n await new Promise<void>((r) => g.requestAnimationFrame(() => g.requestAnimationFrame(() => r())));\n const rect = el.getBoundingClientRect();\n await tween(rect.left + rect.width / 2, rect.top + rect.height / 2, ms);\n },\n scrollTo: async (to: any, ms: number): Promise<void> => {\n const se = doc.scrollingElement || doc.documentElement;\n const max = Math.max(0, se.scrollHeight - se.clientHeight);\n let target = 0;\n if (typeof to === \"number\") target = to * max;\n else if (typeof to === \"string\" && to.trim().endsWith(\"%\")) target = (parseFloat(to) / 100) * max;\n else if (typeof to === \"string\") {\n const el = doc.querySelector(to);\n if (el) target = Math.max(0, Math.min(max, el.getBoundingClientRect().top + (g.pageYOffset || 0)));\n }\n const from = g.pageYOffset || se.scrollTop || 0;\n if (ms <= 0) {\n g.scrollTo(0, target);\n return;\n }\n await new Promise<void>((resolve) => {\n let start: number | null = null;\n const step = (now: number): void => {\n if (start === null) start = now;\n const t = Math.min(1, (now - start) / ms);\n g.scrollTo(0, from + (target - from) * ease(t));\n if (t < 1) g.requestAnimationFrame(step);\n else resolve();\n };\n g.requestAnimationFrame(step);\n });\n },\n pulse: (): void => {\n if (!cur) return;\n cur.style.transform = \"scale(0.6)\";\n g.setTimeout(() => {\n if (cur) cur.style.transform = \"scale(1)\";\n }, 150);\n },\n };\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n/** Drive one interaction step: animate the cursor, then perform the real Playwright action. */\nasync function runAction(page: Page, a: InteractionAction, durationMs: number): Promise<void> {\n switch (a.do) {\n case \"wait\":\n return; // the per-step hold provides the pause\n case \"scrollTo\":\n await page.evaluate(\n (p: { to: number | string; ms: number }) =>\n (globalThis as { __sc?: { scrollTo(to: number | string, ms: number): Promise<void> } }).__sc?.scrollTo(\n p.to,\n p.ms,\n ),\n { to: a.to ?? a.selector ?? 0, ms: durationMs },\n );\n return;\n case \"move\":\n if (a.selector) {\n await page.evaluate(\n (p: { sel: string; ms: number }) =>\n (globalThis as { __sc?: { moveToSelector(s: string, ms: number): Promise<void> } }).__sc?.moveToSelector(\n p.sel,\n p.ms,\n ),\n { sel: a.selector, ms: durationMs },\n );\n } else {\n await page.evaluate(\n (p: { x: number; y: number; ms: number }) =>\n (globalThis as { __sc?: { moveToFraction(x: number, y: number, ms: number): Promise<void> } }).__sc?.moveToFraction(\n p.x,\n p.y,\n p.ms,\n ),\n { x: a.x ?? 0.5, y: a.y ?? 0.5, ms: durationMs },\n );\n }\n return;\n case \"hover\":\n if (!a.selector) return;\n await moveCursorToSelector(page, a.selector, durationMs);\n await page.hover(a.selector);\n return;\n case \"click\":\n if (!a.selector) return;\n await moveCursorToSelector(page, a.selector, durationMs);\n await page.evaluate(() => (globalThis as { __sc?: { pulse(): void } }).__sc?.pulse());\n await page.click(a.selector);\n return;\n case \"type\":\n if (!a.selector) return;\n await moveCursorToSelector(page, a.selector, durationMs);\n await page.click(a.selector);\n if (a.text) await page.type(a.selector, a.text, { delay: 60 });\n return;\n }\n}\n\nfunction moveCursorToSelector(page: Page, selector: string, ms: number): Promise<unknown> {\n return page.evaluate(\n (p: { sel: string; ms: number }) =>\n (globalThis as { __sc?: { moveToSelector(s: string, ms: number): Promise<void> } }).__sc?.moveToSelector(\n p.sel,\n p.ms,\n ),\n { sel: selector, ms },\n );\n}\n\nexport interface InteractionArgs {\n browser: Browser;\n url: string;\n options: ResolvedScrollReelOptions;\n colorScheme?: \"light\" | \"dark\";\n tmpDir: string;\n logger: Logger;\n}\n\nexport interface InteractionResult {\n webmPath: string;\n /** Seconds of nav + warm-up before the first action — trim this off the head. */\n leadSeconds: number;\n /** Intended clip length (after the head trim). */\n durationSeconds: number;\n}\n\n/**\n * Record a scripted interaction in realtime: navigate, clean up, install the synthetic cursor, then run\n * the action sequence live while Playwright records. Returns the recording for downstream transcoding.\n */\nexport async function captureInteractionWebm(args: InteractionArgs): Promise<InteractionResult> {\n const { browser, url, options, tmpDir, logger } = args;\n await ensureDir(tmpDir);\n const recordDir = await mkdtemp(path.join(tmpDir, \"rec-\"));\n const context = await browser.newContext({\n viewport: { width: options.width, height: options.height },\n deviceScaleFactor: options.deviceScaleFactor,\n recordVideo: { dir: recordDir, size: { width: options.width, height: options.height } },\n });\n const page = await context.newPage();\n const video = page.video();\n const actions = options.actions ?? [];\n const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\n const recStart = Date.now();\n let leadSeconds = 0;\n try {\n if (args.colorScheme) await page.emulateMedia({ colorScheme: args.colorScheme });\n await installNetworkHygiene(page, options);\n await installPreNav(page, options);\n logger.debug(`navigating to ${url} (waitUntil=${options.waitUntil})`);\n await page.goto(url, { waitUntil: options.waitUntil });\n if (options.waitForSelector) {\n await page.waitForSelector(options.waitForSelector, { state: \"visible\" });\n }\n await applyPostNav(page, options, logger);\n try {\n await page.evaluate(\n () =>\n (globalThis as { document?: { fonts?: { ready?: Promise<unknown> } } }).document?.fonts?.ready,\n );\n } catch {\n /* no font set */\n }\n await page.evaluate(installCursorRuntime, {\n show: options.cursor?.show ?? true,\n size: options.cursor?.size ?? 22,\n color: options.cursor?.color ?? \"#0b0b0f\",\n });\n\n // Everything above is blank/churn in the recording; the scripted interaction starts now.\n leadSeconds = (Date.now() - recStart) / 1000;\n await sleep(options.startDelayMs);\n for (const a of actions) {\n const durationMs = a.durationMs ?? DEFAULT_ACTION_DURATION_MS;\n try {\n await runAction(page, a, durationMs);\n } catch (e) {\n logger.warn(`interaction step \"${a.do}\"${a.selector ? ` (${a.selector})` : \"\"} failed: ${(e as Error).message}`);\n }\n await sleep(a.holdMs ?? DEFAULT_ACTION_HOLD_MS);\n }\n await sleep(options.endDwellMs);\n } finally {\n await context.close();\n }\n\n if (!video) {\n throw new Error(\"Playwright did not record a video (recordVideo inactive).\");\n }\n return {\n webmPath: await video.path(),\n leadSeconds,\n durationSeconds: interactionTotalMs(actions, options.startDelayMs, options.endDwellMs) / 1000,\n };\n}\n\nexport interface FocusResult extends InteractionResult {\n /** The crop box (CSS px) to apply when transcoding. */\n cropBox: { x: number; y: number; width: number; height: number };\n}\n\n/**\n * Record an element-focused clip in realtime: scroll the component into view, optionally trigger it,\n * hold, and report the crop box (the element's final box + padding, clamped to the viewport). The caller\n * transcodes the recording cropped to that box.\n */\nexport async function captureFocusWebm(args: InteractionArgs): Promise<FocusResult> {\n const { browser, url, options, tmpDir, logger } = args;\n const focus = options.focus;\n if (!focus) throw new Error(\"captureFocusWebm called without options.focus\");\n await ensureDir(tmpDir);\n const recordDir = await mkdtemp(path.join(tmpDir, \"rec-\"));\n const context = await browser.newContext({\n viewport: { width: options.width, height: options.height },\n deviceScaleFactor: options.deviceScaleFactor,\n recordVideo: { dir: recordDir, size: { width: options.width, height: options.height } },\n });\n const page = await context.newPage();\n const video = page.video();\n const actions = focus.actions ?? [];\n const holdMs = focus.holdMs ?? DEFAULT_FOCUS_HOLD_MS;\n const padding = focus.padding ?? DEFAULT_FOCUS_PADDING;\n const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n const twoFrames = (): Promise<void> =>\n page.evaluate(\n () =>\n new Promise<void>((res) => {\n const g = globalThis as unknown as { requestAnimationFrame(cb: () => void): void };\n g.requestAnimationFrame(() => g.requestAnimationFrame(() => res()));\n }),\n );\n\n const recStart = Date.now();\n let leadSeconds = 0;\n let cropBox = { x: 0, y: 0, width: options.width, height: options.height };\n try {\n if (args.colorScheme) await page.emulateMedia({ colorScheme: args.colorScheme });\n await installNetworkHygiene(page, options);\n await installPreNav(page, options);\n await page.goto(url, { waitUntil: options.waitUntil });\n if (options.waitForSelector) {\n await page.waitForSelector(options.waitForSelector, { state: \"visible\" });\n }\n await applyPostNav(page, options, logger);\n await page.evaluate(installCursorRuntime, {\n show: options.cursor?.show ?? true,\n size: options.cursor?.size ?? 22,\n color: options.cursor?.color ?? \"#0b0b0f\",\n });\n // Bring the element to the center before the kept clip starts.\n await page.evaluate((sel: string) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const el = (globalThis as any).document?.querySelector(sel);\n if (el) {\n try {\n el.scrollIntoView({ behavior: \"instant\", block: \"center\" });\n } catch {\n /* ignore */\n }\n }\n }, focus.selector);\n await twoFrames();\n\n leadSeconds = (Date.now() - recStart) / 1000;\n await sleep(options.startDelayMs);\n for (const a of actions) {\n const durationMs = a.durationMs ?? DEFAULT_ACTION_DURATION_MS;\n try {\n await runAction(page, a, durationMs);\n } catch (e) {\n logger.warn(`focus step \"${a.do}\" failed: ${(e as Error).message}`);\n }\n await sleep(a.holdMs ?? DEFAULT_ACTION_HOLD_MS);\n }\n // Measure the element's final box (covers any expansion from the trigger) for the crop.\n const box = await page.evaluate((sel: string) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const el = (globalThis as any).document?.querySelector(sel);\n if (!el) return null;\n const r = el.getBoundingClientRect();\n return { x: r.left, y: r.top, w: r.width, h: r.height };\n }, focus.selector);\n if (box && box.w > 0 && box.h > 0) {\n cropBox = clampCrop(box, padding, options.width, options.height);\n } else {\n logger.warn(`focus: selector \"${focus.selector}\" not found — capturing the full viewport`);\n }\n await sleep(holdMs);\n await sleep(options.endDwellMs);\n } finally {\n await context.close();\n }\n\n if (!video) {\n throw new Error(\"Playwright did not record a video (recordVideo inactive).\");\n }\n const durationSeconds =\n (options.startDelayMs + interactionTotalMs(actions, 0, 0) + holdMs + options.endDwellMs) / 1000;\n return { webmPath: await video.path(), leadSeconds, durationSeconds, cropBox };\n}\n","import type { PipelineContext } from \"@/generators/types\";\n\n/** Narrow a url-based generator's target url, with a clear error when it's missing. */\nexport function requireUrl(ctx: PipelineContext): string {\n if (!ctx.target.url) {\n throw new Error(`Asset \"${ctx.target.name}\" requires a \"url\" in its config.`);\n }\n return ctx.target.url;\n}\n","import { writeFile } from \"node:fs/promises\";\nimport { imageSize } from \"image-size\";\nimport {\n screenshotsOptionsSchema,\n type ResolvedScreenshotsOptions,\n} from \"@/generators/screenshots/options\";\nimport { captureScreenshots } from \"@/generators/screenshots/capture\";\nimport { requireUrl } from \"@/generators/require-url\";\nimport { sha256Buffer } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const SCREENSHOTS_ID = \"screenshots\";\n\nfunction dimensions(buffer: Buffer): { width: number; height: number } {\n try {\n const size = imageSize(buffer);\n return { width: size.width ?? 0, height: size.height ?? 0 };\n } catch {\n return { width: 0, height: 0 };\n }\n}\n\nasync function run(\n ctx: PipelineContext,\n options: ResolvedScreenshotsOptions,\n): Promise<{ assets: AssetRecord[] }> {\n const ext = options.format === \"jpeg\" ? \"jpg\" : \"png\";\n const url = requireUrl(ctx);\n ctx.logger.info(`capturing ${url}`);\n\n const shots = await captureScreenshots({\n browser: ctx.browser,\n url,\n options,\n logger: ctx.logger,\n });\n\n const records: AssetRecord[] = [];\n for (const shot of shots) {\n const fileName = `${slugify(ctx.target.name)}-${slugify(shot.key)}.${ext}`;\n const outPath = ctx.resolveOutPath(fileName);\n await writeFile(outPath, shot.buffer);\n\n const { width, height } = dimensions(shot.buffer);\n if (width === 0 || height === 0) {\n ctx.logger.warn(`could not read dimensions for ${fileName} (recording 0×0)`);\n } else {\n ctx.logger.debug(`${fileName}: ${width}×${height}`);\n // Chromium caps screenshots near ~32767px; a very tall fullPage shot may be clipped/huge.\n if (width > 16000 || height > 16000) {\n ctx.logger.warn(\n `${fileName} is very large (${width}×${height}); a fullPage shot at deviceScaleFactor ${options.deviceScaleFactor} may be clipped or slow — consider a lower scale.`,\n );\n }\n }\n const record: AssetRecord = {\n id: `${ctx.target.name}-${shot.key}`,\n generator: SCREENSHOTS_ID,\n sourceUrl: url,\n file: ctx.toManifestPath(outPath),\n format: ext,\n width,\n height,\n bytes: shot.buffer.length,\n contentHash: sha256Buffer(shot.buffer),\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n records.push(record);\n ctx.logger.success(`${record.id} → ${record.file}`);\n }\n\n return { assets: records };\n}\n\nexport const screenshotsGenerator: Generator<ResolvedScreenshotsOptions> = {\n id: SCREENSHOTS_ID,\n requiresUrl: true,\n optionsSchema: screenshotsOptionsSchema,\n run,\n};\n","import { z } from \"zod\";\n\n/** A named viewport to capture at. */\nconst breakpointSchema = z\n .object({\n name: z\n .string()\n .min(1)\n .describe(\"Label for this viewport — used in the filename + manifest id (e.g. \\\"desktop\\\").\"),\n width: z.number().int().positive().describe(\"Viewport width in CSS px.\"),\n /** Viewport height. Note: ignored for `fullPage` shots (Playwright resizes to the page height);\n * only affects viewport/element captures. */\n height: z\n .number()\n .int()\n .positive()\n .default(900)\n .describe(\n \"Viewport height in CSS px. Default 900. Ignored for fullPage shots; only affects viewport/element captures.\",\n ),\n /** Override the generator-level deviceScaleFactor for this breakpoint. */\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .optional()\n .describe(\"Override the generator-level deviceScaleFactor for this breakpoint. Omit to inherit it.\"),\n })\n .strict();\nexport type Breakpoint = z.infer<typeof breakpointSchema>;\n\n/** A specific element to capture (in addition to the page) at each breakpoint. */\nconst elementShotSchema = z\n .object({\n selector: z.string().min(1).describe(\"CSS selector of the element to shoot.\"),\n /** Used in the filename + manifest id. */\n name: z.string().min(1).describe(\"Name used in the filename + manifest id for this element shot.\"),\n })\n .strict();\n\nexport const screenshotsOptionsSchema = z\n .object({\n breakpoints: z\n .array(breakpointSchema)\n .min(1)\n .default([\n { name: \"desktop\", width: 1440, height: 900 },\n { name: \"mobile\", width: 390, height: 844 },\n ])\n .describe(\n \"Viewports to capture at (at least one); each emits its own asset. Default: desktop 1440×900 + mobile 390×844.\",\n ),\n /** Capture the entire scrollable page (vs. just the viewport). */\n fullPage: z\n .boolean()\n .default(true)\n .describe(\"Capture the entire scrollable page (vs. just the viewport). Default true.\"),\n format: z.enum([\"png\", \"jpeg\"]).default(\"png\").describe(\"Image format. Default \\\"png\\\".\"),\n /** jpeg only, 1–100. */\n quality: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe(\"JPEG quality, 1–100 (jpeg only; rejected for png). Omit for the encoder default.\"),\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(2)\n .describe(\"Render scale (2 = retina-crisp). Default 2.\"),\n waitUntil: z\n .enum([\"load\", \"domcontentloaded\", \"networkidle\", \"commit\"])\n .default(\"networkidle\")\n .describe(\"Page-load milestone to wait for before capturing. Default \\\"networkidle\\\".\"),\n waitForSelector: z\n .string()\n .optional()\n .describe(\"Optional element to wait for before capturing (e.g. a hero image). Omit to skip.\"),\n /** Element captures taken at every breakpoint. */\n elements: z\n .array(elementShotSchema)\n .default([])\n .describe(\"Specific elements to crop (in addition to the page) at every breakpoint. Default none.\"),\n /** png only: capture with a transparent background. */\n omitBackground: z\n .boolean()\n .default(false)\n .describe(\"Capture with a transparent background (png only). Default false.\"),\n /** Extra settle time after load before capturing (ms). */\n settleMs: z\n .number()\n .int()\n .nonnegative()\n .default(0)\n .describe(\"Extra settle time after load before capturing (ms). Default 0.\"),\n })\n .strict()\n .refine((o) => !(o.format === \"png\" && o.quality != null), {\n message: \"`quality` only applies to jpeg; remove it or set format: \\\"jpeg\\\".\",\n path: [\"quality\"],\n });\n\n// ---------------------------------------------------------------------------\n// Author-facing input types (editor autocomplete + hover docs). JSDoc on the\n// zod schema above does NOT surface on hover through `z.input`, so the docs\n// live on these hand-written interfaces, kept in sync with the schema by the\n// Exact<> guard at the bottom — a drift is a compile error.\n// ---------------------------------------------------------------------------\n\n/** A named viewport to capture at; each breakpoint emits its own asset. */\nexport interface BreakpointInput {\n /** Label for this viewport — used in the filename + manifest id (e.g. \"desktop\"). */\n name: string;\n /** Viewport width in CSS px. */\n width: number;\n /**\n * Viewport height in CSS px. Default 900. Ignored for `fullPage` shots (Playwright resizes to the\n * full page height); only affects viewport/element captures.\n */\n height?: number;\n /** Override the generator-level `deviceScaleFactor` for this breakpoint. Omit to inherit it. */\n deviceScaleFactor?: number;\n}\n\n/** A specific element to capture (in addition to the page) at each breakpoint. */\nexport interface ElementShotInput {\n /** CSS selector of the element to shoot. */\n selector: string;\n /** Name used in the filename + manifest id for this element shot. */\n name: string;\n}\n\n/**\n * Author-facing options for the `screenshots` generator — responsive stills of a page (one per\n * breakpoint), plus optional per-element crops. Everything is optional; sensible defaults apply.\n */\nexport interface ScreenshotsOptionsInput {\n /**\n * Viewports to capture at (at least one); each emits its own asset. Default:\n * desktop 1440×900 + mobile 390×844.\n */\n breakpoints?: BreakpointInput[];\n /** Capture the entire scrollable page (vs. just the viewport). Default true. */\n fullPage?: boolean;\n /** Image format. Default \"png\". */\n format?: \"png\" | \"jpeg\";\n /** JPEG quality, 1–100 (jpeg only; rejected for png). Omit for the encoder default. */\n quality?: number;\n /** Render scale (2 = retina-crisp). Default 2. */\n deviceScaleFactor?: number;\n /** Page-load milestone to wait for before capturing. Default \"networkidle\". */\n waitUntil?: \"load\" | \"domcontentloaded\" | \"networkidle\" | \"commit\";\n /** Optional element to wait for before capturing (e.g. a hero image). Omit to skip. */\n waitForSelector?: string;\n /** Specific elements to crop (in addition to the page) at every breakpoint. Default none. */\n elements?: ElementShotInput[];\n /** Capture with a transparent background (png only). Default false. */\n omitBackground?: boolean;\n /** Extra settle time after load before capturing (ms). Default 0. */\n settleMs?: number;\n}\n\n/** Author-facing input (documented for editor hover; the schema validates it at run time). */\nexport type ScreenshotsOptions = ScreenshotsOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedScreenshotsOptions = z.infer<typeof screenshotsOptionsSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _screenshotsInputInSync: Exact<\n ScreenshotsOptionsInput,\n z.input<typeof screenshotsOptionsSchema>\n> = true;\nvoid _screenshotsInputInSync;\n","/**\n * Run `fn` over `items` with at most `limit` in flight, preserving input order in the\n * result. `fn` should not reject — callers handle per-item errors and return a value.\n */\nexport async function mapLimit<T, R>(\n items: T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let cursor = 0;\n const workerCount = Math.max(1, Math.min(limit, items.length));\n\n const worker = async (): Promise<void> => {\n while (true) {\n const index = cursor++;\n if (index >= items.length) return;\n results[index] = await fn(items[index] as T, index);\n }\n };\n\n await Promise.all(Array.from({ length: workerCount }, worker));\n return results;\n}\n","import type { Browser, Page } from \"playwright-core\";\nimport type { Logger } from \"@/utils/logger\";\nimport { mapLimit } from \"@/utils/concurrency\";\nimport { prepareScroll } from \"@/generators/scroll-reel/scroll\";\nimport type { ResolvedScreenshotsOptions } from \"@/generators/screenshots/options\";\n\n/** Minimum settle at the bottom for lazy/below-the-fold content (esp. for fullPage shots). */\nconst MIN_PREPARE_SETTLE_MS = 600;\n\nexport interface Shot {\n /** Suffix appended to the asset name for the filename + manifest id. */\n key: string;\n buffer: Buffer;\n}\n\nexport interface CaptureArgs {\n browser: Browser;\n url: string;\n options: ResolvedScreenshotsOptions;\n logger: Logger;\n}\n\ntype PageShotOptions = NonNullable<Parameters<Page[\"screenshot\"]>[0]>;\n\n/**\n * Capture page (and optional element) screenshots across every configured breakpoint.\n * Breakpoints render in parallel (each in its own isolated context, modest cap — full-page\n * buffers are memory-heavy); element shots stay sequential within a breakpoint. mapLimit\n * preserves input order, so filenames/manifest ids are stable.\n */\nexport async function captureScreenshots(args: CaptureArgs): Promise<Shot[]> {\n const { options } = args;\n const perBreakpoint = await mapLimit(\n options.breakpoints,\n Math.min(3, options.breakpoints.length),\n (bp) => captureBreakpoint(args, bp),\n );\n return perBreakpoint.flat();\n}\n\n/** One breakpoint: fresh context → navigate → warm the page → page shot + element shots. */\nasync function captureBreakpoint(\n args: CaptureArgs,\n bp: ResolvedScreenshotsOptions[\"breakpoints\"][number],\n): Promise<Shot[]> {\n const { browser, url, options, logger } = args;\n const shots: Shot[] = [];\n {\n const context = await browser.newContext({\n viewport: { width: bp.width, height: bp.height },\n deviceScaleFactor: bp.deviceScaleFactor ?? options.deviceScaleFactor,\n });\n const page = await context.newPage();\n try {\n logger.debug(`[${bp.name}] navigating to ${url}`);\n await page.goto(url, { waitUntil: options.waitUntil });\n if (options.waitForSelector) {\n await page.waitForSelector(options.waitForSelector, { state: \"visible\" });\n }\n // Drive the page so lazy-loaded / intersection-mounted content, web fonts, and image decode\n // are done before we shoot — otherwise fullPage shots capture blank/placeholder regions and\n // fallback fonts. Returns to the top when finished.\n await page.evaluate(prepareScroll, {\n settleMs: Math.max(options.settleMs, MIN_PREPARE_SETTLE_MS),\n });\n\n const pageShotOptions: PageShotOptions = {\n type: options.format,\n fullPage: options.fullPage,\n };\n if (options.format === \"png\" && options.omitBackground) {\n pageShotOptions.omitBackground = true;\n }\n if (options.format === \"jpeg\" && options.quality != null) {\n pageShotOptions.quality = options.quality;\n }\n shots.push({ key: bp.name, buffer: await page.screenshot(pageShotOptions) });\n\n for (const element of options.elements) {\n const locator = page.locator(element.selector).first();\n if ((await locator.count()) === 0) {\n logger.warn(`[${bp.name}] selector not found, skipping: ${element.selector}`);\n continue;\n }\n const elShotOptions: PageShotOptions = { type: options.format };\n if (options.format === \"png\" && options.omitBackground) {\n elShotOptions.omitBackground = true;\n }\n if (options.format === \"jpeg\" && options.quality != null) {\n elShotOptions.quality = options.quality;\n }\n // A present-but-hidden element (display:none until interaction) makes locator.screenshot\n // throw; warn + skip it instead of aborting the whole breakpoint loop.\n try {\n shots.push({\n key: `${bp.name}-${element.name}`,\n buffer: await locator.screenshot(elShotOptions),\n });\n } catch (err) {\n logger.warn(\n `[${bp.name}] could not capture \"${element.selector}\": ${(err as Error).message}`,\n );\n }\n }\n } finally {\n await context.close();\n }\n }\n\n return shots;\n}\n","import { z } from \"zod\";\nimport {\n wallColumnSchema,\n wallPanSchema,\n wallPulseSchema,\n fauxTileSchema,\n type WallPanInput,\n type WallColumnInput,\n type WallPulseInput,\n type FauxTileInput,\n} from \"@/generators/scene/scene-options\";\n\n/**\n * Author-facing options for the `wall` generator — a media wall whose `columns` are self-contained\n * units: each column owns its `tiles` (the assets stacked in it, by name) AND its own Y motion. The\n * dependency map is derived from those tile names, so there's no separate `inputs` list to maintain.\n *\n * Motion is built from one uniform \"pulse\" primitive, shared by columns, the wall-level default, and\n * the pan. A track's travel = `loops` continuous whole-clip periods + the sum of its `pulses` (each\n * an eased move of `distance` periods starting at `at`, lasting `duration` — both 0..1 fractions of\n * the clip). The total is rounded UP to a whole number of periods — the remainder folds into the\n * continuous scroll — so every track lands back on its start at the clip's end: the wall ALWAYS loops\n * seamlessly. (A pulse with `at + duration > 1` is shifted back to end at the loop point, so it can\n * never overrun the clip.)\n * `pan` — System 1: the whole wall pans on X.\n * `columns` — System 2: each column scrolls on Y (its own `direction` / `loops` / `pulses`).\n */\nexport const wallOptionsSchema = z\n .object({\n // --- output ---\n /** Output width (CSS px). */\n width: z.number().int().positive().default(1920).describe(\"Output width in CSS px. Default 1920.\"),\n /** Output height (CSS px). */\n height: z.number().int().positive().default(1080).describe(\"Output height in CSS px. Default 1080.\"),\n /** Render scale (2 = retina-crisp, downscaled into the video). */\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(2)\n .describe(\"Render scale (2 = retina-crisp, downscaled into the video). Default 2.\"),\n /** Output frames per second. */\n fps: z.number().int().positive().max(120).default(30).describe(\"Output frames per second. Default 30.\"),\n /** Clip length (seconds) — the whole loop. Tile videos should loop within a length dividing this. */\n durationSeconds: z\n .number()\n .positive()\n .default(16)\n .describe(\"Clip length in seconds — the whole loop. Default 16.\"),\n /** x264 quality, 0–51 (lower = better/larger). */\n crf: z\n .number()\n .int()\n .min(0)\n .max(51)\n .default(18)\n .describe(\"x264 quality, 0–51 (lower = better quality / larger file). Default 18.\"),\n /** Capture strategy. \"frames\" (default) is deterministic + parallelizable. */\n capture: z\n .enum([\"frames\", \"realtime\"])\n .default(\"frames\")\n .describe('Capture strategy. \"frames\" (default) is deterministic + parallelizable; \"realtime\" records the live session.'),\n /** Parallel frame-render workers. Video-heavy walls can cold-start black under many workers —\n * set 1 (or omit) for those. */\n workers: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Parallel frame-render workers. Video-heavy walls can cold-start to black under many workers — set 1 (or omit). Auto-picks from cores.\"),\n /** Intermediate frame format (frames capture only). \"jpeg\" (default) is fast; \"png\" is lossless. */\n frameFormat: z\n .enum([\"jpeg\", \"png\"])\n .default(\"jpeg\")\n .describe('Intermediate frame format (frames capture). \"jpeg\" (default) is fast; \"png\" is lossless.'),\n /** Backdrop shown in the gutters between tiles. */\n background: z\n .string()\n .default(\"#0b0b0f\")\n .describe('Backdrop shown in the gutters between tiles. Default \"#0b0b0f\".'),\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName: z.string().optional().describe('Output filename; defaults to \"<slug(asset name)>.mp4\".'),\n\n // --- columns (System 2 + layout): each column = its tiles + its own motion ---\n /** The columns (≥3) — each its own tiles + motion. Count = array length (fewer = larger tiles). */\n columns: z\n .array(wallColumnSchema)\n .min(3)\n .describe(\"The columns (≥3) — each lists its stacked `tiles` (by name) and may carry its own motion. Count = columns.length.\"),\n /** Gap between columns and between tiles (px). */\n gap: z.number().nonnegative().default(8).describe(\"Gap between columns and between tiles (px). Default 8.\"),\n /** Default/fallback tile aspect (width / height). Tiles fit the column width and take their OWN\n * height from their media's aspect (16:9 → short, 9:16 → tall); this is only used for faux\n * (`test`) tiles that don't set their own `aspect`. 0.75 = 3:4 portrait. */\n tileAspect: z\n .number()\n .positive()\n .default(0.75)\n .describe(\"Default/fallback tile aspect (w/h) — only for faux (test) tiles without their own `aspect`. 0.75 = 3:4 portrait. Default 0.75.\"),\n /** Tile corner radius (px). */\n cornerRadius: z.number().nonnegative().default(6).describe(\"Tile corner radius (px). Default 6.\"),\n\n // --- motion (uniform pulse model) ---\n /** System 1 — the whole-wall X pan. */\n pan: wallPanSchema\n .default({})\n .describe(\"System 1 — the whole wall's horizontal pan (`direction` / `loops` / `pulses`). Default: no pan.\"),\n /** Default continuous whole-clip loops for columns that omit their own `loops` (0 = static unless\n * a pulse moves it; one pulse then rounds the total up to a single loop). */\n loops: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Default continuous whole-clip loops for columns that omit their own `loops`. Default 0 (static unless a pulse moves it).\"),\n /** Default pulses for columns that omit their own `pulses` (the uniform wall-level motion). */\n pulses: z\n .array(wallPulseSchema)\n .default([])\n .describe(\"Default pulses for columns that omit their own `pulses` (the uniform wall-level motion). Default none.\"),\n\n // --- test / preview ---\n /** Preview mode: render every tile as a flat labeled color box (see `testTiles`) instead of the\n * real assets. No producer assets run, so the wall renders in seconds — use it to dial in\n * layout + motion, then turn it off for the real render. */\n test: z\n .boolean()\n .default(false)\n .describe(\"Preview mode: render every tile as a flat labeled color box instead of real assets, so the wall renders in seconds. Default false.\"),\n /** Per-tile faux appearance for `test` mode, keyed by tile name. Tiles not listed get an\n * auto-derived color and their name as the label. */\n testTiles: z\n .record(z.string(), fauxTileSchema)\n .default({})\n .describe(\"Per-tile faux appearance for `test` mode, keyed by tile name (color + caption). Default {} (auto colors + names).\"),\n })\n .strict();\n\n// ---------------------------------------------------------------------------\n// Author-facing input type (editor autocomplete + hover docs). JSDoc on the\n// zod schema above does NOT surface on hover through `z.input`, so the docs\n// live on this hand-written interface, kept in sync with the schema by the\n// Exact<> guard at the bottom — a drift is a compile error.\n// ---------------------------------------------------------------------------\n\n/** Re-exported so configs can type a single column / pulse / pan / faux tile. */\nexport type { WallColumnInput, WallPanInput, WallPulseInput, FauxTileInput };\n\n/**\n * Author-facing options for the `wall` generator. Each entry in `columns` is a self-contained unit\n * (its `tiles` + its own optional motion); everything else is optional, with defaults noted below.\n * Motion is the uniform pulse model: `loops` (continuous base) + `pulses` (eased moves), summed and\n * rounded up to a whole number of periods so the wall always loops seamlessly.\n */\nexport interface WallOptionsInput {\n // --- output ---\n /** Output width in CSS px. Default 1920. */\n width?: number;\n /** Output height in CSS px. Default 1080. */\n height?: number;\n /** Render scale (2 = retina-crisp, downscaled into the video). Default 2. */\n deviceScaleFactor?: number;\n /** Output frames per second. Default 30. */\n fps?: number;\n /** Clip length in seconds — the whole loop. Default 16. */\n durationSeconds?: number;\n /** x264 quality, 0–51 (lower = better quality / larger file). Default 18. */\n crf?: number;\n /**\n * Capture strategy. \"frames\" (default) is deterministic + parallelizable; \"realtime\" records the\n * live session. Default \"frames\".\n */\n capture?: \"frames\" | \"realtime\";\n /**\n * Parallel frame-render workers. Video-heavy walls can cold-start to black tiles under many\n * workers — set 1 (or omit) for those. Omit to auto-pick from cores.\n */\n workers?: number;\n /** Intermediate frame format (frames capture). \"jpeg\" (default) is fast; \"png\" is lossless. */\n frameFormat?: \"jpeg\" | \"png\";\n /** Backdrop shown in the gutters between tiles. Default \"#0b0b0f\". */\n background?: string;\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName?: string;\n\n // --- columns: each its own tiles + motion ---\n /**\n * The columns (≥3). Each column lists the assets stacked in it (`tiles`, by name — cycled to fill\n * the height) and may carry its own motion (`direction` / `loops` / `pulses`); omitted `loops` /\n * `pulses` inherit the wall-level defaults below. Column count = `columns.length`.\n */\n columns: WallColumnInput[];\n /** Gap between columns and between tiles (px). Default 8. */\n gap?: number;\n /** Default/fallback tile aspect (width / height). Tiles fit the column width and take their OWN\n * height from their media's aspect (16:9 → short, 9:16 → tall); this is only used for faux\n * (`test`) tiles that don't set their own `aspect`. 0.75 = 3:4 portrait. Default 0.75. */\n tileAspect?: number;\n /** Tile corner radius (px). Default 6. */\n cornerRadius?: number;\n\n // --- motion (uniform pulse model) ---\n /** The whole wall's horizontal pan (`direction` / `loops` / `pulses`). Default: no pan. */\n pan?: WallPanInput;\n /** Default continuous whole-clip loops for columns that omit their own `loops`. Default 0 (static\n * unless a pulse moves it — a single pulse then rounds up to one loop). */\n loops?: number;\n /** Default pulses for columns that omit their own `pulses` (the uniform wall-level motion). Default none. */\n pulses?: WallPulseInput[];\n\n // --- test / preview ---\n /**\n * Preview mode: render every tile as a flat labeled color box instead of the real assets. No\n * producer assets run, so the wall renders in seconds — flip it on to dial in layout + motion,\n * then off for the real render. Default false.\n */\n test?: boolean;\n /** Per-tile faux appearance for `test` mode, keyed by tile name (color + caption). Default {} (auto colors + names). */\n testTiles?: Record<string, FauxTileInput>;\n}\n\n/** Author-facing input (documented for editor hover; the schema validates it at run time). */\nexport type WallOptions = WallOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedWallOptions = z.infer<typeof wallOptionsSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _wallInputInSync: Exact<WallOptionsInput, z.input<typeof wallOptionsSchema>> = true;\nvoid _wallInputInSync;\n","import { z } from \"zod\";\nimport { pulseSchema } from \"@/generators/specimen/options\";\n\n/**\n * Per-scene `sceneOptions` schemas — the knobs each built-in scene understands. `renderScene`\n * validates against the selected scene's schema before serializing props, so a typo'd key or an\n * unknown scene id fails fast with a named error instead of silently rendering defaults. Every\n * default equals the value the scene previously hardcoded, so existing configs render identically.\n *\n * The author-facing *Input interfaces below mirror each schema's input shape (compile-time-guarded);\n * the friendly `wall` / `specimen` / `palette-reel` generators own the config authoring surface.\n */\n\n/**\n * The specimen scene's wire format — produced by the specimen *generator* (its friendly options\n * are the real authoring surface), validated here so generator and scene can't drift apart.\n */\nconst specimenSceneOptionsSchema = z\n .object({\n label: z.string().default(\"\"),\n demo: z.boolean().default(false),\n weight: z.number().min(1).max(1000).default(820),\n lines: z.number().int().min(1).max(40).default(3),\n blacklist: z.string().default(\"\"),\n colors: z\n .object({\n background: z.string(),\n foreground: z.string(),\n muted: z.string(),\n accent: z.string().optional(),\n label: z.string().optional(),\n })\n .partial()\n .default({}),\n colorWeights: z\n .object({\n foreground: z.number().nonnegative(),\n muted: z.number().nonnegative(),\n accent: z.number().nonnegative(),\n })\n .partial()\n .default({}),\n pulses: z.array(pulseSchema).default([]),\n mirror: z.boolean().default(true),\n characterIntensity: z.number().nonnegative().default(1),\n colorIntensity: z.number().nonnegative().default(1),\n /** Max fraction a line's width may drift as glyphs change (right-edge stability). */\n maxLineDrift: z.number().positive().max(0.5).default(0.05),\n /** Schedule seed — same seed ⇒ identical animation (parallel workers must agree). */\n seed: z.number().int().default(1),\n /** Line-height of the glyph block. */\n leading: z.number().positive().default(0.78),\n /** Glyph pool override (≥2 distinct characters after the blacklist). */\n characterPool: z.string().min(2).optional(),\n })\n .strict();\n\n/** The wall's easing curves (kebab-cased, matching the rest of the tool). */\nconst wallEasingEnum = z.enum([\n \"linear\",\n \"ease-in\",\n \"ease-out\",\n \"ease-in-out\",\n \"ease-in-out-strong\",\n]);\n\n/**\n * One \"pulse\" — the uniform motion primitive shared by columns, the wall-level default, and the pan.\n * It adds `distance` periods of eased travel, starting at `at` and lasting `duration` — both 0..1\n * fractions of the clip. If `at + duration > 1`, the start shifts back so the move ends exactly at the\n * loop point (a 0.2 pulse at 0.9 starts at 0.8), so a pulse can never overrun the clip.\n */\nexport const wallPulseSchema = z\n .object({\n /** When the pulse starts, as a fraction of the clip (0..1). */\n at: z.number().min(0).max(1).describe(\"When the pulse starts, as a fraction of the clip (0..1).\"),\n /** How long the move takes, as a fraction of the clip (0..1). */\n duration: z\n .number()\n .positive()\n .max(1)\n .describe(\"How long the move takes, as a fraction of the clip (0..1).\"),\n /** How far it travels, in periods (1 = one full tile-set / one wrap). Usually 0..1. */\n distance: z\n .number()\n .nonnegative()\n .describe(\"How far it travels, in periods (1 = one full tile-set / wrap). Usually 0..1.\"),\n /** Easing of the move's ramp. */\n easing: wallEasingEnum.default(\"ease-in-out\").describe(\"Easing of the move's ramp. Default 'ease-in-out'.\"),\n })\n .strict();\n\n/** System 1 — the whole-wall X pan: a continuous base scroll (`loops`) + optional `pulses`. */\nexport const wallPanSchema = z\n .object({\n /** Pan direction. */\n direction: z.enum([\"left\", \"right\"]).default(\"left\").describe(\"Pan direction. Default 'left'.\"),\n /** Continuous whole-clip horizontal loops (0 = no pan unless `pulses` move it). */\n loops: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Continuous whole-clip horizontal loops (0 = no pan unless pulses move it). Default 0.\"),\n /** Pulses added on top of the base loops. */\n pulses: z.array(wallPulseSchema).default([]).describe(\"Pulses added on top of the base loops.\"),\n })\n .strict();\n\n/**\n * One column of the wall: its tiles (the assets stacked in it, by name — cycled to fill the column\n * height) plus its own optional Y motion. Omitted `loops`/`pulses` inherit the wall-level defaults;\n * an omitted `direction` defaults to \"down\".\n */\nexport const wallColumnSchema = z\n .object({\n /** Assets stacked in this column, by name (cycled to fill the height). At least one. */\n tiles: z\n .array(z.string().min(1))\n .min(1)\n .describe(\"Assets stacked in this column, by name (cycled to fill the height). At least one.\"),\n /** Constant start-position shift, 0..1 of a tile-set — de-aligns columns with similar content. */\n stagger: z\n .number()\n .min(0)\n .max(1)\n .default(0)\n .describe(\"Start-position shift (0..1 of a tile-set) that de-aligns columns with similar content. Default 0.\"),\n /** Scroll direction. Defaults to \"down\". */\n direction: z.enum([\"up\", \"down\"]).optional().describe(\"Scroll direction. Default 'down'.\"),\n /** Continuous whole-clip loops for this column. Omit to inherit the wall-level `loops`. */\n loops: z\n .number()\n .nonnegative()\n .optional()\n .describe(\"Continuous whole-clip loops for this column. Omit to inherit the wall-level loops.\"),\n /** This column's pulses. Omit to inherit the wall-level `pulses`. */\n pulses: z\n .array(wallPulseSchema)\n .optional()\n .describe(\"This column's pulses. Omit to inherit the wall-level pulses.\"),\n })\n .strict();\n\n/**\n * A faux tile for the wall's `test` preview mode — a flat colored box labeled with its name, rendered\n * instead of a real asset so layout + motion can be dialed in instantly (no producers run).\n */\nexport const fauxTileSchema = z\n .object({\n /** Box fill (any CSS color). Omit to auto-derive a distinct color from the tile name. */\n color: z\n .string()\n .optional()\n .describe(\"Box fill (any CSS color). Omit to auto-derive a distinct color from the tile name.\"),\n /** Optional caption shown under the name (e.g. \"16:9\") — purely cosmetic. */\n size: z.string().optional().describe(\"Optional caption shown under the name (e.g. 16:9) — purely cosmetic.\"),\n /** This faux tile's aspect ratio (width / height): 1.78 = 16:9 (short), 0.56 = 9:16 (tall), 1 =\n * square. Omit to use the wall's `tileAspect` default. Real tiles use their media's own aspect. */\n aspect: z\n .number()\n .positive()\n .optional()\n .describe(\"Faux tile aspect (w/h): 1.78 = 16:9, 0.56 = 9:16, 1 = square. Omit to use the wall's tileAspect.\"),\n })\n .strict();\n\n/**\n * The \"wall\" scene: a marquee of media tiles, each column a self-contained unit (its `tiles` + its\n * own motion). Two systems built from the SAME pulse primitive: `pan` (System 1, whole-wall X) and\n * the per-column Y motion (System 2). A track's travel = `loops` continuous periods + the sum of its\n * `pulses`; the total is rounded UP to a whole number of periods (the remainder folds into the\n * continuous scroll), so every track lands back on its start at the clip end → a seamless loop.\n */\nexport const wallSceneOptionsSchema = z\n .object({\n /** The columns (≥3) — each its own tiles + motion. Count = array length (fewer = bigger tiles). */\n columns: z.array(wallColumnSchema).min(3),\n /** Gap between columns and between their tile contents (px). */\n gap: z.number().nonnegative().default(16),\n /** Default/fallback tile aspect (width / height). Tiles fit the column width and take their OWN\n * height from their media's aspect (16:9 → short, 9:16 → tall); this is only used for faux\n * (`test`) tiles that don't set their own `aspect`. 1.6 = 16:10 landscape, <1 = portrait. */\n tileAspect: z.number().positive().default(1.6),\n /** Tile corner radius (px). */\n cornerRadius: z.number().nonnegative().default(12),\n /** Backdrop shown in the gap gutters and behind tiles. Defaults to the scene's `background`. */\n background: z.string().optional(),\n /** System 1 — the X pan. */\n pan: wallPanSchema.default({}),\n /** Default continuous whole-clip loops for columns that omit their own `loops` (0 = static unless\n * a pulse moves it; one pulse then rounds the total up to a single loop). */\n loops: z.number().nonnegative().default(0),\n /** Default pulses for columns that omit their own `pulses` (the uniform wall-level motion). */\n pulses: z.array(wallPulseSchema).default([]),\n /** Preview mode: render every tile as a flat labeled color box (see `testTiles`) instead of the\n * real assets, so you can dial in layout + motion instantly — no producers run. */\n test: z.boolean().default(false),\n /** Per-tile faux appearance for `test` mode, keyed by tile name. Tiles not listed get an\n * auto-derived color and their name as the label. */\n testTiles: z.record(z.string(), fauxTileSchema).default({}),\n })\n .strict();\n\n/**\n * The \"palette-reel\" scene's wire format — produced by the `palette-reel` *generator* (its friendly\n * options are the real authoring surface; it precomputes the per-color display strings since the\n * scene can't import the color math), validated here so generator and scene can't drift apart. The\n * colors start as name-only slivers; one at a time a band expands to reveal its `details`, holds, and\n * collapses, looping seamlessly. Defaults mirror the friendly schema so a direct `scene:\n * \"palette-reel\"` config still renders.\n */\nconst reelItemSchema = z\n .object({\n /** Color display name (already cased per `uppercase`). */\n name: z.string(),\n /** Swatch background hex (`#RRGGBB`). */\n hex: z.string(),\n /** Contrast-picked text color for this band. */\n textColor: z.string(),\n /** Preformatted detail lines revealed on expand (hex / oklch / rgb …). */\n details: z.array(z.string()),\n })\n .strict();\n\nconst paletteReelSceneOptionsSchema = z\n .object({\n items: z.array(reelItemSchema).min(1),\n orientation: z.enum([\"rows\", \"columns\"]).default(\"rows\"),\n holdSeconds: z.number().positive().default(2),\n transitionSeconds: z.number().positive().default(0.7),\n bounce: z.boolean().default(true),\n easing: z.enum([\"linear\", \"ease-in\", \"ease-out\", \"ease-in-out\"]).default(\"ease-in-out\"),\n grownFlex: z.number().min(1).default(12),\n minCrossPx: z.number().nonnegative().default(0),\n nameAlwaysVisible: z.boolean().default(true),\n fontWeight: z.number().int().min(1).max(1000).default(700),\n fontSize: z.number().positive().optional(),\n detailFontScale: z.number().positive().default(0.62),\n gap: z.number().nonnegative().default(0),\n cornerRadius: z.number().nonnegative().default(0),\n })\n .strict();\n\n/** Scene id → its sceneOptions validator. The single source of truth for known scenes. */\nexport const SCENE_OPTION_SCHEMAS = {\n specimen: specimenSceneOptionsSchema,\n wall: wallSceneOptionsSchema,\n \"palette-reel\": paletteReelSceneOptionsSchema,\n} as const;\n\nexport type SceneId = keyof typeof SCENE_OPTION_SCHEMAS;\n\n// ---------------------------------------------------------------------------\n// Author-facing input types (editor autocomplete). Kept in sync with the zod\n// schemas by the Exact<> guards at the bottom — a drift is a compile error.\n// ---------------------------------------------------------------------------\n\n/** The wall's easing curves. */\nexport type WallEasing = z.infer<typeof wallEasingEnum>;\n\n/** One eased move — the uniform motion primitive (columns, wall default, and pan all use it). */\nexport interface WallPulseInput {\n /** When the pulse starts, as a fraction of the clip (0..1). */\n at: number;\n /** How long the move takes, as a fraction of the clip (0..1). If `at + duration > 1`, the start\n * shifts back so the move ends at the loop point. */\n duration: number;\n /** How far it travels, in periods (1 = one full tile-set / one wrap). Usually 0..1. */\n distance: number;\n /** Easing of the move's ramp. Default \"ease-in-out\". */\n easing?: WallEasing;\n}\n\nexport interface WallPanInput {\n /** Pan direction. Default \"left\". */\n direction?: \"left\" | \"right\";\n /** Continuous whole-clip horizontal loops (0 = no pan unless `pulses` move it). Default 0. */\n loops?: number;\n /** Pulses added on top of the base loops. Default none. */\n pulses?: WallPulseInput[];\n}\n\n/** One column of the wall: its tiles (assets by name) + its own optional Y motion. */\nexport interface WallColumnInput {\n /** Assets stacked in this column, by name (cycled to fill the height). At least one. */\n tiles: string[];\n /** Constant start-position shift, 0..1 of a tile-set — de-aligns columns with similar content. Default 0. */\n stagger?: number;\n /** Scroll direction. Defaults to \"down\". */\n direction?: \"up\" | \"down\";\n /** Continuous whole-clip loops for this column. Omit to inherit the wall-level `loops`. */\n loops?: number;\n /** This column's pulses. Omit to inherit the wall-level `pulses`. */\n pulses?: WallPulseInput[];\n}\n\n/** A faux tile for `test` preview mode — a flat color box labeled with its name. */\nexport interface FauxTileInput {\n /** Box fill (any CSS color). Omit to auto-derive a distinct color from the tile name. */\n color?: string;\n /** Optional caption shown under the name (e.g. \"16:9\") — purely cosmetic. */\n size?: string;\n /** This faux tile's aspect ratio (width / height): 1.78 = 16:9 (short), 0.56 = 9:16 (tall), 1 =\n * square. Omit to use the wall's `tileAspect` default. Real tiles use their media's own aspect. */\n aspect?: number;\n}\n\nexport interface WallSceneOptionsInput {\n /** The columns (≥3), each its own tiles + motion. Count = array length (fewer = bigger tiles). */\n columns: WallColumnInput[];\n /** Gap between columns and between their tile contents (px). Default 16. */\n gap?: number;\n /** Default/fallback tile aspect (width / height). Tiles fit the column width and take their OWN\n * height from their media's aspect (16:9 → short, 9:16 → tall); this is only used for faux\n * (`test`) tiles that don't set their own `aspect`. 1.6 = 16:10 landscape, <1 = portrait. Default 1.6. */\n tileAspect?: number;\n /** Tile corner radius (px). Default 12. */\n cornerRadius?: number;\n /** Backdrop shown in the gap gutters and behind tiles. Defaults to the scene's `background`. */\n background?: string;\n /** System 1 — the whole-wall X pan. Default: no pan. */\n pan?: WallPanInput;\n /** Default continuous whole-clip loops for columns that omit their own `loops`. Default 0 (static\n * unless a pulse moves it — a single pulse then rounds up to one loop). */\n loops?: number;\n /** Default pulses for columns that omit their own `pulses` (the uniform wall-level motion). Default none. */\n pulses?: WallPulseInput[];\n /** Preview mode: render faux labeled color boxes instead of real assets (no producers run). Default false. */\n test?: boolean;\n /** Per-tile faux appearance for `test` mode, keyed by tile name. Default {} (auto colors + names). */\n testTiles?: Record<string, FauxTileInput>;\n}\n\n/** One precomputed color in a palette-reel (the generator builds these from the friendly options). */\nexport interface ReelItem {\n /** Color display name (already cased per `uppercase`). */\n name: string;\n /** Swatch background hex (`#RRGGBB`). */\n hex: string;\n /** Contrast-picked text color for this band. */\n textColor: string;\n /** Preformatted detail lines revealed on expand (hex / oklch / rgb …). */\n details: string[];\n}\n\nexport interface PaletteReelSceneOptionsInput {\n /** Precomputed colors to reveal (at least one). */\n items: ReelItem[];\n /** Sliver arrangement. Default \"rows\". */\n orientation?: \"rows\" | \"columns\";\n /** How long each color stays fully open before handing off (s). Default 2. */\n holdSeconds?: number;\n /** Crossfade length from one open color to the next (s). Default 0.7. */\n transitionSeconds?: number;\n /** Ping-pong the sweep so every handoff is between neighbours (no last→first pinch at the seam). Default true. */\n bounce?: boolean;\n /** Easing applied to the crossfade. Default \"ease-in-out\". */\n easing?: \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\";\n /** How many times a sliver's share a fully-open band takes. Default 12. */\n grownFlex?: number;\n /** Minimum cross-size of a sliver in px. Default 0 (derive from the frame size). */\n minCrossPx?: number;\n /** Keep the name visible even in a collapsed sliver. Default true. */\n nameAlwaysVisible?: boolean;\n /** Label font weight. Default 700. */\n fontWeight?: number;\n /** Name font size in px (omit to derive from the frame size). */\n fontSize?: number;\n /** Detail-line font size as a fraction of the name size. Default 0.62. */\n detailFontScale?: number;\n /** Gap between bands (px). Default 0 (bands abut). */\n gap?: number;\n /** Band corner radius (px). Default 0 (square). */\n cornerRadius?: number;\n}\n\n// Compile-time guards: the documented authoring types must stay in sync with the schemas.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _wallInSync: Exact<WallSceneOptionsInput, z.input<typeof wallSceneOptionsSchema>> = true;\nconst _paletteReelInSync: Exact<\n PaletteReelSceneOptionsInput,\n z.input<typeof paletteReelSceneOptionsSchema>\n> = true;\nvoid _wallInSync;\nvoid _paletteReelInSync;\n","import { z } from \"zod\";\n\n/**\n * A \"pulse\" is one beat of the specimen's animation: a named span of time during which some\n * fraction of the glyph cells change letter and/or color. An empty pulse (no changes) is a hold.\n * Compose a sequence of pulses to author the whole clip — varying each one's length, change\n * fractions, and pacing gives every beat its own feel. The clip length is the sum of the durations.\n */\nexport const pulseSchema = z\n .object({\n /** Human label for the beat, e.g. \"color sweep\" — purely to keep the config readable. */\n name: z\n .string()\n .default(\"\")\n .describe('Human label for the beat, e.g. \"color sweep\" — purely to keep the config readable.'),\n /** Length of this beat, in seconds. */\n duration: z.number().positive().describe(\"Length of this beat, in seconds.\"),\n /** Fraction of cells whose glyph changes during this beat (0..1; 1 = every cell once). */\n chars: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Fraction of cells whose glyph changes during this beat (0..1; 1 = every cell once; 0 = a hold). Default 0.\"),\n /** Fraction of cells whose color changes during this beat (0..1; 1 = every cell once). */\n colors: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Fraction of cells whose color changes during this beat (0..1; 1 = every cell once). Default 0.\"),\n /**\n * Target color for this beat's color changes. When set, every color change in the beat goes to\n * this exact token (a deliberate sweep) instead of a weighted-random pick. Set `colors: 1` with\n * `pacing: \"even\"` to wash the whole specimen to one color evenly. Omit for the default\n * scattered, weighted-random recoloring.\n */\n color: z\n .enum([\"foreground\", \"muted\", \"accent\"])\n .optional()\n .describe(\"Target color token for this beat's color changes (a deliberate sweep); omit for the default weighted-random recoloring.\"),\n /**\n * How the changes are distributed in time across the beat — like a CSS easing curve:\n * \"linear\"/\"even\" = uniform, \"ease-in\" = front-loaded, \"ease-out\" = back-loaded,\n * \"ease-in-out\" = bunched at both ends, \"random\" = scattered.\n */\n pacing: z\n .enum([\"even\", \"linear\", \"ease-in\", \"ease-out\", \"ease-in-out\", \"random\"])\n .default(\"even\")\n .describe(\"How changes are distributed in time across the beat (CSS-easing-like): even/linear, ease-in, ease-out, ease-in-out, random. Default even.\"),\n })\n .strict();\n\nexport type Pulse = z.infer<typeof pulseSchema>;\n\n/** One \"pulse\" (beat) of the animation storyboard. */\nexport interface PulseInput {\n /** Human label for the beat, e.g. \"color sweep\" — purely to keep the config readable. */\n name?: string;\n /** Length of this beat, in seconds. */\n duration: number;\n /** Fraction of cells whose glyph changes during this beat (0..1; 1 = every cell once; 0 = a hold). */\n chars?: number;\n /** Fraction of cells whose color changes during this beat (0..1; 1 = every cell once). */\n colors?: number;\n /**\n * Target color for this beat's color changes. When set, every color change goes to this exact\n * token (a deliberate sweep) rather than a weighted-random pick. Set `colors: 1` with\n * `pacing: \"even\"` to evenly wash the whole specimen to one color. Omit for the default\n * scattered, weighted-random recoloring.\n */\n color?: \"foreground\" | \"muted\" | \"accent\";\n /**\n * How the changes are distributed in time across the beat — like a CSS easing curve:\n * \"linear\"/\"even\" = uniform, \"ease-in\" = front-loaded, \"ease-out\" = back-loaded,\n * \"ease-in-out\" = bunched at both ends, \"random\" = scattered.\n */\n pacing?: \"even\" | \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\" | \"random\";\n}\n\n/**\n * Relative likelihood each color token is chosen on a (non-targeted) color change. Higher = more\n * frequent. The default keeps foreground/muted common and accent a rare pop (2 / 2 / 1). Set any to\n * 0 to exclude that token from random recoloring (an explicit pulse `color` can still target it).\n */\nexport interface SpecimenColorWeightsInput {\n foreground?: number;\n muted?: number;\n accent?: number;\n}\n\n/** The specimen's color palette (any CSS colors). Override any subset. */\nexport interface SpecimenColorsInput {\n /** Backdrop behind the glyphs. */\n background?: string;\n /** Primary glyph color — the resting majority. */\n foreground?: string;\n /** Muted/secondary glyph color. */\n muted?: string;\n /** Accent color for occasional pops; defaults to `background` (accent glyphs blend in) if unset. */\n accent?: string;\n /** Color of the font-name label (bottom corner); defaults to `foreground` if unset. */\n label?: string;\n}\n\n/** A named option preset. The keys here are the selectable `template` values. */\nexport type SpecimenTemplate = \"demo\" | \"sweep\";\n\n/**\n * Author-facing options for the `specimen` generator — a looping type-specimen video. Only `font`\n * is required; everything else has a sensible default.\n */\nexport interface SpecimenOptionsInput {\n /** Font file to showcase (path relative to the working dir, or absolute). Required. */\n font: string;\n /**\n * Load a named option preset; your explicit options below still override what it sets. Options:\n * - `\"demo\"` — labeled walkthrough of every pulse behavior (each easing curve, a color sweep, a\n * mingle) with demo mode on; runs once, no mirror. Good for seeing what each setting does.\n * - `\"sweep\"` — seamless-looping showcase of the even per-character color sweeps (muted → accent\n * → foreground) on a dark palette chosen so the accent reads.\n */\n template?: SpecimenTemplate;\n /** Display name shown bottom-left (e.g. \"ABC Oracle\"). Default none. */\n name?: string;\n /** Demo mode: overlay the active pulse's name bottom-right, to see which beat is playing. Default false. */\n demo?: boolean;\n /** Output frames per second. Default 30. */\n fps?: number;\n /** Clip length in seconds. Defaults to the (mirrored) sum of the pulse durations; set to override. */\n durationSeconds?: number;\n /** Output frame width in px. Default 1920. */\n width?: number;\n /** Output frame height in px. Default 1080. */\n height?: number;\n /** Render scale (1 = 1:1; higher = crisper capture, downscaled into the video). Default 1. */\n deviceScaleFactor?: number;\n /** Glyph weight on the variable-font axis, 1–1000. Default 820. */\n weight?: number;\n /** Number of glyph rows. The glyph size is derived so the rows fill the top 80% of the frame. Default 3. */\n lines?: number;\n /** Line-height of the glyph block. Default 0.78 (tight, cap-height-hugging). */\n leading?: number;\n /** Glyphs to exclude from the showcase, e.g. \"QXZ\" (case-insensitive). Default none. */\n blacklist?: string;\n /** Override the glyph pool the specimen draws from (≥2 distinct characters). Default A–Z 0–9 + symbols. */\n characterPool?: string;\n /** Schedule seed — same seed ⇒ identical animation. Change for a different (still deterministic) take. Default 1. */\n seed?: number;\n /** Color tokens the glyphs cycle through. Override any subset. Default: light-grey palette. */\n colors?: SpecimenColorsInput;\n /** Relative likelihood of each color token on a random (non-targeted) color change. Default 2 / 2 / 1. */\n colorWeights?: SpecimenColorWeightsInput;\n /** The animation storyboard: an ordered sequence of pulses (beats). Default: a lively built-in storyboard. */\n pulses?: PulseInput[];\n /** Multiply every pulse's glyph-change fraction (1 = baseline, 2 = twice as busy, 0 = none). Default 1. */\n characterIntensity?: number;\n /** Multiply every pulse's color-change fraction (1 = baseline, 2 = twice as busy, 0 = none). Default 1. */\n colorIntensity?: number;\n /**\n * Max fraction a line's total width may drift as its glyphs change. Default 0.05. Glyph swaps are\n * width-compensated to stay within this, so the left-aligned right edge barely moves.\n */\n maxLineDrift?: number;\n /**\n * Mirror the pulses (play them out and back) so the clip ends on its opening frame and loops\n * seamlessly. Doubles the clip length. Set false for a one-shot that ends on the last state. Default true.\n */\n mirror?: boolean;\n /** x264 quality, 0–51 (lower = better quality / larger file). Default 18. */\n crf?: number;\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName?: string;\n}\n\nconst P = (name: string, duration: number, chars = 0, colors = 0): Pulse => ({\n name,\n duration,\n chars,\n colors,\n pacing: \"even\",\n});\n\n/**\n * A lively default storyboard describing the *outward* half of the loop (sums to ~10s). With\n * mirroring on (the default), the clip plays this out and back for a seamless ~20s loop. The\n * `chars`/`colors` numbers are fractions of the wall (1 = every glyph). Override `pulses` to compose\n * your own.\n */\nconst DEFAULT_PULSES: Pulse[] = [\n P(\"intro hold\", 0.8),\n P(\"first letters\", 0.8, 0.15, 0),\n P(\"settle\", 1.5),\n P(\"ripple\", 1, 0.08, 0.04),\n P(\"color sweep\", 1.2, 0, 0.13),\n P(\"rest\", 1.2),\n P(\"quick burst\", 0.6, 0.18, 0),\n P(\"drift\", 1.2, 0, 0.08),\n P(\"finale\", 1.2, 0.18, 0.13),\n P(\"outro hold\", 0.5),\n];\n\n/**\n * The \"demo\" template: turns on demo mode and walks through each pulse behavior one at a time —\n * every easing curve, a color sweep, and a mingle — each held for 5s with a 2s no-change rest\n * between, and clearly named, so (with demo mode) you can see exactly what each does. Runs once\n * forward (no mirror) for a focused ~47s walkthrough.\n */\nconst DEMO_PULSES: PulseInput[] = [\n { name: \"linear\", duration: 5, chars: 0.5, pacing: \"linear\" },\n { name: \"hold\", duration: 2 },\n { name: \"ease-in\", duration: 5, chars: 0.5, pacing: \"ease-in\" },\n { name: \"hold\", duration: 2 },\n { name: \"ease-out\", duration: 5, chars: 0.5, pacing: \"ease-out\" },\n { name: \"hold\", duration: 2 },\n { name: \"ease-in-out\", duration: 5, chars: 0.5, pacing: \"ease-in-out\" },\n { name: \"hold\", duration: 2 },\n { name: \"random\", duration: 5, chars: 0.5, pacing: \"random\" },\n { name: \"hold\", duration: 2 },\n // Even, per-character color sweeps: each washes every glyph to one token, evenly across the beat.\n { name: \"sweep → muted\", duration: 4, colors: 1, color: \"muted\", pacing: \"even\" },\n { name: \"sweep → accent\", duration: 4, colors: 1, color: \"accent\", pacing: \"even\" },\n { name: \"sweep → foreground\", duration: 4, colors: 1, color: \"foreground\", pacing: \"even\" },\n { name: \"hold\", duration: 2 },\n { name: \"weighted recolor\", duration: 5, colors: 0.6 },\n { name: \"hold\", duration: 2 },\n { name: \"mingle\", duration: 5, chars: 0.3, colors: 0.3 },\n];\n\n/**\n * The \"sweep\" template: a clean, seamless-looping showcase built around the even per-character color\n * sweeps. Every glyph washes evenly from one token to the next (muted → accent → foreground), with a\n * touch of glyph drift for life, on a dark palette chosen so the accent reads clearly. Mirrored, so\n * it loops without a seam. Override `colors`, `lines`, or `pulses` to retheme it.\n */\nconst SWEEP_COLORS: SpecimenColorsInput = {\n background: \"#0b0b0f\",\n foreground: \"#f4f4f5\",\n muted: \"#6b7280\",\n accent: \"#7c9cff\",\n};\n\n// `colors: 1` per sweep lands on every glyph exactly once (one full, clump-free wash). Outward half\n// sums to ~10.6s → ~21s mirrored loop.\nconst SWEEP_PULSES: PulseInput[] = [\n { name: \"hold\", duration: 0.8 },\n { name: \"to muted\", duration: 2.2, colors: 1, color: \"muted\", pacing: \"ease-in-out\" },\n { name: \"settle\", duration: 0.6 },\n { name: \"to accent\", duration: 2.2, colors: 1, color: \"accent\", pacing: \"ease-in-out\" },\n { name: \"settle\", duration: 0.6 },\n { name: \"to foreground\", duration: 2.2, colors: 1, color: \"foreground\", pacing: \"ease-in-out\" },\n { name: \"glyph drift\", duration: 1.4, chars: 0.5, pacing: \"even\" },\n { name: \"hold\", duration: 0.6 },\n];\n\n/** Named option presets. Selected via the `template` option; explicit options override them. */\nconst SPECIMEN_TEMPLATES: Record<SpecimenTemplate, Partial<SpecimenOptionsInput>> = {\n demo: { demo: true, mirror: false, lines: 4, pulses: DEMO_PULSES },\n sweep: {\n mirror: true,\n lines: 4,\n colors: SWEEP_COLORS,\n pulses: SWEEP_PULSES,\n },\n};\n\n/** Merge a selected template underneath the user's explicit options (which win). */\nfunction applyTemplate(raw: unknown): unknown {\n if (!raw || typeof raw !== \"object\") return raw;\n const r = raw as Record<string, unknown>;\n const tmpl =\n typeof r.template === \"string\"\n ? SPECIMEN_TEMPLATES[r.template as SpecimenTemplate]\n : undefined;\n return tmpl ? { ...tmpl, ...r } : raw;\n}\n\n/**\n * A type-specimen video: point it at a font file and give it a name — the tool renders a clip\n * (1920×1080 by default) of the typeface set as a fixed number of left-aligned `lines` (sized to\n * fill the top 80% of the frame) whose glyphs and colors change over a composed sequence of\n * \"pulses\" (mirrored into a seamless loop by default), and captures it. Everything else has\n * sensible defaults.\n */\nconst specimenObjectSchema = z\n .object({\n font: z\n .string()\n .min(1)\n .describe(\"Font file to showcase (path relative to the working dir, or absolute). Required.\"),\n template: z\n .enum([\"demo\", \"sweep\"])\n .optional()\n .describe(\"Load a named option preset (demo or sweep); your explicit options still override what it sets.\"),\n name: z\n .string()\n .default(\"\")\n .describe('Display name shown bottom-left (e.g. \"ABC Oracle\"). Default none.'),\n demo: z\n .boolean()\n .default(false)\n .describe(\"Demo mode: overlay the active pulse's name bottom-right, to see which beat is playing. Default false.\"),\n fps: z\n .number()\n .int()\n .positive()\n .max(120)\n .default(30)\n .describe(\"Output frames per second. Default 30.\"),\n durationSeconds: z\n .number()\n .positive()\n .optional()\n .describe(\"Clip length in seconds. Defaults to the (mirrored) sum of the pulse durations; set to override.\"),\n width: z\n .number()\n .int()\n .positive()\n .default(1920)\n .describe(\"Output frame width in px. Default 1920.\"),\n height: z\n .number()\n .int()\n .positive()\n .default(1080)\n .describe(\"Output frame height in px. Default 1080.\"),\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(1)\n .describe(\"Render scale (1 = 1:1; higher = crisper capture, downscaled into the video). Default 1.\"),\n weight: z\n .number()\n .int()\n .min(1)\n .max(1000)\n .default(820)\n .describe(\"Glyph weight on the variable-font axis, 1–1000. Default 820.\"),\n lines: z\n .number()\n .int()\n .min(1)\n .max(40)\n .default(3)\n .describe(\"Number of glyph rows; glyph size is derived so rows fill the top 80% of the frame. Default 3.\"),\n leading: z\n .number()\n .positive()\n .default(0.78)\n .describe(\"Line-height of the glyph block. Default 0.78 (tight, cap-height-hugging).\"),\n blacklist: z\n .string()\n .default(\"\")\n .describe('Glyphs to exclude from the showcase, e.g. \"QXZ\" (case-insensitive). Default none.'),\n characterPool: z\n .string()\n .refine((s) => new Set([...s.trim()]).size >= 2, \"characterPool needs ≥2 distinct characters\")\n .optional()\n .describe(\"Override the glyph pool the specimen draws from (≥2 distinct characters). Default A–Z 0–9 + symbols.\"),\n seed: z\n .number()\n .int()\n .default(1)\n .describe(\"Schedule seed — same seed ⇒ identical animation. Change for a different deterministic take. Default 1.\"),\n colors: z\n .object({\n background: z\n .string()\n .default(\"#eceef1\")\n .describe(\"Backdrop behind the glyphs.\"),\n foreground: z\n .string()\n .default(\"#16181d\")\n .describe(\"Primary glyph color — the resting majority.\"),\n muted: z\n .string()\n .default(\"#a7adb6\")\n .describe(\"Muted/secondary glyph color.\"),\n accent: z\n .string()\n .optional()\n .describe(\"Accent color for occasional pops; defaults to `background` (accent glyphs blend in) if unset.\"), // defaults to `background` at render (accent glyphs blend in)\n label: z\n .string()\n .optional()\n .describe(\"Color of the font-name label (bottom corner); defaults to `foreground` if unset.\"), // defaults to `foreground` at render\n })\n .default({})\n .describe(\"Color tokens the glyphs cycle through (any CSS colors). Override any subset. Default: light-grey palette.\"),\n colorWeights: z\n .object({\n foreground: z\n .number()\n .nonnegative()\n .default(2)\n .describe(\"Relative likelihood of the foreground token on a random color change. Default 2.\"),\n muted: z\n .number()\n .nonnegative()\n .default(2)\n .describe(\"Relative likelihood of the muted token on a random color change. Default 2.\"),\n accent: z\n .number()\n .nonnegative()\n .default(1)\n .describe(\"Relative likelihood of the accent token on a random color change. Default 1.\"),\n })\n .default({})\n .describe(\"Relative likelihood of each color token on a random (non-targeted) color change. Default 2 / 2 / 1.\"),\n pulses: z\n .array(pulseSchema)\n .min(1)\n .default(DEFAULT_PULSES)\n .describe(\"The animation storyboard: an ordered sequence of pulses (beats). Default: a lively built-in storyboard.\"),\n characterIntensity: z\n .number()\n .nonnegative()\n .default(1)\n .describe(\"Multiply every pulse's glyph-change fraction (1 = baseline, 2 = twice as busy, 0 = none). Default 1.\"),\n colorIntensity: z\n .number()\n .nonnegative()\n .default(1)\n .describe(\"Multiply every pulse's color-change fraction (1 = baseline, 2 = twice as busy, 0 = none). Default 1.\"),\n maxLineDrift: z\n .number()\n .positive()\n .max(0.5)\n .default(0.05)\n .describe(\"Max fraction a line's total width may drift as its glyphs change; swaps are width-compensated. Default 0.05.\"),\n mirror: z\n .boolean()\n .default(true)\n .describe(\"Mirror the pulses (play out and back) for a seamless loop; doubles the clip length. Default true.\"),\n crf: z\n .number()\n .int()\n .min(0)\n .max(51)\n .default(18)\n .describe(\"x264 quality, 0–51 (lower = better quality / larger file). Default 18.\"),\n fileName: z\n .string()\n .optional()\n .describe('Output filename; defaults to \"<slug(asset name)>.mp4\".'),\n })\n .strict();\n\n/** Runtime validator: expands the selected `template`, then validates the merged options. */\nexport const specimenOptionsSchema = z.preprocess(applyTemplate, specimenObjectSchema);\n\n/** Author-facing input (documented for editor hover; the schema above validates it at run time). */\nexport type SpecimenOptions = SpecimenOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedSpecimenOptions = z.infer<typeof specimenObjectSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _specimenInputInSync: Exact<SpecimenOptionsInput, z.input<typeof specimenObjectSchema>> = true;\nvoid _specimenInputInSync;\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { copyFile, rename, stat } from \"node:fs/promises\";\nimport type { ResolvedSceneOptions } from \"@/generators/scene/options\";\nimport { SCENE_OPTION_SCHEMAS } from \"@/generators/scene/scene-options\";\nimport { startSceneServer } from \"@/scene/serve\";\nimport { recordSceneRealtime } from \"@/scene/capture-realtime\";\nimport { captureSceneFrames } from \"@/scene/capture-frames\";\nimport { autoWorkers } from \"@/media/frame-capture\";\nimport { probeVideoDimensions, transcodeToMp4 } from \"@/media/ffmpeg\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { sha256File } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nconst SCENE_ID = \"scene\";\n\n/** The built scene app shipped alongside the CLI bundle (dist/scene-app). */\nfunction sceneAppDir(): string {\n const here = path.dirname(fileURLToPath(import.meta.url)); // dist/cli\n return path.resolve(here, \"..\", \"scene-app\");\n}\n\n/**\n * Render a scene to an mp4 and record it. Shared by the `scene` generator and friendly\n * wrappers (e.g. `specimen`), which pass their own `generatorId` for the manifest.\n */\nexport async function renderScene(\n ctx: PipelineContext,\n options: ResolvedSceneOptions,\n generatorId: string = SCENE_ID,\n): Promise<{ assets: AssetRecord[] }> {\n // Validate the per-scene knobs against the selected scene's schema: a typo'd key or an unknown\n // scene id fails here with a named error instead of capturing a blank \"Unknown scene\" page.\n const sceneSchema = SCENE_OPTION_SCHEMAS[options.scene as keyof typeof SCENE_OPTION_SCHEMAS];\n if (!sceneSchema) {\n throw new Error(\n `Unknown scene \"${options.scene}\". Available: ${Object.keys(SCENE_OPTION_SCHEMAS).join(\", \")}.`,\n );\n }\n const sceneOptions = sceneSchema.parse(options.sceneOptions);\n\n const fileName = options.fileName ?? `${slugify(ctx.target.name)}.mp4`;\n const outPath = ctx.resolveOutPath(fileName);\n\n // Resolve served files (e.g. fonts) to absolute paths (relative to the working dir).\n const filePaths: Record<string, string> = {};\n for (const [name, p] of Object.entries(options.files)) {\n filePaths[name] = path.isAbsolute(p) ? p : path.resolve(process.cwd(), p);\n }\n\n const server = await startSceneServer({\n staticDir: sceneAppDir(),\n inputs: { ...ctx.resolvedInputs, ...filePaths },\n });\n\n try {\n const inputUrls: Record<string, string> = {};\n for (const slot of Object.keys(ctx.resolvedInputs)) {\n inputUrls[slot] = server.inputUrl(slot);\n }\n const fileUrls: Record<string, string> = {};\n for (const name of Object.keys(filePaths)) {\n fileUrls[name] = server.inputUrl(name);\n }\n\n const props = {\n width: options.width,\n height: options.height,\n background: options.background,\n durationSeconds: options.durationSeconds,\n fps: options.fps,\n inputs: inputUrls,\n files: fileUrls,\n options: sceneOptions, // validated + defaulted against the scene's schema\n };\n const sceneUrl = new URL(`${server.origin}/`);\n sceneUrl.searchParams.set(\"scene\", options.scene);\n sceneUrl.searchParams.set(\"props\", JSON.stringify(props));\n\n await ensureDir(path.dirname(outPath));\n const composedTmp = path.join(ctx.tmpDir, `${slugify(ctx.target.name)}-scene.mp4`);\n\n const draft = ctx.quality === \"draft\";\n const preset = draft ? \"ultrafast\" : \"medium\";\n if (options.capture === \"frames\") {\n const workers = options.workers ?? autoWorkers();\n ctx.logger.info(\n `rendering scene \"${options.scene}\" (frame-stepped, ${workers} worker(s))`,\n );\n await captureSceneFrames({\n browser: ctx.browser,\n url: sceneUrl.toString(),\n width: options.width,\n height: options.height,\n deviceScaleFactor: options.deviceScaleFactor,\n fps: options.fps,\n durationSeconds: options.durationSeconds,\n crf: options.crf,\n outPath: composedTmp,\n preset,\n // Draft always uses fast jpeg intermediates; final uses the configured format (png = lossless).\n frameFormat: draft ? \"jpeg\" : options.frameFormat,\n jpegQuality: draft ? 70 : 90,\n workers,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n onProgress: ctx.progress,\n signal: ctx.signal,\n });\n } else {\n ctx.logger.info(`rendering scene \"${options.scene}\" (realtime)`);\n const recording = await recordSceneRealtime({\n browser: ctx.browser,\n url: sceneUrl.toString(),\n width: options.width,\n height: options.height,\n deviceScaleFactor: options.deviceScaleFactor,\n durationSeconds: options.durationSeconds,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n });\n await transcodeToMp4({\n inputPath: recording.path,\n outputPath: composedTmp,\n fps: options.fps,\n width: options.width,\n height: options.height,\n crf: options.crf,\n preset,\n // Trim the blank navigation/readiness lead so the clip opens on the first painted frame,\n // then clamp to the intended length so the output matches the manifest's durationMs.\n startOffsetSeconds: recording.leadSeconds,\n durationSeconds: options.durationSeconds,\n logger: ctx.logger,\n signal: ctx.signal,\n });\n }\n\n try {\n await rename(composedTmp, outPath);\n } catch {\n await copyFile(composedTmp, outPath);\n }\n\n const [dims, stats, contentHash] = await Promise.all([\n probeVideoDimensions(outPath),\n stat(outPath),\n sha256File(outPath),\n ]);\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: generatorId,\n sourceUrl: ctx.target.url ?? `scene:${options.scene}`,\n file: ctx.toManifestPath(outPath),\n format: \"mp4\",\n width: dims?.width ?? options.width,\n height: dims?.height ?? options.height,\n durationMs: Math.round(options.durationSeconds * 1000),\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n } finally {\n await server.close();\n }\n}\n\n// Note: there is no public `scene` generator — the friendly `wall`, `specimen`, and `palette-reel`\n// generators render through `renderScene` (the shared engine) above.\n","import http from \"node:http\";\nimport path from \"node:path\";\nimport { createReadStream } from \"node:fs\";\nimport { stat } from \"node:fs/promises\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nconst MIME: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript\",\n \".mjs\": \"text/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".webp\": \"image/webp\",\n \".ico\": \"image/x-icon\",\n \".mp4\": \"video/mp4\",\n \".webm\": \"video/webm\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".otf\": \"font/otf\",\n \".map\": \"application/json\",\n};\n\nconst mimeFor = (p: string): string =>\n MIME[path.extname(p).toLowerCase()] ?? \"application/octet-stream\";\n\n/**\n * Parse a single HTTP byte-range header against a known size. Handles `bytes=start-end`,\n * `bytes=start-` (open-ended), and `bytes=-N` (suffix: the last N bytes — which Chromium's media\n * stack does issue). Returns null for absent/malformed/unsatisfiable/multi ranges, in which case the\n * caller should serve the full body (200) rather than mis-claim a 206. Pure — unit-tested.\n */\nexport function parseByteRange(\n header: string | undefined,\n size: number,\n): { start: number; end: number } | null {\n if (!header) return null;\n const m = /^bytes=(\\d*)-(\\d*)$/.exec(header.trim());\n if (!m) return null; // not a single bytes range (e.g. multi-range) → caller serves full\n const [, startRaw, endRaw] = m;\n let start: number;\n let end: number;\n if (startRaw === \"\") {\n if (endRaw === \"\") return null;\n const n = Number(endRaw);\n if (!(n > 0)) return null;\n start = Math.max(0, size - n);\n end = size - 1;\n } else {\n start = Number(startRaw);\n end = endRaw === \"\" ? size - 1 : Number(endRaw);\n }\n if (!Number.isFinite(start) || !Number.isFinite(end)) return null;\n end = Math.min(end, size - 1);\n if (start < 0 || start > end || start >= size) return null;\n return { start, end };\n}\n\nexport interface SceneServer {\n origin: string;\n /** Absolute URL of a served input asset by slot name. */\n inputUrl(slot: string): string;\n close(): Promise<void>;\n}\n\nexport interface SceneServerArgs {\n /** Directory of the built scene app (served at \"/\"). */\n staticDir: string;\n /** Slot name → absolute input file path, served under /_inputs/. */\n inputs: Record<string, string>;\n}\n\n/**\n * Ephemeral static server for one scene render: serves the built scene app plus this asset's\n * input files (with HTTP range support so the scene's <video> can seek). No writes anywhere.\n */\nexport async function startSceneServer(args: SceneServerArgs): Promise<SceneServer> {\n const staticRoot = path.resolve(args.staticDir);\n const inputByUrl = new Map<string, string>();\n const slotToUrl = new Map<string, string>();\n for (const [slot, file] of Object.entries(args.inputs)) {\n const ext = path.extname(file) || \".mp4\";\n const url = `/_inputs/${slot}${ext}`;\n inputByUrl.set(url, file);\n slotToUrl.set(slot, url);\n }\n\n const serveFile = async (\n file: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<void> => {\n let info;\n try {\n info = await stat(file);\n } catch {\n res.statusCode = 404;\n res.end();\n return;\n }\n if (info.isDirectory()) return serveFile(path.join(file, \"index.html\"), req, res);\n\n const type = mimeFor(file);\n const rangeable = type.startsWith(\"video/\");\n if (rangeable) res.setHeader(\"Accept-Ranges\", \"bytes\");\n\n const parsed = rangeable ? parseByteRange(req.headers.range, info.size) : null;\n if (parsed) {\n res.writeHead(206, {\n \"Content-Type\": type,\n \"Content-Range\": `bytes ${parsed.start}-${parsed.end}/${info.size}`,\n \"Content-Length\": parsed.end - parsed.start + 1,\n });\n createReadStream(file, { start: parsed.start, end: parsed.end }).pipe(res);\n } else {\n // No range, or malformed/unsatisfiable — serve the whole body rather than mis-claim a 206.\n res.writeHead(200, { \"Content-Type\": type, \"Content-Length\": info.size });\n createReadStream(file).pipe(res);\n }\n };\n\n const handle = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n const urlPath = decodeURIComponent((req.url ?? \"/\").split(\"?\")[0] ?? \"/\");\n\n const inputFile = inputByUrl.get(urlPath);\n if (inputFile) return serveFile(inputFile, req, res);\n\n const rel = urlPath === \"/\" ? \"index.html\" : urlPath.replace(/^\\/+/, \"\");\n const filePath = path.resolve(staticRoot, rel);\n if (filePath !== staticRoot && !filePath.startsWith(staticRoot + path.sep)) {\n res.statusCode = 403;\n res.end();\n return;\n }\n try {\n await stat(filePath);\n return serveFile(filePath, req, res);\n } catch {\n // SPA fallback so the scene app always loads.\n return serveFile(path.join(staticRoot, \"index.html\"), req, res);\n }\n };\n\n const server = http.createServer((req, res) => {\n void handle(req, res).catch(() => {\n res.statusCode = 500;\n res.end();\n });\n });\n\n await new Promise<void>((resolve) => server.listen(0, \"127.0.0.1\", () => resolve()));\n const addr = server.address();\n const port = typeof addr === \"object\" && addr ? addr.port : 0;\n const origin = `http://127.0.0.1:${port}`;\n\n return {\n origin,\n inputUrl: (slot) => {\n const rel = slotToUrl.get(slot);\n if (!rel) throw new Error(`Unknown scene input slot \"${slot}\".`); // don't silently serve the SPA\n return origin + rel;\n },\n close: () => new Promise<void>((resolve) => server.close(() => resolve())),\n };\n}\n","import path from \"node:path\";\nimport { mkdtemp } from \"node:fs/promises\";\nimport type { Browser } from \"playwright-core\";\nimport { ensureDir } from \"@/utils/fs\";\nimport type { Logger } from \"@/utils/logger\";\n\nexport interface RecordSceneArgs {\n browser: Browser;\n url: string;\n width: number;\n height: number;\n deviceScaleFactor: number;\n durationSeconds: number;\n tmpDir: string;\n logger: Logger;\n}\n\n/** The recorded .webm plus the lead time (seconds) of blank navigation/readiness before play()\n * started — Playwright records the whole context lifetime, so the caller trims this off the front. */\nexport interface RecordSceneResult {\n path: string;\n leadSeconds: number;\n}\n\n/**\n * Realtime capture: load the scene, wait until it's ready (fonts loaded and the scene has painted\n * its first frame), play it, and record the page for `durationSeconds` via Playwright's native\n * recorder. Real-time (wall-clock) — simple, but not frame-accurate; the deterministic frame-stepper\n * is the precise path. Playwright records from context creation, so we also return `leadSeconds` (the\n * blank navigation/readiness span before play()) for the caller to trim off the recording's head.\n */\nexport async function recordSceneRealtime(args: RecordSceneArgs): Promise<RecordSceneResult> {\n await ensureDir(args.tmpDir);\n const recordDir = await mkdtemp(path.join(args.tmpDir, \"scene-rec-\"));\n\n const recStart = Date.now(); // recording effectively begins at context creation\n const context = await args.browser.newContext({\n viewport: { width: args.width, height: args.height },\n deviceScaleFactor: args.deviceScaleFactor,\n // Note: Playwright records at the viewport's CSS resolution — `size` places (does not upscale)\n // the viewport into the frame, so it must equal width×height. Realtime can't supersample this\n // way; the frame-stepper is the high-fidelity path. (deviceScaleFactor still sharpens rendering.)\n recordVideo: { dir: recordDir, size: { width: args.width, height: args.height } },\n });\n const page = await context.newPage();\n const video = page.video();\n page.on(\"console\", (m) => args.logger.debug(`[scene] ${m.text()}`));\n page.on(\"pageerror\", (e) => args.logger.debug(`[scene error] ${e.message}`));\n\n let leadSeconds = 0;\n try {\n args.logger.debug(`loading scene ${args.url}`);\n await page.goto(args.url, { waitUntil: \"load\" });\n try {\n await page.waitForFunction(\n () => (globalThis as { __showcaseReady?: boolean }).__showcaseReady === true,\n undefined,\n { timeout: 30_000 },\n );\n } catch (err) {\n const diag = await page\n .evaluate(() => {\n // Runs in the browser; the node tsconfig has no DOM lib, so reach via globalThis.\n const g = globalThis as unknown as {\n __showcase?: unknown;\n document: { querySelectorAll(s: string): ArrayLike<Record<string, unknown>> };\n };\n const vids = Array.from(g.document.querySelectorAll(\"video\"));\n return {\n hasRuntime: Boolean(g.__showcase),\n videoCount: vids.length,\n videos: vids.map((v) => ({\n src: v.currentSrc || v.src,\n readyState: v.readyState,\n networkState: v.networkState,\n error: (v.error as { code?: number } | null)?.code ?? null,\n })),\n };\n })\n .catch(() => null);\n args.logger.error(`scene never became ready: ${JSON.stringify(diag)}`);\n throw err;\n }\n // Everything up to here (navigation + readiness) is blank in the recording; the scene is now\n // painted, so the head trim lands on the first real frame.\n leadSeconds = (Date.now() - recStart) / 1000;\n await page.evaluate(() =>\n (globalThis as { __showcase?: { play(): void } }).__showcase?.play(),\n );\n await page.waitForTimeout(Math.round(args.durationSeconds * 1000));\n } finally {\n await context.close(); // finalizes the webm\n }\n\n if (!video) throw new Error(\"Playwright did not record a scene video.\");\n return { path: await video.path(), leadSeconds };\n}\n","import type { Browser } from \"playwright-core\";\nimport { captureFramedVideo } from \"@/media/frame-capture\";\nimport type { Logger } from \"@/utils/logger\";\n\nexport interface FrameCaptureArgs {\n browser: Browser;\n url: string;\n width: number;\n height: number;\n deviceScaleFactor: number;\n fps: number;\n durationSeconds: number;\n crf: number;\n /** Absolute mp4 path to write. */\n outPath: string;\n /** \"ultrafast\" in draft, \"medium\" otherwise. */\n preset?: string;\n /** Intermediate frame format; \"png\" is lossless into the encoder. Default \"jpeg\". */\n frameFormat?: \"jpeg\" | \"png\";\n /** JPEG quality for intermediate frames (perf vs fidelity; ignored for png). */\n jpegQuality?: number;\n /** Parallel render workers (each its own browser context). Default 1. */\n workers?: number;\n /** Scratch dir for per-worker segments. */\n tmpDir: string;\n logger: Logger;\n /** Fractional progress (0–1) as frames complete. */\n onProgress?: (fraction: number) => void;\n /** Cancels the capture mid-flight. */\n signal?: AbortSignal;\n}\n\n/**\n * Deterministic scene capture: drives the scene-app's `window.__showcase` timeline. Each frame seeks\n * the scene to its exact time, screenshots, and pipes into ffmpeg — frame-accurate and machine-\n * independent. This is now a thin adapter over the shared {@link captureFramedVideo} harness; the only\n * scene-specific parts are the readiness wait and the per-frame `seek(t)`.\n */\nexport async function captureSceneFrames(args: FrameCaptureArgs): Promise<void> {\n await captureFramedVideo({\n browser: args.browser,\n width: args.width,\n height: args.height,\n deviceScaleFactor: args.deviceScaleFactor,\n fps: args.fps,\n durationSeconds: args.durationSeconds,\n crf: args.crf,\n outPath: args.outPath,\n preset: args.preset,\n frameFormat: args.frameFormat,\n jpegQuality: args.jpegQuality,\n workers: args.workers,\n tmpDir: args.tmpDir,\n logger: args.logger,\n onProgress: args.onProgress,\n signal: args.signal,\n prepare: async (page) => {\n await page.goto(args.url, { waitUntil: \"load\" });\n await page.waitForFunction(\n () => (globalThis as { __showcaseReady?: boolean }).__showcaseReady === true,\n undefined,\n { timeout: 30_000 },\n );\n },\n seekToFrame: async (page, t) => {\n await page.evaluate(\n (tt) =>\n (globalThis as { __showcase?: { seek(t: number): Promise<void> } }).__showcase?.seek(tt),\n t,\n );\n },\n });\n}\n","import { wallOptionsSchema, type ResolvedWallOptions } from \"@/generators/wall/options\";\nimport { renderScene } from \"@/generators/scene\";\nimport type { ResolvedSceneOptions } from \"@/generators/scene/options\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const WALL_ID = \"wall\";\n\n/** Map the friendly wall options onto the `wall` scene and render it through the scene engine. */\nasync function run(ctx: PipelineContext, o: ResolvedWallOptions): Promise<{ assets: AssetRecord[] }> {\n const sceneOptions: ResolvedSceneOptions = {\n scene: \"wall\",\n width: o.width,\n height: o.height,\n background: o.background,\n deviceScaleFactor: o.deviceScaleFactor,\n fps: o.fps,\n durationSeconds: o.durationSeconds,\n capture: o.capture,\n workers: o.workers,\n frameFormat: o.frameFormat,\n crf: o.crf,\n fileName: o.fileName,\n files: {},\n sceneOptions: {\n columns: o.columns,\n gap: o.gap,\n tileAspect: o.tileAspect,\n cornerRadius: o.cornerRadius,\n background: o.background,\n pan: o.pan,\n loops: o.loops,\n pulses: o.pulses,\n test: o.test,\n testTiles: o.testTiles,\n },\n };\n return renderScene(ctx, sceneOptions, WALL_ID);\n}\n\n/**\n * The wall declares its dependencies through its columns: every tile is an asset name, so the\n * pipeline can derive the `inputs` map (slot === asset name) instead of the author maintaining a\n * separate list. The `Wall` scene resolves a column's tiles → urls via these slots. In `test`\n * (preview) mode the tiles are faux color boxes, so there are NO real dependencies — return none\n * (column tile names need not reference real assets) and nothing has to be generated first.\n */\nfunction deriveInputs(o: ResolvedWallOptions): Record<string, string> {\n if (o.test) return {};\n const map: Record<string, string> = {};\n for (const col of o.columns) for (const name of col.tiles) map[name] = name;\n return map;\n}\n\nexport const wallGenerator: Generator<ResolvedWallOptions> = {\n id: WALL_ID,\n optionsSchema: wallOptionsSchema,\n deriveInputs,\n run,\n};\n","import {\n specimenOptionsSchema,\n type ResolvedSpecimenOptions,\n} from \"@/generators/specimen/options\";\nimport { renderScene } from \"@/generators/scene\";\nimport type { ResolvedSceneOptions } from \"@/generators/scene/options\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const SPECIMEN_ID = \"specimen\";\n\n/** Map the friendly specimen options onto the `specimen` scene and render it. */\nasync function run(\n ctx: PipelineContext,\n o: ResolvedSpecimenOptions,\n): Promise<{ assets: AssetRecord[] }> {\n // Clip length follows the composed pulses (doubled when mirrored for a seamless loop) unless\n // explicitly overridden.\n const pulsesTotal = o.pulses.reduce((sum, p) => sum + p.duration, 0);\n const durationSeconds = o.durationSeconds ?? (o.mirror ? pulsesTotal * 2 : pulsesTotal);\n\n const sceneOptions: ResolvedSceneOptions = {\n scene: \"specimen\",\n width: o.width,\n height: o.height,\n background: o.colors.background,\n deviceScaleFactor: o.deviceScaleFactor,\n fps: o.fps,\n durationSeconds,\n // The specimen's animation is a seeded, deterministic function of time (it publishes\n // __sceneSeek), so the frame-stepper renders it frame-exact: single encode, supersampling via\n // deviceScaleFactor, and a perfect loop seam — no realtime recording jitter.\n capture: \"frames\",\n frameFormat: \"jpeg\",\n crf: o.crf,\n fileName: o.fileName,\n files: { font: o.font },\n sceneOptions: {\n label: o.name,\n demo: o.demo,\n weight: o.weight,\n lines: o.lines,\n leading: o.leading,\n blacklist: o.blacklist,\n characterPool: o.characterPool,\n colors: o.colors,\n colorWeights: o.colorWeights,\n pulses: o.pulses,\n mirror: o.mirror,\n characterIntensity: o.characterIntensity,\n colorIntensity: o.colorIntensity,\n maxLineDrift: o.maxLineDrift,\n seed: o.seed,\n },\n };\n return renderScene(ctx, sceneOptions, SPECIMEN_ID);\n}\n\nexport const specimenGenerator: Generator<ResolvedSpecimenOptions> = {\n id: SPECIMEN_ID,\n optionsSchema: specimenOptionsSchema,\n // The font's CONTENT shapes the output — hash it into the cache key (edit font → regenerate).\n fileDependencies: (o) => [o.font],\n run,\n};\n","import path from \"node:path\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { imageSize } from \"image-size\";\nimport {\n paletteOptionsSchema,\n type ResolvedPaletteOptions,\n} from \"@/generators/palette/options\";\nimport { buildPaletteHtml } from \"@/generators/palette/html\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { sha256Buffer } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const PALETTE_ID = \"palette\";\n\nconst FONT_MIME: Record<string, string> = {\n \".woff2\": \"font/woff2\",\n \".woff\": \"font/woff\",\n \".ttf\": \"font/ttf\",\n \".otf\": \"font/otf\",\n};\n\n/** Read a custom font file into a base64 data URL so it can be embedded in the render (no server). */\nasync function fontDataUrl(fontFile: string): Promise<string> {\n const abs = path.isAbsolute(fontFile) ? fontFile : path.resolve(process.cwd(), fontFile);\n const mime = FONT_MIME[path.extname(abs).toLowerCase()] ?? \"font/woff2\";\n const data = await readFile(abs);\n return `data:${mime};base64,${data.toString(\"base64\")}`;\n}\n\n/** Render the palette HTML and screenshot it to a PNG (static — no animation). */\nasync function run(\n ctx: PipelineContext,\n o: ResolvedPaletteOptions,\n): Promise<{ assets: AssetRecord[] }> {\n const fileName = o.fileName ?? `${slugify(ctx.target.name)}.png`;\n const outPath = ctx.resolveOutPath(fileName);\n\n const dataUrl = o.fontFile ? await fontDataUrl(o.fontFile) : undefined;\n const html = buildPaletteHtml(o, dataUrl);\n\n ctx.logger.info(`rendering palette (${o.colors.length} colors, ${o.layout})`);\n const context = await ctx.browser.newContext({\n viewport: { width: o.width, height: o.height },\n deviceScaleFactor: o.deviceScaleFactor,\n });\n let buffer: Buffer;\n try {\n const page = await context.newPage();\n await page.setContent(html, { waitUntil: \"load\" });\n // Let a custom font finish loading before the shot so it's actually applied.\n if (dataUrl) {\n await page.evaluate(\n () =>\n (globalThis as { document?: { fonts?: { ready?: Promise<unknown> } } }).document?.fonts\n ?.ready,\n );\n }\n buffer = await page.screenshot({ type: \"png\" });\n } finally {\n await context.close();\n }\n\n await ensureDir(path.dirname(outPath));\n await writeFile(outPath, buffer);\n\n const dims = imageSize(buffer);\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: PALETTE_ID,\n sourceUrl: `palette:${o.colors.length}`,\n file: ctx.toManifestPath(outPath),\n format: \"png\",\n width: dims.width ?? o.width * o.deviceScaleFactor,\n height: dims.height ?? o.height * o.deviceScaleFactor,\n bytes: buffer.length,\n contentHash: sha256Buffer(buffer),\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n}\n\nexport const paletteGenerator: Generator<ResolvedPaletteOptions> = {\n id: PALETTE_ID,\n optionsSchema: paletteOptionsSchema,\n // A custom font's content shapes the output — hash it into the cache key.\n fileDependencies: (o) => (o.fontFile ? [o.fontFile] : []),\n run,\n};\n","import { z } from \"zod\";\nimport { normalizeHex, type FieldId } from \"@/generators/palette/color\";\n\n/** Field ids that can be placed in a swatch corner. */\nconst fieldEnum = z.enum([\"name\", \"hex\", \"rgb\", \"oklch\", \"hsl\", \"cmyk\"]);\n\nconst hexString = z.string().refine(\n (s) => {\n try {\n normalizeHex(s);\n return true;\n } catch {\n return false;\n }\n },\n { message: \"must be a hex color like #D7DBDE\" },\n);\n\n/** One color in the palette. */\nexport interface PaletteColorInput {\n /** Display name, e.g. \"Wet Grey\". */\n name: string;\n /** Hex value (#rgb or #rrggbb, with or without #). */\n hex: string;\n}\n\n/**\n * Author-facing options for the `palette` generator — a still color-palette image. Each color is a\n * swatch labeled with the fields you place in its corners (name / hex / rgb / oklch / hsl / cmyk),\n * with auto-contrasting text. Only `colors` is required.\n */\nexport interface PaletteOptionsInput {\n /** The colors to show (at least one). */\n colors: PaletteColorInput[];\n /** Swatch arrangement: full-width bands, full-height columns, or an N-wide grid. Default \"rows\". */\n layout?: \"rows\" | \"columns\" | \"grid\";\n /** Columns when `layout: \"grid\"`. Default 3. */\n gridColumns?: number;\n /** Output width in px. Default 1400. */\n width?: number;\n /** Output height in px. Default 1750 (portrait 4:5 with the default width). */\n height?: number;\n /** Render scale (2 = retina-crisp). Default 2. */\n deviceScaleFactor?: number;\n /** Page background, shown only in the gaps between swatches. Default \"#ffffff\". */\n background?: string;\n /** Gap between swatches (px). Default 0 (swatches abut). */\n gap?: number;\n /** Swatch corner radius (px). Default 0 (square). */\n cornerRadius?: number;\n /** Fields stacked in the top-left corner. Default name + hex. */\n topLeft?: FieldId[];\n /** Fields stacked in the top-right corner. Default rgb + oklch. */\n topRight?: FieldId[];\n /** Fields stacked in the bottom-left corner. Default none. */\n bottomLeft?: FieldId[];\n /** Fields stacked in the bottom-right corner. Default none. */\n bottomRight?: FieldId[];\n /** Uppercase the color names. Default false. */\n uppercase?: boolean;\n /** RGB string style. Default \"labeled\". */\n rgbStyle?: \"labeled\" | \"css\" | \"plain\";\n /** OKLCH string style. Default \"css\". */\n oklchStyle?: \"css\" | \"labeled\";\n /** Custom font file (woff2/woff/ttf/otf), embedded into the render. Omit for a system bold sans. */\n fontFile?: string;\n /** Label font size in px. Omit to derive from the width. */\n fontSize?: number;\n /** Label font weight. Default 700. */\n fontWeight?: number;\n /** Light text color, used on dark swatches (picked by contrast). Default \"#ffffff\". */\n textLight?: string;\n /** Dark text color, used on light swatches (picked by contrast). Default \"#141414\". */\n textDark?: string;\n /** Luminance above which the dark text is used (0..1). Default 0.5. */\n contrastThreshold?: number;\n /** Inset of the labels from the swatch edges (px). Omit to derive from the width. */\n padding?: number;\n /** Output filename; defaults to \"<slug(asset name)>.png\". */\n fileName?: string;\n}\n\nconst paletteObjectSchema = z\n .object({\n colors: z\n .array(\n z\n .object({\n name: z.string().min(1).describe('Display name, e.g. \"Wet Grey\".'),\n hex: hexString.describe(\"Hex value (#rgb or #rrggbb, with or without #).\"),\n })\n .strict(),\n )\n .min(1)\n .describe(\"The colors to show (at least one).\"),\n layout: z\n .enum([\"rows\", \"columns\", \"grid\"])\n .default(\"rows\")\n .describe('Swatch arrangement: full-width bands, full-height columns, or an N-wide grid. Default \"rows\".'),\n gridColumns: z\n .number()\n .int()\n .min(1)\n .max(12)\n .default(3)\n .describe('Columns when layout is \"grid\". Default 3.'),\n width: z.number().int().positive().default(1400).describe(\"Output width in px. Default 1400.\"),\n height: z\n .number()\n .int()\n .positive()\n .default(1750)\n .describe(\"Output height in px. Default 1750 (portrait 4:5 with the default width).\"),\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(2)\n .describe(\"Render scale (2 = retina-crisp). Default 2.\"),\n background: z\n .string()\n .default(\"#ffffff\")\n .describe('Page background, shown only in the gaps between swatches. Default \"#ffffff\".'),\n gap: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Gap between swatches (px). Default 0 (swatches abut).\"),\n cornerRadius: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Swatch corner radius (px). Default 0 (square).\"),\n topLeft: z\n .array(fieldEnum)\n .default([\"name\", \"hex\"])\n .describe(\"Fields stacked in the top-left corner. Default name + hex.\"),\n topRight: z\n .array(fieldEnum)\n .default([\"rgb\", \"oklch\"])\n .describe(\"Fields stacked in the top-right corner. Default rgb + oklch.\"),\n bottomLeft: z\n .array(fieldEnum)\n .default([])\n .describe(\"Fields stacked in the bottom-left corner. Default none.\"),\n bottomRight: z\n .array(fieldEnum)\n .default([])\n .describe(\"Fields stacked in the bottom-right corner. Default none.\"),\n uppercase: z.boolean().default(false).describe(\"Uppercase the color names. Default false.\"),\n rgbStyle: z\n .enum([\"labeled\", \"css\", \"plain\"])\n .default(\"labeled\")\n .describe('RGB string style. Default \"labeled\".'),\n oklchStyle: z\n .enum([\"css\", \"labeled\"])\n .default(\"css\")\n .describe('OKLCH string style. Default \"css\".'),\n fontFile: z\n .string()\n .optional()\n .describe(\"Custom font file (woff2/woff/ttf/otf), embedded into the render. Omit for a system bold sans.\"),\n fontSize: z\n .number()\n .positive()\n .optional()\n .describe(\"Label font size in px. Omit to derive from the width.\"),\n fontWeight: z\n .number()\n .int()\n .min(1)\n .max(1000)\n .default(700)\n .describe(\"Label font weight. Default 700.\"),\n textLight: z\n .string()\n .default(\"#ffffff\")\n .describe('Light text color, used on dark swatches (picked by contrast). Default \"#ffffff\".'),\n textDark: z\n .string()\n .default(\"#141414\")\n .describe('Dark text color, used on light swatches (picked by contrast). Default \"#141414\".'),\n contrastThreshold: z\n .number()\n .min(0)\n .max(1)\n .default(0.5)\n .describe(\"Luminance above which the dark text is used (0..1). Default 0.5.\"),\n padding: z\n .number()\n .nonnegative()\n .optional()\n .describe(\"Inset of the labels from the swatch edges (px). Omit to derive from the width.\"),\n fileName: z\n .string()\n .optional()\n .describe('Output filename; defaults to \"<slug(asset name)>.png\".'),\n })\n .strict();\n\nexport const paletteOptionsSchema = paletteObjectSchema;\n\n/** Author-facing input (documented for editor hover; the schema validates it at run time). */\nexport type PaletteOptions = PaletteOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedPaletteOptions = z.infer<typeof paletteObjectSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _paletteInputInSync: Exact<PaletteOptionsInput, z.input<typeof paletteObjectSchema>> = true;\nvoid _paletteInputInSync;\n","/**\n * Pure color math for the palette generator: hex parsing and conversions to RGB / OKLCH / HSL /\n * CMYK, a relative-luminance contrast pick (dark-vs-light text on a swatch), and per-field label\n * formatting. No I/O — fully unit-testable.\n */\n\nexport interface Rgb {\n r: number;\n g: number;\n b: number;\n}\n\n/** A field that can be shown on a swatch. */\nexport type FieldId = \"name\" | \"hex\" | \"rgb\" | \"oklch\" | \"hsl\" | \"cmyk\";\n\n/** Normalize `#rgb` / `rgb` / `#rrggbb` (any case) to canonical uppercase `#RRGGBB`. */\nexport function normalizeHex(hex: string): string {\n let h = hex.trim().replace(/^#/, \"\");\n if (/^[0-9a-fA-F]{3}$/.test(h)) {\n h = h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\");\n }\n if (!/^[0-9a-fA-F]{6}$/.test(h)) throw new Error(`Invalid hex color: \"${hex}\"`);\n return `#${h.toUpperCase()}`;\n}\n\nexport function hexToRgb(hex: string): Rgb {\n const h = normalizeHex(hex).slice(1);\n return {\n r: parseInt(h.slice(0, 2), 16),\n g: parseInt(h.slice(2, 4), 16),\n b: parseInt(h.slice(4, 6), 16),\n };\n}\n\nconst clamp = (n: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, n));\n\n/** sRGB channel (0..1) → linear-light. */\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n}\n\nexport interface Oklch {\n /** Lightness 0..1. */\n l: number;\n /** Chroma (~0..0.4). */\n c: number;\n /** Hue degrees 0..360. */\n h: number;\n}\n\n/**\n * sRGB (0..255) → OKLCH, via Björn Ottosson's OKLab matrices. Returns L in [0,1], C ≥ 0, H in\n * [0,360). Hue is undefined for greys (C≈0) — reported as 0.\n */\nexport function rgbToOklch({ r, g, b }: Rgb): Oklch {\n const lr = srgbToLinear(r / 255);\n const lg = srgbToLinear(g / 255);\n const lb = srgbToLinear(b / 255);\n\n const l_ = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);\n const m_ = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);\n const s_ = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);\n\n const L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_;\n const a = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_;\n const bb = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_;\n\n const C = Math.hypot(a, bb);\n let H = (Math.atan2(bb, a) * 180) / Math.PI;\n if (H < 0) H += 360;\n return { l: clamp(L, 0, 1), c: C, h: C < 1e-4 ? 0 : H };\n}\n\nexport interface Hsl {\n h: number;\n s: number;\n l: number;\n}\n\n/** sRGB (0..255) → HSL (h 0..360, s/l 0..100). */\nexport function rgbToHsl({ r, g, b }: Rgb): Hsl {\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const d = max - min;\n const l = (max + min) / 2;\n let h = 0;\n let s = 0;\n if (d !== 0) {\n s = d / (1 - Math.abs(2 * l - 1));\n switch (max) {\n case rn:\n h = ((gn - bn) / d) % 6;\n break;\n case gn:\n h = (bn - rn) / d + 2;\n break;\n default:\n h = (rn - gn) / d + 4;\n }\n h *= 60;\n if (h < 0) h += 360;\n }\n return { h: Math.round(h), s: Math.round(s * 100), l: Math.round(l * 100) };\n}\n\nexport interface Cmyk {\n c: number;\n m: number;\n y: number;\n k: number;\n}\n\n/** sRGB (0..255) → CMYK percentages (naive, profile-less). */\nexport function rgbToCmyk({ r, g, b }: Rgb): Cmyk {\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n const k = 1 - Math.max(rn, gn, bn);\n if (k >= 1 - 1e-9) return { c: 0, m: 0, y: 0, k: 100 };\n return {\n c: Math.round(((1 - rn - k) / (1 - k)) * 100),\n m: Math.round(((1 - gn - k) / (1 - k)) * 100),\n y: Math.round(((1 - bn - k) / (1 - k)) * 100),\n k: Math.round(k * 100),\n };\n}\n\n/** WCAG relative luminance (0..1) of an sRGB color. */\nfunction relativeLuminance({ r, g, b }: Rgb): number {\n return (\n 0.2126 * srgbToLinear(r / 255) +\n 0.7152 * srgbToLinear(g / 255) +\n 0.0722 * srgbToLinear(b / 255)\n );\n}\n\n/** Pick readable text on a swatch: the dark text on light backgrounds, light text on dark ones. */\nexport function pickTextColor(\n hex: string,\n opts: { light: string; dark: string; threshold?: number },\n): string {\n const lum = relativeLuminance(hexToRgb(hex));\n return lum > (opts.threshold ?? 0.5) ? opts.dark : opts.light;\n}\n\nexport interface FieldFormat {\n uppercaseName?: boolean;\n /** \"labeled\" → R:..,G:..,B:.. · \"css\" → rgb(.. .. ..) · \"plain\" → \".. .. ..\" */\n rgbStyle?: \"labeled\" | \"css\" | \"plain\";\n /** \"css\" → oklch(88% 0.012 250) · \"labeled\" → L:88% C:0.012 H:250 */\n oklchStyle?: \"css\" | \"labeled\";\n}\n\nconst round = (n: number, d = 0): number => {\n const p = 10 ** d;\n return Math.round(n * p) / p;\n};\n\n/** Render one field of a color as its display string. */\nexport function formatField(\n color: { name: string; hex: string },\n field: FieldId,\n fmt: FieldFormat = {},\n): string {\n const hex = normalizeHex(color.hex);\n const rgb = hexToRgb(hex);\n switch (field) {\n case \"name\":\n return fmt.uppercaseName ? color.name.toUpperCase() : color.name;\n case \"hex\":\n return hex;\n case \"rgb\": {\n const { r, g, b } = rgb;\n if (fmt.rgbStyle === \"css\") return `rgb(${r}, ${g}, ${b})`;\n if (fmt.rgbStyle === \"plain\") return `${r} ${g} ${b}`;\n return `R:${r},G:${g},B:${b}`;\n }\n case \"oklch\": {\n const { l, c, h } = rgbToOklch(rgb);\n const L = `${round(l * 100)}%`;\n const C = round(c, 3);\n const H = round(h, 1);\n return fmt.oklchStyle === \"labeled\" ? `L:${L} C:${C} H:${H}` : `oklch(${L} ${C} ${H})`;\n }\n case \"hsl\": {\n const { h, s, l } = rgbToHsl(rgb);\n return `H:${h},S:${s},L:${l}`;\n }\n case \"cmyk\": {\n const { c, m, y, k } = rgbToCmyk(rgb);\n return `C:${c},M:${m},Y:${y},K:${k}`;\n }\n }\n}\n","import { formatField, pickTextColor, type FieldId } from \"@/generators/palette/color\";\nimport type { ResolvedPaletteOptions } from \"@/generators/palette/options\";\n\nconst esc = (s: string): string =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n\ntype Corner = \"topLeft\" | \"topRight\" | \"bottomLeft\" | \"bottomRight\";\n\n/** One corner block of stacked field lines (empty string if no fields placed there). */\nfunction corner(\n o: ResolvedPaletteOptions,\n color: { name: string; hex: string },\n which: Corner,\n fields: FieldId[],\n): string {\n if (!fields.length) return \"\";\n const vert = which.startsWith(\"top\") ? \"top:0\" : \"bottom:0\";\n const horiz = which.endsWith(\"Left\") ? \"left:0;text-align:left\" : \"right:0;text-align:right\";\n const lines = fields\n .map(\n (f) =>\n `<div>${esc(\n formatField(color, f, {\n uppercaseName: o.uppercase,\n rgbStyle: o.rgbStyle,\n oklchStyle: o.oklchStyle,\n }),\n )}</div>`,\n )\n .join(\"\");\n return `<div class=\"cnr\" style=\"${vert};${horiz}\">${lines}</div>`;\n}\n\n/** Build a fully self-contained palette HTML document (inline CSS). Pure — unit-tested. */\nexport function buildPaletteHtml(o: ResolvedPaletteOptions, fontDataUrl?: string): string {\n const fontSize = o.fontSize ?? Math.round(o.width * 0.02);\n const pad = o.padding ?? Math.round(o.width * 0.025);\n const fontFace = fontDataUrl\n ? `@font-face{font-family:'PaletteFont';src:url(${fontDataUrl});font-weight:1 1000;font-display:block;}`\n : \"\";\n const family = fontDataUrl\n ? `'PaletteFont', \"Helvetica Neue\", Arial, sans-serif`\n : `\"Helvetica Neue\", Arial, \"Segoe UI\", system-ui, sans-serif`;\n\n // The container that arranges the swatches per layout.\n let container: string;\n if (o.layout === \"columns\") {\n container = `display:flex;flex-direction:row;gap:${o.gap}px`;\n } else if (o.layout === \"grid\") {\n container = `display:grid;grid-template-columns:repeat(${o.gridColumns},1fr);gap:${o.gap}px`;\n } else {\n container = `display:flex;flex-direction:column;gap:${o.gap}px`;\n }\n // rows/columns share their long axis equally; grid cells fill their track.\n const swatchFlex = o.layout === \"grid\" ? \"\" : \"flex:1 1 0;min-width:0;min-height:0;\";\n\n const swatches = o.colors\n .map((color) => {\n const text = pickTextColor(color.hex, {\n light: o.textLight,\n dark: o.textDark,\n threshold: o.contrastThreshold,\n });\n const corners =\n corner(o, color, \"topLeft\", o.topLeft) +\n corner(o, color, \"topRight\", o.topRight) +\n corner(o, color, \"bottomLeft\", o.bottomLeft) +\n corner(o, color, \"bottomRight\", o.bottomRight);\n return `<div class=\"sw\" style=\"background:${esc(color.hex)};color:${text};${swatchFlex}\">${corners}</div>`;\n })\n .join(\"\");\n\n return `<!doctype html><html><head><meta charset=\"utf-8\"><style>\n*{box-sizing:border-box;margin:0;padding:0}\n${fontFace}\nhtml,body{width:${o.width}px;height:${o.height}px}\nbody{background:${esc(o.background)};font-family:${family};font-weight:${o.fontWeight};\n font-size:${fontSize}px;line-height:1.12;letter-spacing:-0.01em}\n.wrap{width:${o.width}px;height:${o.height}px;${container}}\n.sw{position:relative;overflow:hidden;border-radius:${o.cornerRadius}px}\n.cnr{position:absolute;padding:${pad}px}\n.cnr>div{white-space:nowrap}\n</style></head><body><div class=\"wrap\">${swatches}</div></body></html>`;\n}\n","import { z } from \"zod\";\nimport { normalizeHex, type FieldId } from \"@/generators/palette/color\";\nimport type { PaletteColorInput } from \"@/generators/palette/options\";\n\n/** Field ids that can be revealed on a band when it expands (same set as the still palette). */\nconst fieldEnum = z.enum([\"name\", \"hex\", \"rgb\", \"oklch\", \"hsl\", \"cmyk\"]);\n\nconst hexString = z.string().refine(\n (s) => {\n try {\n normalizeHex(s);\n return true;\n } catch {\n return false;\n }\n },\n { message: \"must be a hex color like #D7DBDE\" },\n);\n\n/**\n * Author-facing options for the `palette-reel` generator — a looping reveal *video* of a color\n * palette (the moving counterpart of the still `palette` generator). The colors start as thin\n * slivers showing only their name; one at a time a sliver expands into a band that reveals its\n * configured `details` (hex / oklch / rgb …), holds, then collapses before the next opens — sweeping\n * every color and looping seamlessly. Only `colors` is required; everything else has a default.\n */\nexport interface PaletteReelOptionsInput {\n /** The colors to reveal (at least one). */\n colors: PaletteColorInput[];\n /** Sliver arrangement: horizontal bands (names upright) or full-height vertical strips. Default \"rows\". */\n orientation?: \"rows\" | \"columns\";\n /** Fields revealed when a color expands (the name is always shown, so it's ignored here). Default hex + oklch + rgb. */\n details?: FieldId[];\n\n // --- timing (seconds) ---\n /** How long each color stays fully open before handing off to the next. Default 2. */\n holdSeconds?: number;\n /** Crossfade length from one open color to the next. Default 0.7. */\n transitionSeconds?: number;\n /**\n * Ping-pong the sweep (down the list then back up) so every handoff is between neighbouring bands —\n * the open band only ever slides by one, avoiding the \"pinch\" of a last→first jump at the loop seam.\n * Off wraps directly (last→first): shorter, but crossfades non-adjacent bands at the seam. Default true.\n */\n bounce?: boolean;\n /** Easing applied to the crossfade ramp. Default \"ease-in-out\". */\n easing?: \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\";\n /** Clip length override (s). Omit to derive (count × (hold + transition)) for a clean loop. */\n durationSeconds?: number;\n\n // --- layout / sizing ---\n /** How many times a sliver's share a fully-open band takes (a collapsed sliver is the baseline). Default 12. */\n grownFlex?: number;\n /** Minimum cross-size of a sliver in px so its name stays legible. Default 0 (derive from height). */\n minCrossPx?: number;\n /** Keep the name fully visible even in a collapsed sliver (else it fades with the band). Default true. */\n nameAlwaysVisible?: boolean;\n\n // --- styling (carried from the still palette) ---\n /** Uppercase the color names. Default false. */\n uppercase?: boolean;\n /** RGB string style. Default \"labeled\". */\n rgbStyle?: \"labeled\" | \"css\" | \"plain\";\n /** OKLCH string style. Default \"css\". */\n oklchStyle?: \"css\" | \"labeled\";\n /** Light text color, used on dark bands (picked by contrast). Default \"#ffffff\". */\n textLight?: string;\n /** Dark text color, used on light bands (picked by contrast). Default \"#141414\". */\n textDark?: string;\n /** Luminance above which the dark text is used (0..1). Default 0.5. */\n contrastThreshold?: number;\n /** Custom font file (woff2/woff/ttf/otf), served into the render. Omit for a system bold sans. */\n fontFile?: string;\n /** Label font weight. Default 700. */\n fontWeight?: number;\n /** Name font size in px. Omit to derive from the frame size. */\n fontSize?: number;\n /** Detail-line font size as a fraction of the name size. Default 0.62. */\n detailFontScale?: number;\n /** Backdrop behind the bands (shown in `gap` between them). Default \"#ffffff\". */\n background?: string;\n /** Gap between bands (px). Default 0 (bands abut). */\n gap?: number;\n /** Band corner radius (px). Default 0 (square). */\n cornerRadius?: number;\n\n // --- output ---\n /** Output frame width in px. Default 1920. */\n width?: number;\n /** Output frame height in px. Default 1080. */\n height?: number;\n /** Render scale (higher = crisper capture, downscaled into the video). Default 1. */\n deviceScaleFactor?: number;\n /** Output frames per second. Default 30. */\n fps?: number;\n /** x264 quality, 0–51 (lower = better quality / larger file). Default 18. */\n crf?: number;\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName?: string;\n}\n\nconst paletteReelObjectSchema = z\n .object({\n colors: z\n .array(\n z\n .object({\n name: z.string().min(1).describe(\"Display name shown on the color's sliver/band.\"),\n hex: hexString.describe(\"Color value as a hex string like #D7DBDE.\"),\n })\n .strict(),\n )\n .min(1)\n .describe(\"The colors to reveal (at least one).\"),\n orientation: z\n .enum([\"rows\", \"columns\"])\n .default(\"rows\")\n .describe(\n 'Sliver arrangement: horizontal bands (names upright) or full-height vertical strips. Default \"rows\".',\n ),\n details: z\n .array(fieldEnum)\n .default([\"hex\", \"oklch\", \"rgb\"])\n .describe(\n \"Fields revealed when a color expands (the name is always shown, so it's ignored here). Default hex + oklch + rgb.\",\n ),\n\n holdSeconds: z\n .number()\n .positive()\n .default(2)\n .describe(\"How long each color stays fully open before handing off to the next (s). Default 2.\"),\n transitionSeconds: z\n .number()\n .positive()\n .default(0.7)\n .describe(\"Crossfade length from one open color to the next (s). Default 0.7.\"),\n bounce: z\n .boolean()\n .default(true)\n .describe(\n \"Ping-pong the sweep so each handoff is between neighbouring bands; off wraps directly (last to first). Default true.\",\n ),\n easing: z\n .enum([\"linear\", \"ease-in\", \"ease-out\", \"ease-in-out\"])\n .default(\"ease-in-out\")\n .describe('Easing applied to the crossfade ramp. Default \"ease-in-out\".'),\n durationSeconds: z\n .number()\n .positive()\n .optional()\n .describe(\"Clip length override (s). Omit to derive (count x (hold + transition)) for a clean loop.\"),\n\n grownFlex: z\n .number()\n .min(1)\n .default(12)\n .describe(\n \"How many times a sliver's share a fully-open band takes (a collapsed sliver is the baseline). Default 12.\",\n ),\n minCrossPx: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Minimum cross-size of a sliver in px so its name stays legible. Default 0 (derive from height).\"),\n nameAlwaysVisible: z\n .boolean()\n .default(true)\n .describe(\n \"Keep the name fully visible even in a collapsed sliver (else it fades with the band). Default true.\",\n ),\n\n uppercase: z.boolean().default(false).describe(\"Uppercase the color names. Default false.\"),\n rgbStyle: z\n .enum([\"labeled\", \"css\", \"plain\"])\n .default(\"labeled\")\n .describe('RGB string style. Default \"labeled\".'),\n oklchStyle: z\n .enum([\"css\", \"labeled\"])\n .default(\"css\")\n .describe('OKLCH string style. Default \"css\".'),\n textLight: z\n .string()\n .default(\"#ffffff\")\n .describe('Light text color, used on dark bands (picked by contrast). Default \"#ffffff\".'),\n textDark: z\n .string()\n .default(\"#141414\")\n .describe('Dark text color, used on light bands (picked by contrast). Default \"#141414\".'),\n contrastThreshold: z\n .number()\n .min(0)\n .max(1)\n .default(0.5)\n .describe(\"Luminance above which the dark text is used (0..1). Default 0.5.\"),\n fontFile: z\n .string()\n .optional()\n .describe(\"Custom font file (woff2/woff/ttf/otf), served into the render. Omit for a system bold sans.\"),\n fontWeight: z\n .number()\n .int()\n .min(1)\n .max(1000)\n .default(700)\n .describe(\"Label font weight. Default 700.\"),\n fontSize: z\n .number()\n .positive()\n .optional()\n .describe(\"Name font size in px. Omit to derive from the frame size.\"),\n detailFontScale: z\n .number()\n .positive()\n .default(0.62)\n .describe(\"Detail-line font size as a fraction of the name size. Default 0.62.\"),\n background: z\n .string()\n .default(\"#ffffff\")\n .describe('Backdrop behind the bands (shown in `gap` between them). Default \"#ffffff\".'),\n gap: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Gap between bands (px). Default 0 (bands abut).\"),\n cornerRadius: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Band corner radius (px). Default 0 (square).\"),\n\n width: z\n .number()\n .int()\n .positive()\n .default(1920)\n .describe(\"Output frame width in px. Default 1920.\"),\n height: z\n .number()\n .int()\n .positive()\n .default(1080)\n .describe(\"Output frame height in px. Default 1080.\"),\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(1)\n .describe(\"Render scale (higher = crisper capture, downscaled into the video). Default 1.\"),\n fps: z\n .number()\n .int()\n .positive()\n .max(120)\n .default(30)\n .describe(\"Output frames per second. Default 30.\"),\n crf: z\n .number()\n .int()\n .min(0)\n .max(51)\n .default(18)\n .describe(\"x264 quality, 0-51 (lower = better quality / larger file). Default 18.\"),\n fileName: z\n .string()\n .optional()\n .describe('Output filename; defaults to \"<slug(asset name)>.mp4\".'),\n })\n .strict();\n\nexport const paletteReelOptionsSchema = paletteReelObjectSchema;\n\n/** Author-facing input (documented for editor hover; the schema validates it at run time). */\nexport type PaletteReelOptions = PaletteReelOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedPaletteReelOptions = z.infer<typeof paletteReelObjectSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _paletteReelInputInSync: Exact<\n PaletteReelOptionsInput,\n z.input<typeof paletteReelObjectSchema>\n> = true;\nvoid _paletteReelInputInSync;\n","import {\n paletteReelOptionsSchema,\n type ResolvedPaletteReelOptions,\n} from \"@/generators/palette-reel/options\";\nimport { formatField, normalizeHex, pickTextColor } from \"@/generators/palette/color\";\nimport { renderScene } from \"@/generators/scene\";\nimport type { ResolvedSceneOptions } from \"@/generators/scene/options\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const PALETTE_REEL_ID = \"palette-reel\";\n\n/**\n * Clip length derived from the timing knobs — must match the scene's `totalDuration(params)` exactly\n * so the last captured frame lands on the loop seam. Every color holds once and hands off once.\n * Mirrors palette-reel-timeline.ts.\n */\nfunction deriveDuration(o: ResolvedPaletteReelOptions): number {\n const n = o.colors.length;\n const stops = n <= 1 ? n : o.bounce ? 2 * (n - 1) : n;\n return stops * (o.holdSeconds + o.transitionSeconds);\n}\n\n/** Map the friendly palette-reel options onto the `palette-reel` scene and render it. */\nasync function run(\n ctx: PipelineContext,\n o: ResolvedPaletteReelOptions,\n): Promise<{ assets: AssetRecord[] }> {\n // Precompute every display string Node-side (the scene is a separate bundle that can't import the\n // color math): per color a swatch hex, its contrast text color, the name, and the detail lines.\n const fmt = { uppercaseName: o.uppercase, rgbStyle: o.rgbStyle, oklchStyle: o.oklchStyle };\n const fields = o.details.filter((f) => f !== \"name\"); // the name is always shown separately\n const items = o.colors.map((color) => {\n const hex = normalizeHex(color.hex);\n return {\n name: formatField(color, \"name\", fmt),\n hex,\n textColor: pickTextColor(hex, {\n light: o.textLight,\n dark: o.textDark,\n threshold: o.contrastThreshold,\n }),\n details: fields.map((f) => formatField(color, f, fmt)),\n };\n });\n\n const durationSeconds = o.durationSeconds ?? deriveDuration(o);\n\n const sceneOptions: ResolvedSceneOptions = {\n scene: PALETTE_REEL_ID,\n width: o.width,\n height: o.height,\n background: o.background,\n deviceScaleFactor: o.deviceScaleFactor,\n fps: o.fps,\n durationSeconds,\n // The reveal is a deterministic, closed-form function of time (it publishes __sceneSeek), so the\n // frame-stepper renders it frame-exact with a perfect loop seam — no realtime recording jitter.\n capture: \"frames\",\n frameFormat: \"jpeg\",\n crf: o.crf,\n fileName: o.fileName,\n // Only serve a font slot when one is configured — renderScene path.resolves every files entry.\n files: o.fontFile ? { font: o.fontFile } : {},\n sceneOptions: {\n items,\n orientation: o.orientation,\n holdSeconds: o.holdSeconds,\n transitionSeconds: o.transitionSeconds,\n bounce: o.bounce,\n easing: o.easing,\n grownFlex: o.grownFlex,\n minCrossPx: o.minCrossPx,\n nameAlwaysVisible: o.nameAlwaysVisible,\n fontWeight: o.fontWeight,\n fontSize: o.fontSize,\n detailFontScale: o.detailFontScale,\n gap: o.gap,\n cornerRadius: o.cornerRadius,\n },\n };\n return renderScene(ctx, sceneOptions, PALETTE_REEL_ID);\n}\n\nexport const paletteReelGenerator: Generator<ResolvedPaletteReelOptions> = {\n id: PALETTE_REEL_ID,\n optionsSchema: paletteReelOptionsSchema,\n // A custom font's content shapes the output — hash it into the cache key (edit font → regenerate).\n fileDependencies: (o) => (o.fontFile ? [o.fontFile] : []),\n run,\n};\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { copyFile, readFile, stat } from \"node:fs/promises\";\nimport { imageSize } from \"image-size\";\nimport { imageOptionsSchema, type ResolvedImageOptions } from \"@/generators/image/options\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { sha256File } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const IMAGE_ID = \"image\";\n\n/** File extension → manifest `format` label. */\nconst EXT_FORMAT: Record<string, string> = {\n \".jpg\": \"jpg\",\n \".jpeg\": \"jpg\",\n \".png\": \"png\",\n \".webp\": \"webp\",\n \".avif\": \"avif\",\n \".gif\": \"gif\",\n};\n\n/**\n * Copy an existing image into the output dir and record it — a passthrough so real, full-resolution\n * assets (photos, exported graphics) can be used directly as scene inputs (e.g. crisp media-wall\n * tiles) instead of re-capturing them at a lower resolution.\n */\nasync function run(ctx: PipelineContext, o: ResolvedImageOptions): Promise<{ assets: AssetRecord[] }> {\n const abs = path.isAbsolute(o.src) ? o.src : path.resolve(process.cwd(), o.src);\n if (!existsSync(abs)) throw new Error(`image src not found: ${o.src} (resolved ${abs})`);\n\n const ext = path.extname(abs).toLowerCase();\n const format = EXT_FORMAT[ext] ?? ext.replace(\".\", \"\") ?? \"png\";\n const fileName = o.fileName ?? `${slugify(ctx.target.name)}${ext || \".png\"}`;\n const outPath = ctx.resolveOutPath(fileName);\n\n await ensureDir(path.dirname(outPath));\n await copyFile(abs, outPath);\n\n const [buf, stats, contentHash] = await Promise.all([\n readFile(outPath),\n stat(outPath),\n sha256File(outPath),\n ]);\n let width = 0;\n let height = 0;\n try {\n const d = imageSize(buf);\n width = d.width ?? 0;\n height = d.height ?? 0;\n } catch {\n ctx.logger.warn(`could not read dimensions for ${fileName}`);\n }\n\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: IMAGE_ID,\n sourceUrl: `image:${path.basename(abs)}`,\n file: ctx.toManifestPath(outPath),\n format,\n width,\n height,\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n}\n\nexport const imageGenerator: Generator<ResolvedImageOptions> = {\n id: IMAGE_ID,\n optionsSchema: imageOptionsSchema,\n // The source file's content shapes the output — hash it into the cache key (and fail early if missing).\n fileDependencies: (o) => [o.src],\n run,\n};\n","import { z } from \"zod\";\n\n/**\n * Author-facing options for the `image` generator — a passthrough that records an existing image\n * file as an asset so it can feed a scene (e.g. high-resolution photos as media-wall tiles) or be\n * tracked in the manifest. Only `src` is required.\n */\nexport interface ImageOptions {\n /** Path to the source image (relative to the cwd, or absolute). */\n src: string;\n /** Output filename; defaults to \"<slug(asset name)><ext of src>\". */\n fileName?: string;\n}\n\nexport const imageOptionsSchema = z\n .object({\n src: z.string().min(1).describe(\"Path to the source image (relative to the cwd, or absolute).\"),\n fileName: z\n .string()\n .optional()\n .describe('Output filename; defaults to \"<slug(asset name)><ext of src>\".'),\n })\n .strict();\n\nexport type ResolvedImageOptions = z.infer<typeof imageOptionsSchema>;\n","import type { Generator } from \"@/generators/types\";\nimport { scrollReelGenerator } from \"@/generators/scroll-reel\";\nimport { screenshotsGenerator } from \"@/generators/screenshots\";\nimport { wallGenerator } from \"@/generators/wall\";\nimport { specimenGenerator } from \"@/generators/specimen\";\nimport { paletteGenerator } from \"@/generators/palette\";\nimport { paletteReelGenerator } from \"@/generators/palette-reel\";\nimport { imageGenerator } from \"@/generators/image\";\n\nconst registry = new Map<string, Generator<unknown>>();\n\nfunction register<T>(gen: Generator<T>): void {\n registry.set(gen.id, gen as Generator<unknown>);\n}\n\nexport function getGenerator(id: string): Generator<unknown> | undefined {\n return registry.get(id);\n}\n\nexport function listGenerators(): Generator<unknown>[] {\n return [...registry.values()];\n}\n\nexport function generatorIds(): string[] {\n return [...registry.keys()];\n}\n\n// Register built-in generators. New asset types add a line here.\nregister(scrollReelGenerator);\nregister(screenshotsGenerator);\nregister(wallGenerator);\nregister(specimenGenerator);\nregister(paletteGenerator);\nregister(paletteReelGenerator);\nregister(imageGenerator);\n","import path from \"node:path\";\nimport { writeFile } from \"node:fs/promises\";\nimport { resolveCwd } from \"@/utils/paths\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { createLogger } from \"@/utils/logger\";\nimport { serializeConfigJsonSchema } from \"@/config/json-schema\";\n\nexport const DEFAULT_SCHEMA_FILE = \"pro-visu.schema.json\";\n\nexport interface SchemaOptions {\n cwd?: string;\n /** Output path, relative to cwd (default pro-visu.schema.json). */\n out?: string;\n}\n\n/**\n * Write a JSON Schema for `pro-visu.config.json` so editors give autocomplete + validation for a\n * dependency-free (JSON) config. Reference it from the config with `\"$schema\": \"./pro-visu.schema.json\"`.\n * Re-run after upgrading the tool to refresh the schema to the new version.\n */\nexport async function runSchema(options: SchemaOptions = {}): Promise<void> {\n const cwd = resolveCwd(options.cwd);\n const logger = createLogger(\"info\");\n const outPath = path.resolve(cwd, options.out ?? DEFAULT_SCHEMA_FILE);\n\n await ensureDir(path.dirname(outPath));\n await writeFile(outPath, serializeConfigJsonSchema(), \"utf8\");\n logger.success(`wrote ${path.relative(cwd, outPath) || DEFAULT_SCHEMA_FILE}`);\n logger.info('Reference it from your JSON config: \"$schema\": \"./pro-visu.schema.json\"');\n}\n","import path from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { loadConfig } from \"c12\";\nimport type { ZodError } from \"zod\";\nimport { showcaseConfigSchema, type ResolvedConfig } from \"@/config/schema\";\nimport { CONFIG_NAME } from \"@/config/defaults\";\n\nexport class ConfigNotFoundError extends Error {\n constructor(public readonly cwd: string) {\n super(\n `No pro-visu config found in ${cwd}.\\n` +\n \"Run `pro-visu init` to create one, or pass --config <path>.\",\n );\n this.name = \"ConfigNotFoundError\";\n }\n}\n\nexport class ConfigValidationError extends Error {\n constructor(\n public readonly zodError: ZodError,\n public readonly file?: string,\n ) {\n super(\"Invalid pro-visu config.\");\n this.name = \"ConfigValidationError\";\n }\n}\n\nexport interface LoadedConfig {\n config: ResolvedConfig;\n /** Absolute path of the discovered config file, if it came from a file. */\n configFile?: string;\n}\n\nexport interface LoadOptions {\n cwd: string;\n /** Explicit config path from --config. */\n configFile?: string;\n}\n\n/** rc files in the JS ecosystem (.prettierrc, .eslintrc) are JSON — treat ours the same. */\nconst RC_FILES = [\".pro-visurc.json\", \".pro-visurc\"];\n\n/**\n * Discover + load the repo config (pro-visu.config.{ts,js,mjs,cjs,json}, .pro-visurc[.json],\n * or a `pro-visu` key in package.json), then validate it with zod.\n */\nexport async function loadShowcaseConfig(opts: LoadOptions): Promise<LoadedConfig> {\n // 1. Explicit --config wins.\n if (opts.configFile) {\n const abs = path.resolve(opts.cwd, opts.configFile);\n if (!existsSync(abs)) throw new Error(`Config file not found: ${abs}`);\n if (abs.endsWith(\".json\") || RC_FILES.some((rc) => abs.endsWith(rc))) {\n return validate(readJson(abs), abs);\n }\n return loadViaC12({ ...opts, configFile: abs });\n }\n\n // 2. rc files (parsed as JSON), highest precedence among discovered files.\n const rc = findRcJson(opts.cwd);\n if (rc) return validate(rc.data, rc.file);\n\n // 3. c12 discovery: pro-visu.config.* and the package.json \"pro-visu\" key.\n return loadViaC12(opts);\n}\n\nasync function loadViaC12(opts: LoadOptions): Promise<LoadedConfig> {\n const result = await loadConfig<Record<string, unknown>>({\n cwd: opts.cwd,\n name: CONFIG_NAME,\n configFile: opts.configFile,\n rcFile: false,\n packageJson: true,\n globalRc: false,\n dotenv: false,\n });\n\n const raw = result.config ?? {};\n const hasContent = Object.keys(raw).length > 0;\n const hasFile = !!result.configFile && existsSync(result.configFile);\n const hasLayer = (result.layers ?? []).some(\n (layer) => layer.config && Object.keys(layer.config).length > 0,\n );\n\n if (!hasContent && !hasFile && !hasLayer) {\n throw new ConfigNotFoundError(opts.cwd);\n }\n return validate(raw, result.configFile);\n}\n\nfunction validate(raw: unknown, file?: string): LoadedConfig {\n const parsed = showcaseConfigSchema.safeParse(raw);\n if (!parsed.success) throw new ConfigValidationError(parsed.error, file);\n return { config: parsed.data, configFile: file };\n}\n\nfunction findRcJson(cwd: string): { data: unknown; file: string } | undefined {\n for (const name of RC_FILES) {\n const file = path.join(cwd, name);\n if (existsSync(file)) return { data: readJson(file), file };\n }\n return undefined;\n}\n\nfunction readJson(file: string): unknown {\n try {\n return JSON.parse(readFileSync(file, \"utf8\"));\n } catch {\n throw new Error(`Invalid JSON in ${path.basename(file)}`);\n }\n}\n","import type { ResolvedAssetSpec } from \"@/config/schema\";\n\n/** Resolve an author URL/route against the base (absolute inputs pass through unchanged). */\nfunction resolveAgainst(value: string, base: string): string {\n return new URL(value, base).toString();\n}\n\n/** Resolve any relative entries in a `routes` option (string or `{ url }` object forms). */\nfunction resolveRoutes(\n options: Record<string, unknown>,\n base: string,\n): Record<string, unknown> {\n const routes = options.routes;\n if (!Array.isArray(routes)) return options;\n const resolved = routes.map((route) => {\n if (typeof route === \"string\") return resolveAgainst(route, base);\n if (\n route &&\n typeof route === \"object\" &&\n typeof (route as { url?: unknown }).url === \"string\"\n ) {\n const r = route as Record<string, unknown> & { url: string };\n return { ...r, url: resolveAgainst(r.url, base) };\n }\n return route;\n });\n return { ...options, routes: resolved };\n}\n\n/**\n * With a managed server, its URL is the default base for capture targets: a url-based asset that\n * omits `url` captures the server root, and any relative `url` or `routes` entry is resolved\n * against it. Absolute URLs pass through unchanged. With no base (no managed server), assets are\n * returned unchanged — explicit absolute URLs are then required, as before. Pure (no mutation).\n */\nexport function resolveTargets(\n assets: ResolvedAssetSpec[],\n base: string | undefined,\n requiresUrl: (generatorId: string) => boolean,\n): ResolvedAssetSpec[] {\n if (!base) return assets;\n return assets.map((asset) => {\n const url =\n asset.url == null\n ? requiresUrl(asset.generator)\n ? base\n : asset.url\n : resolveAgainst(asset.url, base);\n return { ...asset, url, options: resolveRoutes(asset.options, base) };\n });\n}\n","import readline from \"node:readline\";\n\nexport interface InterruptWatch {\n /** Tear down listeners and restore the terminal. */\n dispose: () => void;\n /** Fire a stop request programmatically (1st → onStop, 2nd → onForce). The live dashboard,\n * which owns the keyboard itself, calls this from its own Esc/Ctrl+C handler. */\n trigger: () => void;\n}\n\n/**\n * Watch for a stop request — Esc or Ctrl+C on a TTY, plus SIGINT/SIGTERM. The first request calls\n * `onStop()` (graceful: finish in-flight, then tear down); a second calls `onForce()` (bail now).\n *\n * Set `keyboard: false` to watch only signals and leave the keyboard alone — used when the live\n * dashboard owns input (Ink raw mode): it calls the returned `trigger` so there's a single keypress\n * owner. (Enabling raw mode here ourselves would also suppress the terminal's Ctrl+C → SIGINT, which\n * is why on the keyboard path we detect Ctrl+C as a keypress.)\n */\nexport function watchForInterrupt(\n onStop: () => void,\n onForce: () => void,\n opts: { keyboard?: boolean } = {},\n): InterruptWatch {\n const keyboard = opts.keyboard ?? true;\n let count = 0;\n const trigger = (): void => {\n count += 1;\n if (count === 1) onStop();\n else onForce();\n };\n\n const stdin = process.stdin;\n const useKeys = keyboard && Boolean(stdin.isTTY);\n let onKey: ((str: string, key: readline.Key) => void) | undefined;\n\n if (useKeys) {\n readline.emitKeypressEvents(stdin);\n try {\n stdin.setRawMode(true);\n } catch {\n /* some pseudo-TTYs reject raw mode — fall back to signals only */\n }\n onKey = (_str, key) => {\n if (!key) return;\n if (key.name === \"escape\" || (key.ctrl && key.name === \"c\")) trigger();\n };\n stdin.on(\"keypress\", onKey);\n stdin.resume();\n }\n\n const onSignal = (): void => trigger();\n process.on(\"SIGINT\", onSignal);\n process.on(\"SIGTERM\", onSignal);\n\n const dispose = (): void => {\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n if (useKeys && onKey) {\n stdin.off(\"keypress\", onKey);\n try {\n stdin.setRawMode(false);\n } catch {\n /* ignore */\n }\n stdin.pause();\n }\n };\n\n return { dispose, trigger };\n}\n","import { spawn } from \"node:child_process\";\nimport v8 from \"node:v8\";\n\n/** Env flag set on the re-exec'd child so it doesn't try to re-exec again (infinite loop guard). */\nconst REEXEC_FLAG = \"SHOWCASE_MEM_REEXEC\";\n\n/** This process's V8 old-space (heap) limit, in MB. */\nexport function currentHeapLimitMB(): number {\n return Math.round(v8.getHeapStatistics().heap_size_limit / (1024 * 1024));\n}\n\n/**\n * Whether a re-exec is warranted: a target is set, we haven't already re-exec'd, and it's\n * meaningfully above the current limit (5% slack avoids a pointless re-exec for a near-match).\n * Pure — unit-tested.\n */\nexport function shouldReexec(\n maxMemoryMB: number | undefined,\n currentLimitMB: number,\n alreadyReexec: boolean,\n): boolean {\n if (!maxMemoryMB || maxMemoryMB <= 0) return false;\n if (alreadyReexec) return false;\n return currentLimitMB < maxMemoryMB * 0.95;\n}\n\n/**\n * Honor `settings.maxMemoryMB`: if it asks for more heap than this process has, re-exec the CLI with\n * `--max-old-space-size=<MB>` so a heavy run has room to finish instead of crashing with a V8 OOM.\n * Returns true if it re-exec'd — the child ran the whole command (stdio inherited), so the caller\n * should simply return. Signals are forwarded to the child so Esc/Ctrl+C still work.\n */\nexport async function reexecWithMemory(maxMemoryMB: number | undefined): Promise<boolean> {\n if (!shouldReexec(maxMemoryMB, currentHeapLimitMB(), process.env[REEXEC_FLAG] === \"1\")) {\n return false;\n }\n const args = [`--max-old-space-size=${maxMemoryMB}`, process.argv[1]!, ...process.argv.slice(2)];\n const child = spawn(process.execPath, args, {\n stdio: \"inherit\",\n env: { ...process.env, [REEXEC_FLAG]: \"1\" },\n });\n const forward = (sig: NodeJS.Signals): void => {\n try {\n child.kill(sig);\n } catch {\n /* already gone */\n }\n };\n process.on(\"SIGINT\", forward);\n process.on(\"SIGTERM\", forward);\n const code = await new Promise<number>((resolve) => {\n child.on(\"exit\", (c, signal) => resolve(c ?? (signal ? 1 : 0)));\n child.on(\"error\", () => resolve(1));\n });\n process.off(\"SIGINT\", forward);\n process.off(\"SIGTERM\", forward);\n process.exitCode = code;\n return true;\n}\n","import { resolveCwd, resolveOutDir } from \"@/utils/paths\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { createLogger, createReportingLogger, type Logger } from \"@/utils/logger\";\nimport { loadShowcaseConfig } from \"@/config/load\";\nimport type { ResolvedConfig } from \"@/config/schema\";\nimport { resolveTargets } from \"@/config/resolve-targets\";\nimport { getGenerator } from \"@/generators/registry\";\nimport { watchForInterrupt } from \"@/cli/interrupt\";\nimport { reexecWithMemory, currentHeapLimitMB } from \"@/cli/reexec\";\nimport v8 from \"node:v8\";\nimport { startRunState, updateRunState, clearRunState } from \"@/cli/run-state\";\nimport { ensureChromium } from \"@/browser-install/ensure-chromium\";\nimport { ensureFfmpeg } from \"@/media/ensure-ffmpeg\";\nimport {\n startManagedServer,\n resolveServerUrl,\n type ServerHandle,\n type ServerTasks,\n type TaskHandle,\n} from \"@/server/manage-server\";\nimport { runPipeline, applyDerivedInputs, type AssetOutcome } from \"@/pipeline/runner\";\nimport { expandSelection, dependenciesOf } from \"@/pipeline/graph\";\nimport { createReporter } from \"@/cli/dashboard\";\nimport type { Reporter } from \"@/pipeline/reporter\";\nimport { TOOL_VERSION } from \"@/version\";\nimport { reportConfigError, printSummary } from \"@/cli/ui\";\n\nexport interface GenerateOptions {\n cwd?: string;\n config?: string;\n asset?: string | string[];\n concurrency?: string | number;\n skipBrowser?: boolean;\n /** Skip the managed server (use an already-running site at the asset URLs). */\n skipServer?: boolean;\n /** Keep the managed server but skip its build step (fast iteration when the site is unchanged). */\n skipBuild?: boolean;\n /** Draft quality: faster, lower-fidelity renders for iteration. */\n draft?: boolean;\n /** Skip assets whose inputs+options+tool fingerprint is unchanged. */\n cache?: boolean;\n verbose?: boolean;\n}\n\nexport async function runGenerate(options: GenerateOptions = {}): Promise<void> {\n const cwd = resolveCwd(options.cwd);\n const bootstrapLog: Logger = createLogger(options.verbose ? \"debug\" : \"info\");\n\n let loaded;\n try {\n loaded = await loadShowcaseConfig({ cwd, configFile: options.config });\n } catch (err) {\n reportConfigError(bootstrapLog, err);\n process.exitCode = 1;\n return;\n }\n const { config } = loaded;\n\n // Honor settings.maxMemoryMB: if it wants more heap than this process has, re-exec with a larger\n // --max-old-space-size and let the child run the whole command. Must happen before any heavy work.\n if (await reexecWithMemory(config.settings.maxMemoryMB)) return;\n if (config.settings.maxMemoryMB) {\n bootstrapLog.info(`Node heap limit: ${currentHeapLimitMB()} MB (settings.maxMemoryMB=${config.settings.maxMemoryMB})`);\n }\n\n const level = options.verbose ? \"debug\" : config.settings.logLevel;\n // Live job tracker on an interactive TTY; per-asset logs feed each row's current step.\n const reporter = createReporter({ tty: Boolean(process.stdout.isTTY), verbose: !!options.verbose });\n const logger = reporter.isLive ? createReportingLogger(level, reporter) : createLogger(level);\n const outDir = resolveOutDir(cwd, config.settings.outDir);\n\n const requested = normalizeAssetNames(options.asset);\n if (requested) {\n const known = new Set(config.assets.map((a) => a.name));\n const unknown = requested.filter((name) => !known.has(name));\n if (unknown.length) logger.warn(`Unknown asset(s): ${unknown.join(\", \")}`);\n if (requested.every((name) => !known.has(name))) {\n logger.error(\"No matching assets to generate.\");\n process.exitCode = 1;\n return;\n }\n }\n\n // Ensure a managed Chromium unless a system channel/executable is configured, or skipped.\n const usingManaged =\n !config.settings.browser.channel && !config.settings.browser.executablePath;\n if (!options.skipBrowser && usingManaged) {\n const ready = await ensureChromium({ logger });\n if (!ready) {\n logger.error(\"Chromium is required. Run `pro-visu init` or install it manually.\");\n process.exitCode = 1;\n return;\n }\n }\n\n // Self-heal ffmpeg: the video generators shell out to it, and the bundled binary may be\n // missing/corrupt when the consumer's package manager skipped build scripts.\n if (!(await ensureFfmpeg({ logger }))) {\n logger.error(\"A working ffmpeg is required for video generators.\");\n process.exitCode = 1;\n return;\n }\n\n // Track this run on disk so `pro-visu reset` can clean up if it's killed hard, and let Esc /\n // Ctrl+C stop it gracefully (a second press bails immediately; reset mops up any orphans).\n await ensureDir(outDir);\n await startRunState(outDir);\n const abort = new AbortController();\n let interrupted = false;\n let lowMemory = false;\n // The live dashboard owns the keyboard (Ink raw mode), so the watcher only handles signals there\n // and the dashboard calls `trigger` on Esc/Ctrl+C — one keypress owner, no clash.\n const { dispose: disposeInterrupt, trigger: interruptTrigger } = watchForInterrupt(\n () => {\n interrupted = true;\n abort.abort(); // graceful: stop launching new work, let in-flight finish, then tear down\n // Acknowledge the keypress immediately: the live tracker flips to a \"cancelling…\" banner;\n // without it (non-TTY/--verbose) print a line, since in-flight renders can take seconds.\n if (reporter.isLive) reporter.cancelling();\n else logger.warn(\"Cancelling — finishing in-flight work… (press again to force-quit)\");\n },\n () => {\n try {\n process.stdin.setRawMode?.(false); // restore the terminal before bailing\n process.stdout.write(\"\\x1b[?25h\"); // and the cursor (force-quit skips Ink's own cleanup)\n } catch {\n /* ignore */\n }\n process.exit(130);\n },\n { keyboard: !reporter.isLive },\n );\n // Hand the cancel trigger to the live dashboard so its useInput handler drives the same flow.\n if (reporter.isLive) reporter.attachInput?.(interruptTrigger);\n\n // From here the live tracker owns the terminal. Plan ALL rows up front — setup (build/server)\n // then every asset, with the assets gated on setup so they read \"waiting for build\" until it\n // finishes. The build no longer dumps raw CLI output; it feeds the \"build\" row's step. The\n // tracker's footer shows the \"esc to cancel\" hint.\n reporter.begin();\n\n // Memory watchdog: if the Node heap nears its limit, stop the run gracefully (like Esc) with a clear\n // message instead of letting V8 hard-crash (\"JavaScript heap out of memory\"). Fires once; raise\n // settings.maxMemoryMB (more heap) or lower settings.concurrency to avoid it.\n const memTimer = setInterval(() => {\n if (interrupted) return;\n const limit = v8.getHeapStatistics().heap_size_limit;\n const used = process.memoryUsage().heapUsed;\n if (limit > 0 && used / limit > 0.88) {\n lowMemory = true;\n interrupted = true;\n abort.abort();\n if (reporter.isLive) reporter.cancelling(\"low memory — stopping…\");\n else\n logger.warn(\n \"Low memory — stopping early to avoid a crash (raise settings.maxMemoryMB or lower concurrency).\",\n );\n }\n }, 1500);\n memTimer.unref?.();\n\n // The managed server only matters to url-based generators (it captures web pages). If nothing in\n // the selection needs a URL — e.g. only a `test`-mode wall, or other local generators — skip it\n // automatically so previews don't pay for a site build/boot they never use. Derive option-declared\n // dependencies first (e.g. a real wall's tile producers) so the selection — and this check — see\n // them; a `test`-mode wall declares none, so it collapses to just itself.\n applyDerivedInputs(config);\n const selected = expandSelection(config.assets, requested);\n const anyNeedsServer = selected.some((s) => Boolean(getGenerator(s.generator)?.requiresUrl));\n if (config.settings.server && !options.skipServer && !anyNeedsServer) {\n logger.info(\"No selected asset needs a URL — skipping the managed server.\");\n }\n\n const baseServerCfg = options.skipServer || !anyNeedsServer ? undefined : config.settings.server;\n // --skip-build keeps the managed server but drops its one-shot build (the site is unchanged).\n const serverCfg =\n baseServerCfg && options.skipBuild ? { ...baseServerCfg, build: undefined } : baseServerCfg;\n\n // The managed server's URL is the default base: a url-based asset that omits `url` captures its\n // root, and relative `url`/`routes` entries resolve against it. (No server → assets as authored.)\n const serverBase = serverCfg ? resolveServerUrl(serverCfg) : undefined;\n const resolvedConfig: ResolvedConfig = {\n ...config,\n assets: resolveTargets(config.assets, serverBase, (id) =>\n Boolean(getGenerator(id)?.requiresUrl),\n ),\n };\n\n const gates: string[] = [];\n const tasks: ServerTasks = {};\n if (reporter.isLive && serverCfg) {\n if (serverCfg.build) {\n reporter.add({ id: \"@build\", name: \"build\", detail: \"server\", system: true });\n gates.push(\"@build\");\n tasks.build = taskHandle(reporter, \"@build\");\n }\n reporter.add({ id: \"@server\", name: \"server\", detail: \"server\", system: true });\n gates.push(\"@server\");\n tasks.server = taskHandle(reporter, \"@server\");\n }\n if (reporter.isLive) {\n for (const spec of selected) {\n reporter.add({\n id: spec.name,\n name: spec.name,\n detail: spec.generator,\n deps: dependenciesOf(spec),\n gatedBy: gates,\n });\n }\n }\n\n let server: ServerHandle | null = null;\n let outcomes: AssetOutcome[] = [];\n let setupFailed = false;\n try {\n if (serverCfg) {\n server = await startManagedServer(serverCfg, cwd, logger, tasks, abort.signal);\n await updateRunState(outDir, { serverPid: server?.pid });\n }\n\n const concurrency =\n options.concurrency != null ? Number(options.concurrency) : undefined;\n const count = requested ? requested.length : config.assets.length;\n if (!reporter.isLive) logger.start(`Generating ${count} asset(s)…`);\n\n outcomes = await runPipeline({\n config: resolvedConfig,\n outDir,\n logger,\n toolVersion: TOOL_VERSION,\n assetNames: requested,\n concurrency: Number.isFinite(concurrency) ? concurrency : undefined,\n quality: options.draft ? \"draft\" : undefined,\n cache: options.cache,\n reporter,\n signal: abort.signal,\n onResources: (r) => void updateRunState(outDir, { tmpDirs: [r.tmpDir] }),\n });\n } catch (err) {\n reporter.stop();\n if (!interrupted) {\n logger.error((err as Error).message);\n process.exitCode = 1;\n setupFailed = true;\n }\n } finally {\n clearInterval(memTimer);\n reporter.stop(); // erase the live block before the final summary\n disposeInterrupt();\n await server?.stop();\n await clearRunState(outDir);\n }\n\n if (interrupted) {\n // A graceful stop is a clean exit, not a failure — exit 0 so the shell/pnpm doesn't print a\n // scary \"command failed\" wrapper. In-flight assets are aborted mid-work, so only the ones that\n // actually completed are \"finished\".\n const finished = outcomes.filter((o) => o.status === \"ok\").length;\n if (lowMemory) {\n logger.warn(\n `Stopped early — low memory (${finished} asset(s) finished). Raise settings.maxMemoryMB or ` +\n `lower settings.concurrency, then re-run (already-finished assets are cached if --cache is on).`,\n );\n } else {\n logger.warn(`Interrupted — stopped cleanly (${finished} asset(s) finished).`);\n }\n process.exitCode = 0;\n return;\n }\n if (setupFailed) return; // error already reported; no summary to show\n\n printSummary(logger, outcomes, outDir);\n if (outcomes.some((outcome) => outcome.status === \"failed\")) {\n process.exitCode = 1;\n }\n}\n\n/** A live-tracker row handle the managed-server lifecycle drives (build/server steps). */\nfunction taskHandle(reporter: Reporter, id: string): TaskHandle {\n return {\n start: () => reporter.status(id, \"running\"),\n step: (t) => reporter.step(id, t),\n ok: () => reporter.status(id, \"ok\"),\n fail: () => reporter.status(id, \"failed\"),\n };\n}\n\nfunction normalizeAssetNames(value?: string | string[]): string[] | undefined {\n if (value == null) return undefined;\n const arr = (Array.isArray(value) ? value : [value]).map(String).filter(Boolean);\n return arr.length ? arr : undefined;\n}\n","import path from \"node:path\";\nimport { readFile, writeFile, rm } from \"node:fs/promises\";\nimport { spawn } from \"node:child_process\";\n\n/**\n * A small record of what a `pro-visu generate` run spawned, written to `<outDir>/.pro-visu-run.json`\n * and deleted on a clean exit. If the run is killed hard (Ctrl+C / crash), the file survives so\n * `pro-visu reset` can find and tear down the orphaned server + browser + temp dirs.\n */\nexport interface RunState {\n /** The `pro-visu generate` process id (used to tell \"still running\" from \"orphaned\"). */\n pid: number;\n startedAt: string;\n /** Root of the managed server's process tree (the shell we spawned) — the main orphan risk\n * (Playwright's browser is tied to this process's lifetime and exits on its own). */\n serverPid?: number;\n /** Temp working dirs to remove. */\n tmpDirs?: string[];\n}\n\nfunction runStatePath(outDir: string): string {\n return path.join(outDir, \".pro-visu-run.json\");\n}\n\nexport async function readRunState(outDir: string): Promise<RunState | null> {\n try {\n return JSON.parse(await readFile(runStatePath(outDir), \"utf8\")) as RunState;\n } catch {\n return null;\n }\n}\n\nasync function write(outDir: string, state: RunState): Promise<void> {\n try {\n await writeFile(runStatePath(outDir), `${JSON.stringify(state, null, 2)}\\n`);\n } catch {\n /* best-effort — tracking is a convenience, never fail the run over it */\n }\n}\n\nexport async function startRunState(outDir: string): Promise<void> {\n await write(outDir, { pid: process.pid, startedAt: new Date().toISOString(), tmpDirs: [] });\n}\n\n/** Merge a patch into the on-disk state (tmpDirs are appended, deduped). */\nexport async function updateRunState(outDir: string, patch: Partial<RunState>): Promise<void> {\n const cur = (await readRunState(outDir)) ?? {\n pid: process.pid,\n startedAt: new Date().toISOString(),\n };\n const tmpDirs = [...new Set([...(cur.tmpDirs ?? []), ...(patch.tmpDirs ?? [])])];\n await write(outDir, { ...cur, ...patch, tmpDirs });\n}\n\nexport async function clearRunState(outDir: string): Promise<void> {\n try {\n await rm(runStatePath(outDir), { force: true });\n } catch {\n /* already gone */\n }\n}\n\n/** Is a pid still running? */\nexport function isAlive(pid: number): boolean {\n if (!pid) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === \"EPERM\"; // exists but not ours to signal\n }\n}\n\n/** Kill a process and its descendants, cross-platform. Resolves to whether it was alive to kill. */\nexport function killTreeByPid(pid?: number): Promise<boolean> {\n return new Promise((resolve) => {\n if (!pid || !isAlive(pid)) return resolve(false);\n if (process.platform === \"win32\") {\n const killer = spawn(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], { stdio: \"ignore\" });\n killer.on(\"error\", () => resolve(false));\n killer.on(\"close\", () => resolve(true));\n return;\n }\n try {\n process.kill(-pid, \"SIGKILL\"); // detached children are group leaders → kills the tree\n } catch {\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n /* already gone */\n }\n }\n resolve(true);\n });\n}\n","import path from \"node:path\";\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport type { ResolvedServerSettings } from \"@/config/schema\";\nimport type { Logger } from \"@/utils/logger\";\n\n/** A started server we own and must tear down (null when we reused an existing one). */\nexport interface ServerHandle {\n stop: () => Promise<void>;\n /** Process id of the spawned server tree's root (for orphan cleanup after a hard kill). */\n pid?: number;\n}\n\nconst delay = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/** Resolve the readiness URL from explicit url or port. */\nexport function resolveServerUrl(server: ResolvedServerSettings): string {\n return server.url ?? `http://127.0.0.1:${server.port}`;\n}\n\n/** Single GET; resolves true if the server answered at all (any status code). */\nfunction probe(url: string): Promise<boolean> {\n return new Promise((resolve) => {\n const lib = url.startsWith(\"https:\") ? https : http;\n const req = lib.get(url, (res) => {\n res.resume();\n resolve((res.statusCode ?? 0) > 0);\n });\n req.on(\"error\", () => resolve(false));\n req.setTimeout(2000, () => {\n req.destroy();\n resolve(false);\n });\n });\n}\n\nasync function waitForUrl(url: string, timeoutMs: number, signal?: AbortSignal): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (signal?.aborted) return false;\n if (await probe(url)) return true;\n await delay(500);\n }\n return false;\n}\n\n/** A row in the live tracker the server lifecycle can drive (build / server steps). */\nexport interface TaskHandle {\n /** Mark the row active (spinner + ticking elapsed). */\n start(): void;\n step(text: string): void;\n ok(): void;\n fail(): void;\n}\n\nexport interface ServerTasks {\n build?: TaskHandle;\n server?: TaskHandle;\n}\n\n/**\n * Run a one-shot command (e.g. a build) to completion, rejecting on non-zero exit. With a\n * `task`, output is captured and the latest line feeds the tracker row (instead of streaming\n * raw to the terminal); the captured tail is included in the error on failure.\n */\nfunction runOnce(\n command: string,\n cwd: string,\n task?: TaskHandle,\n signal?: AbortSignal,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(new Error(\"Aborted.\"));\n const tracked = Boolean(task);\n const child = spawn(command, {\n cwd,\n shell: true,\n stdio: tracked ? [\"ignore\", \"pipe\", \"pipe\"] : \"inherit\",\n });\n let tail = \"\";\n const onData = (b: Buffer): void => {\n const text = b.toString();\n tail = (tail + text).slice(-4000);\n const last = text\n .split(/\\r?\\n/)\n .map((l) => l.trim())\n .filter(Boolean)\n .at(-1);\n if (last) task?.step(last);\n };\n if (tracked) {\n child.stdout?.on(\"data\", onData);\n child.stderr?.on(\"data\", onData);\n }\n const onAbort = (): void => {\n void killTree(child); // stop the build (and its tree) on a graceful interrupt\n reject(new Error(\"Aborted.\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n const settle = (fn: () => void): void => {\n signal?.removeEventListener(\"abort\", onAbort);\n fn();\n };\n child.on(\"error\", (err) => settle(() => reject(err)));\n child.on(\"close\", (code) =>\n settle(() =>\n code === 0\n ? resolve()\n : reject(\n new Error(\n `\"${command}\" failed (exit ${code}).${tracked ? `\\n${tail.slice(-1500)}` : \"\"}`,\n ),\n ),\n ),\n );\n });\n}\n\n/** Kill a process and its whole tree, cross-platform. */\nfunction killTree(child: ChildProcess): Promise<void> {\n return new Promise((resolve) => {\n if (child.pid == null || child.exitCode != null) return resolve();\n if (process.platform === \"win32\") {\n // The shell-spawned server has child processes (next → node); /T kills the tree.\n const killer = spawn(\"taskkill\", [\"/pid\", String(child.pid), \"/T\", \"/F\"], {\n stdio: \"ignore\",\n });\n killer.on(\"error\", () => resolve());\n killer.on(\"close\", () => resolve());\n } else {\n try {\n process.kill(-child.pid, \"SIGTERM\"); // negative pid = the process group\n } catch {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* already gone */\n }\n }\n resolve();\n }\n });\n}\n\n/**\n * Ensure a server is reachable for the capture. If one is already up (and reuseExisting),\n * returns null — we leave it alone. Otherwise optionally builds, starts the server, waits for\n * readiness, and returns a handle whose stop() tears it (and its children) down.\n */\nexport async function startManagedServer(\n server: ResolvedServerSettings,\n baseCwd: string,\n logger: Logger,\n tasks: ServerTasks = {},\n signal?: AbortSignal,\n): Promise<ServerHandle | null> {\n const cwd = server.cwd ? path.resolve(baseCwd, server.cwd) : baseCwd;\n const url = resolveServerUrl(server);\n\n // In live mode the rows convey progress; avoid untagged logs that would corrupt the block.\n const live = Boolean(tasks.build || tasks.server);\n\n if (server.reuseExisting && (await probe(url))) {\n if (live) {\n tasks.build?.step(\"reused\");\n tasks.build?.ok();\n tasks.server?.step(`reusing existing server at ${url}`);\n tasks.server?.ok();\n } else {\n logger.info(`Reusing the server already running at ${url}`);\n }\n return null;\n }\n\n if (server.build) {\n if (live) {\n tasks.build?.start();\n tasks.build?.step(`building (${server.build})…`);\n } else {\n logger.info(`Building: ${server.build}`);\n }\n try {\n await runOnce(server.build, cwd, tasks.build, signal);\n } catch (err) {\n tasks.build?.fail();\n throw err;\n }\n tasks.build?.ok();\n }\n\n if (live) {\n tasks.server?.start();\n tasks.server?.step(`starting (${server.command})…`);\n } else {\n logger.info(`Starting server: ${server.command}`);\n }\n // Pass the readiness port/host to the command as PORT/HOST so frameworks that honor them\n // (Next, Vite, …) bind exactly the port we probe — no need to repeat it in the command. An\n // explicit flag in the command (e.g. `next start -p 4000`) still wins.\n const probed = new URL(url);\n const child = spawn(server.command, {\n cwd,\n shell: true,\n stdio: \"ignore\",\n env: {\n ...process.env,\n PORT: probed.port || (probed.protocol === \"https:\" ? \"443\" : \"80\"),\n HOST: probed.hostname,\n },\n // POSIX: own process group so we can signal the whole tree. Windows uses taskkill /T.\n detached: process.platform !== \"win32\",\n });\n\n let exited = false;\n child.on(\"exit\", () => {\n exited = true;\n });\n\n tasks.server?.step(`waiting for ${url}…`);\n const ready = await waitForUrl(url, server.readyTimeoutMs, signal);\n if (!ready) {\n tasks.server?.fail();\n await killTree(child);\n throw new Error(\n signal?.aborted\n ? \"Aborted.\"\n : exited\n ? `Server command exited before ${url} became reachable.`\n : `Server did not become reachable at ${url} within ${server.readyTimeoutMs}ms.`,\n );\n }\n tasks.server?.step(`ready at ${url}`);\n tasks.server?.ok();\n if (!live) logger.success(`Server ready at ${url}`); // in live mode the row conveys this\n\n return {\n pid: child.pid,\n stop: async () => {\n logger.info(\"Shutting down server…\");\n await killTree(child);\n },\n };\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { mkdtemp } from \"node:fs/promises\";\nimport { launchBrowser } from \"@/pipeline/browser\";\nimport { createContext } from \"@/pipeline/context\";\nimport { buildGraph, dependenciesOf, expandSelection } from \"@/pipeline/graph\";\nimport { computeCacheKey } from \"@/pipeline/cache\";\nimport type { Reporter } from \"@/pipeline/reporter\";\nimport { getGenerator } from \"@/generators/registry\";\nimport { ManifestStore } from \"@/manifest/manifest\";\nimport { ensureDir, removeDir } from \"@/utils/fs\";\nimport { sha256File } from \"@/utils/hash\";\nimport type { ResolvedAssetSpec, ResolvedConfig } from \"@/config/schema\";\nimport type { Logger } from \"@/utils/logger\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport interface AssetOutcome {\n name: string;\n generator: string;\n status: \"ok\" | \"failed\";\n records: AssetRecord[];\n error?: Error;\n /** True when the asset was served from cache (skipped, unchanged). */\n cached?: boolean;\n /** True when the asset was aborted in-flight by a cancel (not a real failure). */\n cancelled?: boolean;\n}\n\nexport interface RunOptions {\n config: ResolvedConfig;\n /** Absolute output dir. */\n outDir: string;\n logger: Logger;\n toolVersion: string;\n /** Restrict to these asset names (undefined/empty = all). */\n assetNames?: string[];\n /** Override settings.concurrency. */\n concurrency?: number;\n /** Override settings.quality. */\n quality?: \"draft\" | \"final\";\n /** Override settings.cache (skip unchanged assets). */\n cache?: boolean;\n /** Live progress sink (job tracker). Optional. */\n reporter?: Reporter;\n /** Aborts the run gracefully: stop launching new assets and let in-flight ones finish. */\n signal?: AbortSignal;\n /** Reports the run's temp working dir so the caller can track it for cleanup after a hard kill. */\n onResources?: (info: { tmpDir: string }) => void;\n}\n\n/**\n * Draft trades fidelity for iteration speed: fewer frames, no retina scale, looser quality.\n * Applied to the common video-option names shared across generators before validation.\n */\nexport function applyQuality(\n options: Record<string, unknown>,\n quality: \"draft\" | \"final\",\n): Record<string, unknown> {\n if (quality !== \"draft\") return options;\n const o = { ...options };\n if (typeof o.fps === \"number\") o.fps = Math.min(o.fps as number, 15);\n if (typeof o.deviceScaleFactor === \"number\") o.deviceScaleFactor = 1;\n if (typeof o.crf === \"number\") o.crf = Math.max(o.crf as number, 30);\n return o;\n}\n\n/**\n * Generate each selected asset, isolating failures so one bad asset never aborts the run.\n * Assets form a dependency DAG (via `inputs`): a producer runs before its consumers, and its\n * output file is exposed to them. Independent assets run up to `concurrency` at a time; one\n * shared browser + one manifest store for the whole run.\n */\nexport async function runPipeline(opts: RunOptions): Promise<AssetOutcome[]> {\n applyDerivedInputs(opts.config); // generators that declare deps via options (e.g. wall columns)\n buildGraph(opts.config.assets); // validate refs + reject cycles up front\n const specs = expandSelection(opts.config.assets, opts.assetNames);\n if (specs.length === 0) return [];\n\n await ensureDir(opts.outDir);\n const manifest = await ManifestStore.load(opts.outDir);\n const tmpRoot = await mkdtemp(path.join(os.tmpdir(), \"pro-visu-\"));\n const browser = await launchBrowser(opts.config.settings.browser);\n opts.onResources?.({ tmpDir: tmpRoot });\n const concurrency = Math.max(1, opts.concurrency ?? opts.config.settings.concurrency);\n const quality = opts.quality ?? opts.config.settings.quality;\n const cacheEnabled = opts.cache ?? opts.config.settings.cache;\n const reporter = opts.reporter;\n for (const s of specs) {\n reporter?.add({ id: s.name, name: s.name, detail: s.generator, deps: dependenciesOf(s) });\n }\n\n const outcomes = new Map<string, AssetOutcome>();\n /** Primary output (absolute path) per completed asset, for consumers' `inputs`. */\n const primaryOutput = new Map<string, string>();\n /** Primary output contentHash per completed asset, for consumers' cache keys. */\n const primaryHash = new Map<string, string>();\n\n const recordDone = (name: string, record: AssetRecord | undefined): void => {\n if (!record) return;\n primaryOutput.set(name, path.resolve(opts.outDir, record.file));\n primaryHash.set(name, record.contentHash);\n };\n\n const runSpec = async (spec: ResolvedAssetSpec): Promise<AssetOutcome> => {\n const log = opts.logger.withTag(spec.name);\n try {\n const generator = getGenerator(spec.generator);\n if (!generator) throw new Error(`Unknown generator \"${spec.generator}\".`);\n\n const resolvedInputs: Record<string, string> = {};\n const inputHashes: Record<string, string> = {};\n for (const [slot, dep] of Object.entries(spec.inputs)) {\n const file = primaryOutput.get(dep);\n if (!file) throw new Error(`Input \"${slot}\" (asset \"${dep}\") produced no file.`);\n resolvedInputs[slot] = file;\n inputHashes[slot] = primaryHash.get(dep) ?? \"\";\n }\n\n const merged = applyQuality(\n mergeGeneratorOptions(opts.config.settings.defaults, spec),\n quality,\n );\n const options = generator.optionsSchema.parse(merged);\n\n // Hash the content of declared file dependencies (e.g. fonts) into the cache key, so\n // editing the file regenerates the asset. Missing files fail here, early and clearly,\n // instead of producing a blank render from a 404'd URL later.\n let fileHashes: Record<string, string> | undefined;\n for (const dep of generator.fileDependencies?.(options) ?? []) {\n const resolved = path.isAbsolute(dep) ? dep : path.resolve(process.cwd(), dep);\n try {\n (fileHashes ??= {})[resolved] = await sha256File(resolved);\n } catch {\n throw new Error(`File dependency not found: ${dep}`);\n }\n }\n\n const cacheKey = computeCacheKey({\n generator: spec.generator,\n url: spec.url,\n options,\n inputs: inputHashes,\n files: fileHashes,\n quality,\n toolVersion: opts.toolVersion,\n });\n\n if (cacheEnabled) {\n const existing = manifest.find(spec.name);\n if (\n existing?.cacheKey === cacheKey &&\n existsSync(path.resolve(opts.outDir, existing.file))\n ) {\n log.info(\"cached — unchanged, skipped\");\n reporter?.status(spec.name, \"cached\");\n recordDone(spec.name, existing);\n return {\n name: spec.name,\n generator: spec.generator,\n status: \"ok\",\n records: [existing],\n cached: true,\n };\n }\n }\n\n reporter?.status(spec.name, \"running\");\n\n const ctx = await createContext({\n browser,\n generatorId: generator.id,\n target: { name: spec.name, url: spec.url },\n resolvedInputs,\n outDir: opts.outDir,\n tmpDir: tmpRoot,\n logger: log,\n toolVersion: opts.toolVersion,\n quality,\n manifest,\n onProgress: reporter ? (v) => reporter.progress(spec.name, v) : undefined,\n signal: opts.signal,\n });\n\n const result = await generator.run(ctx, options);\n // Stamp the cache key onto produced records (primary first) so reruns can skip.\n for (const record of result.assets) {\n record.cacheKey = cacheKey;\n await manifest.upsert(record);\n }\n recordDone(spec.name, result.assets[0]);\n reporter?.status(spec.name, \"ok\");\n return { name: spec.name, generator: spec.generator, status: \"ok\", records: result.assets };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n // A cancelled run aborts in-flight work mid-flight: that's an expected stop, not a failure —\n // don't log a scary error or flag the row red; mark it cancelled and move on.\n if (opts.signal?.aborted) {\n return { name: spec.name, generator: spec.generator, status: \"failed\", records: [], error: err, cancelled: true };\n }\n log.error(err.message);\n reporter?.status(spec.name, \"failed\");\n return { name: spec.name, generator: spec.generator, status: \"failed\", records: [], error: err };\n }\n };\n\n try {\n await scheduleDag(specs, concurrency, outcomes, runSpec, reporter, opts.signal);\n return specs.map((s) => outcomes.get(s.name)).filter((o): o is AssetOutcome => Boolean(o));\n } finally {\n // The caller owns begin()/stop() (it spans the build/server setup rows too).\n await browser.close();\n await removeDir(tmpRoot);\n }\n}\n\n/**\n * Run a dependency DAG with a concurrency cap: a spec starts once all its dependencies have\n * completed `ok`; if any dependency failed, the spec is skipped (recorded as failed). Cycles\n * were already rejected by buildGraph, so this always drains.\n */\nasync function scheduleDag(\n specs: ResolvedAssetSpec[],\n concurrency: number,\n outcomes: Map<string, AssetOutcome>,\n runSpec: (spec: ResolvedAssetSpec) => Promise<AssetOutcome>,\n reporter?: Reporter,\n signal?: AbortSignal,\n): Promise<void> {\n const remaining = new Map(specs.map((s) => [s.name, s]));\n const inflight = new Map<string, Promise<void>>();\n\n const depState = (spec: ResolvedAssetSpec): \"ready\" | \"blocked\" | \"failed\" => {\n let ready = true;\n for (const dep of dependenciesOf(spec)) {\n const r = outcomes.get(dep);\n if (!r) ready = false;\n else if (r.status === \"failed\") return \"failed\";\n }\n return ready ? \"ready\" : \"blocked\";\n };\n\n while (remaining.size > 0 || inflight.size > 0) {\n // Graceful stop: don't launch anything new; let in-flight assets finish, then drain. Un-started\n // assets are simply omitted from the results (not marked failed).\n if (signal?.aborted) remaining.clear();\n for (const [name, spec] of [...remaining]) {\n if (inflight.size >= concurrency) break;\n const state = depState(spec);\n if (state === \"blocked\") continue;\n remaining.delete(name);\n if (state === \"failed\") {\n outcomes.set(name, {\n name,\n generator: spec.generator,\n status: \"failed\",\n records: [],\n error: new Error(\"Skipped — a dependency failed.\"),\n });\n reporter?.status(name, \"failed\");\n continue;\n }\n const p = runSpec(spec)\n .then((outcome) => {\n outcomes.set(name, outcome);\n })\n .finally(() => {\n inflight.delete(name);\n });\n inflight.set(name, p);\n }\n\n if (inflight.size > 0) {\n await Promise.race(inflight.values());\n } else if (remaining.size > 0) {\n // No work in flight and nothing became ready — only possible if every remaining spec is\n // blocked by a skipped dep. Re-loop resolves them as \"failed\"; guard against a spin.\n const stuck = [...remaining.values()].every((s) => depState(s) === \"blocked\");\n if (stuck) break;\n }\n }\n}\n\n/**\n * Merge each generator's option-derived dependencies (`deriveInputs`) into its asset's `inputs`,\n * in place, before the graph is built — so producers are ordered ahead of consumers that declared\n * their dependencies through options (e.g. a wall whose columns reference assets by name). Author-\n * declared `inputs` win on conflict. Options that fail to parse here are skipped; `runSpec`'s own\n * parse surfaces the real validation error for that asset.\n */\nexport function applyDerivedInputs(config: ResolvedConfig): void {\n for (const spec of config.assets) {\n const generator = getGenerator(spec.generator);\n if (!generator?.deriveInputs) continue;\n let options: unknown;\n try {\n options = generator.optionsSchema.parse(mergeGeneratorOptions(config.settings.defaults, spec));\n } catch {\n continue;\n }\n spec.inputs = { ...generator.deriveInputs(options), ...spec.inputs };\n }\n}\n\n/**\n * Merge a spec's options on top of the repo's per-generator defaults. Defaults are keyed\n * by generator id (the same string used in `assets[].generator`); per-asset options win.\n */\nexport function mergeGeneratorOptions(\n defaults: Record<string, Record<string, unknown>>,\n spec: ResolvedAssetSpec,\n): Record<string, unknown> {\n const generatorDefaults = defaults[spec.generator] ?? {};\n return { ...generatorDefaults, ...spec.options };\n}\n","import { chromium } from \"playwright-core\";\nimport type { Browser } from \"playwright-core\";\nimport type { ResolvedBrowserSettings } from \"@/config/schema\";\n\n/** Launch Chromium with the repo's browser settings. */\nexport async function launchBrowser(settings: ResolvedBrowserSettings): Promise<Browser> {\n return chromium.launch({\n headless: settings.headless,\n channel: settings.channel,\n executablePath: settings.executablePath,\n args: settings.args,\n timeout: settings.timeout,\n });\n}\n","import path from \"node:path\";\nimport type { Browser } from \"playwright-core\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { generatorDir, relPosix } from \"@/utils/paths\";\nimport type { Logger } from \"@/utils/logger\";\nimport type { ManifestStore } from \"@/manifest/manifest\";\nimport type { AssetTarget, PipelineContext } from \"@/generators/types\";\n\nexport interface CreateContextArgs {\n browser: Browser;\n generatorId: string;\n target: AssetTarget;\n /** Absolute file paths of declared `inputs`, keyed by slot name. */\n resolvedInputs: Record<string, string>;\n /** Absolute output root. */\n outDir: string;\n tmpDir: string;\n logger: Logger;\n toolVersion: string;\n quality: \"draft\" | \"final\";\n manifest: ManifestStore;\n /** Forwarded to the live dashboard as this asset's progress (0–1). */\n onProgress?: (value: number) => void;\n /** Aborts in-flight work when the run is cancelled. */\n signal?: AbortSignal;\n}\n\n/** Build a per-(generator, target) context, ensuring the generator's output subdir exists. */\nexport async function createContext(args: CreateContextArgs): Promise<PipelineContext> {\n const genDir = generatorDir(args.outDir, args.generatorId);\n await ensureDir(genDir);\n\n return {\n browser: args.browser,\n target: args.target,\n resolvedInputs: args.resolvedInputs,\n outDir: args.outDir,\n resolveOutPath: (filename) => path.join(genDir, filename),\n toManifestPath: (absPath) => relPosix(args.outDir, absPath),\n tmpDir: args.tmpDir,\n logger: args.logger,\n toolVersion: args.toolVersion,\n quality: args.quality,\n writeAsset: (record) => args.manifest.upsert(record),\n progress: args.onProgress,\n signal: args.signal,\n };\n}\n","import type { ResolvedAssetSpec } from \"@/config/schema\";\n\n/** Unique dependency asset-names for a spec (the values of its `inputs` map). */\nexport function dependenciesOf(spec: ResolvedAssetSpec): string[] {\n return [...new Set(Object.values(spec.inputs))];\n}\n\n/**\n * Validate the asset dependency graph: every `inputs` reference must name a real asset, and\n * there must be no cycles. Throws a descriptive error otherwise. Returns the spec list by name.\n */\nexport function buildGraph(specs: ResolvedAssetSpec[]): Map<string, ResolvedAssetSpec> {\n const byName = new Map(specs.map((s) => [s.name, s]));\n\n for (const spec of specs) {\n for (const [slot, dep] of Object.entries(spec.inputs)) {\n if (!byName.has(dep)) {\n throw new Error(\n `Asset \"${spec.name}\" input \"${slot}\" references unknown asset \"${dep}\".`,\n );\n }\n if (dep === spec.name) {\n throw new Error(`Asset \"${spec.name}\" cannot depend on itself.`);\n }\n }\n }\n\n assertAcyclic(specs);\n return byName;\n}\n\n/** DFS cycle check; throws with the offending cycle path. */\nfunction assertAcyclic(specs: ResolvedAssetSpec[]): void {\n const byName = new Map(specs.map((s) => [s.name, s]));\n const state = new Map<string, \"visiting\" | \"done\">();\n const stack: string[] = [];\n\n const visit = (name: string): void => {\n const s = state.get(name);\n if (s === \"done\") return;\n if (s === \"visiting\") {\n const from = stack.indexOf(name);\n const cycle = [...stack.slice(from), name].join(\" → \");\n throw new Error(`Cyclic asset dependency: ${cycle}.`);\n }\n state.set(name, \"visiting\");\n stack.push(name);\n const spec = byName.get(name);\n if (spec) for (const dep of dependenciesOf(spec)) visit(dep);\n stack.pop();\n state.set(name, \"done\");\n };\n\n for (const spec of specs) visit(spec.name);\n}\n\n/**\n * Given a user selection of asset names, expand it to include every transitive dependency\n * (you can't build an asset without its inputs). Preserves config order. Unknown selected\n * names are ignored here (the caller warns); unknown *dependencies* are caught by buildGraph.\n */\nexport function expandSelection(\n specs: ResolvedAssetSpec[],\n names: string[] | undefined,\n): ResolvedAssetSpec[] {\n if (!names || names.length === 0) return specs;\n const byName = new Map(specs.map((s) => [s.name, s]));\n const wanted = new Set<string>();\n\n const add = (name: string): void => {\n if (wanted.has(name)) return;\n const spec = byName.get(name);\n if (!spec) return;\n wanted.add(name);\n for (const dep of dependenciesOf(spec)) add(dep);\n };\n for (const name of names) add(name);\n\n return specs.filter((s) => wanted.has(s.name));\n}\n","import { createHash } from \"node:crypto\";\n\nexport interface CacheKeyParts {\n generator: string;\n url?: string;\n /** Fully-resolved generator options. */\n options: unknown;\n /** Slot name → producing asset's output contentHash. */\n inputs: Record<string, string>;\n /**\n * Resolved path → content hash of the generator's declared file dependencies (e.g. fonts).\n * Pass `undefined` when there are none — stableStringify drops undefined entries, keeping keys\n * byte-identical for generators that don't use the feature.\n */\n files?: Record<string, string>;\n quality: string;\n toolVersion: string;\n}\n\n/** Deterministic JSON: object keys sorted recursively so the hash is stable. */\nfunction stableStringify(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`);\n return `{${entries.join(\",\")}}`;\n}\n\n/**\n * Fingerprint of everything that determines an asset's output: its generator, options, the\n * content hashes of its inputs, the quality profile, and the tool version (which covers tool\n * + scene-app changes). A stable key here means the cached output is still valid.\n */\nexport function computeCacheKey(parts: CacheKeyParts): string {\n return createHash(\"sha256\").update(stableStringify(parts)).digest(\"hex\");\n}\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { ensureDir } from \"@/utils/fs\";\nimport {\n emptyManifest,\n manifestSchema,\n type AssetRecord,\n type Manifest,\n} from \"@/manifest/schema\";\n\nfunction manifestPath(outDir: string): string {\n return path.join(outDir, \"manifest.json\");\n}\n\n/** Read + validate the manifest, returning an empty one if none exists. */\nexport async function readManifest(outDir: string): Promise<Manifest> {\n const file = manifestPath(outDir);\n if (!existsSync(file)) return emptyManifest();\n\n const text = await readFile(file, \"utf8\");\n let json: unknown;\n try {\n json = JSON.parse(text);\n } catch {\n throw new Error(`Corrupt manifest (invalid JSON): ${file}`);\n }\n const parsed = manifestSchema.safeParse(json);\n if (!parsed.success) {\n throw new Error(`Corrupt manifest (schema mismatch): ${file}`);\n }\n return parsed.data;\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** Lock errors the tmp→rename swap can hit transiently on Windows (held handles, AV scans). */\nconst TRANSIENT_RENAME_CODES = new Set([\"EPERM\", \"EACCES\", \"EBUSY\"]);\n\n/**\n * Atomically write the manifest (sorted by id for stable diffs).\n *\n * The tmp→rename swap can fail with EPERM/EACCES/EBUSY on Windows when another process holds a\n * brief handle on the target — the managed server serving `public/`, an editor, or antivirus\n * scanning the just-written file. Retry the rename a few times with backoff, then fall back to a\n * direct (non-atomic) overwrite, so a long generation run never dies on a flaky lock.\n */\nexport async function writeManifest(outDir: string, manifest: Manifest): Promise<void> {\n await ensureDir(outDir);\n const file = manifestPath(outDir);\n const tmp = `${file}.tmp`;\n const sorted: Manifest = {\n ...manifest,\n assets: [...manifest.assets].sort((a, b) => a.id.localeCompare(b.id)),\n };\n const contents = `${JSON.stringify(sorted, null, 2)}\\n`;\n await writeFile(tmp, contents, \"utf8\");\n\n const maxAttempts = 5;\n for (let attempt = 1; ; attempt++) {\n try {\n await rename(tmp, file);\n return;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (!code || !TRANSIENT_RENAME_CODES.has(code)) throw err;\n if (attempt < maxAttempts) {\n await sleep(attempt * 50); // 50,100,150,200ms backoff\n continue;\n }\n // The lock outlived our retries — overwrite in place (non-atomic) and clean up the tmp.\n await writeFile(file, contents, \"utf8\");\n await rm(tmp, { force: true });\n return;\n }\n }\n}\n\n/**\n * In-memory manifest with serialized, idempotent (by id) upserts flushed to disk.\n * Serializing through a promise chain keeps concurrent generators from racing on the file.\n */\nexport class ManifestStore {\n private chain: Promise<void> = Promise.resolve();\n\n private constructor(\n private readonly outDir: string,\n private readonly manifest: Manifest,\n ) {}\n\n static async load(outDir: string): Promise<ManifestStore> {\n return new ManifestStore(outDir, await readManifest(outDir));\n }\n\n /** Replace an existing record with the same id, or append a new one; then flush. */\n upsert(record: AssetRecord): Promise<void> {\n this.chain = this.chain.then(async () => {\n const index = this.manifest.assets.findIndex((a) => a.id === record.id);\n if (index >= 0) this.manifest.assets[index] = record;\n else this.manifest.assets.push(record);\n await writeManifest(this.outDir, this.manifest);\n });\n return this.chain;\n }\n\n get records(): readonly AssetRecord[] {\n return this.manifest.assets;\n }\n\n /** Look up the existing record for an asset id (for cache checks). */\n find(id: string): AssetRecord | undefined {\n return this.manifest.assets.find((a) => a.id === id);\n }\n}\n","import { z } from \"zod\";\n\n/** One generated asset, recorded in pro-visu/manifest.json. Keyed by `id` (the asset name). */\nconst assetRecordSchema = z.object({\n /** Unique asset id — the asset spec's `name`. */\n id: z.string(),\n generator: z.string(),\n sourceUrl: z.string(),\n /** Path relative to the output dir, forward-slashed. */\n file: z.string(),\n format: z.string(),\n width: z.number().int(),\n height: z.number().int(),\n durationMs: z.number().optional(),\n bytes: z.number().int(),\n /** sha256 of the output file. */\n contentHash: z.string(),\n createdAt: z.string(),\n toolVersion: z.string(),\n /** Inputs+options+tool fingerprint; lets `--cache` skip unchanged assets. */\n cacheKey: z.string().optional(),\n});\nexport type AssetRecord = z.infer<typeof assetRecordSchema>;\n\nexport const manifestSchema = z.object({\n version: z.literal(1),\n assets: z.array(assetRecordSchema),\n});\nexport type Manifest = z.infer<typeof manifestSchema>;\n\nexport function emptyManifest(): Manifest {\n return { version: 1, assets: [] };\n}\n","import { render, type Instance } from \"ink\";\nimport type { JobStatus, Reporter, RowInit } from \"@/pipeline/reporter\";\nimport { DashboardStore } from \"./store\";\nimport { Dashboard } from \"./Dashboard\";\n\n/** No-op reporter for non-TTY / CI / --verbose: logs print normally, nothing is rendered. */\nexport class NoopReporter implements Reporter {\n readonly isLive = false;\n begin(): void {}\n add(): void {}\n status(): void {}\n step(): void {}\n progress(): void {}\n cancelling(): void {}\n stop(): void {}\n route(): boolean {\n return false;\n }\n}\n\n/**\n * Live terminal dashboard backed by Ink (React). The {@link DashboardStore} holds run state; this\n * class drives it through the {@link Reporter} API and owns the Ink render lifecycle. Tagged\n * generator logs become a row's current step; everything else is committed above the dashboard.\n */\nexport class InkReporter implements Reporter {\n readonly isLive = true;\n private readonly store = new DashboardStore();\n private instance?: Instance;\n private stopped = false;\n private onInterrupt?: () => void;\n\n constructor(private readonly out: NodeJS.WriteStream = process.stdout) {}\n\n /** Let the dashboard own Esc/Ctrl+C; calls this trigger on each press. Call before begin(). */\n attachInput(onInterrupt: () => void): void {\n this.onInterrupt = onInterrupt;\n }\n\n begin(): void {\n if (this.instance || this.stopped) return;\n this.instance = render(<Dashboard store={this.store} onInterrupt={() => this.onInterrupt?.()} />, {\n stdout: this.out,\n // We route logs through the dashboard ourselves, so Ink shouldn't also patch console.\n patchConsole: false,\n // Esc/Ctrl+C is handled inside the dashboard via useInput; Ink must not exit on its own.\n exitOnCtrlC: false,\n });\n }\n\n add(row: RowInit): void {\n this.store.add(row);\n }\n\n status(id: string, status: JobStatus): void {\n this.store.status(id, status);\n }\n\n step(id: string, text: string): void {\n this.store.step(id, text);\n }\n\n progress(id: string, value: number): void {\n this.store.progress(id, value);\n }\n\n cancelling(reason?: string): void {\n this.store.cancelling(reason);\n }\n\n route(tag: string, type: string, message: string): boolean {\n if (this.stopped) return false; // after teardown, let logs print normally\n if (tag && this.store.has(tag)) {\n this.store.step(tag, message);\n return true;\n }\n this.store.log(type, message);\n return true;\n }\n\n stop(): void {\n if (this.stopped) return;\n this.stopped = true;\n // Erase the live box before the final summary prints (committed Static logs stay in scrollback).\n // clear() + unmount() run synchronously so the animation timer can't redraw between them.\n this.instance?.clear();\n this.instance?.unmount();\n }\n}\n\n/**\n * Pick the live dashboard on an interactive TTY (unless --verbose, which wants full logs).\n * `SHOWCASE_LIVE=1` forces it on (e.g. terminals that mis-report TTY); `=0` forces it off.\n */\nexport function createReporter(opts: { tty: boolean; verbose: boolean }): Reporter {\n const forced = process.env.SHOWCASE_LIVE;\n if (forced === \"0\") return new NoopReporter();\n if (forced === \"1\") return new InkReporter();\n return opts.tty && !opts.verbose ? new InkReporter() : new NoopReporter();\n}\n","import type { JobStatus, RowInit } from \"@/pipeline/reporter\";\n\n/** A row's live state: its init data plus where it is in the run and its current step text. */\nexport interface JobView extends RowInit {\n status: JobStatus;\n step: string;\n startedAt?: number;\n endedAt?: number;\n /** Fractional progress (0–1) while running, if the generator reports it; else undefined. */\n progress?: number;\n}\n\n/** A log line committed above the dashboard (untagged logs and logs for unknown tags). */\nexport interface LogLine {\n /** Monotonic id so React keys stay stable as the list grows. */\n key: number;\n /** consola log type (info/warn/error/success/…) — drives the line's color. */\n type: string;\n message: string;\n}\n\n/** An immutable view of the whole run, handed to React via useSyncExternalStore. */\nexport interface DashboardSnapshot {\n jobs: JobView[];\n logs: LogLine[];\n startTime: number;\n cancelRequested: boolean;\n /** Optional banner verb shown while cancelling (e.g. \"low memory — stopping…\"). */\n cancelReason?: string;\n}\n\n/**\n * The mutable run state behind the live dashboard, deliberately decoupled from React: the Ink\n * `<Dashboard>` subscribes to it with `useSyncExternalStore`, and {@link InkReporter} drives it\n * through the {@link Reporter} API. Each mutation rebuilds an immutable {@link DashboardSnapshot};\n * `getSnapshot` returns the same reference until the next change, so React's referential check\n * stays stable across spinner re-renders (which read state but never mutate it).\n */\nexport class DashboardStore {\n private jobs = new Map<string, JobView>();\n private order: string[] = [];\n private logs: LogLine[] = [];\n private logSeq = 0;\n private cancelRequested = false;\n private cancelReason?: string;\n private readonly startTime: number;\n private snap: DashboardSnapshot;\n private readonly listeners = new Set<() => void>();\n\n constructor(now: number = Date.now()) {\n this.startTime = now;\n this.snap = this.build();\n }\n\n /** Subscribe to changes (useSyncExternalStore). Returns an unsubscribe. */\n readonly subscribe = (cb: () => void): (() => void) => {\n this.listeners.add(cb);\n return () => void this.listeners.delete(cb);\n };\n\n /** The current immutable snapshot (stable between mutations). */\n readonly getSnapshot = (): DashboardSnapshot => this.snap;\n\n has(id: string): boolean {\n return this.jobs.has(id);\n }\n\n private build(): DashboardSnapshot {\n return {\n jobs: this.order\n .map((id) => this.jobs.get(id))\n .filter((v): v is JobView => Boolean(v)),\n logs: this.logs,\n startTime: this.startTime,\n cancelRequested: this.cancelRequested,\n cancelReason: this.cancelReason,\n };\n }\n\n private commit(): void {\n this.snap = this.build();\n for (const cb of this.listeners) cb();\n }\n\n add(row: RowInit): void {\n if (this.jobs.has(row.id)) return;\n // The waiting reason is derived at render time from gate/dep state, so it stays accurate.\n this.jobs.set(row.id, { ...row, deps: row.deps ?? [], status: \"waiting\", step: \"\" });\n this.order.push(row.id);\n this.commit();\n }\n\n status(id: string, status: JobStatus, now: number = Date.now()): void {\n const j = this.jobs.get(id);\n if (!j) return;\n if (status === \"running\" && j.status !== \"running\") {\n j.startedAt = now;\n if (!j.step || j.step === \"queued\" || j.step.startsWith(\"waiting\")) j.step = \"starting…\";\n }\n if (status === \"ok\" || status === \"failed\" || status === \"cached\") {\n j.endedAt = now;\n if (status === \"cached\") j.step = \"cached\";\n else if (status === \"ok\" && (j.step === \"\" || j.step === \"starting…\" || j.step === \"queued\"))\n j.step = \"done\";\n }\n j.status = status;\n this.commit();\n }\n\n step(id: string, text: string): void {\n const j = this.jobs.get(id);\n if (!j) return;\n j.step = text;\n this.commit();\n }\n\n progress(id: string, value: number): void {\n const j = this.jobs.get(id);\n if (!j) return;\n j.progress = Math.max(0, Math.min(1, value));\n this.commit();\n }\n\n /** Commit a log line above the dashboard. */\n log(type: string, message: string): void {\n this.logs = [...this.logs, { key: this.logSeq++, type, message }];\n this.commit();\n }\n\n /** Flip into the \"cancelling…\" state (idempotent). An optional reason customizes the banner verb. */\n cancelling(reason?: string): void {\n if (this.cancelRequested) return;\n this.cancelRequested = true;\n this.cancelReason = reason;\n this.commit();\n }\n}\n","import { useEffect, useState, useSyncExternalStore, type ReactElement } from \"react\";\nimport { Box, Static, Text, useInput, useStdin, useWindowSize } from \"ink\";\nimport type { DashboardStore } from \"./store\";\nimport { buildView, type DashboardVM, type RowVM, type TallyCell } from \"./view-model\";\n\n/** Spinner / progress animation cadence. */\nconst TICK_MS = 90;\n/** Don't let the dashboard box grow wider than this even on very wide terminals. */\nconst MAX_WIDTH = 100;\n\nfunction Row({ row, vm }: { row: RowVM; vm: DashboardVM }): ReactElement {\n // Fixed columns are flexShrink={0} so only the step (flexGrow) absorbs overflow and truncates —\n // otherwise Ink's flexbox shrinks the name/detail columns under a long step and they misalign.\n return (\n <Box paddingLeft={1}>\n <Box flexShrink={0} marginRight={1}>\n <Text color={row.glyph.color} dimColor={row.glyph.dim}>\n {row.glyph.text}\n </Text>\n </Box>\n <Box flexShrink={0} width={vm.nameWidth} marginRight={1}>\n <Text bold wrap=\"truncate-end\">\n {row.name}\n </Text>\n </Box>\n {vm.showDetail ? (\n <Box flexShrink={0} width={vm.detailWidth} marginRight={1}>\n <Text dimColor wrap=\"truncate-end\">\n {row.detail}\n </Text>\n </Box>\n ) : null}\n {vm.showProgress ? (\n <Box flexShrink={0} marginRight={1}>\n <Text color={row.progress.color} dimColor={row.progress.dim}>\n {row.progress.text}\n </Text>\n </Box>\n ) : null}\n <Box flexGrow={1} flexShrink={1}>\n <Text color={row.step.color} dimColor={row.step.dim} wrap=\"truncate-end\">\n {row.step.text}\n </Text>\n </Box>\n {row.elapsed ? (\n <Box flexShrink={0} marginLeft={1}>\n <Text dimColor>{row.elapsed}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction Tally({ cells }: { cells: TallyCell[] }): ReactElement {\n return (\n <Box>\n {cells.map((c, i) => (\n <Box key={c.text}>\n {i > 0 ? <Text dimColor> · </Text> : null}\n <Text color={c.color}>{c.text}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction Section({\n label,\n rows,\n vm,\n tally,\n before = 0,\n after = 0,\n}: {\n label: string;\n rows: RowVM[];\n vm: DashboardVM;\n tally?: TallyCell[];\n /** Rows hidden above / below this window (for the \"N more\" markers). */\n before?: number;\n after?: number;\n}): ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box justifyContent=\"space-between\">\n <Text bold dimColor>\n {label}\n </Text>\n {tally && tally.length > 0 ? <Tally cells={tally} /> : null}\n </Box>\n {before > 0 ? <Text dimColor>{` ↑ ${before} more above`}</Text> : null}\n {rows.map((r) => (\n <Row key={r.id} row={r} vm={vm} />\n ))}\n {after > 0 ? <Text dimColor>{` ↓ ${after} more below`}</Text> : null}\n </Box>\n );\n}\n\nfunction Header({ vm }: { vm: DashboardVM }): ReactElement {\n const { done, total, cached } = vm.overall;\n return (\n <Box justifyContent=\"space-between\">\n <Text bold>pro-visu</Text>\n <Box>\n <Text dimColor>{`${done}/${total} done`}</Text>\n {cached > 0 ? <Text dimColor>{` · ${cached} cached`}</Text> : null}\n <Text dimColor>{` ${vm.elapsed}`}</Text>\n </Box>\n </Box>\n );\n}\n\n/** The live run dashboard. Reads run state from the store; animates a spinner/bar on a timer. */\nexport function Dashboard({\n store,\n onInterrupt,\n}: {\n store: DashboardStore;\n onInterrupt?: () => void;\n}): ReactElement {\n const snapshot = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n const [frame, setFrame] = useState(0);\n // Manual scroll position; null = auto-follow the running rows (the default).\n const [viewStart, setViewStart] = useState<number | null>(null);\n useEffect(() => {\n const timer = setInterval(() => setFrame((f) => f + 1), TICK_MS);\n return () => clearInterval(timer);\n }, []);\n\n const { isRawModeSupported } = useStdin();\n const { columns, rows } = useWindowSize();\n const width = columns > 0 ? Math.min(columns, MAX_WIDTH) : undefined;\n const vm = buildView(\n snapshot,\n frame,\n Date.now(),\n columns > 0 ? width : undefined,\n rows > 0 ? rows : undefined,\n viewStart,\n );\n\n const scrollable = vm.assetsBefore > 0 || vm.assetsAfter > 0;\n // Ink owns keyboard input while live (one keypress owner), so the caller's watcher handles only\n // signals. Esc/Ctrl+C cancels; ↑/↓ + PgUp/PgDn scroll the asset window (which suspends auto-follow);\n // `f` returns to following the running rows. Gated on raw-mode support so a forced dashboard on a\n // non-TTY doesn't throw.\n useInput(\n (input, key) => {\n if (key.escape || (key.ctrl && input === \"c\")) {\n onInterrupt?.();\n return;\n }\n const here = viewStart ?? vm.assetsBefore; // current top, whether auto or manual\n const page = Math.max(1, vm.assets.length);\n if (key.upArrow) setViewStart(Math.max(0, here - 1));\n else if (key.downArrow) setViewStart(Math.min(vm.maxStart, here + 1));\n else if (key.pageUp) setViewStart(Math.max(0, here - page));\n else if (key.pageDown) setViewStart(Math.min(vm.maxStart, here + page));\n else if (input === \"f\") setViewStart(null);\n },\n { isActive: isRawModeSupported },\n );\n\n const manual = viewStart !== null;\n let footerText = vm.footer.text;\n if (!vm.cancelling && isRawModeSupported && scrollable) {\n footerText += manual ? \" · ↑↓ scroll · f follow\" : \" · ↑↓ scroll\";\n }\n\n return (\n <>\n {/* Committed above the live box, in scrollback — completed logs that won't change. */}\n <Static items={vm.logs}>\n {(line) => (\n <Text key={line.key} color={line.color} dimColor={line.dim}>\n {line.text}\n </Text>\n )}\n </Static>\n\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={vm.cancelling ? \"yellow\" : \"gray\"}\n borderDimColor={!vm.cancelling}\n paddingX={1}\n width={width}\n >\n <Header vm={vm} />\n\n {vm.setup.length > 0 ? <Section label=\"SETUP\" rows={vm.setup} vm={vm} /> : null}\n\n <Section\n label=\"ASSETS\"\n rows={vm.assets}\n vm={vm}\n tally={vm.tally}\n before={vm.assetsBefore}\n after={vm.assetsAfter}\n />\n\n <Box marginTop={1}>\n <Text\n color={vm.footer.tone === \"warn\" ? \"yellow\" : undefined}\n dimColor={vm.footer.tone === \"dim\"}\n wrap=\"truncate-end\"\n >\n {footerText}\n </Text>\n </Box>\n </Box>\n </>\n );\n}\n","import type { LogSink } from \"@/utils/logger\";\n\nexport type JobStatus = \"waiting\" | \"running\" | \"ok\" | \"failed\" | \"cached\";\n\nexport interface RowInit {\n /** Stable id; for assets this is the asset name (so tagged logs route to it). */\n id: string;\n /** Primary label. */\n name: string;\n /** Dim secondary column — the generator id, or \"server\" for setup rows. */\n detail: string;\n /** Ids this row waits on (assets it depends on). */\n deps?: string[];\n /** Setup row ids (build/server) that must finish before this row can start. */\n gatedBy?: string[];\n /** Setup rows (build/server) — shown, but excluded from the asset tally. */\n system?: boolean;\n}\n\n/**\n * A progress sink driven across a whole run: setup steps (build/server) and then each asset as\n * it moves through the DAG. Implemented by the live terminal tracker (and a no-op for\n * non-TTY/CI). Also a {@link LogSink} so tagged generator logs become a row's current step.\n * The caller owns begin()/stop(); rows are added incrementally with add().\n */\nexport interface Reporter extends LogSink {\n /** True for the live renderer; false for the no-op. */\n readonly isLive: boolean;\n begin(): void;\n add(row: RowInit): void;\n status(id: string, status: JobStatus): void;\n step(id: string, text: string): void;\n /** Report fractional progress (0–1) for a running row, for a determinate bar + ETA. */\n progress(id: string, value: number): void;\n /** Signal a graceful cancellation is underway, so the UI can show it's winding down. An optional\n * reason replaces the default \"cancelling…\" banner verb (e.g. \"low memory — stopping…\"). */\n cancelling(reason?: string): void;\n stop(): void;\n /**\n * Hand the live renderer a cancel trigger so it can own keyboard input (Esc/Ctrl+C) itself —\n * one keypress owner, no clash with the signal/keypress watcher. No-op renderers don't implement\n * it (the caller keeps its own keypress watching).\n */\n attachInput?(onInterrupt: () => void): void;\n}\n\n/** mm:ss for a millisecond duration (pure — unit-tested). */\nexport function formatElapsed(ms: number): string {\n const sec = Math.max(0, Math.round(ms / 1000));\n return `${Math.floor(sec / 60)}:${String(sec % 60).padStart(2, \"0\")}`;\n}\n\n/** Truncate to a visible width with an ellipsis (pure — unit-tested). */\nexport function truncate(text: string, width: number): string {\n if (width <= 0) return \"\";\n if (text.length <= width) return text;\n return `${text.slice(0, Math.max(0, width - 1))}…`;\n}\n","import { formatElapsed } from \"@/pipeline/reporter\";\nimport type { DashboardSnapshot, JobView } from \"./store\";\n\n/** Braille spinner frames for running rows. */\nexport const SPINNER = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\n\n/** Width of the bar itself, in cells. */\nconst BAR_WIDTH = 8;\n/** Full width of the progress column: bar + space + a right-aligned \"NN%\". */\nexport const PROGRESS_COL = BAR_WIDTH + 4;\nconst BAR_FULL = \"▓\";\nconst BAR_EMPTY = \"░\";\n\n/** Below these terminal widths, columns drop out so rows never wrap. */\nconst HIDE_DETAIL_BELOW = 76;\nconst HIDE_PROGRESS_BELOW = 60;\n\n/** A chalk color name Ink understands, or undefined for the terminal default. */\nexport type Tone = string | undefined;\n\n/** Text + how to paint it (Ink `<Text>` props). */\nexport interface Paint {\n text: string;\n color?: Tone;\n dim?: boolean;\n}\n\n/** A committed log line plus a stable React key. */\nexport interface LogVM extends Paint {\n key: number;\n}\n\n/** A fully-derived row, ready to lay out. */\nexport interface RowVM {\n id: string;\n glyph: Paint;\n name: string;\n detail: string;\n /** The progress column (fixed width PROGRESS_COL): bar, plus \"NN%\" when determinate. */\n progress: Paint;\n step: Paint;\n /** Elapsed, with a \"~ETA\" suffix once a determinate row is underway. */\n elapsed: string;\n}\n\nexport interface TallyCell {\n text: string;\n color: Tone;\n}\n\nexport interface DashboardVM {\n /** Total run elapsed (mm:ss), shown in the header. */\n elapsed: string;\n /** Header rollup across assets: done/total + cached. */\n overall: { done: number; total: number; cached: number };\n setup: RowVM[];\n /** The asset rows to render — a window around the active rows when they don't all fit the height. */\n assets: RowVM[];\n /** Asset rows hidden above / below the visible window (0 when everything fits). */\n assetsBefore: number;\n assetsAfter: number;\n /** Largest valid window start (0 when everything fits) — lets the UI clamp manual scrolling. */\n maxStart: number;\n /** Running / waiting / failed cells for the ASSETS section header (done lives in the rollup). */\n tally: TallyCell[];\n footer: { text: string; tone: \"dim\" | \"warn\" };\n cancelling: boolean;\n /** Column widths so setup + asset rows align. */\n nameWidth: number;\n detailWidth: number;\n /** Adaptive flags: which columns fit the current terminal width. */\n showDetail: boolean;\n showProgress: boolean;\n logs: LogVM[];\n}\n\nconst isDone = (v?: JobView): boolean =>\n Boolean(v && (v.status === \"ok\" || v.status === \"cached\"));\n\n/** Collapse newlines/tabs so routed (multi-line) output renders as one row. */\nconst oneLine = (s: string): string => s.replace(/[\\r\\n\\t]+/g, \" \");\n\n/** Live \"why is this waiting\" text: gates first (build/server), then asset deps, then queued. */\nexport function waitingReason(v: JobView, jobs: Map<string, JobView>): string {\n const label = (id: string): string => jobs.get(id)?.name ?? id;\n const gates = (v.gatedBy ?? []).filter((id) => !isDone(jobs.get(id)));\n if (gates.length) return `waiting for ${gates.map(label).join(\", \")}`;\n const deps = (v.deps ?? []).filter((id) => !isDone(jobs.get(id)));\n if (deps.length) return `waiting on ${deps.map(label).join(\", \")}`;\n return \"queued\";\n}\n\nexport function glyph(v: JobView, frame: number, cancelling: boolean): Paint {\n switch (v.status) {\n case \"running\":\n return { text: SPINNER[frame % SPINNER.length] ?? \"◐\", color: cancelling ? \"yellow\" : \"cyan\" };\n case \"ok\":\n return { text: \"✓\", color: \"green\" };\n case \"failed\":\n return { text: \"✗\", color: \"red\" };\n case \"cached\":\n return { text: \"⊙\", color: \"blue\" };\n default:\n return { text: \"·\", dim: true };\n }\n}\n\n/**\n * The progress column (fixed width {@link PROGRESS_COL} so the step column stays aligned).\n * - done/cached/failed → a full bar (green/blue/red).\n * - running with a reported fraction → a determinate bar + \"NN%\".\n * - running with no fraction → an indeterminate cell that ping-pongs across the bar.\n * - waiting → an empty bar.\n */\nexport function progressColumn(v: JobView, frame: number, cancelling: boolean): Paint {\n const w = BAR_WIDTH;\n const pad = (s: string): string => s.padEnd(PROGRESS_COL);\n const live: Tone = cancelling ? \"yellow\" : \"cyan\";\n\n switch (v.status) {\n case \"ok\":\n return { text: pad(BAR_FULL.repeat(w)), color: \"green\" };\n case \"cached\":\n return { text: pad(BAR_FULL.repeat(w)), color: \"blue\" };\n case \"failed\":\n return { text: pad(BAR_FULL.repeat(w)), color: \"red\" };\n case \"running\": {\n if (typeof v.progress === \"number\") {\n const filled = Math.max(0, Math.min(w, Math.round(v.progress * w)));\n const bar = BAR_FULL.repeat(filled) + BAR_EMPTY.repeat(w - filled);\n const pct = `${Math.min(99, Math.floor(v.progress * 100))}%`.padStart(3);\n return { text: `${bar} ${pct}`, color: live };\n }\n const span = Math.max(1, w * 2 - 2); // ping-pong period over [0, w-1]\n const m = frame % span;\n const pos = m < w ? m : span - m;\n const cells = Array.from({ length: w }, (_, i) => (i === pos ? BAR_FULL : BAR_EMPTY)).join(\"\");\n return { text: pad(cells), color: live };\n }\n default:\n return { text: pad(BAR_EMPTY.repeat(w)), dim: true };\n }\n}\n\n/** Elapsed for a row, plus a \"~ETA\" suffix once a determinate row is meaningfully underway. */\nfunction rowElapsed(v: JobView, now: number): string {\n if (v.status === \"waiting\") return \"\";\n const dur = (v.endedAt ?? now) - (v.startedAt ?? now);\n const base = formatElapsed(dur);\n if (v.status === \"running\" && typeof v.progress === \"number\" && v.progress > 0.02 && v.progress < 1) {\n const eta = (dur * (1 - v.progress)) / v.progress;\n return `${base} ~${formatElapsed(eta)}`;\n }\n return base;\n}\n\nfunction rowOf(v: JobView, jobs: Map<string, JobView>, frame: number, now: number, cancelling: boolean): RowVM {\n const rawStep = v.status === \"waiting\" ? waitingReason(v, jobs) : v.step;\n const step: Paint =\n v.status === \"failed\"\n ? { text: oneLine(rawStep), color: \"red\" }\n : v.status === \"running\"\n ? { text: oneLine(rawStep) }\n : { text: oneLine(rawStep), dim: true };\n return {\n id: v.id,\n glyph: glyph(v, frame, cancelling),\n name: v.name,\n detail: v.detail,\n progress: progressColumn(v, frame, cancelling),\n step,\n elapsed: rowElapsed(v, now),\n };\n}\n\nconst LOG_TONE: Record<string, Paint> = {\n error: { text: \"\", color: \"red\" },\n fail: { text: \"\", color: \"red\" },\n warn: { text: \"\", color: \"yellow\" },\n success: { text: \"\", color: \"green\" },\n debug: { text: \"\", dim: true },\n};\n\n/** Derive everything the `<Dashboard>` needs from a snapshot. Pure — unit-tested. */\nexport function buildView(\n snapshot: DashboardSnapshot,\n frame: number,\n now: number,\n columns?: number,\n rows?: number,\n /** Manual window start (row index) for scrolling; null/undefined = auto-follow the running rows. */\n viewStart?: number | null,\n): DashboardVM {\n const jobs = new Map(snapshot.jobs.map((j) => [j.id, j]));\n const cancelling = snapshot.cancelRequested;\n\n const nameWidth = Math.min(22, Math.max(4, ...snapshot.jobs.map((j) => j.name.length)));\n const detailWidth = Math.min(14, Math.max(6, ...snapshot.jobs.map((j) => j.detail.length)));\n\n const setup: RowVM[] = [];\n const assets: RowVM[] = [];\n for (const j of snapshot.jobs) {\n (j.system ? setup : assets).push(rowOf(j, jobs, frame, now, cancelling));\n }\n\n // Counts cover assets only; setup rows (build/server) are shown but not counted.\n const assetJobs = snapshot.jobs.filter((j) => !j.system);\n const cached = assetJobs.filter((j) => j.status === \"cached\").length;\n const done = assetJobs.filter((j) => j.status === \"ok\" || j.status === \"cached\").length;\n const running = assetJobs.filter((j) => j.status === \"running\").length;\n const waiting = assetJobs.filter((j) => j.status === \"waiting\").length;\n const failed = assetJobs.filter((j) => j.status === \"failed\").length;\n\n const tally: TallyCell[] = [];\n if (running) tally.push({ text: `${running} running`, color: \"cyan\" });\n if (waiting) tally.push({ text: `${waiting} waiting`, color: undefined });\n if (failed) tally.push({ text: `${failed} failed`, color: \"red\" });\n\n const verb = snapshot.cancelReason ?? \"cancelling…\";\n const footer: DashboardVM[\"footer\"] = cancelling\n ? { text: running ? `${verb} finishing ${running} · esc to force` : `${verb} · esc to force`, tone: \"warn\" }\n : { text: \"esc to cancel\", tone: \"dim\" };\n\n const logs: LogVM[] = snapshot.logs.map((l) => ({\n ...(LOG_TONE[l.type] ?? {}),\n key: l.key,\n text: oneLine(l.message),\n }));\n\n // Keep the live block within the terminal height. Rendering more lines than the terminal has rows\n // makes Ink's in-place redraw fight the terminal's own scroll (the rows \"jiggle\"), so when the asset\n // rows don't all fit, show a window around the active rows and mark how many are hidden above/below.\n let visibleAssets = assets;\n let assetsBefore = 0;\n let assetsAfter = 0;\n let maxStart = 0;\n if (rows && rows > 0) {\n // Fixed chrome: borders(2) + header(1) + ASSETS margin+label(2) + footer margin+line(2) = 7,\n // plus the setup block (margin+label+rows) when present, plus a 1-row safety margin.\n const chrome = 7 + (setup.length > 0 ? 2 + setup.length : 0) + 1;\n const budget = Math.max(1, rows - chrome);\n if (assets.length > budget) {\n const show = Math.max(1, budget - 2); // reserve up to two \"N more\" marker lines\n maxStart = assets.length - show;\n let start: number;\n if (typeof viewStart === \"number\") {\n // Manual scroll: honor the requested start, clamped to the list.\n start = Math.max(0, Math.min(viewStart, maxStart));\n } else {\n // Auto-follow: centre the window on the running rows (or the next waiting one).\n const runningPos: number[] = [];\n assetJobs.forEach((j, i) => {\n if (j.status === \"running\") runningPos.push(i);\n });\n let anchor: number;\n if (runningPos.length) {\n anchor = Math.floor((runningPos[0]! + runningPos[runningPos.length - 1]!) / 2);\n } else {\n const firstWaiting = assetJobs.findIndex((j) => j.status === \"waiting\");\n anchor = firstWaiting >= 0 ? firstWaiting : assetJobs.length - 1;\n }\n start = Math.max(0, Math.min(anchor - Math.floor(show / 2), maxStart));\n }\n visibleAssets = assets.slice(start, start + show);\n assetsBefore = start;\n assetsAfter = assets.length - (start + show);\n }\n }\n\n return {\n elapsed: formatElapsed(now - snapshot.startTime),\n overall: { done, total: assetJobs.length, cached },\n setup,\n assets: visibleAssets,\n assetsBefore,\n assetsAfter,\n maxStart,\n tally,\n footer,\n cancelling,\n nameWidth,\n detailWidth,\n showDetail: columns === undefined || columns >= HIDE_DETAIL_BELOW,\n showProgress: columns === undefined || columns >= HIDE_PROGRESS_BELOW,\n logs,\n };\n}\n","import type { ReactElement } from \"react\";\nimport { Box, Text, renderToString } from \"ink\";\nimport path from \"node:path\";\nimport { formatBytes } from \"@/utils/format\";\nimport type { AssetOutcome } from \"@/pipeline/runner\";\n\n/** A flattened summary line — one per produced record, or one per failed asset. */\ninterface Line {\n key: string;\n glyph: { text: string; color: string };\n name: string;\n /** dimensions / format / size, or the error message for failures. */\n meta: string;\n metaDim: boolean;\n}\n\nfunction toLines(outcomes: AssetOutcome[]): Line[] {\n const lines: Line[] = [];\n for (const o of outcomes) {\n if (o.status === \"failed\") {\n lines.push({\n key: o.name,\n glyph: { text: \"✗\", color: \"red\" },\n name: o.name,\n meta: o.error?.message ?? \"failed\",\n metaDim: false,\n });\n continue;\n }\n const glyph = o.cached ? { text: \"⊙\", color: \"blue\" } : { text: \"✓\", color: \"green\" };\n for (const r of o.records) {\n lines.push({\n key: `${o.name}:${r.file}`,\n glyph,\n name: o.name,\n meta: o.cached\n ? `${r.width}×${r.height} ${r.format} cached`\n : `${r.width}×${r.height} ${r.format} ${formatBytes(r.bytes)}`,\n metaDim: true,\n });\n }\n }\n return lines;\n}\n\nfunction Summary({ outcomes, outDir }: { outcomes: AssetOutcome[]; outDir: string }): ReactElement {\n const lines = toLines(outcomes);\n const nameWidth = Math.min(28, Math.max(4, ...lines.map((l) => l.name.length)));\n\n const ok = outcomes.filter((o) => o.status === \"ok\");\n const generated = ok.filter((o) => !o.cached).length;\n const cached = ok.filter((o) => o.cached).length;\n const failed = outcomes.length - ok.length;\n const totalBytes = ok.reduce((sum, o) => sum + o.records.reduce((s, r) => s + r.bytes, 0), 0);\n\n const totals: { text: string; color?: string }[] = [\n { text: `${generated} generated`, color: \"green\" },\n ];\n if (cached) totals.push({ text: `${cached} cached`, color: \"blue\" });\n totals.push({ text: `${failed} failed`, color: failed ? \"red\" : undefined });\n totals.push({ text: formatBytes(totalBytes), color: undefined });\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor={failed ? \"yellow\" : \"green\"} borderDimColor paddingX={1}>\n <Box justifyContent=\"space-between\">\n <Text bold>{failed ? \"done with failures\" : \"done\"}</Text>\n <Text dimColor>{outDir}</Text>\n </Box>\n <Box flexDirection=\"column\" marginTop={1}>\n {lines.map((l) => (\n <Box key={l.key}>\n <Box flexShrink={0} marginRight={1}>\n <Text color={l.glyph.color}>{l.glyph.text}</Text>\n </Box>\n <Box flexShrink={0} width={nameWidth} marginRight={1}>\n <Text bold wrap=\"truncate-end\">\n {l.name}\n </Text>\n </Box>\n <Box flexGrow={1}>\n <Text color={l.metaDim ? undefined : \"red\"} dimColor={l.metaDim} wrap=\"truncate-end\">\n {l.meta}\n </Text>\n </Box>\n </Box>\n ))}\n </Box>\n <Box marginTop={1}>\n {totals.map((c, i) => (\n <Box key={c.text}>\n {i > 0 ? <Text dimColor> · </Text> : null}\n <Text color={c.color}>{c.text}</Text>\n </Box>\n ))}\n </Box>\n </Box>\n );\n}\n\n/** Render the final results panel to a string (static — safe to print after the live dashboard stops). */\nexport function renderSummary(outcomes: AssetOutcome[], outDir: string, columns = 80): string {\n const display = path.isAbsolute(outDir) ? path.relative(process.cwd(), outDir) || outDir : outDir;\n return renderToString(<Summary outcomes={outcomes} outDir={display} />, {\n columns: Math.max(40, Math.min(columns, 100)),\n });\n}\n","/** Human-readable byte size (e.g. 2.4 MB). */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n const units = [\"KB\", \"MB\", \"GB\"];\n let value = bytes / 1024;\n let i = 0;\n while (value >= 1024 && i < units.length - 1) {\n value /= 1024;\n i += 1;\n }\n return `${value.toFixed(1)} ${units[i]}`;\n}\n","import { ConfigNotFoundError, ConfigValidationError } from \"@/config/load\";\nimport type { Logger } from \"@/utils/logger\";\nimport type { AssetOutcome } from \"@/pipeline/runner\";\nimport { renderSummary } from \"@/cli/dashboard/Summary\";\n\n/** Print a friendly, actionable message for config load/validation errors. */\nexport function reportConfigError(logger: Logger, err: unknown): void {\n if (err instanceof ConfigNotFoundError) {\n logger.error(err.message);\n return;\n }\n if (err instanceof ConfigValidationError) {\n logger.error(`Invalid config${err.file ? ` (${err.file})` : \"\"}:`);\n for (const issue of err.zodError.issues) {\n const where = issue.path.length ? issue.path.join(\".\") : \"(root)\";\n logger.error(` • ${where}: ${issue.message}`);\n }\n return;\n }\n logger.error(err instanceof Error ? err.message : String(err));\n}\n\n/** Print the final results panel (a rendered Ink summary) and a one-line note when nothing matched. */\nexport function printSummary(\n logger: Logger,\n outcomes: AssetOutcome[],\n outDir: string,\n): void {\n if (outcomes.length === 0) {\n logger.warn(\"No assets matched.\");\n return;\n }\n logger.log(\"\");\n logger.log(renderSummary(outcomes, outDir, process.stdout.columns ?? 80));\n}\n","import pc from \"picocolors\";\nimport { resolveCwd, resolveOutDir } from \"@/utils/paths\";\nimport { createLogger } from \"@/utils/logger\";\nimport { loadShowcaseConfig, ConfigNotFoundError } from \"@/config/load\";\nimport { readManifest } from \"@/manifest/manifest\";\nimport { DEFAULT_OUTDIR } from \"@/config/defaults\";\nimport { reportConfigError } from \"@/cli/ui\";\nimport { formatBytes } from \"@/utils/format\";\n\nexport interface ListOptions {\n cwd?: string;\n config?: string;\n}\n\nexport async function runList(options: ListOptions = {}): Promise<void> {\n const cwd = resolveCwd(options.cwd);\n const logger = createLogger(\"info\");\n\n let outDir: string;\n try {\n const { config } = await loadShowcaseConfig({ cwd, configFile: options.config });\n outDir = resolveOutDir(cwd, config.settings.outDir);\n } catch (err) {\n if (err instanceof ConfigNotFoundError) {\n outDir = resolveOutDir(cwd, DEFAULT_OUTDIR);\n } else {\n reportConfigError(logger, err);\n process.exitCode = 1;\n return;\n }\n }\n\n const manifest = await readManifest(outDir);\n if (manifest.assets.length === 0) {\n logger.info(\"Nothing generated yet. Run `pro-visu generate`.\");\n return;\n }\n\n logger.log(pc.bold(`Assets in ${outDir}:`));\n logger.log(\"\");\n for (const asset of manifest.assets) {\n logger.log(\n ` ${pc.bold(asset.id)} ${pc.dim(asset.generator)} ${pc.cyan(asset.file)} ` +\n `${pc.dim(`${asset.width}×${asset.height}`)} ${pc.dim(formatBytes(asset.bytes))}`,\n );\n }\n logger.log(\"\");\n logger.log(pc.dim(`${manifest.assets.length} asset(s)`));\n}\n","import path from \"node:path\";\nimport { resolveCwd, resolveOutDir } from \"@/utils/paths\";\nimport { createLogger } from \"@/utils/logger\";\nimport { loadShowcaseConfig } from \"@/config/load\";\nimport { removeDir } from \"@/utils/fs\";\nimport { readRunState, clearRunState, isAlive, killTreeByPid } from \"@/cli/run-state\";\n\nexport interface ResetOptions {\n cwd?: string;\n config?: string;\n /** Clean up even if a run still looks active. */\n force?: boolean;\n}\n\n/**\n * Tear down whatever a previous `generate` left running when it was killed hard (Ctrl+C/crash):\n * the managed server and browser process trees, plus its temp working dirs, recorded in the\n * run-state file. A clean exit removes that file, so there's normally nothing to do.\n */\nexport async function runReset(options: ResetOptions = {}): Promise<void> {\n const cwd = resolveCwd(options.cwd);\n const log = createLogger(\"info\");\n\n // The run-state file lives under the output dir; fall back to the default if config can't load.\n let outDir = path.join(cwd, \"pro-visu\");\n try {\n const { config } = await loadShowcaseConfig({ cwd, configFile: options.config });\n outDir = resolveOutDir(cwd, config.settings.outDir);\n } catch {\n /* no/invalid config — still try the default output dir */\n }\n\n const state = await readRunState(outDir);\n if (!state) {\n log.success(\"Nothing to reset — no interrupted run recorded.\");\n return;\n }\n if (isAlive(state.pid) && !options.force) {\n log.warn(\n `A run looks active (pid ${state.pid}). If it's stuck, re-run with --force to clean up anyway.`,\n );\n return;\n }\n\n let killed = 0;\n if (await killTreeByPid(state.serverPid)) {\n killed += 1;\n log.info(`Stopped orphaned server (pid ${state.serverPid}).`);\n }\n let removed = 0;\n for (const dir of state.tmpDirs ?? []) {\n await removeDir(dir);\n removed += 1;\n }\n await clearRunState(outDir);\n\n log.success(\n `Reset complete — stopped ${killed} process tree(s), removed ${removed} temp dir(s).`,\n );\n}\n"],"mappings":"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","names":["path","existsSync","readFile","writeFile","path","path","require","cli","path","existsSync","rm","spawn","createRequire","path","spawn","rm","writeFile","spawn","path","writeFile","rm","require","createRequire","existsSync","spawn","path","rm","path","stat","z","path","sleep","path","path","path","PREWARM_SETTLE_MS","path","readFile","path","path","mkdtemp","mkdtemp","path","sleep","stat","workers","path","baseName","writeFile","z","run","writeFile","z","z","z","z","z","path","copyFile","stat","path","stat","parsed","path","mkdtemp","mkdtemp","path","path","copyFile","stat","run","run","path","readFile","writeFile","imageSize","z","z","fontDataUrl","path","readFile","run","writeFile","imageSize","z","fieldEnum","z","hexString","run","path","existsSync","copyFile","readFile","stat","imageSize","z","run","path","existsSync","copyFile","readFile","stat","imageSize","path","writeFile","path","writeFile","writeFile","path","existsSync","readFile","path","existsSync","readFileSync","path","existsSync","rc","parsed","readFileSync","spawn","v8","path","readFile","writeFile","rm","spawn","path","http","spawn","os","path","existsSync","mkdtemp","chromium","path","path","createHash","path","existsSync","readFile","rename","rm","writeFile","z","path","existsSync","readFile","parsed","writeFile","rename","rm","mkdtemp","path","os","existsSync","jsx","Box","Text","path","jsx","jsxs","glyph","Box","Text","path","v8","path","path"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts","../../src/version.ts","../../src/cli/update-check.ts","../../src/cli/commands/init.ts","../../src/utils/paths.ts","../../src/utils/fs.ts","../../src/utils/logger.ts","../../src/browser-install/ensure-chromium.ts","../../src/media/ensure-ffmpeg.ts","../../src/media/ffmpeg.ts","../../src/config/defaults.ts","../../src/config/json-schema.ts","../../src/config/schema.ts","../../src/generators/scroll-reel/index.ts","../../src/generators/scroll-reel/options.ts","../../src/generators/scroll-reel/capture.ts","../../src/generators/scroll-reel/scroll.ts","../../src/media/frame-capture.ts","../../src/generators/scroll-reel/clean-capture.ts","../../src/generators/scroll-reel/annotations.ts","../../src/generators/scroll-reel/timeline.ts","../../src/generators/scroll-reel/capture-frames.ts","../../src/generators/scroll-reel/variants.ts","../../src/generators/scroll-reel/outputs.ts","../../src/generators/scroll-reel/cards.ts","../../src/utils/hash.ts","../../src/generators/scroll-reel/interaction.ts","../../src/generators/require-url.ts","../../src/generators/screenshots/index.ts","../../src/generators/screenshots/options.ts","../../src/utils/concurrency.ts","../../src/generators/screenshots/capture.ts","../../src/generators/wall/options.ts","../../src/generators/scene/scene-options.ts","../../src/generators/specimen/options.ts","../../src/generators/scene/index.ts","../../src/scene/serve.ts","../../src/scene/capture-realtime.ts","../../src/scene/capture-frames.ts","../../src/generators/wall/index.ts","../../src/generators/specimen/index.ts","../../src/generators/palette/index.ts","../../src/generators/palette/options.ts","../../src/generators/palette/color.ts","../../src/generators/palette/html.ts","../../src/generators/palette-reel/options.ts","../../src/generators/palette-reel/index.ts","../../src/generators/image/index.ts","../../src/generators/image/options.ts","../../src/generators/registry.ts","../../src/cli/commands/schema.ts","../../src/config/load.ts","../../src/config/resolve-targets.ts","../../src/cli/interrupt.ts","../../src/cli/reexec.ts","../../src/cli/commands/generate.ts","../../src/cli/run-state.ts","../../src/server/manage-server.ts","../../src/pipeline/runner.ts","../../src/pipeline/browser.ts","../../src/pipeline/context.ts","../../src/pipeline/graph.ts","../../src/pipeline/cache.ts","../../src/manifest/manifest.ts","../../src/manifest/schema.ts","../../src/cli/dashboard/index.tsx","../../src/cli/dashboard/store.ts","../../src/cli/dashboard/Dashboard.tsx","../../src/pipeline/reporter.ts","../../src/cli/dashboard/view-model.ts","../../src/cli/dashboard/Summary.tsx","../../src/utils/format.ts","../../src/cli/ui.ts","../../src/cli/commands/list.ts","../../src/cli/commands/reset.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { cac } from \"cac\";\nimport { TOOL_VERSION } from \"@/version\";\nimport { checkForUpdates } from \"@/cli/update-check\";\nimport { runInit } from \"@/cli/commands/init\";\nimport { runGenerate } from \"@/cli/commands/generate\";\nimport { runList } from \"@/cli/commands/list\";\nimport { runReset } from \"@/cli/commands/reset\";\nimport { runSchema } from \"@/cli/commands/schema\";\n\nconst cli = cac(\"pro-visu\");\n\ncli\n .command(\"init\", \"Scaffold config, gitignore the output dir, and ensure a browser\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .option(\"--no-script\", \"Do not add a package.json script\")\n .option(\"--skip-browser\", \"Do not install Chromium\")\n .option(\"--json\", \"Scaffold a dependency-free JSON config + JSON Schema (for npx / global use)\")\n .action(runInit);\n\ncli\n .command(\"generate\", \"Generate showcase assets defined in your config\")\n .alias(\"gen\")\n .option(\"--config <path>\", \"Path to a config file\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .option(\"--asset <name>\", \"Only generate these assets (repeatable)\")\n .option(\"--concurrency <n>\", \"Override parallelism\")\n .option(\"--skip-browser\", \"Skip the Chromium check/install\")\n .option(\"--skip-server\", \"Skip the managed server (use an already-running site)\")\n .option(\"--skip-build\", \"Skip the server build step (fast iteration when the site is unchanged)\")\n .option(\"--draft\", \"Draft quality: faster, lower-fidelity renders for iteration\")\n .option(\"--cache\", \"Skip assets whose inputs+options are unchanged\")\n .option(\"--verbose\", \"Verbose (debug) logging\")\n .action(runGenerate);\n\ncli\n .command(\"list\", \"List assets recorded in the manifest\")\n .alias(\"ls\")\n .option(\"--config <path>\", \"Path to a config file\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .action(runList);\n\ncli\n .command(\"schema\", \"Write a JSON Schema for pro-visu.config.json (editor autocomplete)\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .option(\"--out <path>\", \"Output path (default pro-visu.schema.json)\")\n .action(runSchema);\n\ncli\n .command(\"reset\", \"Clean up orphaned processes/temp from an interrupted run\")\n .option(\"--config <path>\", \"Path to a config file\")\n .option(\"--cwd <dir>\", \"Working directory\")\n .option(\"--force\", \"Clean up even if a run still looks active\")\n .action(runReset);\n\ncli.help();\ncli.version(TOOL_VERSION);\n\nconst parsed = cli.parse(process.argv, { run: false });\n\nasync function main(): Promise<void> {\n // Fire-and-forget: arms a deferred \"newer version on npm\" notice (best-effort, non-blocking).\n checkForUpdates();\n if (!cli.matchedCommand) {\n // --help / --version were already handled by parse(); otherwise show help.\n if (!parsed.options.help && !parsed.options.version) cli.outputHelp();\n return;\n }\n await cli.runMatchedCommand();\n}\n\nmain().catch((err: unknown) => {\n process.exitCode = 1;\n console.error(err instanceof Error ? (err.stack ?? err.message) : String(err));\n});\n","// Replaced at build time by tsup `define`. Falls back for tests / ts-node runs.\ndeclare const __TOOL_VERSION__: string | undefined;\n\nexport const TOOL_VERSION: string =\n typeof __TOOL_VERSION__ !== \"undefined\" ? __TOOL_VERSION__ : \"0.0.0-dev\";\n","import updateNotifier from \"update-notifier\";\nimport { TOOL_VERSION } from \"@/version\";\n\nconst PKG_NAME = \"pro-visu\";\n\n/**\n * Best-effort \"a newer version is on npm\" notice.\n *\n * update-notifier does the registry lookup in a detached, unref'd background process and persists\n * the result, so this call never blocks, slows, or fails a command — it just reads the previous\n * run's cached result and, if a newer version exists, prints a notice when the process exits.\n *\n * It self-suppresses where a notice would be unwelcome: in CI, in non-interactive (non-TTY) output,\n * when `NO_UPDATE_NOTIFIER` is set, with the `--no-update-notifier` flag, under `NODE_ENV=test`, or\n * when the user has opted out via configstore. We additionally skip the unpublished dev build, which\n * has no meaningful version to compare against.\n */\nexport function checkForUpdates(version: string = TOOL_VERSION): void {\n if (version === \"0.0.0-dev\") return;\n try {\n updateNotifier({\n pkg: { name: PKG_NAME, version },\n // Most people run `pro-visu` from a package.json script — surface the notice there too.\n shouldNotifyInNpmScript: true,\n }).notify({\n // Defer so the notice prints after the command finishes (the last thing on screen).\n defer: true,\n // {updateCommand} is install-mode aware (update-notifier detects global vs local), so the\n // suggested command is right whether the tool is a dev-dep or a global install.\n message: \"Update available {currentVersion} → {latestVersion}\\nRun {updateCommand} to update\",\n });\n } catch {\n // An update check must never break the CLI.\n }\n}\n","import path from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { resolveCwd } from \"@/utils/paths\";\nimport { ensureDir, ensureGitignoreEntry, pathExists } from \"@/utils/fs\";\nimport { createLogger } from \"@/utils/logger\";\nimport { ensureChromium } from \"@/browser-install/ensure-chromium\";\nimport { ensureFfmpeg } from \"@/media/ensure-ffmpeg\";\nimport { DEFAULT_OUTDIR } from \"@/config/defaults\";\nimport { serializeConfigJsonSchema } from \"@/config/json-schema\";\nimport { DEFAULT_SCHEMA_FILE } from \"@/cli/commands/schema\";\n\nconst CONFIG_FILES = [\n \"pro-visu.config.ts\",\n \"pro-visu.config.js\",\n \"pro-visu.config.mjs\",\n \"pro-visu.config.cjs\",\n \"pro-visu.config.json\",\n \".pro-visurc\",\n \".pro-visurc.json\",\n];\n\nconst CONFIG_TEMPLATE = `import { defineConfig } from \"pro-visu\";\n\n// URL the capture assets point at. The optional managed server (below) binds this port.\nconst URL = \"http://localhost:3101\";\n\nexport default defineConfig({\n settings: {\n outDir: \"pro-visu\",\n concurrency: 2,\n // Uses Playwright's managed Chromium by default. Set channel: \"chrome\" to use your\n // installed Chrome, or args: [\"--no-sandbox\"] on CI.\n browser: { headless: true },\n // Optional: let the tool build + start your site, wait for it, capture, then stop it.\n // 'port' defaults to 3101 (off the common 3000 dev port); your command must bind it.\n // server: {\n // build: \"npm run build\",\n // command: \"npm start -- -p 3101\", // e.g. Next: \"npx next start -p 3101\"\n // port: 3101,\n // },\n defaults: {\n // Keyed by generator id. Merged underneath each asset's own options.\n \"scroll-reel\": { width: 1440, height: 900, fps: 30 },\n },\n },\n assets: [\n {\n name: \"home-reel\",\n url: URL,\n generator: \"scroll-reel\",\n // options: { duration: 7000, waitForSelector: \"main\" },\n },\n // A looping type-specimen from a font file (no URL needed):\n // {\n // name: \"font-specimen\",\n // generator: \"specimen\",\n // options: { font: \"public/fonts/YourFont.woff2\", name: \"Your Font\", template: \"sweep\" },\n // },\n ],\n});\n`;\n\n// A dependency-free JSON config + a sibling JSON Schema (for editor autocomplete). Use this when\n// running the tool via npx / a global install rather than as a project dev-dependency.\nconst JSON_CONFIG_TEMPLATE = `{\n \"$schema\": \"./${DEFAULT_SCHEMA_FILE}\",\n \"settings\": {\n \"outDir\": \"pro-visu\",\n \"concurrency\": 2,\n \"browser\": { \"headless\": true },\n \"defaults\": {\n \"scroll-reel\": { \"width\": 1440, \"height\": 900, \"fps\": 30 }\n }\n },\n \"assets\": [\n { \"name\": \"home-reel\", \"url\": \"http://localhost:3101\", \"generator\": \"scroll-reel\" }\n ]\n}\n`;\n\nexport interface InitOptions {\n cwd?: string;\n /** --no-script sets this to false. */\n script?: boolean;\n skipBrowser?: boolean;\n /** Scaffold a JSON config + JSON Schema instead of a TS `defineConfig` file. */\n json?: boolean;\n}\n\nexport async function runInit(options: InitOptions = {}): Promise<void> {\n const cwd = resolveCwd(options.cwd);\n const logger = createLogger(\"info\");\n let createdSomething = false;\n const configFile = options.json ? \"pro-visu.config.json\" : \"pro-visu.config.ts\";\n\n // 1. Config file\n const existingConfig = findExistingConfig(cwd);\n if (existingConfig) {\n logger.info(`config exists (${existingConfig}) — leaving it untouched`);\n } else if (options.json) {\n await writeFile(path.join(cwd, configFile), JSON_CONFIG_TEMPLATE, \"utf8\");\n logger.success(`created ${configFile}`);\n // Materialize the matching JSON Schema so the JSON config gets editor autocomplete + validation\n // with no dependency on this package — it works the same whether run via npx, global, or a dep.\n await writeFile(path.join(cwd, DEFAULT_SCHEMA_FILE), serializeConfigJsonSchema(), \"utf8\");\n logger.success(`created ${DEFAULT_SCHEMA_FILE}`);\n createdSomething = true;\n } else {\n await writeFile(path.join(cwd, configFile), CONFIG_TEMPLATE, \"utf8\");\n logger.success(`created ${configFile}`);\n createdSomething = true;\n }\n\n // 2. Output directory\n const outDirAbs = path.join(cwd, DEFAULT_OUTDIR);\n if (await pathExists(outDirAbs)) {\n logger.info(`${DEFAULT_OUTDIR}/ exists`);\n } else {\n await ensureDir(outDirAbs);\n logger.success(`created ${DEFAULT_OUTDIR}/`);\n createdSomething = true;\n }\n\n // 3. .gitignore\n const gitignore = await ensureGitignoreEntry(cwd, `${DEFAULT_OUTDIR}/`);\n if (gitignore.changed) {\n logger.success(`added ${DEFAULT_OUTDIR}/ to .gitignore`);\n createdSomething = true;\n } else {\n logger.info(`.gitignore already ignores ${DEFAULT_OUTDIR}/`);\n }\n\n // 4. package.json script (opt-out via --no-script)\n if (options.script !== false) {\n const result = await addPackageScript(cwd);\n if (result === \"added\") {\n logger.success('added \"pro-visu\" script to package.json');\n createdSomething = true;\n } else if (result === \"exists\") {\n logger.info('\"pro-visu\" script already present');\n } else {\n logger.info(\"no package.json found — skipped script wiring\");\n }\n }\n\n // 5. Browser + ffmpeg\n if (!options.skipBrowser) {\n try {\n await ensureChromium({ logger });\n } catch (err) {\n logger.warn(`Could not install Chromium now: ${(err as Error).message}`);\n logger.warn(\"It will be installed on first `pro-visu generate`.\");\n }\n try {\n await ensureFfmpeg({ logger });\n } catch (err) {\n logger.warn(`Could not fetch ffmpeg now: ${(err as Error).message}`);\n logger.warn(\"It will be fetched on first `pro-visu generate`.\");\n }\n }\n\n logger.log(\"\");\n logger.info(\n createdSomething\n ? `Next: edit ${configFile}, start your site (or use a deployed URL), then run \\`pro-visu generate\\`.`\n : `Already initialized. Edit ${configFile}, then run \\`pro-visu generate\\`.`,\n );\n}\n\nfunction findExistingConfig(cwd: string): string | undefined {\n for (const file of CONFIG_FILES) {\n if (existsSync(path.join(cwd, file))) return file;\n }\n const pkgPath = path.join(cwd, \"package.json\");\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as Record<string, unknown>;\n if (pkg && typeof pkg === \"object\" && \"pro-visu\" in pkg) {\n return 'package.json \"pro-visu\" key';\n }\n } catch {\n // ignore unparseable package.json\n }\n }\n return undefined;\n}\n\nasync function addPackageScript(cwd: string): Promise<\"added\" | \"exists\" | \"none\"> {\n const pkgPath = path.join(cwd, \"package.json\");\n if (!existsSync(pkgPath)) return \"none\";\n\n const raw = await readFile(pkgPath, \"utf8\");\n let pkg: { scripts?: Record<string, string> } & Record<string, unknown>;\n try {\n pkg = JSON.parse(raw);\n } catch {\n return \"none\";\n }\n\n pkg.scripts ??= {};\n if (pkg.scripts[\"pro-visu\"]) return \"exists\";\n pkg.scripts[\"pro-visu\"] = \"pro-visu generate\";\n await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`, \"utf8\");\n return \"added\";\n}\n","import path from \"node:path\";\n\n/** Resolve a working directory to an absolute path (defaults to process.cwd()). */\nexport function resolveCwd(cwd?: string): string {\n return path.resolve(cwd ?? process.cwd());\n}\n\n/** Resolve the output directory (e.g. `pro-visu/`) against the consuming repo root. */\nexport function resolveOutDir(cwd: string, outDir: string): string {\n return path.resolve(cwd, outDir);\n}\n\n/** Per-generator subdirectory inside the output dir, e.g. `pro-visu/scroll-reel`. */\nexport function generatorDir(outDir: string, generatorId: string): string {\n return path.join(outDir, generatorId);\n}\n\n/** Normalize a path to forward slashes (for stable, cross-platform manifest entries). */\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** Path of `to` relative to `from`, normalized to forward slashes. */\nexport function relPosix(from: string, to: string): string {\n return toPosix(path.relative(from, to));\n}\n\n/** Turn an asset name into a safe filename slug. */\nexport function slugify(name: string): string {\n return (\n name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"asset\"\n );\n}\n","import { access, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport path from \"node:path\";\n\n/** mkdir -p */\nexport async function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n\n/** Does a path exist? */\nexport async function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/** rm -rf (never throws if missing). */\nexport async function removeDir(dir: string): Promise<void> {\n await rm(dir, { recursive: true, force: true });\n}\n\nexport interface GitignoreResult {\n /** Whether the file was modified. */\n changed: boolean;\n /** Whether a new .gitignore was created. */\n created: boolean;\n}\n\n/**\n * Idempotently ensure `entry` is present in the repo's `.gitignore`, appended under a\n * labelled comment block. Treats trailing-slash variants as equal so we never duplicate.\n */\nexport async function ensureGitignoreEntry(\n repoRoot: string,\n entry: string,\n label = \"pro-visu\",\n): Promise<GitignoreResult> {\n const file = path.join(repoRoot, \".gitignore\");\n const exists = await pathExists(file);\n const content = exists ? await readFile(file, \"utf8\") : \"\";\n const normalizedEntry = entry.replace(/\\/+$/, \"\");\n\n const already = content\n .split(/\\r?\\n/)\n .map((line) => line.trim().replace(/\\/+$/, \"\"))\n .some((line) => line === normalizedEntry);\n if (already) return { changed: false, created: false };\n\n let next = content;\n if (next.length > 0 && !next.endsWith(\"\\n\")) next += \"\\n\";\n if (next.length > 0) next += \"\\n\";\n next += `# ${label}\\n${entry}\\n`;\n\n await writeFile(file, next, \"utf8\");\n return { changed: true, created: !exists };\n}\n","import { createConsola } from \"consola\";\nimport type { ConsolaInstance } from \"consola\";\nimport type { LogLevel } from \"@/config/schema\";\n\n/** Map our friendly log levels onto consola's numeric levels. */\nconst LEVEL_MAP: Record<LogLevel, number> = {\n silent: Number.NEGATIVE_INFINITY,\n error: 0,\n warn: 1,\n info: 3,\n debug: 4,\n};\n\nexport type Logger = ConsolaInstance;\n\nexport function createLogger(level: LogLevel = \"info\"): Logger {\n return createConsola({ level: LEVEL_MAP[level] });\n}\n\n/** A destination for tagged log entries (the live job tracker implements this). */\nexport interface LogSink {\n /** Consume a tagged log line. Return true if handled (suppress normal printing). */\n route(tag: string, type: string, message: string): boolean;\n}\n\n/**\n * A logger that diverts entries to a sink: *tagged* entries (e.g.\n * `logger.withTag(\"home-reel\").info(…)`) feed each asset's progress into the live dashboard, while\n * untagged entries are committed above it — so the live renderer owns the terminal and stray logs\n * never corrupt its output. Anything the sink declines (e.g. after teardown) prints normally.\n */\nexport function createReportingLogger(level: LogLevel, sink: LogSink): Logger {\n const passthrough = createConsola({ level: LEVEL_MAP[level] });\n return createConsola({\n level: LEVEL_MAP[level],\n reporters: [\n {\n log(logObj) {\n const tag = typeof logObj.tag === \"string\" ? logObj.tag : \"\";\n const args = (logObj.args ?? []) as unknown[];\n const message = args.map((a) => (typeof a === \"string\" ? a : String(a))).join(\" \");\n if (sink.route(tag, logObj.type, message)) return;\n const fn = (passthrough as unknown as Record<string, unknown>)[logObj.type];\n const emit =\n typeof fn === \"function\"\n ? (fn as (...a: unknown[]) => void)\n : (passthrough.log as unknown as (...a: unknown[]) => void);\n emit.call(passthrough, ...args);\n },\n },\n ],\n });\n}\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { chromium } from \"playwright-core\";\nimport type { Logger } from \"@/utils/logger\";\n\nconst require = createRequire(import.meta.url);\n\n/** Is a managed Chromium present on disk for playwright-core? */\nfunction isChromiumInstalled(): boolean {\n try {\n const exe = chromium.executablePath();\n return !!exe && existsSync(exe);\n } catch {\n return false;\n }\n}\n\n/** Locate playwright-core's CLI so we can drive its browser installer. */\nfunction resolveCliPath(): string {\n try {\n return require.resolve(\"playwright-core/cli.js\");\n } catch {\n const pkgJson = require.resolve(\"playwright-core/package.json\");\n return path.join(path.dirname(pkgJson), \"cli.js\");\n }\n}\n\nexport interface EnsureChromiumOptions {\n logger: Logger;\n /** When true, only report status; never install. */\n checkOnly?: boolean;\n}\n\n/**\n * Ensure a Chromium build is available. playwright-core ships no browser at install time,\n * so we fetch it on demand (cached in the shared PLAYWRIGHT_BROWSERS_PATH). Returns whether\n * Chromium is present afterward.\n */\nexport async function ensureChromium(opts: EnsureChromiumOptions): Promise<boolean> {\n if (isChromiumInstalled()) return true;\n if (opts.checkOnly) return false;\n\n opts.logger.info(\"Installing Chromium for Playwright (one-time, ~100–150 MB)…\");\n const cli = resolveCliPath();\n await new Promise<void>((resolve, reject) => {\n const child = spawn(process.execPath, [cli, \"install\", \"chromium\"], {\n stdio: \"inherit\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) =>\n code === 0\n ? resolve()\n : reject(new Error(`Chromium install failed (exit code ${code}).`)),\n );\n });\n opts.logger.success(\"Chromium installed.\");\n return true;\n}\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { rm } from \"node:fs/promises\";\nimport { spawn } from \"node:child_process\";\nimport { createRequire } from \"node:module\";\nimport { ffmpegPath } from \"@/media/ffmpeg\";\nimport type { Logger } from \"@/utils/logger\";\n\nconst require = createRequire(import.meta.url);\n\n/** Does the bundled ffmpeg binary exist AND actually execute on this platform? */\nasync function ffmpegWorks(): Promise<boolean> {\n let bin: string;\n try {\n bin = ffmpegPath();\n } catch {\n return false;\n }\n if (!existsSync(bin)) return false;\n return await new Promise<boolean>((resolve) => {\n // A corrupt / wrong-platform binary fails here — on Windows spawn() throws synchronously\n // (spawn UNKNOWN/EFTYPE); on POSIX it emits an async 'error'. Handle both.\n let child: ReturnType<typeof spawn>;\n try {\n child = spawn(bin, [\"-version\"], { stdio: \"ignore\" });\n } catch {\n resolve(false);\n return;\n }\n child.on(\"error\", () => resolve(false));\n child.on(\"close\", (code) => resolve(code === 0));\n });\n}\n\n/** Locate ffmpeg-static's bundled downloader script. */\nfunction resolveInstallScript(): string | null {\n try {\n return require.resolve(\"ffmpeg-static/install.js\");\n } catch {\n try {\n const pkg = require.resolve(\"ffmpeg-static/package.json\");\n return path.join(path.dirname(pkg), \"install.js\");\n } catch {\n return null;\n }\n }\n}\n\nexport interface EnsureFfmpegOptions {\n logger: Logger;\n /** When true, only report status; never download. */\n checkOnly?: boolean;\n}\n\n/**\n * Ensure a working ffmpeg binary. ffmpeg-static fetches its binary via a postinstall script,\n * which pnpm/npm block unless the consumer approves build scripts — leaving the binary\n * missing or corrupt so it fails to spawn (EFTYPE). We self-heal by running ffmpeg-static's\n * own downloader on demand (mirroring how we fetch Chromium), so consumers never have to\n * approve build scripts or install ffmpeg by hand.\n */\nexport async function ensureFfmpeg(opts: EnsureFfmpegOptions): Promise<boolean> {\n if (await ffmpegWorks()) return true;\n if (opts.checkOnly) return false;\n\n const installScript = resolveInstallScript();\n if (!installScript) {\n opts.logger.error(\"ffmpeg-static is not installed; cannot fetch an ffmpeg binary.\");\n return false;\n }\n\n opts.logger.info(\"Fetching ffmpeg (one-time, ~80 MB)…\");\n // ffmpeg-static's installer treats any existing file as already-done, so clear a corrupt\n // binary first to force a clean re-download.\n try {\n await rm(ffmpegPath(), { force: true });\n } catch {\n /* path may not resolve yet — the download recreates it */\n }\n await new Promise<void>((resolve, reject) => {\n const child = spawn(process.execPath, [installScript], {\n cwd: path.dirname(installScript),\n stdio: \"inherit\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) =>\n code === 0\n ? resolve()\n : reject(new Error(`ffmpeg download failed (exit code ${code}).`)),\n );\n });\n\n if (!(await ffmpegWorks())) {\n opts.logger.error(\"ffmpeg was downloaded but still won't run on this platform.\");\n return false;\n }\n opts.logger.success(\"ffmpeg ready.\");\n return true;\n}\n","import path from \"node:path\";\nimport { spawn } from \"node:child_process\";\nimport { rm, writeFile } from \"node:fs/promises\";\nimport ffmpegStatic from \"ffmpeg-static\";\nimport { ensureDir } from \"@/utils/fs\";\nimport type { Logger } from \"@/utils/logger\";\n\nexport interface TranscodeArgs {\n inputPath: string;\n outputPath: string;\n fps: number;\n width: number;\n height: number;\n crf: number;\n /** x264 speed/size tradeoff; \"ultrafast\" for draft, \"medium\" (default) for final. */\n preset?: string;\n /** Seconds to trim off the head of the input (e.g. the blank navigation lead of a recording). */\n startOffsetSeconds?: number;\n /** Clamp the output to exactly this many seconds (after any head trim) so length is deterministic. */\n durationSeconds?: number;\n /** Crop the source to this box (CSS px) before scaling — used for element-focused clips. */\n crop?: { x: number; y: number; width: number; height: number };\n}\n\n/**\n * Color correctness: every encode converts to and TAGS bt709 limited (tv) range — the standard for\n * web/HD video. Untagged output forces players to guess the matrix (bt601 vs bt709), which shifts\n * colors subtly; explicit conversion + tagging keeps brand colors exact everywhere.\n *\n * `SCALE_COLOR` goes on the scale filter (which performs the conversion); `format=yuv420p` must\n * directly follow scale in the chain so scale itself does the RGB→YUV conversion with the declared\n * matrix (for RGB inputs like PNG frames/overlays, a later auto-inserted converter would otherwise\n * use an unspecified matrix). `COLOR_TAGS` writes the matching metadata into the x264 VUI/container.\n */\nconst SCALE_COLOR = \"out_color_matrix=bt709:out_range=tv\";\nconst COLOR_TAGS = [\n \"-colorspace\",\n \"bt709\",\n \"-color_primaries\",\n \"bt709\",\n \"-color_trc\",\n \"bt709\",\n \"-color_range\",\n \"tv\",\n];\n\n/** Absolute path to the bundled ffmpeg binary. */\nexport function ffmpegPath(): string {\n const p = ffmpegStatic as unknown as string | null;\n if (!p) {\n throw new Error(\"ffmpeg-static did not provide a binary for this platform.\");\n }\n return p;\n}\n\n/**\n * Build the ffmpeg argument vector to re-encode the recording into a web-friendly\n * h264 mp4 at a fixed fps. Pure — unit-tested.\n */\nexport function buildTranscodeArgs(args: TranscodeArgs): string[] {\n // Input-side seek (`-ss` before `-i`) skips the blank navigation/readiness lead Playwright records\n // before playback starts, so the mp4 opens on the first real frame instead of a blank one.\n const seek =\n args.startOffsetSeconds && args.startOffsetSeconds > 0\n ? [\"-ss\", args.startOffsetSeconds.toFixed(3)]\n : [];\n // Output-side limit (after the head seek) so the clip is exactly `durationSeconds` long.\n const limit =\n args.durationSeconds && args.durationSeconds > 0\n ? [\"-t\", args.durationSeconds.toFixed(3)]\n : [];\n const crop = args.crop\n ? `crop=${args.crop.width}:${args.crop.height}:${args.crop.x}:${args.crop.y},`\n : \"\";\n return [\n \"-y\",\n ...seek,\n \"-i\",\n args.inputPath,\n \"-vf\",\n `${crop}scale=${args.width}:${args.height}:flags=lanczos:${SCALE_COLOR},format=yuv420p,fps=${args.fps}`,\n \"-c:v\",\n \"libx264\",\n \"-preset\",\n args.preset ?? \"medium\",\n \"-crf\",\n String(args.crf),\n \"-pix_fmt\",\n \"yuv420p\",\n ...COLOR_TAGS,\n \"-movflags\",\n \"+faststart\",\n \"-an\",\n ...limit,\n args.outputPath,\n ];\n}\n\n/** Read a video's pixel dimensions by parsing ffmpeg's stream info (no ffprobe needed). */\nexport async function probeVideoDimensions(\n file: string,\n): Promise<{ width: number; height: number } | null> {\n return new Promise((resolve) => {\n const child = spawn(ffmpegPath(), [\"-hide_banner\", \"-i\", file], {\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n });\n let stderr = \"\";\n child.stderr.on(\"data\", (chunk: Buffer) => (stderr += chunk.toString()));\n child.on(\"error\", () => resolve(null));\n // ffmpeg exits non-zero with no output file; the info we want is already on stderr. Anchor to\n // the video stream line so a \"1920x1080\"-looking tag on another line can't be picked up first.\n child.on(\"close\", () => {\n const videoLine = stderr.split(\"\\n\").find((l) => l.includes(\"Video:\"));\n const match = videoLine ? /,\\s(\\d+)x(\\d+)[\\s,]/.exec(videoLine) : null;\n resolve(match ? { width: Number(match[1]), height: Number(match[2]) } : null);\n });\n });\n}\n\n/** Read a video's duration in ms by parsing ffmpeg's stream info (no ffprobe needed). */\nexport async function probeVideoDurationMs(file: string): Promise<number | null> {\n return new Promise((resolve) => {\n const child = spawn(ffmpegPath(), [\"-hide_banner\", \"-i\", file], {\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n });\n let stderr = \"\";\n child.stderr.on(\"data\", (chunk: Buffer) => (stderr += chunk.toString()));\n child.on(\"error\", () => resolve(null));\n child.on(\"close\", () => {\n const m = /Duration:\\s*(\\d+):(\\d+):(\\d+)\\.(\\d+)/.exec(stderr);\n if (!m) return resolve(null);\n const frac = Number(`0.${m[4]}`);\n resolve((Number(m[1]) * 3600 + Number(m[2]) * 60 + Number(m[3]) + frac) * 1000);\n });\n });\n}\n\nexport interface FramePipeArgs {\n fps: number;\n width: number;\n height: number;\n crf: number;\n outPath: string;\n /** x264 speed/size tradeoff; \"ultrafast\" for draft, \"medium\" for final. */\n preset?: string;\n /** Piped frame format; \"png\" is the lossless (slower) path. Default \"jpeg\". */\n inputFormat?: \"jpeg\" | \"png\";\n}\n\n/**\n * ffmpeg argv to encode a stream of image frames (image2pipe on stdin) into an mp4. Pure —\n * unit-tested. Frames are scaled to the output size so we can screenshot at a higher device\n * scale and downsample for crispness. The input codec is set explicitly (mjpeg/png) rather than\n * relying on pipe content probing, so behavior is deterministic.\n */\nexport function buildFramePipeArgs(a: FramePipeArgs): string[] {\n return [\n \"-y\",\n \"-f\",\n \"image2pipe\",\n \"-framerate\",\n String(a.fps),\n \"-c:v\",\n a.inputFormat === \"png\" ? \"png\" : \"mjpeg\",\n \"-i\",\n \"pipe:0\",\n \"-vf\",\n `scale=${a.width}:${a.height}:flags=lanczos:${SCALE_COLOR},format=yuv420p`,\n \"-r\",\n String(a.fps),\n \"-c:v\",\n \"libx264\",\n \"-preset\",\n a.preset ?? \"medium\",\n \"-crf\",\n String(a.crf),\n \"-pix_fmt\",\n \"yuv420p\",\n ...COLOR_TAGS,\n \"-movflags\",\n \"+faststart\",\n \"-an\",\n a.outPath,\n ];\n}\n\nexport interface FrameEncoder {\n /** Push one encoded frame (JPEG buffer), respecting backpressure. */\n write(frame: Buffer): Promise<void>;\n /** Flush + wait for ffmpeg to finish writing the mp4. */\n done(): Promise<void>;\n}\n\n/** Spawn an ffmpeg that consumes piped JPEG frames and writes an mp4 — no frames hit disk. */\nexport function startFrameEncoder(a: FramePipeArgs, logger?: Logger, signal?: AbortSignal): FrameEncoder {\n const child = spawn(ffmpegPath(), buildFramePipeArgs(a), {\n stdio: [\"pipe\", \"ignore\", \"pipe\"],\n signal, // a cancelled run kills the encoder instead of waiting for it to drain\n });\n let stderr = \"\";\n let failed: Error | null = null;\n child.stderr.on(\"data\", (d: Buffer) => {\n stderr += d.toString();\n logger?.debug(d.toString().trim());\n });\n child.on(\"error\", (e) => {\n failed = e;\n });\n const stdin = child.stdin;\n\n return {\n write: (frame) =>\n new Promise<void>((resolve, reject) => {\n if (failed) return reject(failed);\n const flushed = stdin.write(frame, (err) => {\n if (err) reject(err);\n });\n if (flushed) resolve();\n else stdin.once(\"drain\", resolve);\n }),\n done: () =>\n new Promise<void>((resolve, reject) => {\n stdin.end();\n child.on(\"close\", (code) =>\n code === 0\n ? resolve()\n : reject(new Error(`ffmpeg frame encode failed (${code}):\\n${stderr.slice(-2000)}`)),\n );\n }),\n };\n}\n\n/** ffmpeg argv to losslessly concat mp4 segments (same codec params) via the concat demuxer. */\nexport function buildConcatArgs(listFile: string, outPath: string): string[] {\n return [\n \"-y\",\n \"-f\",\n \"concat\",\n \"-safe\",\n \"0\",\n \"-i\",\n listFile,\n \"-c\",\n \"copy\",\n \"-movflags\",\n \"+faststart\",\n outPath,\n ];\n}\n\n/** Concatenate mp4 segments into one file (stream copy — no re-encode). */\nexport async function concatMp4(\n segments: string[],\n outPath: string,\n logger?: Logger,\n signal?: AbortSignal,\n): Promise<void> {\n await ensureDir(path.dirname(outPath));\n // concat demuxer wants forward slashes and single-quoted paths.\n const listFile = `${outPath}.concat.txt`;\n const list = segments.map((s) => `file '${s.replace(/\\\\/g, \"/\")}'`).join(\"\\n\");\n await writeFile(listFile, `${list}\\n`, \"utf8\");\n try {\n await runFfmpeg(buildConcatArgs(listFile, outPath), logger, signal);\n } finally {\n await rm(listFile, { force: true });\n }\n}\n\n/** Run ffmpeg with an explicit argv, rejecting on a non-zero exit (or when `signal` aborts). */\nexport async function runFfmpeg(argv: string[], logger?: Logger, signal?: AbortSignal): Promise<void> {\n const bin = ffmpegPath();\n await new Promise<void>((resolve, reject) => {\n const child = spawn(bin, argv, { stdio: [\"ignore\", \"ignore\", \"pipe\"], signal });\n let stderr = \"\";\n child.stderr.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString();\n stderr += text;\n logger?.debug(text.trim());\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`ffmpeg exited with code ${code}:\\n${stderr.slice(-2000)}`));\n });\n });\n}\n\n/** Re-encode the recorded webm into an mp4 at outputPath. */\nexport async function transcodeToMp4(\n args: TranscodeArgs & { logger?: Logger; signal?: AbortSignal },\n): Promise<void> {\n await ensureDir(path.dirname(args.outputPath));\n await runFfmpeg(buildTranscodeArgs(args), args.logger, args.signal);\n}\n\n// --- output transforms: aspect reframing + alternate formats (gif / webp / poster) ---\n\nexport type AspectPreset = \"16:9\" | \"9:16\" | \"1:1\";\n\n/** Pure: resolve an aspect preset (or explicit size) to concrete output pixel dimensions. */\nexport function aspectTarget(\n aspect: AspectPreset | { width: number; height: number },\n): { width: number; height: number } {\n if (typeof aspect === \"object\") return { width: aspect.width, height: aspect.height };\n switch (aspect) {\n case \"9:16\":\n return { width: 1080, height: 1920 };\n case \"1:1\":\n return { width: 1080, height: 1080 };\n case \"16:9\":\n default:\n return { width: 1920, height: 1080 };\n }\n}\n\nexport interface AspectArgs {\n inputPath: string;\n outputPath: string;\n width: number;\n height: number;\n /** \"cover\" scales to fill + center-crops; \"contain\" scales to fit + pads. */\n fit: \"cover\" | \"contain\";\n padColor: string;\n fps: number;\n crf: number;\n preset?: string;\n}\n\n/** ffmpeg argv to reframe a video to a target aspect (h264, bt709 preserved). Pure — unit-tested. */\nexport function buildAspectArgs(a: AspectArgs): string[] {\n const vf =\n a.fit === \"contain\"\n ? `scale=${a.width}:${a.height}:force_original_aspect_ratio=decrease:flags=lanczos:${SCALE_COLOR},` +\n `pad=${a.width}:${a.height}:(ow-iw)/2:(oh-ih)/2:${a.padColor},format=yuv420p`\n : `scale=${a.width}:${a.height}:force_original_aspect_ratio=increase:flags=lanczos:${SCALE_COLOR},` +\n `crop=${a.width}:${a.height},format=yuv420p`;\n return [\n \"-y\",\n \"-i\",\n a.inputPath,\n \"-vf\",\n vf,\n \"-r\",\n String(a.fps),\n \"-c:v\",\n \"libx264\",\n \"-preset\",\n a.preset ?? \"medium\",\n \"-crf\",\n String(a.crf),\n \"-pix_fmt\",\n \"yuv420p\",\n ...COLOR_TAGS,\n \"-movflags\",\n \"+faststart\",\n \"-an\",\n a.outputPath,\n ];\n}\n\nexport interface GifArgs {\n inputPath: string;\n outputPath: string;\n fps: number;\n /** Optional output width (height auto, keeps aspect). */\n width?: number;\n}\n\n/** ffmpeg argv to encode an optimized GIF (per-clip palette). Pure — unit-tested. */\nexport function buildGifArgs(a: GifArgs): string[] {\n const scale = a.width ? `,scale=${a.width}:-1:flags=lanczos` : \"\";\n const filter =\n `[0:v] fps=${a.fps}${scale},split [s0][s1];` +\n `[s0] palettegen=stats_mode=diff [p];[s1][p] paletteuse=dither=bayer:bayer_scale=5`;\n return [\"-y\", \"-i\", a.inputPath, \"-filter_complex\", filter, a.outputPath];\n}\n\nexport interface WebpArgs {\n inputPath: string;\n outputPath: string;\n fps: number;\n /** 0–100 (libwebp q:v). */\n quality: number;\n}\n\n/** ffmpeg argv to encode an animated WebP. Pure — unit-tested. */\nexport function buildWebpArgs(a: WebpArgs): string[] {\n return [\n \"-y\",\n \"-i\",\n a.inputPath,\n \"-vcodec\",\n \"libwebp\",\n \"-filter:v\",\n `fps=${a.fps}`,\n \"-lossless\",\n \"0\",\n \"-q:v\",\n String(a.quality),\n \"-loop\",\n \"0\",\n \"-an\",\n \"-vsync\",\n \"0\",\n a.outputPath,\n ];\n}\n\nexport interface PosterArgs {\n inputPath: string;\n outputPath: string;\n /** Seek to this time (seconds) before grabbing one frame. */\n atSeconds: number;\n}\n\n/** ffmpeg argv to grab a single still frame (poster/thumbnail). Pure — unit-tested. */\nexport function buildPosterArgs(a: PosterArgs): string[] {\n const seek = a.atSeconds > 0 ? [\"-ss\", a.atSeconds.toFixed(3)] : [];\n return [\"-y\", ...seek, \"-i\", a.inputPath, \"-frames:v\", \"1\", a.outputPath];\n}\n\nexport interface StillSegmentArgs {\n pngPath: string;\n outPath: string;\n seconds: number;\n fps: number;\n width: number;\n height: number;\n /** Fade-from-black duration at the start (s); 0 to disable. */\n fadeInSec: number;\n /** Fade-to-black duration at the end (s); 0 to disable. */\n fadeOutSec: number;\n crf: number;\n preset?: string;\n}\n\n/**\n * ffmpeg argv to turn a still PNG into a fixed-length mp4 segment with optional fade in/out — used for\n * intro/outro cards. Same h264 / bt709 / yuv420p settings as the main clip so segments concat cleanly.\n * Pure — unit-tested.\n */\nexport function buildStillSegmentArgs(a: StillSegmentArgs): string[] {\n const filters = [`scale=${a.width}:${a.height}:flags=lanczos:${SCALE_COLOR}`, \"format=yuv420p\"];\n if (a.fadeInSec > 0) filters.push(`fade=t=in:st=0:d=${a.fadeInSec.toFixed(3)}`);\n if (a.fadeOutSec > 0) {\n filters.push(`fade=t=out:st=${Math.max(0, a.seconds - a.fadeOutSec).toFixed(3)}:d=${a.fadeOutSec.toFixed(3)}`);\n }\n return [\n \"-y\",\n \"-loop\",\n \"1\",\n \"-t\",\n a.seconds.toFixed(3),\n \"-i\",\n a.pngPath,\n \"-vf\",\n filters.join(\",\"),\n \"-r\",\n String(a.fps),\n \"-c:v\",\n \"libx264\",\n \"-preset\",\n a.preset ?? \"medium\",\n \"-crf\",\n String(a.crf),\n \"-pix_fmt\",\n \"yuv420p\",\n ...COLOR_TAGS,\n \"-movflags\",\n \"+faststart\",\n \"-an\",\n a.outPath,\n ];\n}\n","/** The config base name c12 discovers: pro-visu.config.*, .pro-visurc, package.json \"pro-visu\". */\nexport const CONFIG_NAME = \"pro-visu\";\nconst RC_FILE = \".pro-visurc\";\n\nexport const DEFAULT_OUTDIR = \"pro-visu\";\nconst DEFAULT_CONCURRENCY = 2;\n","import { zodToJsonSchema } from \"zod-to-json-schema\";\nimport type { ZodTypeAny } from \"zod\";\nimport { settingsSchema } from \"@/config/schema\";\nimport { listGenerators } from \"@/generators/registry\";\n\ntype JsonObject = Record<string, unknown>;\n\n/** Convert a zod schema to an inlined (no $ref) JSON Schema fragment, without its own $schema tag. */\nfunction toJson(schema: ZodTypeAny): JsonObject {\n const out = zodToJsonSchema(schema, { $refStrategy: \"none\" }) as JsonObject;\n // Strip the per-fragment $schema tag — we only want one at the document root.\n delete out.$schema;\n return out;\n}\n\n/**\n * Build a draft-07 JSON Schema for a `pro-visu.config.json`, assembled from the live, registered\n * schemas. A JSON config that references it via `$schema` then gets the same editor autocomplete +\n * validation as the typed `defineConfig` path — with no project dependency on this package.\n *\n * Per-generator `options` autocomplete comes from a discriminated union: once `generator` is set,\n * the matching `allOf` branch refines `options` to that generator's own schema. Because it's built\n * from the registry at runtime, `pro-visu init --json` / `pro-visu schema` materialize a schema that\n * always matches the installed tool version (works offline and in any install mode).\n */\nexport function generateConfigJsonSchema(): JsonObject {\n const generators = listGenerators();\n\n const optionBranches = generators.map((gen) => ({\n if: { properties: { generator: { const: gen.id } } },\n then: { properties: { options: toJson(gen.optionsSchema) } },\n }));\n\n const assetItem: JsonObject = {\n type: \"object\",\n required: [\"name\", \"generator\"],\n additionalProperties: false,\n properties: {\n name: {\n type: \"string\",\n minLength: 1,\n description: \"Unique id — also the output filename and manifest key.\",\n },\n url: {\n type: \"string\",\n description:\n \"Absolute http(s) URL, or a /path resolved against the managed server. Omit for local generators (wall, specimen, palette, image).\",\n },\n generator: {\n description: \"Which generator produces this asset.\",\n enum: generators.map((g) => g.id),\n },\n options: {\n type: \"object\",\n description: \"Generator-specific options — autocompletes once `generator` is set.\",\n },\n inputs: {\n type: \"object\",\n additionalProperties: { type: \"string\" },\n description: \"Other assets this one consumes, as { slotName: assetName }. Producers run first.\",\n },\n },\n allOf: optionBranches,\n };\n\n return {\n $schema: \"http://json-schema.org/draft-07/schema#\",\n title: \"pro-visu config\",\n description: \"Configuration for the pro-visu CLI (pro-visu.config.json).\",\n type: \"object\",\n required: [\"assets\"],\n additionalProperties: false,\n properties: {\n // The config file itself may carry a $schema pointer; the runtime validator ignores it.\n $schema: { type: \"string\" },\n settings: toJson(settingsSchema),\n assets: {\n type: \"array\",\n minItems: 1,\n description: \"What to generate (at least one).\",\n items: assetItem,\n },\n },\n };\n}\n\n/** Serialize the JSON Schema document for writing to disk. */\nexport function serializeConfigJsonSchema(): string {\n return `${JSON.stringify(generateConfigJsonSchema(), null, 2)}\\n`;\n}\n","import { z } from \"zod\";\n\n/** Friendly log levels surfaced in config + CLI. */\nconst logLevelSchema = z.enum([\"silent\", \"error\", \"warn\", \"info\", \"debug\"]);\nexport type LogLevel = z.infer<typeof logLevelSchema>;\n\n/** Playwright launch controls, settable per-repo. */\nconst browserSettingsSchema = z\n .object({\n headless: z\n .boolean()\n .default(true)\n .describe(\"Run the browser without a visible window (default true). Set false to watch captures.\"),\n /** Browser channel, e.g. \"chrome\" or \"msedge\". Omit to use the managed Chromium. */\n channel: z\n .string()\n .optional()\n .describe('Browser channel, e.g. \"chrome\" or \"msedge\". Omit to use the managed Chromium.'),\n /** Absolute path to a browser executable (overrides channel + managed Chromium). */\n executablePath: z\n .string()\n .optional()\n .describe(\"Absolute path to a browser executable (overrides channel + managed Chromium).\"),\n /** Extra launch args, e.g. [\"--no-sandbox\"] on CI. */\n args: z\n .array(z.string())\n .default([])\n .describe('Extra launch args, e.g. [\"--no-sandbox\"] on CI.'),\n /** Launch timeout (ms). */\n timeout: z\n .number()\n .int()\n .nonnegative()\n .default(30_000)\n .describe(\"Browser launch timeout in ms (default 30000).\"),\n })\n .strict();\nexport type ResolvedBrowserSettings = z.infer<typeof browserSettingsSchema>;\n\n/**\n * Optional managed server. When set, `pro-visu generate` builds (if given), starts the\n * server, waits for it to respond, runs the capture, then shuts it down — so a project's\n * npm script can be just `pro-visu generate`.\n */\nexport const serverSettingsSchema = z\n .object({\n /**\n * Command that starts the server, run via the shell. The tool sets PORT/HOST in the\n * command's environment to the readiness port/host, so frameworks that honor PORT (Next,\n * Vite, …) bind it automatically — `command: \"next start\"` is enough. An explicit flag\n * (e.g. `next start -p 4000`) still wins.\n */\n command: z\n .string()\n .min(1)\n .describe(\"Command that starts the server, run via the shell. PORT/HOST are set in its env so PORT-honoring frameworks bind automatically.\"),\n /** Optional one-shot build to run first, e.g. \"next build\". */\n build: z\n .string()\n .min(1)\n .optional()\n .describe('Optional one-shot build to run first, e.g. \"next build\".'),\n /** Health-check URL polled until it responds. Defaults to http://127.0.0.1:<port>. */\n url: z\n .string()\n .url()\n .optional()\n .describe(\"Health-check URL polled until it responds. Defaults to http://127.0.0.1:<port>.\"),\n /**\n * Port the readiness check polls — also derives `url` when `url` is omitted, and is passed to\n * the command as the PORT env var so it binds the same port automatically. Defaults to 3101\n * (off the common 3000 dev port).\n */\n port: z\n .number()\n .int()\n .positive()\n .default(3101)\n .describe(\"Port the readiness check polls; also derives `url` and is passed as PORT so the server binds it. Defaults to 3101.\"),\n /** Working dir for build + command, relative to the config dir. Defaults to it. */\n cwd: z\n .string()\n .optional()\n .describe(\"Working dir for build + command, relative to the config dir. Defaults to it.\"),\n /** Max time to wait for the server to become reachable (ms). */\n readyTimeoutMs: z\n .number()\n .int()\n .positive()\n .default(120_000)\n .describe(\"Max time to wait for the server to become reachable, in ms (default 120000).\"),\n /** If a server is already reachable at the URL, use it as-is (don't start or stop one). */\n reuseExisting: z\n .boolean()\n .default(true)\n .describe(\"If a server is already reachable at the URL, use it as-is (don't start or stop one). Default true.\"),\n })\n .strict();\nexport type ResolvedServerSettings = z.infer<typeof serverSettingsSchema>;\n\n/** Repo-level CLI behavior (the `settings` block). */\nexport const settingsSchema = z.object({\n /** Output directory, relative to the repo root. */\n outDir: z\n .string()\n .min(1)\n .default(\"pro-visu\")\n .describe('Output directory for generated assets, relative to the repo root (default \"pro-visu\").'),\n /** How many assets to generate in parallel (shared browser, separate contexts). */\n concurrency: z\n .number()\n .int()\n .positive()\n .default(2)\n .describe(\"How many assets to generate in parallel, sharing one browser with separate contexts (default 2).\"),\n /**\n * Raise the Node heap (V8 old-space) to this many MB for the run. Heavy jobs — large frame-stepped\n * walls especially — can exceed Node's default ~4 GB limit and crash with \"JavaScript heap out of\n * memory\". When set above the current limit, the CLI re-execs itself with `--max-old-space-size`.\n * (This is the Node process heap, not the browser's — the browser manages its own memory.)\n */\n maxMemoryMB: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Raise the Node heap (V8 old-space) to this many MB to avoid out-of-memory on heavy jobs; re-execs with --max-old-space-size.\"),\n logLevel: logLevelSchema.default(\"info\").describe(\"CLI log verbosity (default \\\"info\\\").\"),\n browser: browserSettingsSchema.default({}).describe(\"Playwright launch controls.\"),\n /**\n * Per-generator option defaults, keyed by generator id, merged underneath each asset's\n * own `options`. Validated loosely here; each generator validates its own option shape.\n */\n defaults: z\n .record(z.string(), z.record(z.string(), z.unknown()))\n .default({})\n .describe(\"Per-generator option defaults, keyed by generator id, merged underneath each asset's own options.\"),\n /** Optional managed dev/prod server lifecycle (build → start → wait → … → stop). */\n server: serverSettingsSchema\n .optional()\n .describe(\"Build → start → wait → capture → stop a server automatically.\"),\n /** Render quality. \"draft\" lowers fps/scale and speeds the encoder for fast iteration. */\n quality: z\n .enum([\"draft\", \"final\"])\n .default(\"final\")\n .describe('Render quality; \"draft\" lowers fps/scale and speeds the encoder for fast iteration (default \"final\").'),\n /** Skip assets whose inputs+options+tool fingerprint is unchanged (opt-in; can be stale). */\n cache: z\n .boolean()\n .default(false)\n .describe(\"Skip assets whose inputs+options+tool fingerprint is unchanged (opt-in; can be stale). Default false.\"),\n});\nexport type ResolvedSettings = z.infer<typeof settingsSchema>;\n\n/** One thing to generate. Options are validated by the target generator at run time. */\nexport const assetSpecSchema = z\n .object({\n name: z.string().min(1),\n /**\n * Page to capture: an absolute `https://…` URL, or a path like `/shop` resolved against the\n * managed server's URL. Optional — with a managed server, a url-based asset that omits it\n * captures the server root; local generators (`scene`, `palette`) need no url.\n */\n url: z\n .string()\n .min(1)\n .refine((s) => /^https?:\\/\\//i.test(s) || s.startsWith(\"/\"), {\n message:\n 'url must be an absolute http(s) URL or a path starting with \"/\" (resolved against the managed server)',\n })\n .optional(),\n generator: z.string().min(1),\n options: z.record(z.string(), z.unknown()).default({}),\n /**\n * Other assets this one consumes, as `{ slotName: assetName }`. The producing assets run\n * first and their output files are exposed to this asset (e.g. a scene playing an earlier\n * recording). Forms a DAG; cycles are rejected.\n */\n inputs: z.record(z.string(), z.string()).default({}),\n })\n .strict();\nexport type ResolvedAssetSpec = z.infer<typeof assetSpecSchema>;\n\nexport const showcaseConfigSchema = z\n .object({\n settings: settingsSchema.default({}),\n assets: z.array(assetSpecSchema).min(1, \"Define at least one asset in `assets`.\"),\n })\n .superRefine((cfg, ctx) => {\n const seen = new Set<string>();\n for (const [i, asset] of cfg.assets.entries()) {\n if (seen.has(asset.name)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate asset name \"${asset.name}\" — names must be unique.`,\n path: [\"assets\", i, \"name\"],\n });\n }\n seen.add(asset.name);\n }\n });\nexport type ResolvedConfig = z.infer<typeof showcaseConfigSchema>;\n","import path from \"node:path\";\nimport { stat } from \"node:fs/promises\";\nimport {\n scrollReelOptionsSchema,\n type ResolvedScrollReelOptions,\n} from \"@/generators/scroll-reel/options\";\nimport { captureScrollWebm } from \"@/generators/scroll-reel/capture\";\nimport { captureScrollFrames } from \"@/generators/scroll-reel/capture-frames\";\nimport { scrollTimelineTotalMs } from \"@/generators/scroll-reel/timeline\";\nimport { buildVariants } from \"@/generators/scroll-reel/variants\";\nimport { produceOutputs } from \"@/generators/scroll-reel/outputs\";\nimport { captureFocusWebm, captureInteractionWebm } from \"@/generators/scroll-reel/interaction\";\nimport { requireUrl } from \"@/generators/require-url\";\nimport { concatMp4, transcodeToMp4 } from \"@/media/ffmpeg\";\nimport { autoWorkers } from \"@/media/frame-capture\";\nimport { sha256File } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const SCROLL_REEL_ID = \"scroll-reel\";\n\nasync function run(\n ctx: PipelineContext,\n options: ResolvedScrollReelOptions,\n): Promise<{ assets: AssetRecord[] }> {\n const url = requireUrl(ctx);\n const draft = ctx.quality === \"draft\";\n const preset = draft ? \"ultrafast\" : \"medium\";\n\n // Element-focused clip: realtime capture of one component, cropped to its box — single asset.\n if (options.focus) {\n const scheme =\n options.colorScheme === \"dark\" ? \"dark\" : options.colorScheme === \"light\" ? \"light\" : undefined;\n const fileName = options.fileName ?? `${slugify(ctx.target.name)}.mp4`;\n const outPath = ctx.resolveOutPath(fileName);\n ctx.logger.info(`recording ${url} (focus: ${options.focus.selector})`);\n const { webmPath, leadSeconds, durationSeconds, cropBox } = await captureFocusWebm({\n browser: ctx.browser,\n url,\n options,\n colorScheme: scheme,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n });\n ctx.logger.debug(\"transcoding to mp4 (cropped)\");\n await transcodeToMp4({\n inputPath: webmPath,\n outputPath: outPath,\n fps: options.fps,\n width: cropBox.width,\n height: cropBox.height,\n crf: options.crf,\n preset,\n startOffsetSeconds: leadSeconds,\n durationSeconds,\n crop: cropBox,\n logger: ctx.logger,\n signal: ctx.signal,\n });\n const [stats, contentHash] = await Promise.all([stat(outPath), sha256File(outPath)]);\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: SCROLL_REEL_ID,\n sourceUrl: url,\n file: ctx.toManifestPath(outPath),\n format: \"mp4\",\n width: cropBox.width,\n height: cropBox.height,\n durationMs: Math.round(durationSeconds * 1000),\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n }\n\n // Interaction: a scripted realtime \"tour\" with a synthetic cursor — single asset, always realtime.\n if (options.actions && options.actions.length > 0) {\n if (options.viewports?.length || options.colorScheme === \"both\") {\n ctx.logger.warn('viewports / colorScheme:\"both\" are not expanded for interaction reels');\n }\n if (options.intro || options.outro || options.annotations?.length) {\n ctx.logger.warn(\"intro/outro/annotations are not applied to interaction reels\");\n }\n const scheme =\n options.colorScheme === \"dark\" ? \"dark\" : options.colorScheme === \"light\" ? \"light\" : undefined;\n const fileName = options.fileName ?? `${slugify(ctx.target.name)}.mp4`;\n const outPath = ctx.resolveOutPath(fileName);\n ctx.logger.info(`recording ${url} (interaction, ${options.actions.length} action(s))`);\n const { webmPath, leadSeconds, durationSeconds } = await captureInteractionWebm({\n browser: ctx.browser,\n url,\n options,\n colorScheme: scheme,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n });\n ctx.logger.debug(\"transcoding to mp4\");\n await transcodeToMp4({\n inputPath: webmPath,\n outputPath: outPath,\n fps: options.fps,\n width: options.width,\n height: options.height,\n crf: options.crf,\n preset,\n startOffsetSeconds: leadSeconds,\n durationSeconds,\n logger: ctx.logger,\n signal: ctx.signal,\n });\n const [stats, contentHash] = await Promise.all([stat(outPath), sha256File(outPath)]);\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: SCROLL_REEL_ID,\n sourceUrl: url,\n file: ctx.toManifestPath(outPath),\n format: \"mp4\",\n width: options.width,\n height: options.height,\n durationMs: Math.round(durationSeconds * 1000),\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n }\n\n // Multi-page tour: capture each route as a frame-stepped segment, then concatenate into one reel.\n if (options.routes && options.routes.length > 0) {\n if (options.viewports?.length || options.colorScheme === \"both\") {\n ctx.logger.warn('viewports / colorScheme:\"both\" are not expanded for route tours');\n }\n const scheme =\n options.colorScheme === \"dark\" ? \"dark\" : options.colorScheme === \"light\" ? \"light\" : undefined;\n const workers = options.workers ?? autoWorkers();\n const segments: string[] = [];\n const routeCount = options.routes.length; // captured: narrowing doesn't persist into the progress closure\n let totalMs = 0;\n for (let i = 0; i < options.routes.length; i++) {\n const r = options.routes[i]!;\n const routeUrl = typeof r === \"string\" ? r : r.url;\n const routeOpts =\n typeof r === \"string\"\n ? { ...options, choreography: undefined, autoSections: undefined }\n : {\n ...options,\n choreography: r.choreography,\n autoSections: r.autoSections,\n duration: r.durationMs ?? options.duration,\n };\n const segPath = path.join(ctx.tmpDir, `${slugify(ctx.target.name)}-route-${i}.mp4`);\n ctx.logger.info(\n `recording route ${i + 1}/${options.routes.length}: ${routeUrl} (frame-stepped)`,\n );\n await captureScrollFrames({\n browser: ctx.browser,\n url: routeUrl,\n options: routeOpts,\n outPath: segPath,\n preset,\n workers,\n frameFormat: draft ? \"jpeg\" : options.frameFormat,\n jpegQuality: draft ? 70 : 90,\n settlePerFrame: options.settlePerFrame ?? !draft,\n settleMaxMs: options.settleMaxMs,\n colorScheme: scheme,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n // Weight each route's capture into one overall 0–1 across the whole tour.\n onProgress: ctx.progress ? (f) => ctx.progress?.((i + f) / routeCount) : undefined,\n signal: ctx.signal,\n });\n segments.push(segPath);\n totalMs += scrollTimelineTotalMs(routeOpts);\n }\n const tourMp4 = path.join(ctx.tmpDir, `${slugify(ctx.target.name)}-tour.mp4`);\n await concatMp4(segments, tourMp4, ctx.logger, ctx.signal);\n const baseName = (options.fileName ?? `${slugify(ctx.target.name)}.mp4`).replace(/\\.mp4$/i, \"\");\n const recs = await produceOutputs({\n ctx,\n generatorId: SCROLL_REEL_ID,\n sourceMp4: tourMp4,\n fileBase: baseName,\n assetId: ctx.target.name,\n sourceUrl: url,\n width: options.width,\n height: options.height,\n deviceScaleFactor: options.deviceScaleFactor,\n durationMs: totalMs,\n options,\n preset,\n });\n for (const r of recs) await ctx.writeAsset(r);\n return { assets: recs };\n }\n\n // Realtime: a single capture. Choreography / auto-sections / variants are frames-only.\n if (options.capture !== \"frames\") {\n if (options.choreography?.length || options.autoSections) {\n ctx.logger.warn('choreography/autoSections are ignored for capture:\"realtime\"');\n }\n if (options.viewports?.length || options.colorScheme === \"both\") {\n ctx.logger.warn('viewports / colorScheme:\"both\" are ignored for capture:\"realtime\"');\n }\n if (options.intro || options.outro || options.annotations?.length) {\n ctx.logger.warn('intro/outro/annotations are not applied to capture:\"realtime\"');\n }\n const fileName = options.fileName ?? `${slugify(ctx.target.name)}.mp4`;\n const outPath = ctx.resolveOutPath(fileName);\n const durationSeconds = (options.startDelayMs + options.duration + options.endDwellMs) / 1000;\n ctx.logger.info(`recording ${url} (realtime)`);\n const { webmPath, leadSeconds } = await captureScrollWebm({\n browser: ctx.browser,\n url,\n options,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n });\n ctx.logger.debug(\"transcoding to mp4\");\n await transcodeToMp4({\n inputPath: webmPath,\n outputPath: outPath,\n fps: options.fps,\n width: options.width,\n height: options.height,\n crf: options.crf,\n preset,\n // Drop the navigation + warm-up lead, then clamp to the intended length.\n startOffsetSeconds: leadSeconds,\n durationSeconds,\n logger: ctx.logger,\n signal: ctx.signal,\n });\n const [stats, contentHash] = await Promise.all([stat(outPath), sha256File(outPath)]);\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: SCROLL_REEL_ID,\n sourceUrl: url,\n file: ctx.toManifestPath(outPath),\n format: \"mp4\",\n width: options.width,\n height: options.height,\n durationMs: options.startDelayMs + options.duration + options.endDwellMs,\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n }\n\n // Frames: expand the viewport × color-scheme matrix; each variant is its own asset.\n const variants = buildVariants({\n width: options.width,\n height: options.height,\n deviceScaleFactor: options.deviceScaleFactor,\n viewports: options.viewports,\n colorScheme: options.colorScheme,\n });\n const baseName = (options.fileName ?? `${slugify(ctx.target.name)}.mp4`).replace(/\\.mp4$/i, \"\");\n const workers = options.workers ?? autoWorkers();\n const assets: AssetRecord[] = [];\n\n for (const v of variants) {\n const fileBase = v.suffix ? `${baseName}-${v.suffix}` : baseName;\n const assetId = v.suffix ? `${ctx.target.name}-${v.suffix}` : ctx.target.name;\n const captureMp4 = path.join(ctx.tmpDir, `${slugify(assetId)}-capture.mp4`);\n const vopts = {\n ...options,\n width: v.width,\n height: v.height,\n deviceScaleFactor: v.deviceScaleFactor,\n };\n const label = v.suffix ? ` [${v.suffix}]` : \"\";\n ctx.logger.info(`recording ${url}${label} (frame-stepped, ${workers} worker(s))`);\n await captureScrollFrames({\n browser: ctx.browser,\n url,\n options: vopts,\n outPath: captureMp4,\n preset,\n workers,\n // Draft always uses fast jpeg intermediates; final uses the configured format (png = lossless).\n frameFormat: draft ? \"jpeg\" : options.frameFormat,\n jpegQuality: draft ? 70 : 90,\n // Per-frame settling defaults on, off in draft for speed (override with the explicit option).\n settlePerFrame: options.settlePerFrame ?? !draft,\n settleMaxMs: options.settleMaxMs,\n colorScheme: v.colorScheme,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n onProgress: ctx.progress,\n signal: ctx.signal,\n });\n // Reframe to the requested aspect (if any) and emit each output format as its own asset.\n const recs = await produceOutputs({\n ctx,\n generatorId: SCROLL_REEL_ID,\n sourceMp4: captureMp4,\n fileBase,\n assetId,\n sourceUrl: url,\n width: v.width,\n height: v.height,\n deviceScaleFactor: v.deviceScaleFactor,\n durationMs: scrollTimelineTotalMs(vopts),\n options,\n preset,\n });\n for (const r of recs) await ctx.writeAsset(r);\n assets.push(...recs);\n }\n\n return { assets };\n}\n\nexport const scrollReelGenerator: Generator<ResolvedScrollReelOptions> = {\n id: SCROLL_REEL_ID,\n requiresUrl: true,\n optionsSchema: scrollReelOptionsSchema,\n run,\n};\n","import { z } from \"zod\";\n\nconst easingSchema = z.enum([\n \"linear\",\n \"easeInOutCubic\",\n \"easeInOutQuad\",\n \"easeOutCubic\",\n \"easeInOutSine\",\n \"easeInOutExpo\",\n \"easeOutQuint\",\n]);\nexport type Easing = z.infer<typeof easingSchema>;\n\n/** One choreographed scroll step (see `choreography` below). */\nconst choreographyStepSchema = z\n .object({\n /** Target: a 0..1 number, an \"NN%\" string, or a CSS selector to bring into view. */\n to: z\n .union([z.number(), z.string()])\n .describe('Target: a 0..1 number, an \"NN%\" string, or a CSS selector to bring into view.'),\n /** Travel time to this target (ms). Default 1200. */\n durationMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Travel time to this target (ms). Default 1200.\"),\n /** Hold time at this target after arriving (ms). Default 800. */\n holdMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Hold time at this target after arriving (ms). Default 800.\"),\n easing: easingSchema\n .optional()\n .describe('Easing for the travel to this target. Default \"easeInOutCubic\".'),\n })\n .strict();\n\n/** One step of a scripted interaction (see `actions` below). */\nconst interactionActionSchema = z\n .object({\n do: z\n .enum([\"move\", \"click\", \"hover\", \"type\", \"scrollTo\", \"wait\"])\n .describe(\"What this step does.\"),\n /** Target element for move/click/hover/type. */\n selector: z.string().optional().describe(\"Target element for move/click/hover/type.\"),\n /** Viewport-relative target for `move` without a selector (0..1). */\n x: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe(\"Viewport-relative X target for a selector-less `move` (0..1).\"),\n y: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe(\"Viewport-relative Y target for a selector-less `move` (0..1).\"),\n /** Text to type (for `type`). */\n text: z.string().optional().describe(\"Text to type (for `type`).\"),\n /** Scroll target for `scrollTo`: a 0..1 number, an \"NN%\" string, or a CSS selector. */\n to: z\n .union([z.number(), z.string()])\n .optional()\n .describe('Scroll target for `scrollTo`: a 0..1 number, an \"NN%\" string, or a CSS selector.'),\n /** Cursor travel / scroll animation time (ms). Default 700. */\n durationMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Cursor travel / scroll animation time (ms). Default 700.\"),\n /** Pause after the step (ms). Default 600. */\n holdMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Pause after the step (ms). Default 600.\"),\n })\n .strict();\n\n/** Tuning for auto-section choreography (see `autoSections` below). */\nconst autoSectionsSchema = z\n .object({\n /** Min element height (as a fraction of the viewport) to count as a section. Default 0.5. */\n minHeightFraction: z\n .number()\n .positive()\n .max(2)\n .optional()\n .describe(\n \"Min element height (as a fraction of the viewport) to count as a section. Default 0.5.\",\n ),\n /** Explicit section selector; overrides the heuristic. */\n selector: z\n .string()\n .optional()\n .describe(\"Explicit section selector; overrides the heuristic. Omit to auto-detect.\"),\n /** Hold at each detected section (ms). Default 700. */\n holdMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Hold at each detected section (ms). Default 700.\"),\n /** Total clip length (ms) split across detected sections. Default 12000. */\n durationMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Total clip length (ms) split across detected sections. Default 12000.\"),\n /** Cap on the number of sections. Default 8. */\n maxSections: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Cap on the number of sections. Default 8.\"),\n /** Distribute travel time by distance for uniform scroll speed. Default true. */\n constantVelocity: z\n .boolean()\n .optional()\n .describe(\"Distribute travel time by distance for uniform scroll speed. Default true.\"),\n })\n .strict();\n\n/** An intro/outro card (see `intro` / `outro`). */\nconst cardSchema = z\n .object({\n title: z.string().optional().describe(\"Card title (large). Omit for none.\"),\n subtitle: z\n .string()\n .optional()\n .describe(\"Card subtitle (small, under the title). Omit for none.\"),\n background: z.string().optional().describe(\"Card background color. Default black.\"),\n color: z.string().optional().describe(\"Card text color. Default white.\"),\n durationMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"How long the card holds (ms). Default 1500.\"),\n fadeMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Fade in/out length (ms). Default 400.\"),\n })\n .strict();\n\n/** A timed on-screen annotation (see `annotations`). */\nconst annotationSchema = z\n .object({\n /** Caption text shown while active. */\n text: z\n .string()\n .optional()\n .describe(\"Caption text shown while active. Omit for a ring/spotlight with no caption.\"),\n /** Selector to outline with a highlight ring. */\n ring: z.string().optional().describe(\"Selector to outline with a highlight ring.\"),\n /** Selector to spotlight (everything else dimmed). */\n spotlight: z.string().optional().describe(\"Selector to spotlight (everything else dimmed).\"),\n /** When it appears (clip time, ms). Default 0. */\n atMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"When it appears (clip time, ms). Default 0.\"),\n /** When it disappears (clip time, ms). Default = end. */\n untilMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"When it disappears (clip time, ms). Default = end of clip.\"),\n /** Caption placement. Default \"bottom\". */\n position: z\n .enum([\"top\", \"bottom\", \"center\"])\n .optional()\n .describe('Caption placement. Default \"bottom\".'),\n })\n .strict();\n\nexport const scrollReelOptionsSchema = z\n .object({\n /** Viewport + output width in CSS pixels. */\n width: z\n .number()\n .int()\n .positive()\n .default(1280)\n .describe(\"Viewport + output width in CSS px. Default 1280.\"),\n /** Viewport + output height in CSS pixels. */\n height: z\n .number()\n .int()\n .positive()\n .default(800)\n .describe(\"Viewport + output height in CSS px. Default 800.\"),\n /** Render scale (2 = retina-crisp capture, downscaled into the video). */\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(2)\n .describe(\"Render scale (2 = retina-crisp capture, downscaled into the video). Default 2.\"),\n /** Output frames per second (re-encoded from the recording). */\n fps: z\n .number()\n .int()\n .positive()\n .max(120)\n .default(30)\n .describe(\"Output frames per second (re-encoded from the recording). Default 30.\"),\n /** Time to scroll from top to bottom (ms). */\n duration: z\n .number()\n .int()\n .positive()\n .default(6000)\n .describe(\"Time to scroll from top to bottom (ms). Default 6000.\"),\n easing: easingSchema\n .default(\"easeInOutCubic\")\n .describe('Easing for the default top→bottom scroll. Default \"easeInOutCubic\".'),\n /** Dwell at the top before scrolling (ms). */\n startDelayMs: z\n .number()\n .int()\n .nonnegative()\n .default(500)\n .describe(\"Dwell at the top before scrolling (ms). Default 500.\"),\n /** Dwell at the bottom after scrolling (ms). */\n endDwellMs: z\n .number()\n .int()\n .nonnegative()\n .default(800)\n .describe(\"Dwell at the bottom after scrolling (ms). Default 800.\"),\n waitUntil: z\n .enum([\"load\", \"domcontentloaded\", \"networkidle\", \"commit\"])\n .default(\"networkidle\")\n .describe('Page-load milestone to wait for before recording. Default \"networkidle\".'),\n /** Optional element to wait for before recording (e.g. a hero section). */\n waitForSelector: z\n .string()\n .optional()\n .describe(\"Optional element to wait for before recording (e.g. a hero section). Omit to skip.\"),\n /** x264 quality, 0–51 (lower = better/larger). */\n crf: z\n .number()\n .int()\n .min(0)\n .max(51)\n .default(18)\n .describe(\"x264 quality, 0–51 (lower = better quality / larger file). Default 18.\"),\n /**\n * Capture strategy. \"frames\" (default) steps a virtual clock, sets the scroll position per frame and\n * screenshots — frame-accurate, crisp (supersampled) and reproducible. \"realtime\" records the live\n * browser session; use it only when the page's hero relies on time-based (not scroll-driven)\n * animation or autoplay video that should play during the capture.\n */\n capture: z\n .enum([\"frames\", \"realtime\"])\n .default(\"frames\")\n .describe(\n '\"frames\" (default) steps a virtual clock per frame — frame-accurate, crisp, reproducible; \"realtime\" records the live session for time-based hero animation or autoplay video.',\n ),\n /** Parallel render workers for \"frames\" (each its own browser context). Omit to auto-pick by cores. */\n workers: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\n 'Parallel render workers for \"frames\" (each its own browser context). Omit to auto-pick by cores.',\n ),\n /** Intermediate frame format for \"frames\"; \"png\" is lossless (slower), \"jpeg\" (default) is faster. */\n frameFormat: z\n .enum([\"jpeg\", \"png\"])\n .default(\"jpeg\")\n .describe(\n 'Intermediate frame format for \"frames\". \"jpeg\" (default) is faster; \"png\" is lossless.',\n ),\n\n /**\n * Choreographed scroll: an ordered list of steps instead of one top→bottom sweep. Each step scrolls\n * to a target (a 0..1 number, an \"NN%\" string, or a CSS selector to bring into view), then holds —\n * the \"pause on each section\" look. \"frames\" capture only (ignored by \"realtime\"). Omit for the\n * default single eased sweep. Clip length becomes startDelay + Σ(step travel + hold) + endDwell.\n */\n choreography: z\n .array(choreographyStepSchema)\n .optional()\n .describe(\n 'Choreographed scroll: an ordered list of steps instead of one top→bottom sweep (\"frames\" only). Omit for the default single eased sweep.',\n ),\n\n /**\n * Auto-choreograph: detect the page's sections and pan/hold through them automatically (no manual\n * selectors). `true` for defaults, or an object to tune. The clip is a fixed budget (`durationMs`,\n * default 12000) split across detected sections. \"frames\" capture only; ignored if `choreography`\n * is set.\n */\n autoSections: z\n .union([z.boolean(), autoSectionsSchema])\n .optional()\n .describe(\n 'Auto-choreograph: detect sections and pan/hold through them (\"frames\" only). `true` for defaults, or an object to tune. Ignored if `choreography` is set.',\n ),\n\n /** Loop style. \"boomerang\" plays the scroll forward then back within the clip for a seamless loop. */\n loop: z\n .enum([\"none\", \"boomerang\"])\n .default(\"none\")\n .describe(\n 'Loop style. \"boomerang\" plays the scroll forward then back for a seamless loop. Default \"none\".',\n ),\n\n /**\n * Ken Burns: a slow zoom over the clip (\"frames\" only). Scales the page toward an origin each frame\n * (folds automatically under a boomerang loop to stay seamless). May affect position:fixed elements —\n * pair with clean-capture if needed.\n */\n kenBurns: z\n .object({\n /** Start scale (1 = no zoom). Default 1. */\n scaleFrom: z\n .number()\n .positive()\n .optional()\n .describe(\"Start scale (1 = no zoom). Default 1.\"),\n /** End scale. Default 1.08. */\n scaleTo: z.number().positive().optional().describe(\"End scale. Default 1.08.\"),\n easing: easingSchema\n .optional()\n .describe('Easing for the zoom ramp. Default \"easeInOutCubic\".'),\n /** Zoom origin X within the viewport (0 = left, 1 = right). Default 0.5. */\n originX: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe(\"Zoom origin X within the viewport (0 = left, 1 = right). Default 0.5.\"),\n /** Zoom origin Y within the viewport (0 = top, 1 = bottom). Default 0.5. */\n originY: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe(\"Zoom origin Y within the viewport (0 = top, 1 = bottom). Default 0.5.\"),\n })\n .strict()\n .optional()\n .describe('Ken Burns slow zoom over the clip (\"frames\" only). Omit for no zoom.'),\n\n // --- interaction (scripted realtime \"tour\" with a synthetic cursor) ---\n /**\n * Drive a scripted interaction instead of an auto-scroll: a sequence of steps (move / click /\n * hover / type / scrollTo / wait) performed live with a visible cursor. Setting this records in\n * REALTIME (interactions and their animations are inherently time-based) and emits a single asset\n * (variants / aspect / extra outputs are skipped).\n */\n actions: z\n .array(interactionActionSchema)\n .optional()\n .describe(\n \"Drive a scripted interaction instead of an auto-scroll (move/click/hover/type/scrollTo/wait). Records in REALTIME and emits a single asset (variants/aspect/extra outputs skipped).\",\n ),\n /** The synthetic cursor shown during an interaction. */\n cursor: z\n .object({\n show: z\n .boolean()\n .optional()\n .describe(\"Show the cursor. Default true (when `actions` is set).\"),\n size: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Cursor size (px). Default 22.\"),\n color: z.string().optional().describe(\"Cursor color. Default white-with-shadow.\"),\n })\n .strict()\n .optional()\n .describe(\"The synthetic cursor shown during an interaction. Omit for the default cursor.\"),\n\n /**\n * Element-focused clip: scroll one component into view, optionally trigger it (`actions`), hold,\n * and crop the output to its box (+padding). Realtime; emits a single asset (variants / aspect /\n * outputs / cards are skipped).\n */\n focus: z\n .object({\n selector: z\n .string()\n .describe(\"Selector of the element to scroll into view and crop to.\"),\n /** Padding (px) around the element when cropping. Default 24. */\n padding: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Padding (px) around the element when cropping. Default 24.\"),\n /** Optional steps to trigger the component (e.g. open a dropdown) before holding. */\n actions: z\n .array(interactionActionSchema)\n .optional()\n .describe(\n \"Optional steps to trigger the component (e.g. open a dropdown) before holding.\",\n ),\n /** Time to dwell on the element after positioning/triggering (ms). Default 2000. */\n holdMs: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Time to dwell on the element after positioning / triggering (ms). Default 2000.\"),\n })\n .strict()\n .optional()\n .describe(\n \"Element-focused clip: scroll one component into view, optionally trigger it, hold, and crop the output to its box. Realtime; emits a single asset (variants/aspect/outputs/cards skipped).\",\n ),\n\n // --- variants (each emitted as its own asset; \"frames\" path) ---\n /** Force a color scheme. \"both\" emits a light AND a dark asset (<name>-light / <name>-dark). */\n colorScheme: z\n .enum([\"light\", \"dark\", \"both\"])\n .optional()\n .describe(\n 'Force a color scheme. \"both\" emits a light AND a dark asset (<name>-light / <name>-dark). Omit to leave as-is.',\n ),\n /** Add this class to <html> before capture (e.g. to trigger a CSS-class dark theme). */\n themeClass: z\n .string()\n .optional()\n .describe(\n \"Add this class to <html> before capture (e.g. to trigger a CSS-class dark theme). Omit for none.\",\n ),\n /** Capture the same reel at multiple viewports; each emits an asset (<name>-<viewport name>). */\n viewports: z\n .array(\n z\n .object({\n name: z\n .string()\n .describe(\"Name appended to the asset (<name>-<viewport name>).\"),\n width: z.number().int().positive().describe(\"Viewport width in CSS px.\"),\n height: z.number().int().positive().describe(\"Viewport height in CSS px.\"),\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .optional()\n .describe(\n \"Override the generator-level `deviceScaleFactor` for this viewport. Omit to inherit it.\",\n ),\n })\n .strict(),\n )\n .optional()\n .describe(\n \"Also capture the reel at these viewports; each emits an asset (<name>-<viewport name>).\",\n ),\n\n // --- multi-page tour (\"frames\" path) ---\n /**\n * Capture several routes and concatenate them into one reel. Each entry is a URL, or an object with\n * per-route `choreography` / `autoSections` / `durationMs`. Frame-stepped per route; emits a single\n * asset (variants are skipped; aspect/outputs apply to the final tour).\n */\n routes: z\n .array(\n z.union([\n z.string(),\n z\n .object({\n url: z\n .string()\n .describe('Route URL (absolute, or a \"/path\" against the managed server).'),\n choreography: z\n .array(choreographyStepSchema)\n .optional()\n .describe(\"Per-route choreographed scroll (overrides the tour default).\"),\n autoSections: z\n .union([z.boolean(), autoSectionsSchema])\n .optional()\n .describe(\"Per-route auto-section choreography.\"),\n durationMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"This route's slice of the tour (ms).\"),\n })\n .strict(),\n ]),\n )\n .optional()\n .describe(\n 'Capture several routes and concatenate them into one reel (\"frames\" path). Emits a single asset (variants skipped; aspect/outputs apply to the final tour).',\n ),\n\n // --- clean capture (suppress real-site noise; applied on the \"frames\" path) ---\n /** Hide elements matching these CSS selectors before capture (cookie banners, chat widgets, …). */\n hideSelectors: z\n .array(z.string())\n .default([])\n .describe(\n \"Hide elements matching these CSS selectors before capture (cookie banners, chat widgets, …). Default none.\",\n ),\n /** Extra CSS injected before capture (e.g. a brand backdrop, or hiding a sticky header). */\n injectCss: z\n .string()\n .optional()\n .describe(\n \"Extra CSS injected before capture (e.g. a brand backdrop, or hiding a sticky header). Omit for none.\",\n ),\n /** Click these selectors once after load to dismiss overlays (consent dialogs); best-effort. */\n clickSelectors: z\n .array(z.string())\n .default([])\n .describe(\n \"Click these selectors once after load to dismiss overlays (consent dialogs); best-effort. Default none.\",\n ),\n /** Hide scrollbars so they don't appear in the capture. */\n hideScrollbars: z\n .boolean()\n .default(true)\n .describe(\"Hide scrollbars so they don't appear in the capture. Default true.\"),\n /** Pause CSS animations/transitions for fully static, deterministic frames. */\n pauseAnimations: z\n .boolean()\n .default(false)\n .describe(\"Pause CSS animations/transitions for fully static, deterministic frames. Default false.\"),\n /** Freeze Date.now / performance.now / Math.random (seeded) so time/random content is stable. */\n freezeClock: z\n .boolean()\n .default(false)\n .describe(\n \"Freeze Date.now / performance.now / Math.random (seeded) so time/random content is stable. Default false.\",\n ),\n /** Abort common analytics/ads/session-replay requests during capture (cleaner, faster). */\n blockTrackers: z\n .boolean()\n .default(true)\n .describe(\n \"Abort common analytics/ads/session-replay requests during capture (cleaner, faster). Default true.\",\n ),\n /** Extra hostname substrings to block during capture. */\n blockHosts: z\n .array(z.string())\n .default([])\n .describe(\"Extra hostname substrings to block during capture. Default none.\"),\n /** Playwright resource types to block (e.g. \"media\", \"font\", \"image\"). */\n blockResourceTypes: z\n .array(z.string())\n .default([])\n .describe('Playwright resource types to block (e.g. \"media\", \"font\", \"image\"). Default none.'),\n\n // --- per-frame settling (\"frames\" path) ---\n /** Wait for fonts + in-view images before each frame's screenshot. Defaults on (off in draft). */\n settlePerFrame: z\n .boolean()\n .optional()\n .describe(\n \"Wait for fonts + in-view images before each frame's screenshot (\\\"frames\\\"). Defaults on (off in draft).\",\n ),\n /** Max time (ms) to wait per frame for settling before screenshotting anyway. */\n settleMaxMs: z\n .number()\n .int()\n .nonnegative()\n .default(250)\n .describe(\n \"Max time (ms) to wait per frame for settling before screenshotting anyway. Default 250.\",\n ),\n\n // --- output formats & reframing (\"frames\" path) ---\n /** Reframe the output to a target aspect: a preset (\"16:9\"|\"9:16\"|\"1:1\") or explicit {width,height}. */\n aspect: z\n .union([\n z.enum([\"16:9\", \"9:16\", \"1:1\"]),\n z.object({ width: z.number().int().positive(), height: z.number().int().positive() }).strict(),\n ])\n .optional()\n .describe(\n 'Reframe the output to a target aspect: a preset (\"16:9\"|\"9:16\"|\"1:1\") or explicit {width,height}. Omit to keep the capture aspect.',\n ),\n /** How to fit the capture into the aspect: \"cover\" (scale + center-crop) or \"contain\" (scale + pad). */\n fit: z\n .enum([\"cover\", \"contain\"])\n .default(\"cover\")\n .describe(\n 'How to fit the capture into `aspect`: \"cover\" (scale + center-crop) or \"contain\" (scale + pad). Default \"cover\".',\n ),\n /** Pad color used by \"contain\". */\n padColor: z\n .string()\n .default(\"#0b0b0f\")\n .describe('Pad color used by \"contain\". Default \"#0b0b0f\".'),\n /** Files to emit per variant; each becomes its own asset. */\n outputs: z\n .array(z.enum([\"mp4\", \"gif\", \"webp\", \"poster\"]))\n .default([\"mp4\"])\n .describe('Files to emit per variant; each becomes its own asset. Default [\"mp4\"].'),\n /** GIF / animated-WebP frame rate. Defaults to min(fps, 15). */\n gifFps: z\n .number()\n .int()\n .positive()\n .max(50)\n .optional()\n .describe(\"GIF / animated-WebP frame rate. Defaults to min(fps, 15).\"),\n /** Intro card shown before the reel (fades from black). Applies to frames / route tours. */\n intro: cardSchema\n .optional()\n .describe(\n \"Intro card shown before the reel (fades from black). Applies to frames / route tours. Omit for none.\",\n ),\n /** Outro / end card shown after the reel. */\n outro: cardSchema.optional().describe(\"Outro / end card shown after the reel. Omit for none.\"),\n /** Timed on-screen annotations (caption text, a highlight ring, or a spotlight on a selector). */\n annotations: z\n .array(annotationSchema)\n .optional()\n .describe(\n \"Timed on-screen annotations (caption text, a highlight ring, or a spotlight on a selector).\",\n ),\n\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName: z\n .string()\n .optional()\n .describe('Output filename; defaults to \"<slug(asset name)>.mp4\".'),\n })\n .strict();\n\n// ---------------------------------------------------------------------------\n// Author-facing input types (editor autocomplete + hover docs). JSDoc on the\n// zod schema above does NOT surface on hover through `z.input`, so the docs\n// live on these hand-written interfaces, kept in sync with the schema by the\n// Exact<> guard at the bottom — a drift is a compile error.\n// ---------------------------------------------------------------------------\n\n/** One choreographed scroll step. */\nexport interface ChoreographyStepInput {\n /** Target: a 0..1 number, an \"NN%\" string, or a CSS selector to bring into view. */\n to: number | string;\n /** Travel time to this target (ms). Default 1200. */\n durationMs?: number;\n /** Hold time at this target after arriving (ms). Default 800. */\n holdMs?: number;\n /** Easing for the travel to this target. Default \"easeInOutCubic\". */\n easing?: Easing;\n}\n\n/** One step of a scripted interaction (`actions` / `focus.actions`). */\nexport interface InteractionActionInput {\n /** What this step does. */\n do: \"move\" | \"click\" | \"hover\" | \"type\" | \"scrollTo\" | \"wait\";\n /** Target element for move/click/hover/type. */\n selector?: string;\n /** Viewport-relative X target for a selector-less `move` (0..1). */\n x?: number;\n /** Viewport-relative Y target for a selector-less `move` (0..1). */\n y?: number;\n /** Text to type (for `type`). */\n text?: string;\n /** Scroll target for `scrollTo`: a 0..1 number, an \"NN%\" string, or a CSS selector. */\n to?: number | string;\n /** Cursor travel / scroll animation time (ms). Default 700. */\n durationMs?: number;\n /** Pause after the step (ms). Default 600. */\n holdMs?: number;\n}\n\n/** Tuning for auto-section choreography (`autoSections`). */\nexport interface AutoSectionsInput {\n /** Min element height (as a fraction of the viewport) to count as a section. Default 0.5. */\n minHeightFraction?: number;\n /** Explicit section selector; overrides the heuristic. Omit to auto-detect. */\n selector?: string;\n /** Hold at each detected section (ms). Default 700. */\n holdMs?: number;\n /** Total clip length (ms) split across detected sections. Default 12000. */\n durationMs?: number;\n /** Cap on the number of sections. Default 8. */\n maxSections?: number;\n /** Distribute travel time by distance for uniform scroll speed. Default true. */\n constantVelocity?: boolean;\n}\n\n/** An intro / outro card (`intro` / `outro`). */\nexport interface CardInput {\n /** Card title (large). Omit for none. */\n title?: string;\n /** Card subtitle (small, under the title). Omit for none. */\n subtitle?: string;\n /** Card background color. Default black. */\n background?: string;\n /** Card text color. Default white. */\n color?: string;\n /** How long the card holds (ms). Default 1500. */\n durationMs?: number;\n /** Fade in/out length (ms). Default 400. */\n fadeMs?: number;\n}\n\n/** A timed on-screen annotation (`annotations`). */\nexport interface AnnotationInput {\n /** Caption text shown while active. Omit for a ring/spotlight with no caption. */\n text?: string;\n /** Selector to outline with a highlight ring. */\n ring?: string;\n /** Selector to spotlight (everything else dimmed). */\n spotlight?: string;\n /** When it appears (clip time, ms). Default 0. */\n atMs?: number;\n /** When it disappears (clip time, ms). Default = end of clip. */\n untilMs?: number;\n /** Caption placement. Default \"bottom\". */\n position?: \"top\" | \"bottom\" | \"center\";\n}\n\n/** Ken Burns slow-zoom config (\"frames\" capture only). */\nexport interface KenBurnsInput {\n /** Start scale (1 = no zoom). Default 1. */\n scaleFrom?: number;\n /** End scale. Default 1.08. */\n scaleTo?: number;\n /** Easing for the zoom ramp. Default \"easeInOutCubic\". */\n easing?: Easing;\n /** Zoom origin X within the viewport (0 = left, 1 = right). Default 0.5. */\n originX?: number;\n /** Zoom origin Y within the viewport (0 = top, 1 = bottom). Default 0.5. */\n originY?: number;\n}\n\n/** The synthetic cursor shown during an interaction. */\nexport interface CursorInput {\n /** Show the cursor. Default true (when `actions` is set). */\n show?: boolean;\n /** Cursor size (px). Default 22. */\n size?: number;\n /** Cursor color. Default white-with-shadow. */\n color?: string;\n}\n\n/** Element-focused clip config (`focus`). */\nexport interface FocusInput {\n /** Selector of the element to scroll into view and crop to. */\n selector: string;\n /** Padding (px) around the element when cropping. Default 24. */\n padding?: number;\n /** Optional steps to trigger the component (e.g. open a dropdown) before holding. */\n actions?: InteractionActionInput[];\n /** Time to dwell on the element after positioning / triggering (ms). Default 2000. */\n holdMs?: number;\n}\n\n/** One extra viewport to also capture the reel at (`viewports`). */\nexport interface ViewportInput {\n /** Name appended to the asset (<name>-<viewport name>). */\n name: string;\n /** Viewport width in CSS px. */\n width: number;\n /** Viewport height in CSS px. */\n height: number;\n /** Override the generator-level `deviceScaleFactor` for this viewport. Omit to inherit it. */\n deviceScaleFactor?: number;\n}\n\n/** One route in a multi-page tour: a URL string, or an object with per-route choreography. */\nexport type RouteInput =\n | string\n | {\n /** Route URL (absolute, or a \"/path\" against the managed server). */\n url: string;\n /** Per-route choreographed scroll (overrides the tour default). */\n choreography?: ChoreographyStepInput[];\n /** Per-route auto-section choreography. */\n autoSections?: boolean | AutoSectionsInput;\n /** This route's slice of the tour (ms). */\n durationMs?: number;\n };\n\n/** Target output aspect: a preset, or an explicit pixel box. */\nexport type AspectInput = \"16:9\" | \"9:16\" | \"1:1\" | { width: number; height: number };\n\n/**\n * Author-facing options for the `scroll-reel` generator — a video of a page. By default it eases a\n * single top→bottom scroll; the options below switch it into choreographed / auto-section / focus /\n * interaction / multi-route modes, add cards & annotations, clean up the page, and reframe / re-encode\n * the output. Everything is optional, with the defaults noted below.\n */\nexport interface ScrollReelOptionsInput {\n /** Viewport + output width in CSS px. Default 1280. */\n width?: number;\n /** Viewport + output height in CSS px. Default 800. */\n height?: number;\n /** Render scale (2 = retina-crisp capture, downscaled into the video). Default 2. */\n deviceScaleFactor?: number;\n /** Output frames per second (re-encoded from the recording). Default 30. */\n fps?: number;\n /** Time to scroll from top to bottom (ms). Default 6000. */\n duration?: number;\n /** Easing for the default top→bottom scroll. Default \"easeInOutCubic\". */\n easing?: Easing;\n /** Dwell at the top before scrolling (ms). Default 500. */\n startDelayMs?: number;\n /** Dwell at the bottom after scrolling (ms). Default 800. */\n endDwellMs?: number;\n /** Page-load milestone to wait for before recording. Default \"networkidle\". */\n waitUntil?: \"load\" | \"domcontentloaded\" | \"networkidle\" | \"commit\";\n /** Optional element to wait for before recording (e.g. a hero section). Omit to skip. */\n waitForSelector?: string;\n /** x264 quality, 0–51 (lower = better quality / larger file). Default 18. */\n crf?: number;\n /**\n * Capture strategy. \"frames\" (default) steps a virtual clock per frame — frame-accurate, crisp,\n * reproducible. \"realtime\" records the live session; use it only for time-based hero animation or\n * autoplay video. Default \"frames\".\n */\n capture?: \"frames\" | \"realtime\";\n /** Parallel render workers for \"frames\" (each its own browser context). Omit to auto-pick by cores. */\n workers?: number;\n /** Intermediate frame format for \"frames\". \"jpeg\" (default) is faster; \"png\" is lossless. Default \"jpeg\". */\n frameFormat?: \"jpeg\" | \"png\";\n /**\n * Choreographed scroll: an ordered list of steps instead of one top→bottom sweep (\"frames\" only).\n * Omit for the default single eased sweep.\n */\n choreography?: ChoreographyStepInput[];\n /**\n * Auto-choreograph: detect the page's sections and pan/hold through them (\"frames\" only). `true`\n * for defaults, or an object to tune. Ignored if `choreography` is set. Omit to disable.\n */\n autoSections?: boolean | AutoSectionsInput;\n /** Loop style. \"boomerang\" plays the scroll forward then back for a seamless loop. Default \"none\". */\n loop?: \"none\" | \"boomerang\";\n /** Ken Burns slow zoom over the clip (\"frames\" only). Omit for no zoom. */\n kenBurns?: KenBurnsInput;\n /**\n * Drive a scripted interaction instead of an auto-scroll (move/click/hover/type/scrollTo/wait).\n * Setting this records in REALTIME and emits a single asset (variants/aspect/extra outputs skipped).\n */\n actions?: InteractionActionInput[];\n /** The synthetic cursor shown during an interaction. Omit for the default cursor. */\n cursor?: CursorInput;\n /**\n * Element-focused clip: scroll one component into view, optionally trigger it, hold, and crop the\n * output to its box. Realtime; emits a single asset (variants/aspect/outputs/cards skipped).\n */\n focus?: FocusInput;\n /** Force a color scheme. \"both\" emits a light AND a dark asset (<name>-light / <name>-dark). Omit to leave as-is. */\n colorScheme?: \"light\" | \"dark\" | \"both\";\n /** Add this class to <html> before capture (e.g. to trigger a CSS-class dark theme). Omit for none. */\n themeClass?: string;\n /** Also capture the reel at these viewports; each emits an asset (<name>-<viewport name>). */\n viewports?: ViewportInput[];\n /**\n * Capture several routes and concatenate them into one reel (\"frames\" path). Emits a single asset\n * (variants skipped; aspect/outputs apply to the final tour).\n */\n routes?: RouteInput[];\n /** Hide elements matching these CSS selectors before capture (cookie banners, chat widgets, …). Default none. */\n hideSelectors?: string[];\n /** Extra CSS injected before capture (e.g. a brand backdrop, or hiding a sticky header). Omit for none. */\n injectCss?: string;\n /** Click these selectors once after load to dismiss overlays (consent dialogs); best-effort. Default none. */\n clickSelectors?: string[];\n /** Hide scrollbars so they don't appear in the capture. Default true. */\n hideScrollbars?: boolean;\n /** Pause CSS animations/transitions for fully static, deterministic frames. Default false. */\n pauseAnimations?: boolean;\n /** Freeze Date.now / performance.now / Math.random (seeded) so time/random content is stable. Default false. */\n freezeClock?: boolean;\n /** Abort common analytics/ads/session-replay requests during capture (cleaner, faster). Default true. */\n blockTrackers?: boolean;\n /** Extra hostname substrings to block during capture. Default none. */\n blockHosts?: string[];\n /** Playwright resource types to block (e.g. \"media\", \"font\", \"image\"). Default none. */\n blockResourceTypes?: string[];\n /** Wait for fonts + in-view images before each frame's screenshot (\"frames\"). Defaults on (off in draft). */\n settlePerFrame?: boolean;\n /** Max time (ms) to wait per frame for settling before screenshotting anyway. Default 250. */\n settleMaxMs?: number;\n /** Reframe the output to a target aspect: a preset (\"16:9\"|\"9:16\"|\"1:1\") or explicit {width,height}. Omit to keep the capture aspect. */\n aspect?: AspectInput;\n /** How to fit the capture into `aspect`: \"cover\" (scale + center-crop) or \"contain\" (scale + pad). Default \"cover\". */\n fit?: \"cover\" | \"contain\";\n /** Pad color used by \"contain\". Default \"#0b0b0f\". */\n padColor?: string;\n /** Files to emit per variant; each becomes its own asset. Default [\"mp4\"]. */\n outputs?: (\"mp4\" | \"gif\" | \"webp\" | \"poster\")[];\n /** GIF / animated-WebP frame rate. Defaults to min(fps, 15). */\n gifFps?: number;\n /** Intro card shown before the reel (fades from black). Applies to frames / route tours. Omit for none. */\n intro?: CardInput;\n /** Outro / end card shown after the reel. Omit for none. */\n outro?: CardInput;\n /** Timed on-screen annotations (caption text, a highlight ring, or a spotlight on a selector). */\n annotations?: AnnotationInput[];\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName?: string;\n}\n\n/** Author-facing input (documented for editor hover; the schema validates it at run time). */\nexport type ScrollReelOptions = ScrollReelOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedScrollReelOptions = z.infer<typeof scrollReelOptionsSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _scrollReelInputInSync: Exact<\n ScrollReelOptionsInput,\n z.input<typeof scrollReelOptionsSchema>\n> = true;\nvoid _scrollReelInputInSync;\n","import path from \"node:path\";\nimport { mkdtemp } from \"node:fs/promises\";\nimport type { Browser } from \"playwright-core\";\nimport type { Logger } from \"@/utils/logger\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { pageScroll, prepareScroll } from \"@/generators/scroll-reel/scroll\";\nimport type { ResolvedScrollReelOptions } from \"@/generators/scroll-reel/options\";\n\n/** Time at the bottom for lazy/below-the-fold content to load during the (trimmed) warm-up pass. */\nconst PREWARM_SETTLE_MS = 700;\n\nexport interface CaptureArgs {\n browser: Browser;\n url: string;\n options: ResolvedScrollReelOptions;\n /** Scratch root; a unique recording subdir is created inside. */\n tmpDir: string;\n logger: Logger;\n}\n\nexport interface CaptureResult {\n /** Absolute path to the recorded .webm. */\n webmPath: string;\n /** Seconds of navigation + warm-up before the animated scroll began — trim this off the head. */\n leadSeconds: number;\n}\n\n/**\n * Open a recording context, navigate, run the eased scroll, then close to finalize the\n * webm. Returns the recording path for downstream transcoding.\n */\nexport async function captureScrollWebm(args: CaptureArgs): Promise<CaptureResult> {\n const { browser, url, options, tmpDir, logger } = args;\n await ensureDir(tmpDir);\n const recordDir = await mkdtemp(path.join(tmpDir, \"rec-\"));\n\n const context = await browser.newContext({\n viewport: { width: options.width, height: options.height },\n deviceScaleFactor: options.deviceScaleFactor,\n recordVideo: {\n dir: recordDir,\n size: { width: options.width, height: options.height },\n },\n });\n const page = await context.newPage();\n const video = page.video();\n\n const recStart = Date.now(); // Playwright records the whole context lifetime\n let leadSeconds = 0;\n try {\n logger.debug(`navigating to ${url} (waitUntil=${options.waitUntil})`);\n await page.goto(url, { waitUntil: options.waitUntil });\n if (options.waitForSelector) {\n logger.debug(`waiting for selector ${options.waitForSelector}`);\n await page.waitForSelector(options.waitForSelector, { state: \"visible\" });\n }\n // Warm-up (trimmed from the head): load lazy content, fonts and images, settle at the top.\n await page.evaluate(prepareScroll, { settleMs: PREWARM_SETTLE_MS });\n // Everything above is blank/churn in the recording; the animated scroll starts now.\n leadSeconds = (Date.now() - recStart) / 1000;\n const distance = await page.evaluate(pageScroll, {\n durationMs: options.duration,\n easing: options.easing,\n startDelayMs: options.startDelayMs,\n endDwellMs: options.endDwellMs,\n });\n if (!distance) {\n logger.warn(\n \"page did not scroll — no scrollable content found (the reel will be static). Check the URL renders a taller-than-viewport page.\",\n );\n }\n } finally {\n // Closing the context flushes + finalizes the recording file.\n await context.close();\n }\n\n if (!video) {\n throw new Error(\"Playwright did not record a video (recordVideo inactive).\");\n }\n return { webmPath: await video.path(), leadSeconds };\n}\n","export type EasingName =\n | \"linear\"\n | \"easeInOutCubic\"\n | \"easeInOutQuad\"\n | \"easeOutCubic\"\n | \"easeInOutSine\"\n | \"easeInOutExpo\"\n | \"easeOutQuint\";\n\n/**\n * Pure easing functions, t in [0,1] -> [0,1]. Unit-tested on the Node side; the formula inside\n * `pageScroll` below MUST stay in sync with these.\n */\nexport const EASINGS: Record<EasingName, (t: number) => number> = {\n linear: (t) => t,\n easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2),\n easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2),\n easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),\n easeInOutSine: (t) => -(Math.cos(Math.PI * t) - 1) / 2,\n easeInOutExpo: (t) =>\n t === 0\n ? 0\n : t === 1\n ? 1\n : t < 0.5\n ? Math.pow(2, 20 * t - 10) / 2\n : (2 - Math.pow(2, -20 * t + 10)) / 2,\n easeOutQuint: (t) => 1 - Math.pow(1 - t, 5),\n};\n\nexport interface PageScrollArgs {\n durationMs: number;\n easing: EasingName;\n startDelayMs: number;\n endDwellMs: number;\n}\n\nexport interface PrepareScrollArgs {\n /** How long to wait at the bottom for lazy/below-the-fold content to load (ms). */\n settleMs: number;\n}\n\nexport interface SeekScrollArgs {\n /** Normalized scroll position to jump to (0 = top, 1 = bottom). */\n normalizedY: number;\n /** Optional Ken Burns zoom scale for this frame (omit for no zoom). */\n scale?: number;\n /** Zoom origin X within the viewport (0 = left, 1 = right). Default 0.5. */\n originX?: number;\n /** Zoom origin Y within the viewport (0 = top, 1 = bottom). Default 0.5. */\n originY?: number;\n}\n\nexport interface MeasureOffsetsArgs {\n /** CSS selectors to resolve to normalized scroll positions (0..1). */\n selectors: string[];\n /**\n * Pixels a sticky/fixed header intrudes from the top — each resolved target is pulled UP by this so\n * the selector lands below the header, not under it. Measured by {@link measureTopInset}; default 0.\n */\n headerInsetPx?: number;\n}\n\nexport interface MeasureTopInsetArgs {\n /** Don't count a top element taller than this fraction of the viewport (skips full-screen overlays). */\n maxFraction?: number;\n}\n\nexport interface DetectSectionsArgs {\n /** Minimum element height as a fraction of the viewport to count as a section. */\n minHeightFraction: number;\n /** Explicit section selector; overrides the heuristic when set (non-null). */\n selector: string | null;\n /** Cap on the number of returned sections. */\n maxSections: number;\n /**\n * Pixels a sticky/fixed header intrudes from the top of the viewport. Each section target is pulled\n * UP by this, so a scrolled-to section sits just below the header instead of being clipped by it.\n * Measured once by {@link measureTopInset}; defaults to 0 (no header).\n */\n headerInsetPx?: number;\n}\n\n// Browser globals — declared loosely (no DOM lib in this Node project). The exported functions are\n// serialized and run inside the page via page.evaluate, so each must be fully self-contained (no\n// references to module-level bindings); the small scroll helpers are therefore inlined in both.\ndeclare const window: {\n scrollTo: (o: { top: number; left: number; behavior: string }) => void;\n innerHeight: number;\n innerWidth: number;\n pageYOffset: number;\n pageXOffset: number;\n};\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ndeclare const document: any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ndeclare const getComputedStyle: (el: any) => { overflowY: string };\ndeclare const requestAnimationFrame: (cb: (t: number) => void) => number;\ndeclare const setTimeout: (cb: () => void, ms: number) => unknown;\n\n/**\n * Runs INSIDE the page. Warms the page for capture: disables smooth scrolling, jumps to the bottom\n * (instantly) to trigger lazy-loaded / intersection-mounted content and below-the-fold image\n * requests, waits for those plus fonts and image decode, then returns to the top. Meant to run\n * *before* the recording's trim point so none of this churn is visible in the final clip.\n */\nexport async function prepareScroll(args: PrepareScrollArgs): Promise<void> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n forceInstant(target);\n\n const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(() => resolve(), ms));\n\n scrollTargetTo(target, maxScrollOf(target)); // jump to bottom to trigger lazy content\n await sleep(Math.max(0, args.settleMs));\n try {\n await (document.fonts?.ready ?? Promise.resolve());\n } catch {\n /* no font set */\n }\n try {\n const imgs = Array.from(document.images ?? []);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await Promise.all(imgs.map((im: any) => (im.decode ? im.decode().catch(() => {}) : null)));\n } catch {\n /* decode unsupported */\n }\n scrollTargetTo(target, 0); // back to the top for the animated pass\n await sleep(50);\n\n // --- inlined, self-contained scroll helpers (duplicated in pageScroll; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function scrollTargetTo(t: any, y: number): void {\n if (isDocTarget(t)) window.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else if (t.scrollTo) t.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else t.scrollTop = y;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function forceInstant(t: any): void {\n try {\n document.documentElement.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n try {\n if (t && t.style) t.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n }\n}\n\n/**\n * Runs INSIDE the page. Auto-detects the page's section boundaries as normalized scroll positions\n * (0..1), sorted ascending and deduped, with the bottom (1) always included. Uses an explicit selector\n * when given, else a heuristic (<section>, direct children of <main>, [data-section]) filtered to\n * elements at least `minHeightFraction` of the viewport tall. Returns [] for a non-scrollable page.\n * Self-contained (serialized via page.evaluate).\n */\nexport async function detectSectionOffsets(args: DetectSectionsArgs): Promise<number[]> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n const distance = maxScrollOf(target);\n if (distance <= 0) return [];\n const docTarget = isDocTarget(target);\n const vh = window.innerHeight || document.documentElement.clientHeight || 1;\n const containerTop = docTarget ? 0 : target.getBoundingClientRect().top;\n const curScroll = docTarget\n ? window.pageYOffset || document.documentElement.scrollTop || 0\n : target.scrollTop;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let els: any[] = [];\n try {\n els = Array.from(\n document.querySelectorAll(args.selector || \"section, main > *, [data-section]\"),\n );\n } catch {\n els = [];\n }\n const minH = vh * args.minHeightFraction;\n const offsets: number[] = [];\n for (const el of els) {\n let rect: { top: number; height: number };\n try {\n rect = el.getBoundingClientRect();\n } catch {\n continue;\n }\n // Apply the height filter only to the heuristic; trust an explicit selector verbatim.\n if (!args.selector && rect.height < minH) continue;\n const rawTop = docTarget ? rect.top + curScroll : rect.top - containerTop + curScroll;\n // Pull the target up by the sticky/fixed header height so the section isn't clipped under it.\n const top = rawTop - (args.headerInsetPx ?? 0);\n const y = Math.max(0, Math.min(distance, top));\n offsets.push(y / distance);\n }\n offsets.sort((a, b) => a - b);\n const deduped: number[] = [];\n for (const o of offsets) {\n if (deduped.length === 0 || o - deduped[deduped.length - 1]! > 0.01) deduped.push(o);\n }\n // Always end the reel at the bottom.\n if (deduped.length === 0 || deduped[deduped.length - 1]! < 0.99) deduped.push(1);\n return deduped.slice(0, args.maxSections);\n\n // --- inlined, self-contained scroll helpers (duplicated elsewhere in this file; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n}\n\n/**\n * Runs INSIDE the page. Resolves CSS selectors to normalized scroll positions (0..1) on the real scroll\n * target — used by choreography to \"scroll to a section\". Returns null for a selector that matches no\n * element. Measured once after warm-up (positions are stable), so all workers agree. Self-contained.\n */\nexport async function measureNormalizedOffsets(\n args: MeasureOffsetsArgs,\n): Promise<Array<number | null>> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n const distance = maxScrollOf(target);\n const docTarget = isDocTarget(target);\n const containerTop = docTarget ? 0 : target.getBoundingClientRect().top;\n const curScroll = docTarget\n ? window.pageYOffset || document.documentElement.scrollTop || 0\n : target.scrollTop;\n return args.selectors.map((sel) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let el: any = null;\n try {\n el = document.querySelector(sel);\n } catch {\n el = null;\n }\n if (!el) return null;\n const rect = el.getBoundingClientRect();\n const rawTop = docTarget ? rect.top + curScroll : rect.top - containerTop + curScroll;\n // Pull the target up by the sticky/fixed header height so the selector lands below it, not under it.\n const offsetTop = rawTop - (args.headerInsetPx ?? 0);\n const y = Math.max(0, Math.min(distance, offsetTop));\n return distance > 0 ? y / distance : 0;\n });\n\n // --- inlined, self-contained scroll helpers (duplicated elsewhere in this file; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n}\n\n/**\n * Runs INSIDE the page. Measures how far a sticky/fixed header intrudes from the top of the viewport\n * (px) so scroll targets can be pulled up by it — otherwise a scrolled-to section lands UNDER the\n * header and looks clipped. Probe-scrolls down one viewport so a `position: sticky` header is actually\n * pinned, takes the lowest bottom edge among wide, top-anchored fixed/sticky elements, then restores\n * the scroll. Returns 0 when there's no such header. Self-contained (serialized via page.evaluate).\n */\nexport async function measureTopInset(args: MeasureTopInsetArgs): Promise<number> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n const vh = window.innerHeight || document.documentElement.clientHeight || 0;\n const vw = window.innerWidth || document.documentElement.clientWidth || 0;\n const maxFraction = args.maxFraction ?? 0.4;\n const distance = maxScrollOf(target);\n const cur = isDocTarget(target)\n ? window.pageYOffset || document.documentElement.scrollTop || 0\n : target.scrollTop;\n // Pin any top-sticky header by scrolling past its natural position, then settle one frame.\n scrollTargetTo(target, Math.min(cur + vh, distance));\n await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n let inset = 0;\n try {\n const all = document.querySelectorAll(\"body *\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let position = \"\";\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n position = (getComputedStyle(el) as any).position;\n } catch {\n continue;\n }\n if (position !== \"fixed\" && position !== \"sticky\") continue;\n let r: { top: number; bottom: number; width: number; height: number };\n try {\n r = el.getBoundingClientRect();\n } catch {\n continue;\n }\n // A header pinned to the top: starts at the very top, spans most of the width, isn't a\n // full-screen overlay, and reaches lower than anything found so far.\n if (\n r.top <= 2 &&\n r.width >= vw * 0.6 &&\n r.height > 0 &&\n r.bottom > inset &&\n r.bottom <= vh * maxFraction\n ) {\n inset = r.bottom;\n }\n }\n } catch {\n /* ignore */\n }\n scrollTargetTo(target, cur); // restore the original scroll\n await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n return inset;\n\n // --- inlined, self-contained scroll helpers (duplicated elsewhere in this file; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function scrollTargetTo(t: any, y: number): void {\n if (isDocTarget(t)) window.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else if (t.scrollTo) t.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else t.scrollTop = y;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n}\n\n/**\n * Runs INSIDE the page. Waits for the CURRENT scroll position's in-view content to be ready — fonts\n * loaded and visible images decoded — then yields one animation frame. Bounded by the caller (a\n * Node-side timeout) so a stuck decode can't hang a frame. Self-contained (serialized via page.evaluate).\n */\nexport async function settleInView(): Promise<void> {\n try {\n await (document.fonts?.ready ?? Promise.resolve());\n } catch {\n /* no font set */\n }\n try {\n const vh = window.innerHeight || document.documentElement.clientHeight || 0;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const imgs = Array.from(document.images ?? []) as any[];\n const inView = imgs.filter((im) => {\n try {\n const r = im.getBoundingClientRect();\n return r.bottom > 0 && r.top < vh && r.width > 0 && r.height > 0;\n } catch {\n return false;\n }\n });\n await Promise.all(inView.map((im) => (im.decode ? im.decode().catch(() => {}) : null)));\n } catch {\n /* decode unsupported */\n }\n await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n}\n\n/**\n * Runs INSIDE the page. Jumps the real scroll target to a normalized position (0 = top, 1 = bottom)\n * instantly, then waits TWO animation frames so scroll-linked animations, IntersectionObserver reveals,\n * and `position: sticky` elements recompute and PAINT before the caller screenshots. This is the\n * frame-stepped counterpart to `pageScroll`: the capture layer calls it once per frame with the\n * timeline's position for that frame, so the resulting video is frame-accurate and reproducible. Must be\n * self-contained (serialized via page.evaluate); the scroll helpers are inlined as elsewhere in this file.\n */\nexport async function seekScrollTo(args: SeekScrollArgs): Promise<void> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n forceInstant(target);\n const distance = maxScrollOf(target);\n const clamped = args.normalizedY < 0 ? 0 : args.normalizedY > 1 ? 1 : args.normalizedY;\n scrollTargetTo(target, clamped * distance);\n // Ken Burns: scale the page toward a viewport-anchored origin (computed from the live scroll), so\n // the zoom centers on what's on screen. Only touched when a scale is supplied.\n if (typeof args.scale === \"number\") {\n try {\n const vw = window.innerWidth || document.documentElement.clientWidth || 0;\n const vh = window.innerHeight || document.documentElement.clientHeight || 0;\n const sx = window.pageXOffset || 0;\n const sy = window.pageYOffset || document.documentElement.scrollTop || 0;\n const ox = sx + (args.originX ?? 0.5) * vw;\n const oy = sy + (args.originY ?? 0.5) * vh;\n const body = document.body;\n if (body && body.style) {\n body.style.transformOrigin = `${ox}px ${oy}px`;\n body.style.transform = `scale(${args.scale})`;\n }\n } catch {\n /* ignore */\n }\n }\n // Two rAF ticks: let scroll handlers run (frame 1) and the resulting paint commit (frame 2).\n await new Promise<void>((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(() => resolve())),\n );\n\n // --- inlined, self-contained scroll helpers (duplicated in prepareScroll/pageScroll; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function scrollTargetTo(t: any, y: number): void {\n if (isDocTarget(t)) window.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else if (t.scrollTo) t.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else t.scrollTop = y;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function forceInstant(t: any): void {\n try {\n document.documentElement.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n try {\n if (t && t.style) t.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n }\n}\n\n/**\n * Runs INSIDE the page. Smoothly scrolls the real scroll target top -> bottom over `durationMs`\n * using requestAnimationFrame, with a dwell before and after. Forces instant per-frame positioning\n * so a site's `scroll-behavior: smooth` can't fight the animation, and targets the actual scroll\n * container (not just the window) so app-shell layouts scroll too. Returns the scrolled distance in\n * px (0 = nothing scrollable was found — the caller can warn).\n */\nexport async function pageScroll(args: PageScrollArgs): Promise<number> {\n const { durationMs, easing, startDelayMs, endDwellMs } = args;\n\n const ease = (t: number): number => {\n switch (easing) {\n case \"easeInOutCubic\":\n return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;\n case \"easeInOutQuad\":\n return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;\n case \"easeOutCubic\":\n return 1 - Math.pow(1 - t, 3);\n case \"easeInOutSine\":\n return -(Math.cos(Math.PI * t) - 1) / 2;\n case \"easeInOutExpo\":\n return t === 0\n ? 0\n : t === 1\n ? 1\n : t < 0.5\n ? Math.pow(2, 20 * t - 10) / 2\n : (2 - Math.pow(2, -20 * t + 10)) / 2;\n case \"easeOutQuint\":\n return 1 - Math.pow(1 - t, 5);\n default:\n return t;\n }\n };\n const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(() => resolve(), ms));\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const target = findScrollTarget(document, getComputedStyle) as any;\n forceInstant(target);\n\n scrollTargetTo(target, 0);\n await sleep(startDelayMs);\n\n // Snapshot the scroll distance once so lazy content loading mid-scroll can't move the goalpost.\n const distance = maxScrollOf(target);\n if (distance > 0) {\n await new Promise<void>((resolve) => {\n let start: number | null = null;\n const step = (now: number): void => {\n if (start === null) start = now;\n const t = Math.min(1, (now - start) / durationMs);\n scrollTargetTo(target, ease(t) * distance);\n if (t < 1) requestAnimationFrame(step);\n else resolve();\n };\n requestAnimationFrame(step);\n });\n }\n\n await sleep(endDwellMs);\n return distance;\n\n // --- inlined, self-contained scroll helpers (duplicated in prepareScroll; see note above) ---\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function findScrollTarget(doc: any, gcs: (el: any) => { overflowY: string }): unknown {\n const se = doc.scrollingElement || doc.documentElement;\n if (se && se.scrollHeight - se.clientHeight > 1) return se;\n let best: unknown = null;\n let bestArea = 0;\n const all = doc.querySelectorAll(\"*\");\n for (let i = 0; i < all.length; i++) {\n const el = all[i];\n let oy = \"\";\n try {\n oy = gcs(el).overflowY;\n } catch {\n oy = \"\";\n }\n const scrollable = oy === \"auto\" || oy === \"scroll\" || oy === \"overlay\";\n if (scrollable && el.scrollHeight - el.clientHeight > 1) {\n const area = el.clientWidth * el.clientHeight;\n if (area > bestArea) {\n bestArea = area;\n best = el;\n }\n }\n }\n return best || se;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function isDocTarget(t: any): boolean {\n return (\n t === (document.scrollingElement || document.documentElement) ||\n t === document.documentElement ||\n t === document.body\n );\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function scrollTargetTo(t: any, y: number): void {\n if (isDocTarget(t)) window.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else if (t.scrollTo) t.scrollTo({ top: y, left: 0, behavior: \"instant\" });\n else t.scrollTop = y;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function maxScrollOf(t: any): number {\n return Math.max(0, t.scrollHeight - t.clientHeight);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n function forceInstant(t: any): void {\n try {\n document.documentElement.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n try {\n if (t && t.style) t.style.scrollBehavior = \"auto\";\n } catch {\n /* ignore */\n }\n }\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport type { Browser, Page } from \"playwright-core\";\nimport { concatMp4, startFrameEncoder } from \"@/media/ffmpeg\";\nimport { ensureDir } from \"@/utils/fs\";\nimport type { Logger } from \"@/utils/logger\";\n\n/**\n * Deterministic frame-stepped video capture, shared by every \"step a clock and screenshot\" path\n * (the `scene` subsystem and site recording). The only per-path differences are two callbacks:\n * - `prepare` — runs once per worker context (navigate, wait for readiness/warm-up); returns\n * opaque per-page state passed back to `seekToFrame`.\n * - `seekToFrame` — advance the page to clip-time `t` (seconds) before the screenshot.\n *\n * Everything else (frame timing, parallel workers, supersample-then-encode, lossless concat) is the\n * machine-independent core: frame N is a pure function of `t`, so splitting the range across worker\n * contexts and concatenating is safe. Frames never touch disk — they pipe straight into ffmpeg.\n */\nexport interface FrameStepArgs<S = unknown> {\n browser: Browser;\n /** Viewport + screenshot size; supersampled by `deviceScaleFactor`, downscaled in ffmpeg. */\n width: number;\n height: number;\n deviceScaleFactor: number;\n fps: number;\n durationSeconds: number;\n crf: number;\n /** Absolute mp4 path to write. */\n outPath: string;\n /** \"ultrafast\" in draft, \"medium\" otherwise. */\n preset?: string;\n /** Intermediate frame format; \"png\" is lossless into the encoder. Default \"jpeg\". */\n frameFormat?: \"jpeg\" | \"png\";\n /** JPEG quality for intermediate frames (perf vs fidelity; ignored for png). */\n jpegQuality?: number;\n /** Parallel render workers (each its own browser context). Default 1. */\n workers?: number;\n /** Scratch dir for per-worker segments. */\n tmpDir: string;\n logger: Logger;\n /** Runs once per worker context (after newContext/newPage). Returns opaque per-page state. */\n prepare: (page: Page, helpers: { logger: Logger }) => Promise<S>;\n /** Advance the page to clip-time `t` (seconds) before the screenshot. */\n seekToFrame: (page: Page, t: number, state: S) => Promise<void>;\n /** Called with fractional progress (0–1) as frames complete (aggregated across workers). */\n onProgress?: (fraction: number) => void;\n /** Cancels the capture: the frame loop stops at the next boundary and the encoder is killed. */\n signal?: AbortSignal;\n}\n\n/** Default parallel workers: about half the cores, capped at 6 (each is a browser context). */\nexport function autoWorkers(): number {\n const cores = os.cpus()?.length ?? 2;\n return Math.max(1, Math.min(6, Math.floor(cores / 2)));\n}\n\n/**\n * Pure: split `totalFrames` into at most `workers` contiguous, non-overlapping [start, end) ranges\n * that exactly tile [0, totalFrames). Unit-tested (the I/O loop around it is exercised elsewhere).\n */\nexport function planFrames(totalFrames: number, workers: number): Array<{ start: number; end: number }> {\n const w = Math.max(1, Math.min(workers, totalFrames));\n const chunk = Math.ceil(totalFrames / w);\n const ranges: Array<{ start: number; end: number }> = [];\n for (let i = 0; i < w; i++) {\n const start = i * chunk;\n const end = Math.min(totalFrames, start + chunk);\n if (start >= end) break;\n ranges.push({ start, end });\n }\n return ranges;\n}\n\ninterface ChunkArgs<S> {\n browser: Browser;\n width: number;\n height: number;\n deviceScaleFactor: number;\n fps: number;\n crf: number;\n preset?: string;\n frameFormat: \"jpeg\" | \"png\";\n jpegQuality: number;\n /** Seconds advanced per frame (duration / totalFrames) — see captureFramedVideo. */\n timeStep: number;\n /** Inclusive start frame, exclusive end frame. */\n frameStart: number;\n frameEnd: number;\n outPath: string;\n logger: Logger;\n prepare: (page: Page, helpers: { logger: Logger }) => Promise<S>;\n seekToFrame: (page: Page, t: number, state: S) => Promise<void>;\n /** Called once per encoded frame (for aggregated progress). */\n onFrame?: () => void;\n signal?: AbortSignal;\n}\n\n/** Render a contiguous frame range in its own browser context, encoding to one mp4 segment. */\nasync function renderChunk<S>(a: ChunkArgs<S>): Promise<void> {\n const context = await a.browser.newContext({\n viewport: { width: a.width, height: a.height },\n deviceScaleFactor: a.deviceScaleFactor,\n });\n const page = await context.newPage();\n page.on(\"console\", (m) => a.logger.debug(`[capture] ${m.text()}`));\n page.on(\"pageerror\", (e) => a.logger.debug(`[capture error] ${e.message}`));\n\n try {\n const state = await a.prepare(page, { logger: a.logger });\n a.signal?.throwIfAborted(); // bail before spawning the encoder if we were cancelled during prepare\n\n const encoder = startFrameEncoder(\n {\n fps: a.fps,\n width: a.width,\n height: a.height,\n crf: a.crf,\n outPath: a.outPath,\n preset: a.preset,\n inputFormat: a.frameFormat,\n },\n a.logger,\n a.signal,\n );\n // Playwright rejects `quality` for png screenshots, so only pass it on the jpeg path.\n const shotOptions =\n a.frameFormat === \"png\"\n ? ({ type: \"png\" } as const)\n : ({ type: \"jpeg\", quality: a.jpegQuality } as const);\n for (let frame = a.frameStart; frame < a.frameEnd; frame++) {\n a.signal?.throwIfAborted(); // a cancelled run stops here, not after every frame is rendered\n const t = frame * a.timeStep;\n await a.seekToFrame(page, t, state);\n const buf = await page.screenshot(shotOptions);\n await encoder.write(buf);\n a.onFrame?.();\n }\n await encoder.done();\n } finally {\n await context.close();\n }\n}\n\n/**\n * Step each frame to its exact time via `seekToFrame`, screenshot, and pipe straight into ffmpeg —\n * frame-accurate and machine-independent, no frames touch disk. With `workers > 1`, the frame range is\n * split into contiguous segments rendered in parallel contexts, then losslessly concatenated (the\n * determinism is what makes this safe).\n */\nexport async function captureFramedVideo<S>(args: FrameStepArgs<S>): Promise<void> {\n await ensureDir(path.dirname(args.outPath));\n const totalFrames = Math.max(1, Math.round(args.durationSeconds * args.fps));\n // Step time as duration/totalFrames (≈ 1/fps) so the N frames evenly tile [0, duration). The frame\n // that would sit at t=duration is the loop point — which equals t=0 for a seamless clip — so omitting\n // it (we render 0..N-1) makes the last→first wrap exactly one step: no seam hitch.\n const timeStep = args.durationSeconds / totalFrames;\n const frameFormat = args.frameFormat ?? \"jpeg\";\n const jpegQuality = args.jpegQuality ?? 90;\n const ranges = planFrames(totalFrames, Math.max(1, args.workers ?? 1));\n // Aggregate frame completions across parallel workers into a single 0–1 fraction.\n let completed = 0;\n const onFrame = args.onProgress\n ? () => {\n completed += 1;\n args.onProgress?.(completed / totalFrames);\n }\n : undefined;\n const common = {\n browser: args.browser,\n width: args.width,\n height: args.height,\n deviceScaleFactor: args.deviceScaleFactor,\n fps: args.fps,\n crf: args.crf,\n preset: args.preset,\n frameFormat,\n jpegQuality,\n timeStep,\n logger: args.logger,\n prepare: args.prepare,\n seekToFrame: args.seekToFrame,\n onFrame,\n signal: args.signal,\n };\n\n if (ranges.length === 1) {\n args.logger.debug(`stepping ${totalFrames} frames @ ${args.fps}fps`);\n await renderChunk({ ...common, frameStart: 0, frameEnd: totalFrames, outPath: args.outPath });\n return;\n }\n\n const segs = ranges.map((r, i) => ({\n ...r,\n seg: path.join(args.tmpDir, `seg-${i}-${path.basename(args.outPath)}`),\n }));\n args.logger.debug(`stepping ${totalFrames} frames across ${segs.length} workers`);\n await Promise.all(\n segs.map((r) =>\n renderChunk({ ...common, frameStart: r.start, frameEnd: r.end, outPath: r.seg }),\n ),\n );\n await concatMp4(\n segs.map((r) => r.seg),\n args.outPath,\n args.logger,\n args.signal,\n );\n}\n","import type { Page } from \"playwright-core\";\nimport type { ResolvedScrollReelOptions } from \"@/generators/scroll-reel/options\";\nimport type { Logger } from \"@/utils/logger\";\n\nexport interface CleanCssOptions {\n hideSelectors: string[];\n hideScrollbars: boolean;\n pauseAnimations: boolean;\n injectCss?: string;\n}\n\n/**\n * Pure: assemble the CSS injected to suppress capture noise. Returns \"\" when nothing is requested, so\n * callers can skip the (otherwise empty) style tag. Unit-tested.\n */\nexport function buildCleanCss(opts: CleanCssOptions): string {\n const parts: string[] = [];\n if (opts.hideSelectors.length > 0) {\n parts.push(`${opts.hideSelectors.join(\", \")} { display: none !important; }`);\n }\n if (opts.hideScrollbars) {\n parts.push(\n `::-webkit-scrollbar { width: 0 !important; height: 0 !important; display: none !important; }`,\n `html { scrollbar-width: none !important; }`,\n );\n }\n if (opts.pauseAnimations) {\n parts.push(\n `*, *::before, *::after { animation-play-state: paused !important; transition: none !important; }`,\n );\n }\n if (opts.injectCss && opts.injectCss.trim()) {\n parts.push(opts.injectCss);\n }\n return parts.join(\"\\n\");\n}\n\n/**\n * Runs in the page via addInitScript (before any page script, on every navigation): pin the wall clock\n * and randomness so time-/random-driven content renders identically across frames and runs. Best-effort\n * by design — it overrides `Date.now` / `performance.now` / `Math.random` but not the `Date` constructor\n * or the rAF timestamp, so it won't break apps that read `new Date()`; pair with `pauseAnimations` (CSS)\n * for visual stillness. Must be self-contained (serialized into the page).\n */\nfunction freezeClockScript(): void {\n try {\n const FIXED = 1700000000000; // a fixed epoch (2023-11-14) so the page never sees time advance\n Date.now = () => FIXED;\n } catch {\n /* ignore */\n }\n try {\n const perf = (globalThis as { performance?: { now: () => number } }).performance;\n if (perf) perf.now = () => 0;\n } catch {\n /* ignore */\n }\n try {\n let seed = 1234567;\n Math.random = () => {\n seed = (seed * 1103515245 + 12345) & 0x7fffffff;\n return seed / 0x7fffffff;\n };\n } catch {\n /* ignore */\n }\n}\n\n/** Common analytics / ads / tag-manager / session-replay hosts, blocked by default for clean captures. */\nexport const DEFAULT_TRACKER_HOSTS = [\n \"google-analytics.com\",\n \"googletagmanager.com\",\n \"googlesyndication.com\",\n \"doubleclick.net\",\n \"adservice.google.com\",\n \"connect.facebook.net\",\n \"facebook.com/tr\",\n \"hotjar.com\",\n \"hotjar.io\",\n \"segment.com\",\n \"segment.io\",\n \"mixpanel.com\",\n \"amplitude.com\",\n \"fullstory.com\",\n \"clarity.ms\",\n \"sentry.io\",\n \"intercom.io\",\n \"intercomcdn.com\",\n \"analytics.tiktok.com\",\n \"snap.licdn.com\",\n \"bat.bing.com\",\n];\n\n/** Pure: should a request be aborted, given the host denylist and blocked resource types? */\nexport function shouldBlockRequest(\n url: string,\n opts: { hosts: string[]; resourceTypes: string[]; resourceType: string },\n): boolean {\n if (opts.resourceTypes.includes(opts.resourceType)) return true;\n return opts.hosts.some((h) => url.includes(h));\n}\n\n/** Abort tracker/denylisted/blocked-type requests during capture (must run BEFORE `page.goto`). */\nexport async function installNetworkHygiene(\n page: Page,\n opts: ResolvedScrollReelOptions,\n): Promise<void> {\n const hosts = [...(opts.blockTrackers ? DEFAULT_TRACKER_HOSTS : []), ...opts.blockHosts];\n const resourceTypes = opts.blockResourceTypes;\n if (hosts.length === 0 && resourceTypes.length === 0) return;\n await page.route(\"**/*\", (route) => {\n const req = route.request();\n if (shouldBlockRequest(req.url(), { hosts, resourceTypes, resourceType: req.resourceType() })) {\n return route.abort();\n }\n return route.continue();\n });\n}\n\n/**\n * Runs in the page: pause and rewind all media so autoplay video/audio can't make frames depend on\n * wall-clock time (frame-stepping screenshots a static page). Self-contained (serialized into the page).\n */\nfunction pauseAllMedia(): void {\n const doc = (globalThis as { document?: { querySelectorAll(s: string): ArrayLike<unknown> } }).document;\n if (!doc) return;\n try {\n const media = doc.querySelectorAll(\"video, audio\");\n for (let i = 0; i < media.length; i++) {\n const m = media[i] as { autoplay?: boolean; pause?: () => void; currentTime?: number };\n try {\n m.autoplay = false;\n m.pause?.();\n if (typeof m.currentTime === \"number\") m.currentTime = 0;\n } catch {\n /* ignore */\n }\n }\n } catch {\n /* ignore */\n }\n}\n\n/** Runs in the page: add a theme class to <html> (e.g. to force a dark variant). Self-contained. */\nfunction applyThemeClass(cls: string): void {\n try {\n (\n globalThis as { document?: { documentElement?: { classList?: { add(c: string): void } } } }\n ).document?.documentElement?.classList?.add(cls);\n } catch {\n /* ignore */\n }\n}\n\n/** Install pre-navigation hooks (must run BEFORE `page.goto`). */\nexport async function installPreNav(page: Page, opts: ResolvedScrollReelOptions): Promise<void> {\n if (opts.freezeClock) {\n await page.addInitScript(freezeClockScript);\n }\n if (opts.themeClass) {\n await page.addInitScript(applyThemeClass, opts.themeClass);\n }\n}\n\n/** Apply post-navigation cleanup: inject suppression CSS, then dismiss overlays (best-effort). */\nexport async function applyPostNav(\n page: Page,\n opts: ResolvedScrollReelOptions,\n logger: Logger,\n): Promise<void> {\n const css = buildCleanCss({\n hideSelectors: opts.hideSelectors,\n hideScrollbars: opts.hideScrollbars,\n pauseAnimations: opts.pauseAnimations,\n injectCss: opts.injectCss,\n });\n if (css) {\n await page.addStyleTag({ content: css });\n }\n // Re-apply the theme class after the (possibly client-rendered) app has mounted.\n if (opts.themeClass) {\n await page.evaluate(applyThemeClass, opts.themeClass);\n }\n for (const selector of opts.clickSelectors) {\n try {\n await page.click(selector, { timeout: 1000 });\n logger.debug(`dismissed overlay via ${selector}`);\n } catch {\n // Not present / not clickable — fine, these are best-effort consent dismissals.\n }\n }\n // Pause media so autoplay video can't make frames depend on wall-clock time.\n await page.evaluate(pauseAllMedia);\n}\n","export interface Annotation {\n /** Caption text shown while active. */\n text?: string;\n /** Selector to outline with a highlight ring while active. */\n ring?: string;\n /** Selector to spotlight (everything else dimmed) while active. */\n spotlight?: string;\n /** When the annotation appears (clip time, ms). Default 0. */\n atMs?: number;\n /** When it disappears (clip time, ms). Default = end of clip. */\n untilMs?: number;\n /** Caption placement. Default \"bottom\". */\n position?: \"top\" | \"bottom\" | \"center\";\n}\n\nexport interface AnnotationState {\n caption?: { text: string; position: \"top\" | \"bottom\" | \"center\" };\n ringSelector?: string;\n spotlightSelector?: string;\n}\n\n/**\n * Pure: resolve the overlay state at clip-time `tMs`. The first active annotation of each kind\n * (caption / ring / spotlight) wins, so independent annotations can overlap. Unit-tested.\n */\nexport function annotationStateAt(\n annotations: Annotation[],\n tMs: number,\n totalMs: number,\n): AnnotationState {\n const state: AnnotationState = {};\n for (const a of annotations) {\n const start = a.atMs ?? 0;\n const end = a.untilMs ?? totalMs;\n if (tMs < start || tMs >= end) continue;\n if (a.text && !state.caption) state.caption = { text: a.text, position: a.position ?? \"bottom\" };\n if (a.ring && !state.ringSelector) state.ringSelector = a.ring;\n if (a.spotlight && !state.spotlightSelector) state.spotlightSelector = a.spotlight;\n }\n return state;\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Runs INSIDE the page. Builds the annotation overlay (caption box, highlight ring, spotlight dim) and\n * exposes `window.__scAnno.update(state)` to reposition it per frame. The layer is attached to <html>\n * (not <body>) so a Ken Burns body transform doesn't move it; element boxes are read via\n * getBoundingClientRect, which already reflects any transform. Self-contained (serialized into the page).\n */\nexport function installAnnotationRuntime(opts: { color: string }): void {\n const g = globalThis as any;\n const doc = g.document;\n if (!doc || !doc.documentElement) return;\n\n const layer = doc.createElement(\"div\");\n layer.style.cssText = \"position:fixed;inset:0;margin:0;pointer-events:none;z-index:2147483646\";\n\n const spot = doc.createElement(\"div\");\n spot.style.cssText =\n \"position:fixed;display:none;border-radius:12px;box-shadow:0 0 0 9999px rgba(0,0,0,0.62)\";\n\n const ring = doc.createElement(\"div\");\n ring.style.cssText =\n \"position:fixed;display:none;border:3px solid \" +\n opts.color +\n \";border-radius:12px;box-shadow:0 0 0 3px rgba(255,255,255,0.45)\";\n\n const cap = doc.createElement(\"div\");\n cap.style.cssText =\n \"position:fixed;left:50%;display:none;max-width:80%;background:rgba(11,11,15,0.85);color:#fff;\" +\n \"font:600 28px -apple-system,Segoe UI,Roboto,sans-serif;padding:14px 22px;border-radius:12px;text-align:center\";\n\n layer.appendChild(spot);\n layer.appendChild(ring);\n layer.appendChild(cap);\n doc.documentElement.appendChild(layer);\n\n const boxOf = (sel: string): { x: number; y: number; w: number; h: number } | null => {\n try {\n const el = doc.querySelector(sel);\n if (!el) return null;\n const r = el.getBoundingClientRect();\n return { x: r.left, y: r.top, w: r.width, h: r.height };\n } catch {\n return null;\n }\n };\n const place = (el: any, b: { x: number; y: number; w: number; h: number }, pad: number): void => {\n el.style.display = \"block\";\n el.style.left = b.x - pad + \"px\";\n el.style.top = b.y - pad + \"px\";\n el.style.width = b.w + 2 * pad + \"px\";\n el.style.height = b.h + 2 * pad + \"px\";\n };\n\n g.__scAnno = {\n update: (state: any): Promise<void> =>\n new Promise((resolve) => {\n const sb = state.spotlightSelector ? boxOf(state.spotlightSelector) : null;\n if (sb) place(spot, sb, 8);\n else spot.style.display = \"none\";\n\n const rb = state.ringSelector ? boxOf(state.ringSelector) : null;\n if (rb) place(ring, rb, 6);\n else ring.style.display = \"none\";\n\n if (state.caption && state.caption.text) {\n cap.style.display = \"block\";\n cap.textContent = state.caption.text;\n const pos = state.caption.position;\n if (pos === \"top\") {\n cap.style.top = \"6%\";\n cap.style.bottom = \"auto\";\n cap.style.transform = \"translateX(-50%)\";\n } else if (pos === \"center\") {\n cap.style.top = \"50%\";\n cap.style.bottom = \"auto\";\n cap.style.transform = \"translate(-50%,-50%)\";\n } else {\n cap.style.bottom = \"6%\";\n cap.style.top = \"auto\";\n cap.style.transform = \"translateX(-50%)\";\n }\n } else {\n cap.style.display = \"none\";\n }\n g.requestAnimationFrame(() => resolve());\n }),\n };\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n","import { EASINGS, type EasingName } from \"@/generators/scroll-reel/scroll\";\n\n/**\n * A pure, frame-accurate model of how a recording's scroll position moves over time. This is the\n * extensibility seam for cinematic features: the default is still a single eased top→bottom sweep with\n * start/end dwell, but the same `ScrollSegment[]` shape can express pause-on-section choreography,\n * multiple eased segments, and speed ramps without another capture-side refactor.\n *\n * Positions are NORMALIZED (0 = top, 1 = bottom). The actual scrollable distance is only known\n * in-page after warm-up, so the capture layer multiplies `scrollAt(t)` by the measured distance. Keeping\n * the model normalized is what makes it a pure Node function (unit-testable like the easings) and lets\n * parallel workers agree: each re-measures the same distance after the same deterministic warm-up.\n */\nexport interface ScrollSegment {\n /** Normalized scroll position at the segment's start (0..1). */\n fromY: number;\n /** Normalized scroll position at the segment's end (0..1). A hold has `fromY === toY`. */\n toY: number;\n /** Fraction of the whole clip this segment occupies (0..1). All fractions sum to 1. */\n durationFraction: number;\n easing: EasingName;\n}\n\nexport interface TimelineSpec {\n segments: ScrollSegment[];\n}\n\nexport interface ResolvedTimeline {\n /** Normalized scroll position (0..1) at clip-time `tSeconds`. */\n scrollAt: (tSeconds: number) => number;\n totalSeconds: number;\n}\n\nexport interface DefaultTimelineArgs {\n startDelayMs: number;\n durationMs: number;\n endDwellMs: number;\n easing: EasingName;\n}\n\n/**\n * The current scroll-reel behavior expressed as a timeline: hold at top, eased scroll 0→1, hold at\n * bottom. The dwells become HELD FRAMES — the frame-stepped equivalent of the realtime path's\n * `sleep(startDelayMs)` / `sleep(endDwellMs)` — so the clip is exactly `startDelay + duration + endDwell`\n * long, matching the manifest `durationMs`.\n */\nexport function defaultTimelineSpec(a: DefaultTimelineArgs): TimelineSpec {\n const total = a.startDelayMs + a.durationMs + a.endDwellMs;\n if (total <= 0) {\n return { segments: [{ fromY: 0, toY: 0, durationFraction: 1, easing: \"linear\" }] };\n }\n const segments: ScrollSegment[] = [];\n if (a.startDelayMs > 0) {\n segments.push({ fromY: 0, toY: 0, durationFraction: a.startDelayMs / total, easing: \"linear\" });\n }\n if (a.durationMs > 0) {\n segments.push({ fromY: 0, toY: 1, durationFraction: a.durationMs / total, easing: a.easing });\n }\n if (a.endDwellMs > 0) {\n segments.push({ fromY: 1, toY: 1, durationFraction: a.endDwellMs / total, easing: \"linear\" });\n }\n // All-zero durations are guarded above, but keep a defined fallback for safety.\n if (segments.length === 0) {\n segments.push({ fromY: 0, toY: 0, durationFraction: 1, easing: \"linear\" });\n }\n return { segments };\n}\n\n/**\n * Pure: the normalized scroll position at progress `p` (0..1 over the whole clip). Walks the segments,\n * applies the segment's easing to its local progress, and lerps `fromY → toY`. The last segment owns\n * `p === 1` so floating-point fraction sums can't fall through.\n */\nexport function normalizedScrollAt(spec: TimelineSpec, p: number): number {\n const clamped = p < 0 ? 0 : p > 1 ? 1 : p;\n let acc = 0;\n for (let i = 0; i < spec.segments.length; i++) {\n const seg = spec.segments[i];\n if (!seg) continue;\n const segEnd = acc + seg.durationFraction;\n const isLast = i === spec.segments.length - 1;\n if (clamped <= segEnd || isLast) {\n const local = seg.durationFraction > 0 ? (clamped - acc) / seg.durationFraction : 1;\n const lc = local < 0 ? 0 : local > 1 ? 1 : local;\n const eased = EASINGS[seg.easing](lc);\n return seg.fromY + (seg.toY - seg.fromY) * eased;\n }\n acc = segEnd;\n }\n const last = spec.segments[spec.segments.length - 1];\n return last ? last.toY : 0;\n}\n\n/** Bind a spec to a wall-clock length, yielding `scrollAt(tSeconds)`. */\nexport function resolveTimeline(spec: TimelineSpec, totalSeconds: number): ResolvedTimeline {\n return {\n totalSeconds,\n scrollAt: (tSeconds: number) =>\n normalizedScrollAt(spec, totalSeconds > 0 ? tSeconds / totalSeconds : 1),\n };\n}\n\nexport function clamp01(n: number): number {\n return n < 0 ? 0 : n > 1 ? 1 : n;\n}\n\n// --- choreography (pause-on-section / keyframe scrolling) ---\n\n/** Default per-step travel time when a choreography step omits `durationMs`. */\nexport const DEFAULT_STEP_DURATION_MS = 1200;\n/** Default per-step hold time when a choreography step omits `holdMs`. */\nexport const DEFAULT_STEP_HOLD_MS = 800;\n\n/** An authored choreography step (raw, before targets are resolved to a position). */\nexport interface ChoreographyStep {\n /** Where to scroll to: a normalized 0..1 number, an \"NN%\" string, or a CSS selector to bring into view. */\n to: number | string;\n /** Time to travel to this target (ms). Defaults to {@link DEFAULT_STEP_DURATION_MS}. */\n durationMs?: number;\n /** Time to hold at this target after arriving (ms). Defaults to {@link DEFAULT_STEP_HOLD_MS}. */\n holdMs?: number;\n easing?: EasingName;\n}\n\n/** A choreography step whose target has been resolved to a normalized position. */\nexport interface ResolvedChoreographyStep {\n toY: number;\n durationMs: number;\n holdMs: number;\n easing: EasingName;\n}\n\nexport interface ChoreographyTimelineArgs {\n startDelayMs: number;\n endDwellMs: number;\n steps: ResolvedChoreographyStep[];\n}\n\n/**\n * Build a timeline from resolved choreography: an initial hold at the top, then for each step a travel\n * segment (eased) to its target followed by an optional hold, then a final dwell. A hold is a segment\n * with `fromY === toY`, so `normalizedScrollAt` keeps it pinned — exactly the \"pause on a section\"\n * behavior. The fractions are derived from each segment's ms over the total, so the clip length matches\n * {@link scrollTimelineTotalMs} computed from the same step timings.\n */\nexport function choreographyTimelineSpec(a: ChoreographyTimelineArgs): TimelineSpec {\n let total = a.startDelayMs + a.endDwellMs;\n for (const s of a.steps) total += s.durationMs + s.holdMs;\n if (total <= 0) {\n return { segments: [{ fromY: 0, toY: 0, durationFraction: 1, easing: \"linear\" }] };\n }\n const segments: ScrollSegment[] = [];\n let fromY = 0;\n if (a.startDelayMs > 0) {\n segments.push({ fromY: 0, toY: 0, durationFraction: a.startDelayMs / total, easing: \"linear\" });\n }\n for (const s of a.steps) {\n if (s.durationMs > 0) {\n segments.push({ fromY, toY: s.toY, durationFraction: s.durationMs / total, easing: s.easing });\n }\n if (s.holdMs > 0) {\n segments.push({ fromY: s.toY, toY: s.toY, durationFraction: s.holdMs / total, easing: \"linear\" });\n }\n fromY = s.toY;\n }\n if (a.endDwellMs > 0) {\n segments.push({ fromY, toY: fromY, durationFraction: a.endDwellMs / total, easing: \"linear\" });\n }\n if (segments.length === 0) {\n segments.push({ fromY: 0, toY: fromY, durationFraction: 1, easing: \"linear\" });\n }\n return { segments };\n}\n\n// --- auto-section detection (auto-generated choreography) ---\n\n/** Default total clip length (ms) for an auto-section reel. */\nexport const DEFAULT_AUTO_DURATION_MS = 12000;\n/** Default hold (ms) at each detected section. */\nexport const DEFAULT_AUTO_HOLD_MS = 700;\n/** Default minimum element height (as a fraction of the viewport) to count as a section. */\nexport const DEFAULT_AUTO_MIN_HEIGHT_FRACTION = 0.5;\n/** Default cap on the number of detected sections (keeps clips a reasonable length). */\nexport const DEFAULT_AUTO_MAX_SECTIONS = 8;\n\nexport interface AutoSectionsConfig {\n minHeightFraction?: number;\n selector?: string;\n holdMs?: number;\n /** Total clip length in ms (the budget split across detected sections). */\n durationMs?: number;\n maxSections?: number;\n constantVelocity?: boolean;\n}\n\n/** The total clip length (ms) for an auto-section config — a fixed, page-independent budget. */\nexport function autoSectionsBudgetMs(cfg: boolean | AutoSectionsConfig): number {\n const c = typeof cfg === \"object\" ? cfg : {};\n return c.durationMs ?? DEFAULT_AUTO_DURATION_MS;\n}\n\nexport interface AutoSectionStepsArgs {\n /** Detected section positions, normalized 0..1, sorted ascending. */\n offsets: number[];\n /** Total clip budget in ms (== autoSectionsBudgetMs). */\n budgetMs: number;\n startDelayMs: number;\n endDwellMs: number;\n holdMs: number;\n /** Distribute travel time by distance so scroll speed is uniform. */\n constantVelocity: boolean;\n easing: EasingName;\n}\n\n/**\n * Pure: turn detected section offsets into choreography steps that fit exactly into `budgetMs`. The\n * start delay and end dwell are reserved; the remainder is split into a hold at each section plus travel\n * between them. With `constantVelocity`, travel time is proportional to the distance covered (uniform\n * scroll speed); otherwise it's split evenly. Holds are capped so travel always has time. Returns []\n * when there are no offsets (the caller falls back to a default sweep).\n */\nexport function autoSectionSteps(a: AutoSectionStepsArgs): ResolvedChoreographyStep[] {\n const n = a.offsets.length;\n if (n === 0) return [];\n const remaining = Math.max(0, a.budgetMs - a.startDelayMs - a.endDwellMs);\n let holdEach = a.holdMs;\n if (holdEach * n > remaining * 0.7) holdEach = (remaining * 0.7) / n;\n const travelTotal = Math.max(0, remaining - holdEach * n);\n\n let prev = 0;\n const dists = a.offsets.map((o) => {\n const d = Math.max(0, o - prev);\n prev = o;\n return d;\n });\n const sumD = dists.reduce((s, d) => s + d, 0);\n const useVelocity = a.constantVelocity && sumD > 0;\n\n const steps: ResolvedChoreographyStep[] = [];\n let usedTravel = 0;\n for (let i = 0; i < n; i++) {\n const isLast = i === n - 1;\n const weight = useVelocity ? dists[i]! / sumD : 1 / n;\n // Last step absorbs any rounding so travel sums exactly to travelTotal.\n const travel = isLast ? travelTotal - usedTravel : travelTotal * weight;\n usedTravel += travel;\n steps.push({\n toY: clamp01(a.offsets[i]!),\n durationMs: Math.max(0, travel),\n holdMs: holdEach,\n easing: a.easing,\n });\n }\n return steps;\n}\n\nexport interface ScrollTimelineTotalArgs {\n startDelayMs: number;\n duration: number;\n endDwellMs: number;\n choreography?: Array<{ durationMs?: number; holdMs?: number }>;\n autoSections?: boolean | AutoSectionsConfig;\n}\n\n/**\n * Pure: total clip length in ms. Explicit choreography wins; then auto-sections use their fixed budget;\n * otherwise the classic startDelay + duration + endDwell. The capture layer and the manifest's\n * `durationMs` both use this so they always agree.\n */\nexport function scrollTimelineTotalMs(o: ScrollTimelineTotalArgs): number {\n if (o.choreography && o.choreography.length > 0) {\n let total = o.startDelayMs + o.endDwellMs;\n for (const s of o.choreography) {\n total += (s.durationMs ?? DEFAULT_STEP_DURATION_MS) + (s.holdMs ?? DEFAULT_STEP_HOLD_MS);\n }\n return total;\n }\n if (o.autoSections) {\n return autoSectionsBudgetMs(o.autoSections);\n }\n return o.startDelayMs + o.duration + o.endDwellMs;\n}\n\n// --- loop / boomerang ---\n\n/**\n * Pure: mirror a spec so it plays forward then backward within the same total length — a seamless\n * loop (last frame ≈ first). Each segment's fraction is halved; the second half is the segments in\n * reverse with swapped endpoints. Exactly time-symmetric for symmetric easings (linear/easeInOut*);\n * for asymmetric easings the down/up acceleration differs slightly but the loop is still seamless.\n */\nexport function boomerangSpec(spec: TimelineSpec): TimelineSpec {\n const forward = spec.segments.map((s) => ({ ...s, durationFraction: s.durationFraction / 2 }));\n const backward = spec.segments\n .slice()\n .reverse()\n .map((s) => ({\n fromY: s.toY,\n toY: s.fromY,\n durationFraction: s.durationFraction / 2,\n easing: s.easing,\n }));\n return { segments: [...forward, ...backward] };\n}\n\n// --- Ken Burns (slow zoom) ---\n\n/** Fold progress into a tent: 0→0, 0.5→1, 1→0. Keeps a zoom seamless under a boomerang loop. */\nexport function foldProgress(p: number): number {\n const c = clamp01(p);\n return c < 0.5 ? c * 2 : (1 - c) * 2;\n}\n\nexport interface KenBurnsResolved {\n scaleFrom: number;\n scaleTo: number;\n easing: EasingName;\n}\n\n/** Pure: the zoom scale at normalized progress (0..1), eased from scaleFrom to scaleTo. */\nexport function kenBurnsScaleAt(progress: number, cfg: KenBurnsResolved): number {\n const eased = EASINGS[cfg.easing](clamp01(progress));\n return cfg.scaleFrom + (cfg.scaleTo - cfg.scaleFrom) * eased;\n}\n","import type { Browser, Page } from \"playwright-core\";\nimport { captureFramedVideo } from \"@/media/frame-capture\";\nimport {\n detectSectionOffsets,\n measureNormalizedOffsets,\n measureTopInset,\n prepareScroll,\n seekScrollTo,\n settleInView,\n} from \"@/generators/scroll-reel/scroll\";\nimport {\n applyPostNav,\n installNetworkHygiene,\n installPreNav,\n} from \"@/generators/scroll-reel/clean-capture\";\nimport {\n annotationStateAt,\n installAnnotationRuntime,\n} from \"@/generators/scroll-reel/annotations\";\nimport {\n autoSectionSteps,\n autoSectionsBudgetMs,\n boomerangSpec,\n choreographyTimelineSpec,\n clamp01,\n defaultTimelineSpec,\n foldProgress,\n kenBurnsScaleAt,\n resolveTimeline,\n scrollTimelineTotalMs,\n DEFAULT_AUTO_HOLD_MS,\n DEFAULT_AUTO_MAX_SECTIONS,\n DEFAULT_AUTO_MIN_HEIGHT_FRACTION,\n DEFAULT_STEP_DURATION_MS,\n DEFAULT_STEP_HOLD_MS,\n type ResolvedChoreographyStep,\n type ResolvedTimeline,\n type TimelineSpec,\n} from \"@/generators/scroll-reel/timeline\";\nimport type { ResolvedScrollReelOptions } from \"@/generators/scroll-reel/options\";\nimport type { Logger } from \"@/utils/logger\";\n\n/** Time at the bottom for lazy/below-the-fold content to load during the (un-recorded) warm-up pass. */\nconst PREWARM_SETTLE_MS = 700;\n\nexport interface ScrollFramesArgs {\n browser: Browser;\n url: string;\n options: ResolvedScrollReelOptions;\n /** Absolute mp4 path to write. */\n outPath: string;\n preset?: string;\n workers: number;\n frameFormat: \"jpeg\" | \"png\";\n jpegQuality: number;\n /** Wait for fonts + in-view images before each frame (resolved: defaults on, off in draft). */\n settlePerFrame: boolean;\n /** Max ms to wait per frame for settling before screenshotting anyway. */\n settleMaxMs: number;\n /** Force a color scheme for this capture (emulated via prefers-color-scheme). */\n colorScheme?: \"light\" | \"dark\";\n tmpDir: string;\n logger: Logger;\n /** Fractional progress (0–1) as frames complete. */\n onProgress?: (fraction: number) => void;\n /** Cancels the capture mid-flight. */\n signal?: AbortSignal;\n}\n\n/**\n * Build the resolved scroll timeline for this capture. The default (no `choreography`) is a pure Node\n * computation; with choreography, selector targets are measured against the (warmed-up) page so the\n * positions are real and stable. Run once per worker inside `prepare`; since the measurements are\n * deterministic, every worker produces the same timeline.\n */\nasync function buildScrollTimeline(\n page: Page,\n options: ResolvedScrollReelOptions,\n totalSeconds: number,\n logger: Logger,\n): Promise<ResolvedTimeline> {\n const steps = options.choreography;\n // Apply the loop transform (boomerang mirrors the spec) before binding to wall-clock time.\n const finalize = (spec: TimelineSpec): ResolvedTimeline =>\n resolveTimeline(options.loop === \"boomerang\" ? boomerangSpec(spec) : spec, totalSeconds);\n\n // 1. Explicit choreography wins: resolve selector targets in one in-page pass (numbers/% in Node).\n if (steps && steps.length > 0) {\n const selectors = steps\n .map((s) => s.to)\n .filter((to): to is string => typeof to === \"string\" && !to.trim().endsWith(\"%\"));\n const headerInsetPx = selectors.length > 0 ? await page.evaluate(measureTopInset, {}) : 0;\n const measured =\n selectors.length > 0\n ? await page.evaluate(measureNormalizedOffsets, { selectors, headerInsetPx })\n : [];\n\n let mi = 0;\n let prevY = 0;\n const resolved: ResolvedChoreographyStep[] = steps.map((s) => {\n let toY: number;\n if (typeof s.to === \"number\") {\n toY = clamp01(s.to);\n } else if (s.to.trim().endsWith(\"%\")) {\n toY = clamp01(parseFloat(s.to) / 100);\n } else {\n const m = measured[mi++];\n if (m == null) {\n logger.warn(`choreography: selector \"${s.to}\" not found — holding position`);\n toY = prevY;\n } else {\n toY = clamp01(m);\n }\n }\n prevY = toY;\n return {\n toY,\n durationMs: s.durationMs ?? DEFAULT_STEP_DURATION_MS,\n holdMs: s.holdMs ?? DEFAULT_STEP_HOLD_MS,\n easing: s.easing ?? options.easing,\n };\n });\n\n return finalize(\n choreographyTimelineSpec({\n startDelayMs: options.startDelayMs,\n endDwellMs: options.endDwellMs,\n steps: resolved,\n }),\n );\n }\n\n // 2. Auto-sections: detect the page's sections and pan/hold through them within a fixed budget.\n if (options.autoSections) {\n const cfg = options.autoSections === true ? {} : options.autoSections;\n const headerInsetPx = await page.evaluate(measureTopInset, {});\n const offsets = await page.evaluate(detectSectionOffsets, {\n minHeightFraction: cfg.minHeightFraction ?? DEFAULT_AUTO_MIN_HEIGHT_FRACTION,\n selector: cfg.selector ?? null,\n maxSections: cfg.maxSections ?? DEFAULT_AUTO_MAX_SECTIONS,\n headerInsetPx,\n });\n const autoSteps = autoSectionSteps({\n offsets,\n budgetMs: autoSectionsBudgetMs(options.autoSections),\n startDelayMs: options.startDelayMs,\n endDwellMs: options.endDwellMs,\n holdMs: cfg.holdMs ?? DEFAULT_AUTO_HOLD_MS,\n constantVelocity: cfg.constantVelocity ?? true,\n easing: options.easing,\n });\n if (autoSteps.length > 0) {\n logger.debug(`autoSections: ${autoSteps.length} section(s) detected`);\n return finalize(\n choreographyTimelineSpec({\n startDelayMs: options.startDelayMs,\n endDwellMs: options.endDwellMs,\n steps: autoSteps,\n }),\n );\n }\n logger.warn(\"autoSections: no scrollable sections detected — using a default sweep\");\n }\n\n // 3. Default: a single eased top→bottom sweep.\n return finalize(\n defaultTimelineSpec({\n startDelayMs: options.startDelayMs,\n durationMs: options.duration,\n endDwellMs: options.endDwellMs,\n easing: options.easing,\n }),\n );\n}\n\n/**\n * Deterministic, frame-stepped recording of a real site. Each worker navigates + warms the page once\n * (reusing {@link prepareScroll} to trigger lazy content, fonts and image decode), applies clean-capture\n * suppression, builds the scroll timeline (resolving any choreography selectors against the page), then\n * for every frame sets the scroll position via {@link seekScrollTo}, optionally settles in-view content,\n * and screenshots — piped straight into ffmpeg by the shared {@link captureFramedVideo} harness. No\n * realtime recording, no dropped/jittered frames, no navigation lead to trim.\n */\nexport async function captureScrollFrames(a: ScrollFramesArgs): Promise<void> {\n const { options } = a;\n const totalSeconds = scrollTimelineTotalMs(options) / 1000;\n await captureFramedVideo<ResolvedTimeline>({\n browser: a.browser,\n width: options.width,\n height: options.height,\n deviceScaleFactor: options.deviceScaleFactor,\n fps: options.fps,\n durationSeconds: totalSeconds,\n crf: options.crf,\n outPath: a.outPath,\n preset: a.preset,\n frameFormat: a.frameFormat,\n jpegQuality: a.jpegQuality,\n workers: a.workers,\n tmpDir: a.tmpDir,\n logger: a.logger,\n onProgress: a.onProgress,\n signal: a.signal,\n prepare: async (page, { logger }) => {\n // Force the color scheme + block tracker requests before navigation so load-time media queries\n // and the network match the intended capture.\n if (a.colorScheme) await page.emulateMedia({ colorScheme: a.colorScheme });\n await installNetworkHygiene(page, options);\n // Pre-navigation hooks (e.g. freeze the clock, theme class) must be installed before page scripts.\n await installPreNav(page, options);\n logger.debug(`navigating to ${a.url} (waitUntil=${options.waitUntil})`);\n await page.goto(a.url, { waitUntil: options.waitUntil });\n if (options.waitForSelector) {\n logger.debug(`waiting for selector ${options.waitForSelector}`);\n await page.waitForSelector(options.waitForSelector, { state: \"visible\" });\n }\n // Suppress capture noise (hide banners/scrollbars, inject CSS, dismiss consent overlays).\n await applyPostNav(page, options, logger);\n // Warm-up (not recorded): load lazy content, fonts and images, then settle back at the top.\n await page.evaluate(prepareScroll, { settleMs: PREWARM_SETTLE_MS });\n if (options.annotations && options.annotations.length > 0) {\n await page.evaluate(installAnnotationRuntime, { color: \"#7c9cff\" });\n }\n // Resolve the timeline against the (now stable) page; deterministic across workers.\n return buildScrollTimeline(page, options, totalSeconds, logger);\n },\n seekToFrame: async (page, t, timeline) => {\n const kb = options.kenBurns;\n let scale: number | undefined;\n let originX: number | undefined;\n let originY: number | undefined;\n if (kb) {\n const p = totalSeconds > 0 ? t / totalSeconds : 1;\n // Fold the zoom progress under a boomerang loop so the zoom returns to its start (seamless).\n const zp = options.loop === \"boomerang\" ? foldProgress(p) : p;\n scale = kenBurnsScaleAt(zp, {\n scaleFrom: kb.scaleFrom ?? 1,\n scaleTo: kb.scaleTo ?? 1.08,\n easing: kb.easing ?? options.easing,\n });\n originX = kb.originX ?? 0.5;\n originY = kb.originY ?? 0.5;\n }\n await page.evaluate(seekScrollTo, {\n normalizedY: timeline.scrollAt(t),\n scale,\n originX,\n originY,\n });\n if (options.annotations && options.annotations.length > 0) {\n const state = annotationStateAt(options.annotations, t * 1000, totalSeconds * 1000);\n await page.evaluate(\n (s) =>\n (globalThis as { __scAnno?: { update(x: unknown): Promise<void> } }).__scAnno?.update(s),\n state,\n );\n }\n if (a.settlePerFrame) {\n // Bound the in-page settle Node-side so a stuck decode can't hang the frame.\n await Promise.race([\n page.evaluate(settleInView),\n new Promise<void>((resolve) => setTimeout(resolve, a.settleMaxMs)),\n ]);\n }\n },\n });\n}\n","/**\n * A capture \"variant\" is one cell of the viewport × color-scheme matrix. A scroll-reel asset with\n * `viewports` and/or `colorScheme: \"both\"` expands into several variants, each emitted as its own\n * asset (the `suffix` is appended to the base name). The common case — no viewports, no `\"both\"` —\n * yields a single variant with an empty suffix (the asset keeps its base name).\n */\nexport interface VariantViewport {\n name: string;\n width: number;\n height: number;\n deviceScaleFactor?: number;\n}\n\nexport interface CaptureVariant {\n width: number;\n height: number;\n deviceScaleFactor: number;\n colorScheme?: \"light\" | \"dark\";\n /** Appended to the base asset name / filename; \"\" for the single default variant. */\n suffix: string;\n}\n\nexport interface BuildVariantsArgs {\n width: number;\n height: number;\n deviceScaleFactor: number;\n viewports?: VariantViewport[];\n colorScheme?: \"light\" | \"dark\" | \"both\";\n}\n\n/** Pure: expand the viewport × color-scheme matrix into capture variants. Unit-tested. */\nexport function buildVariants(o: BuildVariantsArgs): CaptureVariant[] {\n const viewports: VariantViewport[] =\n o.viewports && o.viewports.length > 0\n ? o.viewports\n : [{ name: \"\", width: o.width, height: o.height, deviceScaleFactor: o.deviceScaleFactor }];\n\n const schemes: Array<\"light\" | \"dark\" | undefined> =\n o.colorScheme === \"both\"\n ? [\"light\", \"dark\"]\n : o.colorScheme === \"light\" || o.colorScheme === \"dark\"\n ? [o.colorScheme]\n : [undefined];\n const multiScheme = schemes.length > 1;\n\n const out: CaptureVariant[] = [];\n for (const vp of viewports) {\n for (const scheme of schemes) {\n const suffix = [vp.name, multiScheme ? (scheme ?? \"\") : \"\"].filter((p) => p).join(\"-\");\n out.push({\n width: vp.width,\n height: vp.height,\n deviceScaleFactor: vp.deviceScaleFactor ?? o.deviceScaleFactor,\n colorScheme: scheme,\n suffix,\n });\n }\n }\n return out;\n}\n","import path from \"node:path\";\nimport { copyFile, stat } from \"node:fs/promises\";\nimport {\n aspectTarget,\n buildAspectArgs,\n buildGifArgs,\n buildPosterArgs,\n buildStillSegmentArgs,\n buildWebpArgs,\n concatMp4,\n probeVideoDurationMs,\n runFfmpeg,\n} from \"@/media/ffmpeg\";\nimport {\n renderCard,\n DEFAULT_CARD_DURATION_MS,\n DEFAULT_CARD_FADE_MS,\n type CardSpec,\n} from \"@/generators/scroll-reel/cards\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { sha256File } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { ResolvedScrollReelOptions } from \"@/generators/scroll-reel/options\";\nimport type { PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport interface OutputJob {\n ctx: PipelineContext;\n generatorId: string;\n /** The captured mp4 (viewport-sized) in tmp. */\n sourceMp4: string;\n /** Filename base (no extension), including any variant suffix. */\n fileBase: string;\n /** Manifest asset id base, including any variant suffix. */\n assetId: string;\n sourceUrl: string;\n /** Capture dimensions (before any aspect reframe). */\n width: number;\n height: number;\n deviceScaleFactor: number;\n durationMs: number;\n options: ResolvedScrollReelOptions;\n preset: string;\n}\n\n// mp4 first so it stays the primary output (used when another asset references this one as an input).\nconst FORMAT_ORDER = [\"mp4\", \"gif\", \"webp\", \"poster\"] as const;\n\n/**\n * From a captured mp4, optionally reframe to a target aspect (shared by every output), then emit the\n * requested formats — mp4 / gif / webp / poster — each as its own manifest asset (id suffixed by format,\n * except mp4 which keeps the base id). The aspect reframe and alternate encodes are pure ffmpeg argv\n * builders in `@/media/ffmpeg`; this just orchestrates them and records the results.\n */\nexport async function produceOutputs(job: OutputJob): Promise<AssetRecord[]> {\n const { ctx, options } = job;\n const logger = ctx.logger;\n const signal = ctx.signal; // a cancel kills any in-progress reframe/encode here too\n\n // 1. Aspect reframe (once, shared by every output) — or use the capture as-is.\n let videoMp4 = job.sourceMp4;\n let outW = job.width;\n let outH = job.height;\n if (options.aspect) {\n const target = aspectTarget(options.aspect);\n outW = target.width;\n outH = target.height;\n const reframed = path.join(ctx.tmpDir, `${slugify(job.assetId)}-aspect.mp4`);\n await runFfmpeg(\n buildAspectArgs({\n inputPath: job.sourceMp4,\n outputPath: reframed,\n width: outW,\n height: outH,\n fit: options.fit,\n padColor: options.padColor,\n fps: options.fps,\n crf: options.crf,\n preset: job.preset,\n }),\n logger,\n signal,\n );\n videoMp4 = reframed;\n }\n\n // The poster always comes from the content clip (never an intro card's fade-from-black).\n const posterSource = videoMp4;\n\n // 2. Intro / outro cards: render at the output size and concatenate around the clip.\n let extraMs = 0;\n if (options.intro || options.outro) {\n const makeCard = async (spec: CardSpec, tag: string): Promise<string> => {\n const ms = spec.durationMs ?? DEFAULT_CARD_DURATION_MS;\n const fade = (spec.fadeMs ?? DEFAULT_CARD_FADE_MS) / 1000;\n const png = path.join(ctx.tmpDir, `${slugify(job.assetId)}-${tag}.png`);\n await renderCard(ctx.browser, {\n spec,\n width: outW,\n height: outH,\n scale: job.deviceScaleFactor,\n outPath: png,\n });\n const seg = path.join(ctx.tmpDir, `${slugify(job.assetId)}-${tag}.mp4`);\n await runFfmpeg(\n buildStillSegmentArgs({\n pngPath: png,\n outPath: seg,\n seconds: ms / 1000,\n fps: options.fps,\n width: outW,\n height: outH,\n fadeInSec: fade,\n fadeOutSec: fade,\n crf: options.crf,\n preset: job.preset,\n }),\n logger,\n signal,\n );\n extraMs += ms;\n return seg;\n };\n const segs: string[] = [];\n if (options.intro) segs.push(await makeCard(options.intro, \"intro\"));\n segs.push(videoMp4);\n if (options.outro) segs.push(await makeCard(options.outro, \"outro\"));\n if (segs.length > 1) {\n const stitched = path.join(ctx.tmpDir, `${slugify(job.assetId)}-carded.mp4`);\n await concatMp4(segs, stitched, logger, signal);\n videoMp4 = stitched;\n }\n }\n // Probe the real container duration (concat/mux can drift a little from the computed sum).\n const videoMs = (await probeVideoDurationMs(videoMp4)) ?? job.durationMs + extraMs;\n\n const gifFps = options.gifFps ?? Math.min(options.fps, 15);\n const want = new Set(options.outputs);\n const records: AssetRecord[] = [];\n\n for (const fmt of FORMAT_ORDER) {\n if (!want.has(fmt)) continue;\n const ext = fmt === \"poster\" ? \"png\" : fmt;\n const outPath = ctx.resolveOutPath(`${job.fileBase}.${ext}`);\n await ensureDir(path.dirname(outPath));\n\n if (fmt === \"mp4\") {\n await copyFile(videoMp4, outPath);\n } else if (fmt === \"gif\") {\n await runFfmpeg(buildGifArgs({ inputPath: videoMp4, outputPath: outPath, fps: gifFps }), logger, signal);\n } else if (fmt === \"webp\") {\n await runFfmpeg(\n buildWebpArgs({ inputPath: videoMp4, outputPath: outPath, fps: gifFps, quality: 75 }),\n logger,\n signal,\n );\n } else {\n await runFfmpeg(\n buildPosterArgs({ inputPath: posterSource, outputPath: outPath, atSeconds: 0 }),\n logger,\n signal,\n );\n }\n\n const [stats, contentHash] = await Promise.all([stat(outPath), sha256File(outPath)]);\n const id = fmt === \"mp4\" ? job.assetId : `${job.assetId}-${fmt}`;\n records.push({\n id,\n generator: job.generatorId,\n sourceUrl: job.sourceUrl,\n file: ctx.toManifestPath(outPath),\n format: ext,\n width: outW,\n height: outH,\n durationMs: fmt === \"poster\" ? undefined : videoMs,\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n });\n logger.success(`${id} → ${ctx.toManifestPath(outPath)}`);\n }\n\n return records;\n}\n","import type { Browser } from \"playwright-core\";\n\nexport interface CardSpec {\n title?: string;\n subtitle?: string;\n background?: string;\n color?: string;\n durationMs?: number;\n fadeMs?: number;\n}\n\n/** Default intro/outro card length (ms). */\nexport const DEFAULT_CARD_DURATION_MS = 1500;\n/** Default card fade in/out (ms). */\nexport const DEFAULT_CARD_FADE_MS = 400;\n\n/** Pure: escape text for safe interpolation into the card HTML. */\nexport function escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\n/** Pure: the HTML document for a card at a given size. */\nexport function cardHtml(spec: CardSpec, width: number, height: number): string {\n const bg = spec.background ?? \"#0b0b0f\";\n const color = spec.color ?? \"#ffffff\";\n const titleSize = Math.round(height * 0.08);\n const subSize = Math.round(height * 0.038);\n const gap = Math.round(height * 0.03);\n const title = spec.title\n ? `<div style=\"font-size:${titleSize}px;font-weight:700;line-height:1.1\">${escapeHtml(spec.title)}</div>`\n : \"\";\n const subtitle = spec.subtitle\n ? `<div style=\"font-size:${subSize}px;opacity:0.75;margin-top:${gap}px\">${escapeHtml(spec.subtitle)}</div>`\n : \"\";\n return (\n `<!doctype html><html><head><meta charset=\"utf-8\"></head>` +\n `<body style=\"margin:0;width:${width}px;height:${height}px;display:flex;flex-direction:column;` +\n `align-items:center;justify-content:center;background:${bg};color:${color};` +\n `font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;text-align:center;` +\n `padding:0 8%;box-sizing:border-box\">${title}${subtitle}</body></html>`\n );\n}\n\n/** Render a card to a PNG via Playwright (HTML → PNG), supersampled by `scale`. */\nexport async function renderCard(\n browser: Browser,\n args: { spec: CardSpec; width: number; height: number; scale: number; outPath: string },\n): Promise<void> {\n const context = await browser.newContext({\n viewport: { width: args.width, height: args.height },\n deviceScaleFactor: args.scale,\n });\n try {\n const page = await context.newPage();\n await page.setContent(cardHtml(args.spec, args.width, args.height), { waitUntil: \"load\" });\n try {\n await page.evaluate(\n () =>\n (globalThis as { document?: { fonts?: { ready?: Promise<unknown> } } }).document?.fonts?.ready,\n );\n } catch {\n /* no font set */\n }\n await page.screenshot({ path: args.outPath, type: \"png\" });\n } finally {\n await context.close();\n }\n}\n","import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\n\n/** sha256 hex digest of a file's contents. */\nexport async function sha256File(file: string): Promise<string> {\n const buf = await readFile(file);\n return createHash(\"sha256\").update(buf).digest(\"hex\");\n}\n\n/** sha256 hex digest of an in-memory buffer. */\nexport function sha256Buffer(buf: Uint8Array): string {\n return createHash(\"sha256\").update(buf).digest(\"hex\");\n}\n","import path from \"node:path\";\nimport { mkdtemp } from \"node:fs/promises\";\nimport type { Browser, Page } from \"playwright-core\";\nimport { ensureDir } from \"@/utils/fs\";\nimport {\n applyPostNav,\n installNetworkHygiene,\n installPreNav,\n} from \"@/generators/scroll-reel/clean-capture\";\nimport type { ResolvedScrollReelOptions } from \"@/generators/scroll-reel/options\";\nimport type { Logger } from \"@/utils/logger\";\n\n/** Default cursor travel / scroll-animation time for a step that omits `durationMs`. */\nexport const DEFAULT_ACTION_DURATION_MS = 700;\n/** Default pause after a step that omits `holdMs`. */\nexport const DEFAULT_ACTION_HOLD_MS = 600;\n/** Default dwell on the focused element. */\nconst DEFAULT_FOCUS_HOLD_MS = 2000;\n/** Default padding around a focused element when cropping. */\nconst DEFAULT_FOCUS_PADDING = 24;\n\n/**\n * Pure: turn an element box (CSS px) + padding into an even-dimensioned crop rectangle clamped to the\n * viewport (h264 needs even width/height). Unit-tested.\n */\nexport function clampCrop(\n box: { x: number; y: number; w: number; h: number },\n padding: number,\n viewportWidth: number,\n viewportHeight: number,\n): { x: number; y: number; width: number; height: number } {\n let x = Math.floor(box.x - padding);\n let y = Math.floor(box.y - padding);\n let w = Math.ceil(box.w + padding * 2);\n let h = Math.ceil(box.h + padding * 2);\n if (x < 0) {\n w += x;\n x = 0;\n }\n if (y < 0) {\n h += y;\n y = 0;\n }\n if (x + w > viewportWidth) w = viewportWidth - x;\n if (y + h > viewportHeight) h = viewportHeight - y;\n w = Math.max(2, w - (w % 2));\n h = Math.max(2, h - (h % 2));\n x = Math.max(0, Math.min(x, viewportWidth - w));\n y = Math.max(0, Math.min(y, viewportHeight - h));\n return { x, y, width: w, height: h };\n}\n\ntype InteractionAction = NonNullable<ResolvedScrollReelOptions[\"actions\"]>[number];\n\n/** Pure: total interaction clip length in ms (start delay + each step's travel + hold + end dwell). */\nexport function interactionTotalMs(\n actions: Array<{ durationMs?: number; holdMs?: number }>,\n startDelayMs: number,\n endDwellMs: number,\n): number {\n let total = startDelayMs + endDwellMs;\n for (const a of actions) {\n total += (a.durationMs ?? DEFAULT_ACTION_DURATION_MS) + (a.holdMs ?? DEFAULT_ACTION_HOLD_MS);\n }\n return total;\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Runs INSIDE the page. Installs a synthetic cursor and a `__sc` driver with eased moves, smooth\n * scrolling and a click pulse. Self-contained (serialized via page.evaluate); all globals are read off\n * `globalThis` so it carries no outer references.\n */\nfunction installCursorRuntime(opts: { show: boolean; size: number; color: string }): void {\n const g = globalThis as any;\n const doc = g.document;\n if (!doc || !doc.body) return;\n\n let cur: any = null;\n if (opts.show) {\n cur = doc.createElement(\"div\");\n cur.id = \"__sc_cursor\";\n cur.style.cssText = [\n \"position:fixed\",\n \"left:50%\",\n \"top:50%\",\n \"width:\" + opts.size + \"px\",\n \"height:\" + opts.size + \"px\",\n \"margin-left:\" + -opts.size / 2 + \"px\",\n \"margin-top:\" + -opts.size / 2 + \"px\",\n \"border-radius:50%\",\n \"background:\" + opts.color,\n \"opacity:0.85\",\n \"pointer-events:none\",\n \"z-index:2147483647\",\n \"box-shadow:0 0 0 2px rgba(255,255,255,0.85)\",\n \"transition:transform 0.12s ease\",\n ].join(\";\");\n doc.body.appendChild(cur);\n }\n\n let curX = (g.innerWidth || 0) / 2;\n let curY = (g.innerHeight || 0) / 2;\n const setPos = (x: number, y: number): void => {\n curX = x;\n curY = y;\n if (cur) {\n cur.style.left = x + \"px\";\n cur.style.top = y + \"px\";\n }\n };\n const ease = (t: number): number => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2);\n const tween = (x1: number, y1: number, ms: number): Promise<void> =>\n new Promise((resolve) => {\n const x0 = curX;\n const y0 = curY;\n if (ms <= 0) {\n setPos(x1, y1);\n resolve();\n return;\n }\n let start: number | null = null;\n const step = (now: number): void => {\n if (start === null) start = now;\n const t = Math.min(1, (now - start) / ms);\n const e = ease(t);\n setPos(x0 + (x1 - x0) * e, y0 + (y1 - y0) * e);\n if (t < 1) g.requestAnimationFrame(step);\n else resolve();\n };\n g.requestAnimationFrame(step);\n });\n\n g.__sc = {\n moveTo: (x: number, y: number, ms: number): Promise<void> => tween(x, y, ms),\n moveToFraction: (fx: number, fy: number, ms: number): Promise<void> =>\n tween((g.innerWidth || 0) * fx, (g.innerHeight || 0) * fy, ms),\n moveToSelector: async (sel: string, ms: number): Promise<void> => {\n const el = doc.querySelector(sel);\n if (!el) return;\n try {\n el.scrollIntoView({ behavior: \"instant\", block: \"center\" });\n } catch {\n try {\n el.scrollIntoView();\n } catch {\n /* ignore */\n }\n }\n await new Promise<void>((r) => g.requestAnimationFrame(() => g.requestAnimationFrame(() => r())));\n const rect = el.getBoundingClientRect();\n await tween(rect.left + rect.width / 2, rect.top + rect.height / 2, ms);\n },\n scrollTo: async (to: any, ms: number): Promise<void> => {\n const se = doc.scrollingElement || doc.documentElement;\n const max = Math.max(0, se.scrollHeight - se.clientHeight);\n let target = 0;\n if (typeof to === \"number\") target = to * max;\n else if (typeof to === \"string\" && to.trim().endsWith(\"%\")) target = (parseFloat(to) / 100) * max;\n else if (typeof to === \"string\") {\n const el = doc.querySelector(to);\n if (el) target = Math.max(0, Math.min(max, el.getBoundingClientRect().top + (g.pageYOffset || 0)));\n }\n const from = g.pageYOffset || se.scrollTop || 0;\n if (ms <= 0) {\n g.scrollTo(0, target);\n return;\n }\n await new Promise<void>((resolve) => {\n let start: number | null = null;\n const step = (now: number): void => {\n if (start === null) start = now;\n const t = Math.min(1, (now - start) / ms);\n g.scrollTo(0, from + (target - from) * ease(t));\n if (t < 1) g.requestAnimationFrame(step);\n else resolve();\n };\n g.requestAnimationFrame(step);\n });\n },\n pulse: (): void => {\n if (!cur) return;\n cur.style.transform = \"scale(0.6)\";\n g.setTimeout(() => {\n if (cur) cur.style.transform = \"scale(1)\";\n }, 150);\n },\n };\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n/** Drive one interaction step: animate the cursor, then perform the real Playwright action. */\nasync function runAction(page: Page, a: InteractionAction, durationMs: number): Promise<void> {\n switch (a.do) {\n case \"wait\":\n return; // the per-step hold provides the pause\n case \"scrollTo\":\n await page.evaluate(\n (p: { to: number | string; ms: number }) =>\n (globalThis as { __sc?: { scrollTo(to: number | string, ms: number): Promise<void> } }).__sc?.scrollTo(\n p.to,\n p.ms,\n ),\n { to: a.to ?? a.selector ?? 0, ms: durationMs },\n );\n return;\n case \"move\":\n if (a.selector) {\n await page.evaluate(\n (p: { sel: string; ms: number }) =>\n (globalThis as { __sc?: { moveToSelector(s: string, ms: number): Promise<void> } }).__sc?.moveToSelector(\n p.sel,\n p.ms,\n ),\n { sel: a.selector, ms: durationMs },\n );\n } else {\n await page.evaluate(\n (p: { x: number; y: number; ms: number }) =>\n (globalThis as { __sc?: { moveToFraction(x: number, y: number, ms: number): Promise<void> } }).__sc?.moveToFraction(\n p.x,\n p.y,\n p.ms,\n ),\n { x: a.x ?? 0.5, y: a.y ?? 0.5, ms: durationMs },\n );\n }\n return;\n case \"hover\":\n if (!a.selector) return;\n await moveCursorToSelector(page, a.selector, durationMs);\n await page.hover(a.selector);\n return;\n case \"click\":\n if (!a.selector) return;\n await moveCursorToSelector(page, a.selector, durationMs);\n await page.evaluate(() => (globalThis as { __sc?: { pulse(): void } }).__sc?.pulse());\n await page.click(a.selector);\n return;\n case \"type\":\n if (!a.selector) return;\n await moveCursorToSelector(page, a.selector, durationMs);\n await page.click(a.selector);\n if (a.text) await page.type(a.selector, a.text, { delay: 60 });\n return;\n }\n}\n\nfunction moveCursorToSelector(page: Page, selector: string, ms: number): Promise<unknown> {\n return page.evaluate(\n (p: { sel: string; ms: number }) =>\n (globalThis as { __sc?: { moveToSelector(s: string, ms: number): Promise<void> } }).__sc?.moveToSelector(\n p.sel,\n p.ms,\n ),\n { sel: selector, ms },\n );\n}\n\nexport interface InteractionArgs {\n browser: Browser;\n url: string;\n options: ResolvedScrollReelOptions;\n colorScheme?: \"light\" | \"dark\";\n tmpDir: string;\n logger: Logger;\n}\n\nexport interface InteractionResult {\n webmPath: string;\n /** Seconds of nav + warm-up before the first action — trim this off the head. */\n leadSeconds: number;\n /** Intended clip length (after the head trim). */\n durationSeconds: number;\n}\n\n/**\n * Record a scripted interaction in realtime: navigate, clean up, install the synthetic cursor, then run\n * the action sequence live while Playwright records. Returns the recording for downstream transcoding.\n */\nexport async function captureInteractionWebm(args: InteractionArgs): Promise<InteractionResult> {\n const { browser, url, options, tmpDir, logger } = args;\n await ensureDir(tmpDir);\n const recordDir = await mkdtemp(path.join(tmpDir, \"rec-\"));\n const context = await browser.newContext({\n viewport: { width: options.width, height: options.height },\n deviceScaleFactor: options.deviceScaleFactor,\n recordVideo: { dir: recordDir, size: { width: options.width, height: options.height } },\n });\n const page = await context.newPage();\n const video = page.video();\n const actions = options.actions ?? [];\n const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\n const recStart = Date.now();\n let leadSeconds = 0;\n try {\n if (args.colorScheme) await page.emulateMedia({ colorScheme: args.colorScheme });\n await installNetworkHygiene(page, options);\n await installPreNav(page, options);\n logger.debug(`navigating to ${url} (waitUntil=${options.waitUntil})`);\n await page.goto(url, { waitUntil: options.waitUntil });\n if (options.waitForSelector) {\n await page.waitForSelector(options.waitForSelector, { state: \"visible\" });\n }\n await applyPostNav(page, options, logger);\n try {\n await page.evaluate(\n () =>\n (globalThis as { document?: { fonts?: { ready?: Promise<unknown> } } }).document?.fonts?.ready,\n );\n } catch {\n /* no font set */\n }\n await page.evaluate(installCursorRuntime, {\n show: options.cursor?.show ?? true,\n size: options.cursor?.size ?? 22,\n color: options.cursor?.color ?? \"#0b0b0f\",\n });\n\n // Everything above is blank/churn in the recording; the scripted interaction starts now.\n leadSeconds = (Date.now() - recStart) / 1000;\n await sleep(options.startDelayMs);\n for (const a of actions) {\n const durationMs = a.durationMs ?? DEFAULT_ACTION_DURATION_MS;\n try {\n await runAction(page, a, durationMs);\n } catch (e) {\n logger.warn(`interaction step \"${a.do}\"${a.selector ? ` (${a.selector})` : \"\"} failed: ${(e as Error).message}`);\n }\n await sleep(a.holdMs ?? DEFAULT_ACTION_HOLD_MS);\n }\n await sleep(options.endDwellMs);\n } finally {\n await context.close();\n }\n\n if (!video) {\n throw new Error(\"Playwright did not record a video (recordVideo inactive).\");\n }\n return {\n webmPath: await video.path(),\n leadSeconds,\n durationSeconds: interactionTotalMs(actions, options.startDelayMs, options.endDwellMs) / 1000,\n };\n}\n\nexport interface FocusResult extends InteractionResult {\n /** The crop box (CSS px) to apply when transcoding. */\n cropBox: { x: number; y: number; width: number; height: number };\n}\n\n/**\n * Record an element-focused clip in realtime: scroll the component into view, optionally trigger it,\n * hold, and report the crop box (the element's final box + padding, clamped to the viewport). The caller\n * transcodes the recording cropped to that box.\n */\nexport async function captureFocusWebm(args: InteractionArgs): Promise<FocusResult> {\n const { browser, url, options, tmpDir, logger } = args;\n const focus = options.focus;\n if (!focus) throw new Error(\"captureFocusWebm called without options.focus\");\n await ensureDir(tmpDir);\n const recordDir = await mkdtemp(path.join(tmpDir, \"rec-\"));\n const context = await browser.newContext({\n viewport: { width: options.width, height: options.height },\n deviceScaleFactor: options.deviceScaleFactor,\n recordVideo: { dir: recordDir, size: { width: options.width, height: options.height } },\n });\n const page = await context.newPage();\n const video = page.video();\n const actions = focus.actions ?? [];\n const holdMs = focus.holdMs ?? DEFAULT_FOCUS_HOLD_MS;\n const padding = focus.padding ?? DEFAULT_FOCUS_PADDING;\n const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n const twoFrames = (): Promise<void> =>\n page.evaluate(\n () =>\n new Promise<void>((res) => {\n const g = globalThis as unknown as { requestAnimationFrame(cb: () => void): void };\n g.requestAnimationFrame(() => g.requestAnimationFrame(() => res()));\n }),\n );\n\n const recStart = Date.now();\n let leadSeconds = 0;\n let cropBox = { x: 0, y: 0, width: options.width, height: options.height };\n try {\n if (args.colorScheme) await page.emulateMedia({ colorScheme: args.colorScheme });\n await installNetworkHygiene(page, options);\n await installPreNav(page, options);\n await page.goto(url, { waitUntil: options.waitUntil });\n if (options.waitForSelector) {\n await page.waitForSelector(options.waitForSelector, { state: \"visible\" });\n }\n await applyPostNav(page, options, logger);\n await page.evaluate(installCursorRuntime, {\n show: options.cursor?.show ?? true,\n size: options.cursor?.size ?? 22,\n color: options.cursor?.color ?? \"#0b0b0f\",\n });\n // Bring the element to the center before the kept clip starts.\n await page.evaluate((sel: string) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const el = (globalThis as any).document?.querySelector(sel);\n if (el) {\n try {\n el.scrollIntoView({ behavior: \"instant\", block: \"center\" });\n } catch {\n /* ignore */\n }\n }\n }, focus.selector);\n await twoFrames();\n\n leadSeconds = (Date.now() - recStart) / 1000;\n await sleep(options.startDelayMs);\n for (const a of actions) {\n const durationMs = a.durationMs ?? DEFAULT_ACTION_DURATION_MS;\n try {\n await runAction(page, a, durationMs);\n } catch (e) {\n logger.warn(`focus step \"${a.do}\" failed: ${(e as Error).message}`);\n }\n await sleep(a.holdMs ?? DEFAULT_ACTION_HOLD_MS);\n }\n // Measure the element's final box (covers any expansion from the trigger) for the crop.\n const box = await page.evaluate((sel: string) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const el = (globalThis as any).document?.querySelector(sel);\n if (!el) return null;\n const r = el.getBoundingClientRect();\n return { x: r.left, y: r.top, w: r.width, h: r.height };\n }, focus.selector);\n if (box && box.w > 0 && box.h > 0) {\n cropBox = clampCrop(box, padding, options.width, options.height);\n } else {\n logger.warn(`focus: selector \"${focus.selector}\" not found — capturing the full viewport`);\n }\n await sleep(holdMs);\n await sleep(options.endDwellMs);\n } finally {\n await context.close();\n }\n\n if (!video) {\n throw new Error(\"Playwright did not record a video (recordVideo inactive).\");\n }\n const durationSeconds =\n (options.startDelayMs + interactionTotalMs(actions, 0, 0) + holdMs + options.endDwellMs) / 1000;\n return { webmPath: await video.path(), leadSeconds, durationSeconds, cropBox };\n}\n","import type { PipelineContext } from \"@/generators/types\";\n\n/** Narrow a url-based generator's target url, with a clear error when it's missing. */\nexport function requireUrl(ctx: PipelineContext): string {\n if (!ctx.target.url) {\n throw new Error(`Asset \"${ctx.target.name}\" requires a \"url\" in its config.`);\n }\n return ctx.target.url;\n}\n","import { writeFile } from \"node:fs/promises\";\nimport { imageSize } from \"image-size\";\nimport {\n screenshotsOptionsSchema,\n type ResolvedScreenshotsOptions,\n} from \"@/generators/screenshots/options\";\nimport { captureScreenshots } from \"@/generators/screenshots/capture\";\nimport { requireUrl } from \"@/generators/require-url\";\nimport { sha256Buffer } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const SCREENSHOTS_ID = \"screenshots\";\n\nfunction dimensions(buffer: Buffer): { width: number; height: number } {\n try {\n const size = imageSize(buffer);\n return { width: size.width ?? 0, height: size.height ?? 0 };\n } catch {\n return { width: 0, height: 0 };\n }\n}\n\nasync function run(\n ctx: PipelineContext,\n options: ResolvedScreenshotsOptions,\n): Promise<{ assets: AssetRecord[] }> {\n const ext = options.format === \"jpeg\" ? \"jpg\" : \"png\";\n const url = requireUrl(ctx);\n ctx.logger.info(`capturing ${url}`);\n\n const shots = await captureScreenshots({\n browser: ctx.browser,\n url,\n options,\n logger: ctx.logger,\n });\n\n const records: AssetRecord[] = [];\n for (const shot of shots) {\n const fileName = `${slugify(ctx.target.name)}-${slugify(shot.key)}.${ext}`;\n const outPath = ctx.resolveOutPath(fileName);\n await writeFile(outPath, shot.buffer);\n\n const { width, height } = dimensions(shot.buffer);\n if (width === 0 || height === 0) {\n ctx.logger.warn(`could not read dimensions for ${fileName} (recording 0×0)`);\n } else {\n ctx.logger.debug(`${fileName}: ${width}×${height}`);\n // Chromium caps screenshots near ~32767px; a very tall fullPage shot may be clipped/huge.\n if (width > 16000 || height > 16000) {\n ctx.logger.warn(\n `${fileName} is very large (${width}×${height}); a fullPage shot at deviceScaleFactor ${options.deviceScaleFactor} may be clipped or slow — consider a lower scale.`,\n );\n }\n }\n const record: AssetRecord = {\n id: `${ctx.target.name}-${shot.key}`,\n generator: SCREENSHOTS_ID,\n sourceUrl: url,\n file: ctx.toManifestPath(outPath),\n format: ext,\n width,\n height,\n bytes: shot.buffer.length,\n contentHash: sha256Buffer(shot.buffer),\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n records.push(record);\n ctx.logger.success(`${record.id} → ${record.file}`);\n }\n\n return { assets: records };\n}\n\nexport const screenshotsGenerator: Generator<ResolvedScreenshotsOptions> = {\n id: SCREENSHOTS_ID,\n requiresUrl: true,\n optionsSchema: screenshotsOptionsSchema,\n run,\n};\n","import { z } from \"zod\";\n\n/** A named viewport to capture at. */\nconst breakpointSchema = z\n .object({\n name: z\n .string()\n .min(1)\n .describe(\"Label for this viewport — used in the filename + manifest id (e.g. \\\"desktop\\\").\"),\n width: z.number().int().positive().describe(\"Viewport width in CSS px.\"),\n /** Viewport height. Note: ignored for `fullPage` shots (Playwright resizes to the page height);\n * only affects viewport/element captures. */\n height: z\n .number()\n .int()\n .positive()\n .default(900)\n .describe(\n \"Viewport height in CSS px. Default 900. Ignored for fullPage shots; only affects viewport/element captures.\",\n ),\n /** Override the generator-level deviceScaleFactor for this breakpoint. */\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .optional()\n .describe(\"Override the generator-level deviceScaleFactor for this breakpoint. Omit to inherit it.\"),\n })\n .strict();\nexport type Breakpoint = z.infer<typeof breakpointSchema>;\n\n/** A specific element to capture (in addition to the page) at each breakpoint. */\nconst elementShotSchema = z\n .object({\n selector: z.string().min(1).describe(\"CSS selector of the element to shoot.\"),\n /** Used in the filename + manifest id. */\n name: z.string().min(1).describe(\"Name used in the filename + manifest id for this element shot.\"),\n })\n .strict();\n\nexport const screenshotsOptionsSchema = z\n .object({\n breakpoints: z\n .array(breakpointSchema)\n .min(1)\n .default([\n { name: \"desktop\", width: 1440, height: 900 },\n { name: \"mobile\", width: 390, height: 844 },\n ])\n .describe(\n \"Viewports to capture at (at least one); each emits its own asset. Default: desktop 1440×900 + mobile 390×844.\",\n ),\n /** Capture the entire scrollable page (vs. just the viewport). */\n fullPage: z\n .boolean()\n .default(true)\n .describe(\"Capture the entire scrollable page (vs. just the viewport). Default true.\"),\n format: z.enum([\"png\", \"jpeg\"]).default(\"png\").describe(\"Image format. Default \\\"png\\\".\"),\n /** jpeg only, 1–100. */\n quality: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe(\"JPEG quality, 1–100 (jpeg only; rejected for png). Omit for the encoder default.\"),\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(2)\n .describe(\"Render scale (2 = retina-crisp). Default 2.\"),\n waitUntil: z\n .enum([\"load\", \"domcontentloaded\", \"networkidle\", \"commit\"])\n .default(\"networkidle\")\n .describe(\"Page-load milestone to wait for before capturing. Default \\\"networkidle\\\".\"),\n waitForSelector: z\n .string()\n .optional()\n .describe(\"Optional element to wait for before capturing (e.g. a hero image). Omit to skip.\"),\n /** Element captures taken at every breakpoint. */\n elements: z\n .array(elementShotSchema)\n .default([])\n .describe(\"Specific elements to crop (in addition to the page) at every breakpoint. Default none.\"),\n /** png only: capture with a transparent background. */\n omitBackground: z\n .boolean()\n .default(false)\n .describe(\"Capture with a transparent background (png only). Default false.\"),\n /** Extra settle time after load before capturing (ms). */\n settleMs: z\n .number()\n .int()\n .nonnegative()\n .default(0)\n .describe(\"Extra settle time after load before capturing (ms). Default 0.\"),\n })\n .strict()\n .refine((o) => !(o.format === \"png\" && o.quality != null), {\n message: \"`quality` only applies to jpeg; remove it or set format: \\\"jpeg\\\".\",\n path: [\"quality\"],\n });\n\n// ---------------------------------------------------------------------------\n// Author-facing input types (editor autocomplete + hover docs). JSDoc on the\n// zod schema above does NOT surface on hover through `z.input`, so the docs\n// live on these hand-written interfaces, kept in sync with the schema by the\n// Exact<> guard at the bottom — a drift is a compile error.\n// ---------------------------------------------------------------------------\n\n/** A named viewport to capture at; each breakpoint emits its own asset. */\nexport interface BreakpointInput {\n /** Label for this viewport — used in the filename + manifest id (e.g. \"desktop\"). */\n name: string;\n /** Viewport width in CSS px. */\n width: number;\n /**\n * Viewport height in CSS px. Default 900. Ignored for `fullPage` shots (Playwright resizes to the\n * full page height); only affects viewport/element captures.\n */\n height?: number;\n /** Override the generator-level `deviceScaleFactor` for this breakpoint. Omit to inherit it. */\n deviceScaleFactor?: number;\n}\n\n/** A specific element to capture (in addition to the page) at each breakpoint. */\nexport interface ElementShotInput {\n /** CSS selector of the element to shoot. */\n selector: string;\n /** Name used in the filename + manifest id for this element shot. */\n name: string;\n}\n\n/**\n * Author-facing options for the `screenshots` generator — responsive stills of a page (one per\n * breakpoint), plus optional per-element crops. Everything is optional; sensible defaults apply.\n */\nexport interface ScreenshotsOptionsInput {\n /**\n * Viewports to capture at (at least one); each emits its own asset. Default:\n * desktop 1440×900 + mobile 390×844.\n */\n breakpoints?: BreakpointInput[];\n /** Capture the entire scrollable page (vs. just the viewport). Default true. */\n fullPage?: boolean;\n /** Image format. Default \"png\". */\n format?: \"png\" | \"jpeg\";\n /** JPEG quality, 1–100 (jpeg only; rejected for png). Omit for the encoder default. */\n quality?: number;\n /** Render scale (2 = retina-crisp). Default 2. */\n deviceScaleFactor?: number;\n /** Page-load milestone to wait for before capturing. Default \"networkidle\". */\n waitUntil?: \"load\" | \"domcontentloaded\" | \"networkidle\" | \"commit\";\n /** Optional element to wait for before capturing (e.g. a hero image). Omit to skip. */\n waitForSelector?: string;\n /** Specific elements to crop (in addition to the page) at every breakpoint. Default none. */\n elements?: ElementShotInput[];\n /** Capture with a transparent background (png only). Default false. */\n omitBackground?: boolean;\n /** Extra settle time after load before capturing (ms). Default 0. */\n settleMs?: number;\n}\n\n/** Author-facing input (documented for editor hover; the schema validates it at run time). */\nexport type ScreenshotsOptions = ScreenshotsOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedScreenshotsOptions = z.infer<typeof screenshotsOptionsSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _screenshotsInputInSync: Exact<\n ScreenshotsOptionsInput,\n z.input<typeof screenshotsOptionsSchema>\n> = true;\nvoid _screenshotsInputInSync;\n","/**\n * Run `fn` over `items` with at most `limit` in flight, preserving input order in the\n * result. `fn` should not reject — callers handle per-item errors and return a value.\n */\nexport async function mapLimit<T, R>(\n items: T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let cursor = 0;\n const workerCount = Math.max(1, Math.min(limit, items.length));\n\n const worker = async (): Promise<void> => {\n while (true) {\n const index = cursor++;\n if (index >= items.length) return;\n results[index] = await fn(items[index] as T, index);\n }\n };\n\n await Promise.all(Array.from({ length: workerCount }, worker));\n return results;\n}\n","import type { Browser, Page } from \"playwright-core\";\nimport type { Logger } from \"@/utils/logger\";\nimport { mapLimit } from \"@/utils/concurrency\";\nimport { prepareScroll } from \"@/generators/scroll-reel/scroll\";\nimport type { ResolvedScreenshotsOptions } from \"@/generators/screenshots/options\";\n\n/** Minimum settle at the bottom for lazy/below-the-fold content (esp. for fullPage shots). */\nconst MIN_PREPARE_SETTLE_MS = 600;\n\nexport interface Shot {\n /** Suffix appended to the asset name for the filename + manifest id. */\n key: string;\n buffer: Buffer;\n}\n\nexport interface CaptureArgs {\n browser: Browser;\n url: string;\n options: ResolvedScreenshotsOptions;\n logger: Logger;\n}\n\ntype PageShotOptions = NonNullable<Parameters<Page[\"screenshot\"]>[0]>;\n\n/**\n * Capture page (and optional element) screenshots across every configured breakpoint.\n * Breakpoints render in parallel (each in its own isolated context, modest cap — full-page\n * buffers are memory-heavy); element shots stay sequential within a breakpoint. mapLimit\n * preserves input order, so filenames/manifest ids are stable.\n */\nexport async function captureScreenshots(args: CaptureArgs): Promise<Shot[]> {\n const { options } = args;\n const perBreakpoint = await mapLimit(\n options.breakpoints,\n Math.min(3, options.breakpoints.length),\n (bp) => captureBreakpoint(args, bp),\n );\n return perBreakpoint.flat();\n}\n\n/** One breakpoint: fresh context → navigate → warm the page → page shot + element shots. */\nasync function captureBreakpoint(\n args: CaptureArgs,\n bp: ResolvedScreenshotsOptions[\"breakpoints\"][number],\n): Promise<Shot[]> {\n const { browser, url, options, logger } = args;\n const shots: Shot[] = [];\n {\n const context = await browser.newContext({\n viewport: { width: bp.width, height: bp.height },\n deviceScaleFactor: bp.deviceScaleFactor ?? options.deviceScaleFactor,\n });\n const page = await context.newPage();\n try {\n logger.debug(`[${bp.name}] navigating to ${url}`);\n await page.goto(url, { waitUntil: options.waitUntil });\n if (options.waitForSelector) {\n await page.waitForSelector(options.waitForSelector, { state: \"visible\" });\n }\n // Drive the page so lazy-loaded / intersection-mounted content, web fonts, and image decode\n // are done before we shoot — otherwise fullPage shots capture blank/placeholder regions and\n // fallback fonts. Returns to the top when finished.\n await page.evaluate(prepareScroll, {\n settleMs: Math.max(options.settleMs, MIN_PREPARE_SETTLE_MS),\n });\n\n const pageShotOptions: PageShotOptions = {\n type: options.format,\n fullPage: options.fullPage,\n };\n if (options.format === \"png\" && options.omitBackground) {\n pageShotOptions.omitBackground = true;\n }\n if (options.format === \"jpeg\" && options.quality != null) {\n pageShotOptions.quality = options.quality;\n }\n shots.push({ key: bp.name, buffer: await page.screenshot(pageShotOptions) });\n\n for (const element of options.elements) {\n const locator = page.locator(element.selector).first();\n if ((await locator.count()) === 0) {\n logger.warn(`[${bp.name}] selector not found, skipping: ${element.selector}`);\n continue;\n }\n const elShotOptions: PageShotOptions = { type: options.format };\n if (options.format === \"png\" && options.omitBackground) {\n elShotOptions.omitBackground = true;\n }\n if (options.format === \"jpeg\" && options.quality != null) {\n elShotOptions.quality = options.quality;\n }\n // A present-but-hidden element (display:none until interaction) makes locator.screenshot\n // throw; warn + skip it instead of aborting the whole breakpoint loop.\n try {\n shots.push({\n key: `${bp.name}-${element.name}`,\n buffer: await locator.screenshot(elShotOptions),\n });\n } catch (err) {\n logger.warn(\n `[${bp.name}] could not capture \"${element.selector}\": ${(err as Error).message}`,\n );\n }\n }\n } finally {\n await context.close();\n }\n }\n\n return shots;\n}\n","import { z } from \"zod\";\nimport {\n wallColumnSchema,\n wallPanSchema,\n wallPulseSchema,\n fauxTileSchema,\n type WallPanInput,\n type WallColumnInput,\n type WallPulseInput,\n type FauxTileInput,\n} from \"@/generators/scene/scene-options\";\n\n/**\n * Author-facing options for the `wall` generator — a media wall whose `columns` are self-contained\n * units: each column owns its `tiles` (the assets stacked in it, by name) AND its own Y motion. The\n * dependency map is derived from those tile names, so there's no separate `inputs` list to maintain.\n *\n * Motion is built from one uniform \"pulse\" primitive, shared by columns, the wall-level default, and\n * the pan. A track's travel = `loops` continuous whole-clip periods + the sum of its `pulses` (each\n * an eased move of `distance` periods starting at `at`, lasting `duration` — both 0..1 fractions of\n * the clip). The total is rounded UP to a whole number of periods — the remainder folds into the\n * continuous scroll — so every track lands back on its start at the clip's end: the wall ALWAYS loops\n * seamlessly. (A pulse with `at + duration > 1` is shifted back to end at the loop point, so it can\n * never overrun the clip.)\n * `pan` — System 1: the whole wall pans on X.\n * `columns` — System 2: each column scrolls on Y (its own `direction` / `loops` / `pulses`).\n */\nexport const wallOptionsSchema = z\n .object({\n // --- output ---\n /** Output width (CSS px). */\n width: z.number().int().positive().default(1920).describe(\"Output width in CSS px. Default 1920.\"),\n /** Output height (CSS px). */\n height: z.number().int().positive().default(1080).describe(\"Output height in CSS px. Default 1080.\"),\n /** Render scale (2 = retina-crisp, downscaled into the video). */\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(2)\n .describe(\"Render scale (2 = retina-crisp, downscaled into the video). Default 2.\"),\n /** Output frames per second. */\n fps: z.number().int().positive().max(120).default(30).describe(\"Output frames per second. Default 30.\"),\n /** Clip length (seconds) — the whole loop. Tile videos should loop within a length dividing this. */\n durationSeconds: z\n .number()\n .positive()\n .default(16)\n .describe(\"Clip length in seconds — the whole loop. Default 16.\"),\n /** x264 quality, 0–51 (lower = better/larger). */\n crf: z\n .number()\n .int()\n .min(0)\n .max(51)\n .default(18)\n .describe(\"x264 quality, 0–51 (lower = better quality / larger file). Default 18.\"),\n /** Capture strategy. \"frames\" (default) is deterministic + parallelizable. */\n capture: z\n .enum([\"frames\", \"realtime\"])\n .default(\"frames\")\n .describe('Capture strategy. \"frames\" (default) is deterministic + parallelizable; \"realtime\" records the live session.'),\n /** Parallel frame-render workers. Video-heavy walls can cold-start black under many workers —\n * set 1 (or omit) for those. */\n workers: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Parallel frame-render workers. Video-heavy walls can cold-start to black under many workers — set 1 (or omit). Auto-picks from cores.\"),\n /** Intermediate frame format (frames capture only). \"jpeg\" (default) is fast; \"png\" is lossless. */\n frameFormat: z\n .enum([\"jpeg\", \"png\"])\n .default(\"jpeg\")\n .describe('Intermediate frame format (frames capture). \"jpeg\" (default) is fast; \"png\" is lossless.'),\n /** Backdrop shown in the gutters between tiles. */\n background: z\n .string()\n .default(\"#0b0b0f\")\n .describe('Backdrop shown in the gutters between tiles. Default \"#0b0b0f\".'),\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName: z.string().optional().describe('Output filename; defaults to \"<slug(asset name)>.mp4\".'),\n\n // --- columns (System 2 + layout): each column = its tiles + its own motion ---\n /** The columns (≥3) — each its own tiles + motion. Count = array length (fewer = larger tiles). */\n columns: z\n .array(wallColumnSchema)\n .min(3)\n .describe(\"The columns (≥3) — each lists its stacked `tiles` (by name) and may carry its own motion. Count = columns.length.\"),\n /** Gap between columns and between tiles (px). */\n gap: z.number().nonnegative().default(8).describe(\"Gap between columns and between tiles (px). Default 8.\"),\n /** Default/fallback tile aspect (width / height). Tiles fit the column width and take their OWN\n * height from their media's aspect (16:9 → short, 9:16 → tall); this is only used for faux\n * (`test`) tiles that don't set their own `aspect`. 0.75 = 3:4 portrait. */\n tileAspect: z\n .number()\n .positive()\n .default(0.75)\n .describe(\"Default/fallback tile aspect (w/h) — only for faux (test) tiles without their own `aspect`. 0.75 = 3:4 portrait. Default 0.75.\"),\n /** Tile corner radius (px). */\n cornerRadius: z.number().nonnegative().default(6).describe(\"Tile corner radius (px). Default 6.\"),\n\n // --- motion (uniform pulse model) ---\n /** System 1 — the whole-wall X pan. */\n pan: wallPanSchema\n .default({})\n .describe(\"System 1 — the whole wall's horizontal pan (`direction` / `loops` / `pulses`). Default: no pan.\"),\n /** Default continuous whole-clip loops for columns that omit their own `loops` (0 = static unless\n * a pulse moves it; one pulse then rounds the total up to a single loop). */\n loops: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Default continuous whole-clip loops for columns that omit their own `loops`. Default 0 (static unless a pulse moves it).\"),\n /** Default pulses for columns that omit their own `pulses` (the uniform wall-level motion). */\n pulses: z\n .array(wallPulseSchema)\n .default([])\n .describe(\"Default pulses for columns that omit their own `pulses` (the uniform wall-level motion). Default none.\"),\n\n // --- test / preview ---\n /** Preview mode: render every tile as a flat labeled color box (see `testTiles`) instead of the\n * real assets. No producer assets run, so the wall renders in seconds — use it to dial in\n * layout + motion, then turn it off for the real render. */\n test: z\n .boolean()\n .default(false)\n .describe(\"Preview mode: render every tile as a flat labeled color box instead of real assets, so the wall renders in seconds. Default false.\"),\n /** Per-tile faux appearance for `test` mode, keyed by tile name. Tiles not listed get an\n * auto-derived color and their name as the label. */\n testTiles: z\n .record(z.string(), fauxTileSchema)\n .default({})\n .describe(\"Per-tile faux appearance for `test` mode, keyed by tile name (color + caption). Default {} (auto colors + names).\"),\n })\n .strict();\n\n// ---------------------------------------------------------------------------\n// Author-facing input type (editor autocomplete + hover docs). JSDoc on the\n// zod schema above does NOT surface on hover through `z.input`, so the docs\n// live on this hand-written interface, kept in sync with the schema by the\n// Exact<> guard at the bottom — a drift is a compile error.\n// ---------------------------------------------------------------------------\n\n/** Re-exported so configs can type a single column / pulse / pan / faux tile. */\nexport type { WallColumnInput, WallPanInput, WallPulseInput, FauxTileInput };\n\n/**\n * Author-facing options for the `wall` generator. Each entry in `columns` is a self-contained unit\n * (its `tiles` + its own optional motion); everything else is optional, with defaults noted below.\n * Motion is the uniform pulse model: `loops` (continuous base) + `pulses` (eased moves), summed and\n * rounded up to a whole number of periods so the wall always loops seamlessly.\n */\nexport interface WallOptionsInput {\n // --- output ---\n /** Output width in CSS px. Default 1920. */\n width?: number;\n /** Output height in CSS px. Default 1080. */\n height?: number;\n /** Render scale (2 = retina-crisp, downscaled into the video). Default 2. */\n deviceScaleFactor?: number;\n /** Output frames per second. Default 30. */\n fps?: number;\n /** Clip length in seconds — the whole loop. Default 16. */\n durationSeconds?: number;\n /** x264 quality, 0–51 (lower = better quality / larger file). Default 18. */\n crf?: number;\n /**\n * Capture strategy. \"frames\" (default) is deterministic + parallelizable; \"realtime\" records the\n * live session. Default \"frames\".\n */\n capture?: \"frames\" | \"realtime\";\n /**\n * Parallel frame-render workers. Video-heavy walls can cold-start to black tiles under many\n * workers — set 1 (or omit) for those. Omit to auto-pick from cores.\n */\n workers?: number;\n /** Intermediate frame format (frames capture). \"jpeg\" (default) is fast; \"png\" is lossless. */\n frameFormat?: \"jpeg\" | \"png\";\n /** Backdrop shown in the gutters between tiles. Default \"#0b0b0f\". */\n background?: string;\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName?: string;\n\n // --- columns: each its own tiles + motion ---\n /**\n * The columns (≥3). Each column lists the assets stacked in it (`tiles`, by name — cycled to fill\n * the height) and may carry its own motion (`direction` / `loops` / `pulses`); omitted `loops` /\n * `pulses` inherit the wall-level defaults below. Column count = `columns.length`.\n */\n columns: WallColumnInput[];\n /** Gap between columns and between tiles (px). Default 8. */\n gap?: number;\n /** Default/fallback tile aspect (width / height). Tiles fit the column width and take their OWN\n * height from their media's aspect (16:9 → short, 9:16 → tall); this is only used for faux\n * (`test`) tiles that don't set their own `aspect`. 0.75 = 3:4 portrait. Default 0.75. */\n tileAspect?: number;\n /** Tile corner radius (px). Default 6. */\n cornerRadius?: number;\n\n // --- motion (uniform pulse model) ---\n /** The whole wall's horizontal pan (`direction` / `loops` / `pulses`). Default: no pan. */\n pan?: WallPanInput;\n /** Default continuous whole-clip loops for columns that omit their own `loops`. Default 0 (static\n * unless a pulse moves it — a single pulse then rounds up to one loop). */\n loops?: number;\n /** Default pulses for columns that omit their own `pulses` (the uniform wall-level motion). Default none. */\n pulses?: WallPulseInput[];\n\n // --- test / preview ---\n /**\n * Preview mode: render every tile as a flat labeled color box instead of the real assets. No\n * producer assets run, so the wall renders in seconds — flip it on to dial in layout + motion,\n * then off for the real render. Default false.\n */\n test?: boolean;\n /** Per-tile faux appearance for `test` mode, keyed by tile name (color + caption). Default {} (auto colors + names). */\n testTiles?: Record<string, FauxTileInput>;\n}\n\n/** Author-facing input (documented for editor hover; the schema validates it at run time). */\nexport type WallOptions = WallOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedWallOptions = z.infer<typeof wallOptionsSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _wallInputInSync: Exact<WallOptionsInput, z.input<typeof wallOptionsSchema>> = true;\nvoid _wallInputInSync;\n","import { z } from \"zod\";\nimport { pulseSchema } from \"@/generators/specimen/options\";\n\n/**\n * Per-scene `sceneOptions` schemas — the knobs each built-in scene understands. `renderScene`\n * validates against the selected scene's schema before serializing props, so a typo'd key or an\n * unknown scene id fails fast with a named error instead of silently rendering defaults. Every\n * default equals the value the scene previously hardcoded, so existing configs render identically.\n *\n * The author-facing *Input interfaces below mirror each schema's input shape (compile-time-guarded);\n * the friendly `wall` / `specimen` / `palette-reel` generators own the config authoring surface.\n */\n\n/**\n * The specimen scene's wire format — produced by the specimen *generator* (its friendly options\n * are the real authoring surface), validated here so generator and scene can't drift apart.\n */\nconst specimenSceneOptionsSchema = z\n .object({\n label: z.string().default(\"\"),\n demo: z.boolean().default(false),\n weight: z.number().min(1).max(1000).default(820),\n lines: z.number().int().min(1).max(40).default(3),\n blacklist: z.string().default(\"\"),\n colors: z\n .object({\n background: z.string(),\n foreground: z.string(),\n muted: z.string(),\n accent: z.string().optional(),\n label: z.string().optional(),\n })\n .partial()\n .default({}),\n colorWeights: z\n .object({\n foreground: z.number().nonnegative(),\n muted: z.number().nonnegative(),\n accent: z.number().nonnegative(),\n })\n .partial()\n .default({}),\n pulses: z.array(pulseSchema).default([]),\n mirror: z.boolean().default(true),\n characterIntensity: z.number().nonnegative().default(1),\n colorIntensity: z.number().nonnegative().default(1),\n /** Max fraction a line's width may drift as glyphs change (right-edge stability). */\n maxLineDrift: z.number().positive().max(0.5).default(0.05),\n /** Schedule seed — same seed ⇒ identical animation (parallel workers must agree). */\n seed: z.number().int().default(1),\n /** Line-height of the glyph block. */\n leading: z.number().positive().default(0.78),\n /** Glyph pool override (≥2 distinct characters after the blacklist). */\n characterPool: z.string().min(2).optional(),\n })\n .strict();\n\n/** The wall's easing curves (kebab-cased, matching the rest of the tool). */\nconst wallEasingEnum = z.enum([\n \"linear\",\n \"ease-in\",\n \"ease-out\",\n \"ease-in-out\",\n \"ease-in-out-strong\",\n]);\n\n/**\n * One \"pulse\" — the uniform motion primitive shared by columns, the wall-level default, and the pan.\n * It adds `distance` periods of eased travel, starting at `at` and lasting `duration` — both 0..1\n * fractions of the clip. If `at + duration > 1`, the start shifts back so the move ends exactly at the\n * loop point (a 0.2 pulse at 0.9 starts at 0.8), so a pulse can never overrun the clip.\n */\nexport const wallPulseSchema = z\n .object({\n /** When the pulse starts, as a fraction of the clip (0..1). */\n at: z.number().min(0).max(1).describe(\"When the pulse starts, as a fraction of the clip (0..1).\"),\n /** How long the move takes, as a fraction of the clip (0..1). */\n duration: z\n .number()\n .positive()\n .max(1)\n .describe(\"How long the move takes, as a fraction of the clip (0..1).\"),\n /** How far it travels, in periods (1 = one full tile-set / one wrap). Usually 0..1. */\n distance: z\n .number()\n .nonnegative()\n .describe(\"How far it travels, in periods (1 = one full tile-set / wrap). Usually 0..1.\"),\n /** Easing of the move's ramp. */\n easing: wallEasingEnum.default(\"ease-in-out\").describe(\"Easing of the move's ramp. Default 'ease-in-out'.\"),\n })\n .strict();\n\n/** System 1 — the whole-wall X pan: a continuous base scroll (`loops`) + optional `pulses`. */\nexport const wallPanSchema = z\n .object({\n /** Pan direction. */\n direction: z.enum([\"left\", \"right\"]).default(\"left\").describe(\"Pan direction. Default 'left'.\"),\n /** Continuous whole-clip horizontal loops (0 = no pan unless `pulses` move it). */\n loops: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Continuous whole-clip horizontal loops (0 = no pan unless pulses move it). Default 0.\"),\n /** Pulses added on top of the base loops. */\n pulses: z.array(wallPulseSchema).default([]).describe(\"Pulses added on top of the base loops.\"),\n })\n .strict();\n\n/**\n * One column of the wall: its tiles (the assets stacked in it, by name — cycled to fill the column\n * height) plus its own optional Y motion. Omitted `loops`/`pulses` inherit the wall-level defaults;\n * an omitted `direction` defaults to \"down\".\n */\nexport const wallColumnSchema = z\n .object({\n /** Assets stacked in this column, by name (cycled to fill the height). At least one. */\n tiles: z\n .array(z.string().min(1))\n .min(1)\n .describe(\"Assets stacked in this column, by name (cycled to fill the height). At least one.\"),\n /** Constant start-position shift, 0..1 of a tile-set — de-aligns columns with similar content. */\n stagger: z\n .number()\n .min(0)\n .max(1)\n .default(0)\n .describe(\"Start-position shift (0..1 of a tile-set) that de-aligns columns with similar content. Default 0.\"),\n /** Scroll direction. Defaults to \"down\". */\n direction: z.enum([\"up\", \"down\"]).optional().describe(\"Scroll direction. Default 'down'.\"),\n /** Continuous whole-clip loops for this column. Omit to inherit the wall-level `loops`. */\n loops: z\n .number()\n .nonnegative()\n .optional()\n .describe(\"Continuous whole-clip loops for this column. Omit to inherit the wall-level loops.\"),\n /** This column's pulses. Omit to inherit the wall-level `pulses`. */\n pulses: z\n .array(wallPulseSchema)\n .optional()\n .describe(\"This column's pulses. Omit to inherit the wall-level pulses.\"),\n })\n .strict();\n\n/**\n * A faux tile for the wall's `test` preview mode — a flat colored box labeled with its name, rendered\n * instead of a real asset so layout + motion can be dialed in instantly (no producers run).\n */\nexport const fauxTileSchema = z\n .object({\n /** Box fill (any CSS color). Omit to auto-derive a distinct color from the tile name. */\n color: z\n .string()\n .optional()\n .describe(\"Box fill (any CSS color). Omit to auto-derive a distinct color from the tile name.\"),\n /** Optional caption shown under the name (e.g. \"16:9\") — purely cosmetic. */\n size: z.string().optional().describe(\"Optional caption shown under the name (e.g. 16:9) — purely cosmetic.\"),\n /** This faux tile's aspect ratio (width / height): 1.78 = 16:9 (short), 0.56 = 9:16 (tall), 1 =\n * square. Omit to use the wall's `tileAspect` default. Real tiles use their media's own aspect. */\n aspect: z\n .number()\n .positive()\n .optional()\n .describe(\"Faux tile aspect (w/h): 1.78 = 16:9, 0.56 = 9:16, 1 = square. Omit to use the wall's tileAspect.\"),\n })\n .strict();\n\n/**\n * The \"wall\" scene: a marquee of media tiles, each column a self-contained unit (its `tiles` + its\n * own motion). Two systems built from the SAME pulse primitive: `pan` (System 1, whole-wall X) and\n * the per-column Y motion (System 2). A track's travel = `loops` continuous periods + the sum of its\n * `pulses`; the total is rounded UP to a whole number of periods (the remainder folds into the\n * continuous scroll), so every track lands back on its start at the clip end → a seamless loop.\n */\nexport const wallSceneOptionsSchema = z\n .object({\n /** The columns (≥3) — each its own tiles + motion. Count = array length (fewer = bigger tiles). */\n columns: z.array(wallColumnSchema).min(3),\n /** Gap between columns and between their tile contents (px). */\n gap: z.number().nonnegative().default(16),\n /** Default/fallback tile aspect (width / height). Tiles fit the column width and take their OWN\n * height from their media's aspect (16:9 → short, 9:16 → tall); this is only used for faux\n * (`test`) tiles that don't set their own `aspect`. 1.6 = 16:10 landscape, <1 = portrait. */\n tileAspect: z.number().positive().default(1.6),\n /** Tile corner radius (px). */\n cornerRadius: z.number().nonnegative().default(12),\n /** Backdrop shown in the gap gutters and behind tiles. Defaults to the scene's `background`. */\n background: z.string().optional(),\n /** System 1 — the X pan. */\n pan: wallPanSchema.default({}),\n /** Default continuous whole-clip loops for columns that omit their own `loops` (0 = static unless\n * a pulse moves it; one pulse then rounds the total up to a single loop). */\n loops: z.number().nonnegative().default(0),\n /** Default pulses for columns that omit their own `pulses` (the uniform wall-level motion). */\n pulses: z.array(wallPulseSchema).default([]),\n /** Preview mode: render every tile as a flat labeled color box (see `testTiles`) instead of the\n * real assets, so you can dial in layout + motion instantly — no producers run. */\n test: z.boolean().default(false),\n /** Per-tile faux appearance for `test` mode, keyed by tile name. Tiles not listed get an\n * auto-derived color and their name as the label. */\n testTiles: z.record(z.string(), fauxTileSchema).default({}),\n })\n .strict();\n\n/**\n * The \"palette-reel\" scene's wire format — produced by the `palette-reel` *generator* (its friendly\n * options are the real authoring surface; it precomputes the per-color display strings since the\n * scene can't import the color math), validated here so generator and scene can't drift apart. The\n * colors start as name-only slivers; one at a time a band expands to reveal its `details`, holds, and\n * collapses, looping seamlessly. Defaults mirror the friendly schema so a direct `scene:\n * \"palette-reel\"` config still renders.\n */\nconst reelItemSchema = z\n .object({\n /** Color display name (already cased per `uppercase`). */\n name: z.string(),\n /** Swatch background hex (`#RRGGBB`). */\n hex: z.string(),\n /** Contrast-picked text color for this band. */\n textColor: z.string(),\n /** Preformatted detail lines revealed on expand (hex / oklch / rgb …). */\n details: z.array(z.string()),\n })\n .strict();\n\nconst paletteReelSceneOptionsSchema = z\n .object({\n items: z.array(reelItemSchema).min(1),\n orientation: z.enum([\"rows\", \"columns\"]).default(\"rows\"),\n holdSeconds: z.number().positive().default(2),\n transitionSeconds: z.number().positive().default(0.7),\n bounce: z.boolean().default(true),\n easing: z.enum([\"linear\", \"ease-in\", \"ease-out\", \"ease-in-out\"]).default(\"ease-in-out\"),\n grownFlex: z.number().min(1).default(12),\n minCrossPx: z.number().nonnegative().default(0),\n nameAlwaysVisible: z.boolean().default(true),\n fontWeight: z.number().int().min(1).max(1000).default(700),\n fontSize: z.number().positive().optional(),\n detailFontScale: z.number().positive().default(0.62),\n gap: z.number().nonnegative().default(0),\n cornerRadius: z.number().nonnegative().default(0),\n })\n .strict();\n\n/** Scene id → its sceneOptions validator. The single source of truth for known scenes. */\nexport const SCENE_OPTION_SCHEMAS = {\n specimen: specimenSceneOptionsSchema,\n wall: wallSceneOptionsSchema,\n \"palette-reel\": paletteReelSceneOptionsSchema,\n} as const;\n\nexport type SceneId = keyof typeof SCENE_OPTION_SCHEMAS;\n\n// ---------------------------------------------------------------------------\n// Author-facing input types (editor autocomplete). Kept in sync with the zod\n// schemas by the Exact<> guards at the bottom — a drift is a compile error.\n// ---------------------------------------------------------------------------\n\n/** The wall's easing curves. */\nexport type WallEasing = z.infer<typeof wallEasingEnum>;\n\n/** One eased move — the uniform motion primitive (columns, wall default, and pan all use it). */\nexport interface WallPulseInput {\n /** When the pulse starts, as a fraction of the clip (0..1). */\n at: number;\n /** How long the move takes, as a fraction of the clip (0..1). If `at + duration > 1`, the start\n * shifts back so the move ends at the loop point. */\n duration: number;\n /** How far it travels, in periods (1 = one full tile-set / one wrap). Usually 0..1. */\n distance: number;\n /** Easing of the move's ramp. Default \"ease-in-out\". */\n easing?: WallEasing;\n}\n\nexport interface WallPanInput {\n /** Pan direction. Default \"left\". */\n direction?: \"left\" | \"right\";\n /** Continuous whole-clip horizontal loops (0 = no pan unless `pulses` move it). Default 0. */\n loops?: number;\n /** Pulses added on top of the base loops. Default none. */\n pulses?: WallPulseInput[];\n}\n\n/** One column of the wall: its tiles (assets by name) + its own optional Y motion. */\nexport interface WallColumnInput {\n /** Assets stacked in this column, by name (cycled to fill the height). At least one. */\n tiles: string[];\n /** Constant start-position shift, 0..1 of a tile-set — de-aligns columns with similar content. Default 0. */\n stagger?: number;\n /** Scroll direction. Defaults to \"down\". */\n direction?: \"up\" | \"down\";\n /** Continuous whole-clip loops for this column. Omit to inherit the wall-level `loops`. */\n loops?: number;\n /** This column's pulses. Omit to inherit the wall-level `pulses`. */\n pulses?: WallPulseInput[];\n}\n\n/** A faux tile for `test` preview mode — a flat color box labeled with its name. */\nexport interface FauxTileInput {\n /** Box fill (any CSS color). Omit to auto-derive a distinct color from the tile name. */\n color?: string;\n /** Optional caption shown under the name (e.g. \"16:9\") — purely cosmetic. */\n size?: string;\n /** This faux tile's aspect ratio (width / height): 1.78 = 16:9 (short), 0.56 = 9:16 (tall), 1 =\n * square. Omit to use the wall's `tileAspect` default. Real tiles use their media's own aspect. */\n aspect?: number;\n}\n\nexport interface WallSceneOptionsInput {\n /** The columns (≥3), each its own tiles + motion. Count = array length (fewer = bigger tiles). */\n columns: WallColumnInput[];\n /** Gap between columns and between their tile contents (px). Default 16. */\n gap?: number;\n /** Default/fallback tile aspect (width / height). Tiles fit the column width and take their OWN\n * height from their media's aspect (16:9 → short, 9:16 → tall); this is only used for faux\n * (`test`) tiles that don't set their own `aspect`. 1.6 = 16:10 landscape, <1 = portrait. Default 1.6. */\n tileAspect?: number;\n /** Tile corner radius (px). Default 12. */\n cornerRadius?: number;\n /** Backdrop shown in the gap gutters and behind tiles. Defaults to the scene's `background`. */\n background?: string;\n /** System 1 — the whole-wall X pan. Default: no pan. */\n pan?: WallPanInput;\n /** Default continuous whole-clip loops for columns that omit their own `loops`. Default 0 (static\n * unless a pulse moves it — a single pulse then rounds up to one loop). */\n loops?: number;\n /** Default pulses for columns that omit their own `pulses` (the uniform wall-level motion). Default none. */\n pulses?: WallPulseInput[];\n /** Preview mode: render faux labeled color boxes instead of real assets (no producers run). Default false. */\n test?: boolean;\n /** Per-tile faux appearance for `test` mode, keyed by tile name. Default {} (auto colors + names). */\n testTiles?: Record<string, FauxTileInput>;\n}\n\n/** One precomputed color in a palette-reel (the generator builds these from the friendly options). */\nexport interface ReelItem {\n /** Color display name (already cased per `uppercase`). */\n name: string;\n /** Swatch background hex (`#RRGGBB`). */\n hex: string;\n /** Contrast-picked text color for this band. */\n textColor: string;\n /** Preformatted detail lines revealed on expand (hex / oklch / rgb …). */\n details: string[];\n}\n\nexport interface PaletteReelSceneOptionsInput {\n /** Precomputed colors to reveal (at least one). */\n items: ReelItem[];\n /** Sliver arrangement. Default \"rows\". */\n orientation?: \"rows\" | \"columns\";\n /** How long each color stays fully open before handing off (s). Default 2. */\n holdSeconds?: number;\n /** Crossfade length from one open color to the next (s). Default 0.7. */\n transitionSeconds?: number;\n /** Ping-pong the sweep so every handoff is between neighbours (no last→first pinch at the seam). Default true. */\n bounce?: boolean;\n /** Easing applied to the crossfade. Default \"ease-in-out\". */\n easing?: \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\";\n /** How many times a sliver's share a fully-open band takes. Default 12. */\n grownFlex?: number;\n /** Minimum cross-size of a sliver in px. Default 0 (derive from the frame size). */\n minCrossPx?: number;\n /** Keep the name visible even in a collapsed sliver. Default true. */\n nameAlwaysVisible?: boolean;\n /** Label font weight. Default 700. */\n fontWeight?: number;\n /** Name font size in px (omit to derive from the frame size). */\n fontSize?: number;\n /** Detail-line font size as a fraction of the name size. Default 0.62. */\n detailFontScale?: number;\n /** Gap between bands (px). Default 0 (bands abut). */\n gap?: number;\n /** Band corner radius (px). Default 0 (square). */\n cornerRadius?: number;\n}\n\n// Compile-time guards: the documented authoring types must stay in sync with the schemas.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _wallInSync: Exact<WallSceneOptionsInput, z.input<typeof wallSceneOptionsSchema>> = true;\nconst _paletteReelInSync: Exact<\n PaletteReelSceneOptionsInput,\n z.input<typeof paletteReelSceneOptionsSchema>\n> = true;\nvoid _wallInSync;\nvoid _paletteReelInSync;\n","import { z } from \"zod\";\n\n/**\n * A \"pulse\" is one beat of the specimen's animation: a named span of time during which some\n * fraction of the glyph cells change letter and/or color. An empty pulse (no changes) is a hold.\n * Compose a sequence of pulses to author the whole clip — varying each one's length, change\n * fractions, and pacing gives every beat its own feel. The clip length is the sum of the durations.\n */\nexport const pulseSchema = z\n .object({\n /** Human label for the beat, e.g. \"color sweep\" — purely to keep the config readable. */\n name: z\n .string()\n .default(\"\")\n .describe('Human label for the beat, e.g. \"color sweep\" — purely to keep the config readable.'),\n /** Length of this beat, in seconds. */\n duration: z.number().positive().describe(\"Length of this beat, in seconds.\"),\n /** Fraction of cells whose glyph changes during this beat (0..1; 1 = every cell once). */\n chars: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Fraction of cells whose glyph changes during this beat (0..1; 1 = every cell once; 0 = a hold). Default 0.\"),\n /** Fraction of cells whose color changes during this beat (0..1; 1 = every cell once). */\n colors: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Fraction of cells whose color changes during this beat (0..1; 1 = every cell once). Default 0.\"),\n /**\n * Target color for this beat's color changes. When set, every color change in the beat goes to\n * this exact token (a deliberate sweep) instead of a weighted-random pick. Set `colors: 1` with\n * `pacing: \"even\"` to wash the whole specimen to one color evenly. Omit for the default\n * scattered, weighted-random recoloring.\n */\n color: z\n .enum([\"foreground\", \"muted\", \"accent\"])\n .optional()\n .describe(\"Target color token for this beat's color changes (a deliberate sweep); omit for the default weighted-random recoloring.\"),\n /**\n * How the changes are distributed in time across the beat — like a CSS easing curve:\n * \"linear\"/\"even\" = uniform, \"ease-in\" = front-loaded, \"ease-out\" = back-loaded,\n * \"ease-in-out\" = bunched at both ends, \"random\" = scattered.\n */\n pacing: z\n .enum([\"even\", \"linear\", \"ease-in\", \"ease-out\", \"ease-in-out\", \"random\"])\n .default(\"even\")\n .describe(\"How changes are distributed in time across the beat (CSS-easing-like): even/linear, ease-in, ease-out, ease-in-out, random. Default even.\"),\n })\n .strict();\n\nexport type Pulse = z.infer<typeof pulseSchema>;\n\n/** One \"pulse\" (beat) of the animation storyboard. */\nexport interface PulseInput {\n /** Human label for the beat, e.g. \"color sweep\" — purely to keep the config readable. */\n name?: string;\n /** Length of this beat, in seconds. */\n duration: number;\n /** Fraction of cells whose glyph changes during this beat (0..1; 1 = every cell once; 0 = a hold). */\n chars?: number;\n /** Fraction of cells whose color changes during this beat (0..1; 1 = every cell once). */\n colors?: number;\n /**\n * Target color for this beat's color changes. When set, every color change goes to this exact\n * token (a deliberate sweep) rather than a weighted-random pick. Set `colors: 1` with\n * `pacing: \"even\"` to evenly wash the whole specimen to one color. Omit for the default\n * scattered, weighted-random recoloring.\n */\n color?: \"foreground\" | \"muted\" | \"accent\";\n /**\n * How the changes are distributed in time across the beat — like a CSS easing curve:\n * \"linear\"/\"even\" = uniform, \"ease-in\" = front-loaded, \"ease-out\" = back-loaded,\n * \"ease-in-out\" = bunched at both ends, \"random\" = scattered.\n */\n pacing?: \"even\" | \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\" | \"random\";\n}\n\n/**\n * Relative likelihood each color token is chosen on a (non-targeted) color change. Higher = more\n * frequent. The default keeps foreground/muted common and accent a rare pop (2 / 2 / 1). Set any to\n * 0 to exclude that token from random recoloring (an explicit pulse `color` can still target it).\n */\nexport interface SpecimenColorWeightsInput {\n foreground?: number;\n muted?: number;\n accent?: number;\n}\n\n/** The specimen's color palette (any CSS colors). Override any subset. */\nexport interface SpecimenColorsInput {\n /** Backdrop behind the glyphs. */\n background?: string;\n /** Primary glyph color — the resting majority. */\n foreground?: string;\n /** Muted/secondary glyph color. */\n muted?: string;\n /** Accent color for occasional pops; defaults to `background` (accent glyphs blend in) if unset. */\n accent?: string;\n /** Color of the font-name label (bottom corner); defaults to `foreground` if unset. */\n label?: string;\n}\n\n/** A named option preset. The keys here are the selectable `template` values. */\nexport type SpecimenTemplate = \"demo\" | \"sweep\";\n\n/**\n * Author-facing options for the `specimen` generator — a looping type-specimen video. Only `font`\n * is required; everything else has a sensible default.\n */\nexport interface SpecimenOptionsInput {\n /** Font file to showcase (path relative to the working dir, or absolute). Required. */\n font: string;\n /**\n * Load a named option preset; your explicit options below still override what it sets. Options:\n * - `\"demo\"` — labeled walkthrough of every pulse behavior (each easing curve, a color sweep, a\n * mingle) with demo mode on; runs once, no mirror. Good for seeing what each setting does.\n * - `\"sweep\"` — seamless-looping showcase of the even per-character color sweeps (muted → accent\n * → foreground) on a dark palette chosen so the accent reads.\n */\n template?: SpecimenTemplate;\n /** Display name shown bottom-left (e.g. \"ABC Oracle\"). Default none. */\n name?: string;\n /** Demo mode: overlay the active pulse's name bottom-right, to see which beat is playing. Default false. */\n demo?: boolean;\n /** Output frames per second. Default 30. */\n fps?: number;\n /** Clip length in seconds. Defaults to the (mirrored) sum of the pulse durations; set to override. */\n durationSeconds?: number;\n /** Output frame width in px. Default 1920. */\n width?: number;\n /** Output frame height in px. Default 1080. */\n height?: number;\n /** Render scale (1 = 1:1; higher = crisper capture, downscaled into the video). Default 1. */\n deviceScaleFactor?: number;\n /** Glyph weight on the variable-font axis, 1–1000. Default 820. */\n weight?: number;\n /** Number of glyph rows. The glyph size is derived so the rows fill the top 80% of the frame. Default 3. */\n lines?: number;\n /** Line-height of the glyph block. Default 0.78 (tight, cap-height-hugging). */\n leading?: number;\n /** Glyphs to exclude from the showcase, e.g. \"QXZ\" (case-insensitive). Default none. */\n blacklist?: string;\n /** Override the glyph pool the specimen draws from (≥2 distinct characters). Default A–Z 0–9 + symbols. */\n characterPool?: string;\n /** Schedule seed — same seed ⇒ identical animation. Change for a different (still deterministic) take. Default 1. */\n seed?: number;\n /** Color tokens the glyphs cycle through. Override any subset. Default: light-grey palette. */\n colors?: SpecimenColorsInput;\n /** Relative likelihood of each color token on a random (non-targeted) color change. Default 2 / 2 / 1. */\n colorWeights?: SpecimenColorWeightsInput;\n /** The animation storyboard: an ordered sequence of pulses (beats). Default: a lively built-in storyboard. */\n pulses?: PulseInput[];\n /** Multiply every pulse's glyph-change fraction (1 = baseline, 2 = twice as busy, 0 = none). Default 1. */\n characterIntensity?: number;\n /** Multiply every pulse's color-change fraction (1 = baseline, 2 = twice as busy, 0 = none). Default 1. */\n colorIntensity?: number;\n /**\n * Max fraction a line's total width may drift as its glyphs change. Default 0.05. Glyph swaps are\n * width-compensated to stay within this, so the left-aligned right edge barely moves.\n */\n maxLineDrift?: number;\n /**\n * Mirror the pulses (play them out and back) so the clip ends on its opening frame and loops\n * seamlessly. Doubles the clip length. Set false for a one-shot that ends on the last state. Default true.\n */\n mirror?: boolean;\n /** x264 quality, 0–51 (lower = better quality / larger file). Default 18. */\n crf?: number;\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName?: string;\n}\n\nconst P = (name: string, duration: number, chars = 0, colors = 0): Pulse => ({\n name,\n duration,\n chars,\n colors,\n pacing: \"even\",\n});\n\n/**\n * A lively default storyboard describing the *outward* half of the loop (sums to ~10s). With\n * mirroring on (the default), the clip plays this out and back for a seamless ~20s loop. The\n * `chars`/`colors` numbers are fractions of the wall (1 = every glyph). Override `pulses` to compose\n * your own.\n */\nconst DEFAULT_PULSES: Pulse[] = [\n P(\"intro hold\", 0.8),\n P(\"first letters\", 0.8, 0.15, 0),\n P(\"settle\", 1.5),\n P(\"ripple\", 1, 0.08, 0.04),\n P(\"color sweep\", 1.2, 0, 0.13),\n P(\"rest\", 1.2),\n P(\"quick burst\", 0.6, 0.18, 0),\n P(\"drift\", 1.2, 0, 0.08),\n P(\"finale\", 1.2, 0.18, 0.13),\n P(\"outro hold\", 0.5),\n];\n\n/**\n * The \"demo\" template: turns on demo mode and walks through each pulse behavior one at a time —\n * every easing curve, a color sweep, and a mingle — each held for 5s with a 2s no-change rest\n * between, and clearly named, so (with demo mode) you can see exactly what each does. Runs once\n * forward (no mirror) for a focused ~47s walkthrough.\n */\nconst DEMO_PULSES: PulseInput[] = [\n { name: \"linear\", duration: 5, chars: 0.5, pacing: \"linear\" },\n { name: \"hold\", duration: 2 },\n { name: \"ease-in\", duration: 5, chars: 0.5, pacing: \"ease-in\" },\n { name: \"hold\", duration: 2 },\n { name: \"ease-out\", duration: 5, chars: 0.5, pacing: \"ease-out\" },\n { name: \"hold\", duration: 2 },\n { name: \"ease-in-out\", duration: 5, chars: 0.5, pacing: \"ease-in-out\" },\n { name: \"hold\", duration: 2 },\n { name: \"random\", duration: 5, chars: 0.5, pacing: \"random\" },\n { name: \"hold\", duration: 2 },\n // Even, per-character color sweeps: each washes every glyph to one token, evenly across the beat.\n { name: \"sweep → muted\", duration: 4, colors: 1, color: \"muted\", pacing: \"even\" },\n { name: \"sweep → accent\", duration: 4, colors: 1, color: \"accent\", pacing: \"even\" },\n { name: \"sweep → foreground\", duration: 4, colors: 1, color: \"foreground\", pacing: \"even\" },\n { name: \"hold\", duration: 2 },\n { name: \"weighted recolor\", duration: 5, colors: 0.6 },\n { name: \"hold\", duration: 2 },\n { name: \"mingle\", duration: 5, chars: 0.3, colors: 0.3 },\n];\n\n/**\n * The \"sweep\" template: a clean, seamless-looping showcase built around the even per-character color\n * sweeps. Every glyph washes evenly from one token to the next (muted → accent → foreground), with a\n * touch of glyph drift for life, on a dark palette chosen so the accent reads clearly. Mirrored, so\n * it loops without a seam. Override `colors`, `lines`, or `pulses` to retheme it.\n */\nconst SWEEP_COLORS: SpecimenColorsInput = {\n background: \"#0b0b0f\",\n foreground: \"#f4f4f5\",\n muted: \"#6b7280\",\n accent: \"#7c9cff\",\n};\n\n// `colors: 1` per sweep lands on every glyph exactly once (one full, clump-free wash). Outward half\n// sums to ~10.6s → ~21s mirrored loop.\nconst SWEEP_PULSES: PulseInput[] = [\n { name: \"hold\", duration: 0.8 },\n { name: \"to muted\", duration: 2.2, colors: 1, color: \"muted\", pacing: \"ease-in-out\" },\n { name: \"settle\", duration: 0.6 },\n { name: \"to accent\", duration: 2.2, colors: 1, color: \"accent\", pacing: \"ease-in-out\" },\n { name: \"settle\", duration: 0.6 },\n { name: \"to foreground\", duration: 2.2, colors: 1, color: \"foreground\", pacing: \"ease-in-out\" },\n { name: \"glyph drift\", duration: 1.4, chars: 0.5, pacing: \"even\" },\n { name: \"hold\", duration: 0.6 },\n];\n\n/** Named option presets. Selected via the `template` option; explicit options override them. */\nconst SPECIMEN_TEMPLATES: Record<SpecimenTemplate, Partial<SpecimenOptionsInput>> = {\n demo: { demo: true, mirror: false, lines: 4, pulses: DEMO_PULSES },\n sweep: {\n mirror: true,\n lines: 4,\n colors: SWEEP_COLORS,\n pulses: SWEEP_PULSES,\n },\n};\n\n/** Merge a selected template underneath the user's explicit options (which win). */\nfunction applyTemplate(raw: unknown): unknown {\n if (!raw || typeof raw !== \"object\") return raw;\n const r = raw as Record<string, unknown>;\n const tmpl =\n typeof r.template === \"string\"\n ? SPECIMEN_TEMPLATES[r.template as SpecimenTemplate]\n : undefined;\n return tmpl ? { ...tmpl, ...r } : raw;\n}\n\n/**\n * A type-specimen video: point it at a font file and give it a name — the tool renders a clip\n * (1920×1080 by default) of the typeface set as a fixed number of left-aligned `lines` (sized to\n * fill the top 80% of the frame) whose glyphs and colors change over a composed sequence of\n * \"pulses\" (mirrored into a seamless loop by default), and captures it. Everything else has\n * sensible defaults.\n */\nconst specimenObjectSchema = z\n .object({\n font: z\n .string()\n .min(1)\n .describe(\"Font file to showcase (path relative to the working dir, or absolute). Required.\"),\n template: z\n .enum([\"demo\", \"sweep\"])\n .optional()\n .describe(\"Load a named option preset (demo or sweep); your explicit options still override what it sets.\"),\n name: z\n .string()\n .default(\"\")\n .describe('Display name shown bottom-left (e.g. \"ABC Oracle\"). Default none.'),\n demo: z\n .boolean()\n .default(false)\n .describe(\"Demo mode: overlay the active pulse's name bottom-right, to see which beat is playing. Default false.\"),\n fps: z\n .number()\n .int()\n .positive()\n .max(120)\n .default(30)\n .describe(\"Output frames per second. Default 30.\"),\n durationSeconds: z\n .number()\n .positive()\n .optional()\n .describe(\"Clip length in seconds. Defaults to the (mirrored) sum of the pulse durations; set to override.\"),\n width: z\n .number()\n .int()\n .positive()\n .default(1920)\n .describe(\"Output frame width in px. Default 1920.\"),\n height: z\n .number()\n .int()\n .positive()\n .default(1080)\n .describe(\"Output frame height in px. Default 1080.\"),\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(1)\n .describe(\"Render scale (1 = 1:1; higher = crisper capture, downscaled into the video). Default 1.\"),\n weight: z\n .number()\n .int()\n .min(1)\n .max(1000)\n .default(820)\n .describe(\"Glyph weight on the variable-font axis, 1–1000. Default 820.\"),\n lines: z\n .number()\n .int()\n .min(1)\n .max(40)\n .default(3)\n .describe(\"Number of glyph rows; glyph size is derived so rows fill the top 80% of the frame. Default 3.\"),\n leading: z\n .number()\n .positive()\n .default(0.78)\n .describe(\"Line-height of the glyph block. Default 0.78 (tight, cap-height-hugging).\"),\n blacklist: z\n .string()\n .default(\"\")\n .describe('Glyphs to exclude from the showcase, e.g. \"QXZ\" (case-insensitive). Default none.'),\n characterPool: z\n .string()\n .refine((s) => new Set([...s.trim()]).size >= 2, \"characterPool needs ≥2 distinct characters\")\n .optional()\n .describe(\"Override the glyph pool the specimen draws from (≥2 distinct characters). Default A–Z 0–9 + symbols.\"),\n seed: z\n .number()\n .int()\n .default(1)\n .describe(\"Schedule seed — same seed ⇒ identical animation. Change for a different deterministic take. Default 1.\"),\n colors: z\n .object({\n background: z\n .string()\n .default(\"#eceef1\")\n .describe(\"Backdrop behind the glyphs.\"),\n foreground: z\n .string()\n .default(\"#16181d\")\n .describe(\"Primary glyph color — the resting majority.\"),\n muted: z\n .string()\n .default(\"#a7adb6\")\n .describe(\"Muted/secondary glyph color.\"),\n accent: z\n .string()\n .optional()\n .describe(\"Accent color for occasional pops; defaults to `background` (accent glyphs blend in) if unset.\"), // defaults to `background` at render (accent glyphs blend in)\n label: z\n .string()\n .optional()\n .describe(\"Color of the font-name label (bottom corner); defaults to `foreground` if unset.\"), // defaults to `foreground` at render\n })\n .default({})\n .describe(\"Color tokens the glyphs cycle through (any CSS colors). Override any subset. Default: light-grey palette.\"),\n colorWeights: z\n .object({\n foreground: z\n .number()\n .nonnegative()\n .default(2)\n .describe(\"Relative likelihood of the foreground token on a random color change. Default 2.\"),\n muted: z\n .number()\n .nonnegative()\n .default(2)\n .describe(\"Relative likelihood of the muted token on a random color change. Default 2.\"),\n accent: z\n .number()\n .nonnegative()\n .default(1)\n .describe(\"Relative likelihood of the accent token on a random color change. Default 1.\"),\n })\n .default({})\n .describe(\"Relative likelihood of each color token on a random (non-targeted) color change. Default 2 / 2 / 1.\"),\n pulses: z\n .array(pulseSchema)\n .min(1)\n .default(DEFAULT_PULSES)\n .describe(\"The animation storyboard: an ordered sequence of pulses (beats). Default: a lively built-in storyboard.\"),\n characterIntensity: z\n .number()\n .nonnegative()\n .default(1)\n .describe(\"Multiply every pulse's glyph-change fraction (1 = baseline, 2 = twice as busy, 0 = none). Default 1.\"),\n colorIntensity: z\n .number()\n .nonnegative()\n .default(1)\n .describe(\"Multiply every pulse's color-change fraction (1 = baseline, 2 = twice as busy, 0 = none). Default 1.\"),\n maxLineDrift: z\n .number()\n .positive()\n .max(0.5)\n .default(0.05)\n .describe(\"Max fraction a line's total width may drift as its glyphs change; swaps are width-compensated. Default 0.05.\"),\n mirror: z\n .boolean()\n .default(true)\n .describe(\"Mirror the pulses (play out and back) for a seamless loop; doubles the clip length. Default true.\"),\n crf: z\n .number()\n .int()\n .min(0)\n .max(51)\n .default(18)\n .describe(\"x264 quality, 0–51 (lower = better quality / larger file). Default 18.\"),\n fileName: z\n .string()\n .optional()\n .describe('Output filename; defaults to \"<slug(asset name)>.mp4\".'),\n })\n .strict();\n\n/** Runtime validator: expands the selected `template`, then validates the merged options. */\nexport const specimenOptionsSchema = z.preprocess(applyTemplate, specimenObjectSchema);\n\n/** Author-facing input (documented for editor hover; the schema above validates it at run time). */\nexport type SpecimenOptions = SpecimenOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedSpecimenOptions = z.infer<typeof specimenObjectSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _specimenInputInSync: Exact<SpecimenOptionsInput, z.input<typeof specimenObjectSchema>> = true;\nvoid _specimenInputInSync;\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { copyFile, rename, stat } from \"node:fs/promises\";\nimport type { ResolvedSceneOptions } from \"@/generators/scene/options\";\nimport { SCENE_OPTION_SCHEMAS } from \"@/generators/scene/scene-options\";\nimport { startSceneServer } from \"@/scene/serve\";\nimport { recordSceneRealtime } from \"@/scene/capture-realtime\";\nimport { captureSceneFrames } from \"@/scene/capture-frames\";\nimport { autoWorkers } from \"@/media/frame-capture\";\nimport { probeVideoDimensions, transcodeToMp4 } from \"@/media/ffmpeg\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { sha256File } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nconst SCENE_ID = \"scene\";\n\n/** The built scene app shipped alongside the CLI bundle (dist/scene-app). */\nfunction sceneAppDir(): string {\n const here = path.dirname(fileURLToPath(import.meta.url)); // dist/cli\n return path.resolve(here, \"..\", \"scene-app\");\n}\n\n/**\n * Render a scene to an mp4 and record it. Shared by the `scene` generator and friendly\n * wrappers (e.g. `specimen`), which pass their own `generatorId` for the manifest.\n */\nexport async function renderScene(\n ctx: PipelineContext,\n options: ResolvedSceneOptions,\n generatorId: string = SCENE_ID,\n): Promise<{ assets: AssetRecord[] }> {\n // Validate the per-scene knobs against the selected scene's schema: a typo'd key or an unknown\n // scene id fails here with a named error instead of capturing a blank \"Unknown scene\" page.\n const sceneSchema = SCENE_OPTION_SCHEMAS[options.scene as keyof typeof SCENE_OPTION_SCHEMAS];\n if (!sceneSchema) {\n throw new Error(\n `Unknown scene \"${options.scene}\". Available: ${Object.keys(SCENE_OPTION_SCHEMAS).join(\", \")}.`,\n );\n }\n const sceneOptions = sceneSchema.parse(options.sceneOptions);\n\n const fileName = options.fileName ?? `${slugify(ctx.target.name)}.mp4`;\n const outPath = ctx.resolveOutPath(fileName);\n\n // Resolve served files (e.g. fonts) to absolute paths (relative to the working dir).\n const filePaths: Record<string, string> = {};\n for (const [name, p] of Object.entries(options.files)) {\n filePaths[name] = path.isAbsolute(p) ? p : path.resolve(process.cwd(), p);\n }\n\n const server = await startSceneServer({\n staticDir: sceneAppDir(),\n inputs: { ...ctx.resolvedInputs, ...filePaths },\n });\n\n try {\n const inputUrls: Record<string, string> = {};\n for (const slot of Object.keys(ctx.resolvedInputs)) {\n inputUrls[slot] = server.inputUrl(slot);\n }\n const fileUrls: Record<string, string> = {};\n for (const name of Object.keys(filePaths)) {\n fileUrls[name] = server.inputUrl(name);\n }\n\n const props = {\n width: options.width,\n height: options.height,\n background: options.background,\n durationSeconds: options.durationSeconds,\n fps: options.fps,\n inputs: inputUrls,\n files: fileUrls,\n options: sceneOptions, // validated + defaulted against the scene's schema\n };\n const sceneUrl = new URL(`${server.origin}/`);\n sceneUrl.searchParams.set(\"scene\", options.scene);\n sceneUrl.searchParams.set(\"props\", JSON.stringify(props));\n\n await ensureDir(path.dirname(outPath));\n const composedTmp = path.join(ctx.tmpDir, `${slugify(ctx.target.name)}-scene.mp4`);\n\n const draft = ctx.quality === \"draft\";\n const preset = draft ? \"ultrafast\" : \"medium\";\n if (options.capture === \"frames\") {\n const workers = options.workers ?? autoWorkers();\n ctx.logger.info(\n `rendering scene \"${options.scene}\" (frame-stepped, ${workers} worker(s))`,\n );\n await captureSceneFrames({\n browser: ctx.browser,\n url: sceneUrl.toString(),\n width: options.width,\n height: options.height,\n deviceScaleFactor: options.deviceScaleFactor,\n fps: options.fps,\n durationSeconds: options.durationSeconds,\n crf: options.crf,\n outPath: composedTmp,\n preset,\n // Draft always uses fast jpeg intermediates; final uses the configured format (png = lossless).\n frameFormat: draft ? \"jpeg\" : options.frameFormat,\n jpegQuality: draft ? 70 : 90,\n workers,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n onProgress: ctx.progress,\n signal: ctx.signal,\n });\n } else {\n ctx.logger.info(`rendering scene \"${options.scene}\" (realtime)`);\n const recording = await recordSceneRealtime({\n browser: ctx.browser,\n url: sceneUrl.toString(),\n width: options.width,\n height: options.height,\n deviceScaleFactor: options.deviceScaleFactor,\n durationSeconds: options.durationSeconds,\n tmpDir: ctx.tmpDir,\n logger: ctx.logger,\n });\n await transcodeToMp4({\n inputPath: recording.path,\n outputPath: composedTmp,\n fps: options.fps,\n width: options.width,\n height: options.height,\n crf: options.crf,\n preset,\n // Trim the blank navigation/readiness lead so the clip opens on the first painted frame,\n // then clamp to the intended length so the output matches the manifest's durationMs.\n startOffsetSeconds: recording.leadSeconds,\n durationSeconds: options.durationSeconds,\n logger: ctx.logger,\n signal: ctx.signal,\n });\n }\n\n try {\n await rename(composedTmp, outPath);\n } catch {\n await copyFile(composedTmp, outPath);\n }\n\n const [dims, stats, contentHash] = await Promise.all([\n probeVideoDimensions(outPath),\n stat(outPath),\n sha256File(outPath),\n ]);\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: generatorId,\n sourceUrl: ctx.target.url ?? `scene:${options.scene}`,\n file: ctx.toManifestPath(outPath),\n format: \"mp4\",\n width: dims?.width ?? options.width,\n height: dims?.height ?? options.height,\n durationMs: Math.round(options.durationSeconds * 1000),\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n } finally {\n await server.close();\n }\n}\n\n// Note: there is no public `scene` generator — the friendly `wall`, `specimen`, and `palette-reel`\n// generators render through `renderScene` (the shared engine) above.\n","import http from \"node:http\";\nimport path from \"node:path\";\nimport { createReadStream } from \"node:fs\";\nimport { stat } from \"node:fs/promises\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nconst MIME: Record<string, string> = {\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript\",\n \".mjs\": \"text/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".webp\": \"image/webp\",\n \".ico\": \"image/x-icon\",\n \".mp4\": \"video/mp4\",\n \".webm\": \"video/webm\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".otf\": \"font/otf\",\n \".map\": \"application/json\",\n};\n\nconst mimeFor = (p: string): string =>\n MIME[path.extname(p).toLowerCase()] ?? \"application/octet-stream\";\n\n/**\n * Parse a single HTTP byte-range header against a known size. Handles `bytes=start-end`,\n * `bytes=start-` (open-ended), and `bytes=-N` (suffix: the last N bytes — which Chromium's media\n * stack does issue). Returns null for absent/malformed/unsatisfiable/multi ranges, in which case the\n * caller should serve the full body (200) rather than mis-claim a 206. Pure — unit-tested.\n */\nexport function parseByteRange(\n header: string | undefined,\n size: number,\n): { start: number; end: number } | null {\n if (!header) return null;\n const m = /^bytes=(\\d*)-(\\d*)$/.exec(header.trim());\n if (!m) return null; // not a single bytes range (e.g. multi-range) → caller serves full\n const [, startRaw, endRaw] = m;\n let start: number;\n let end: number;\n if (startRaw === \"\") {\n if (endRaw === \"\") return null;\n const n = Number(endRaw);\n if (!(n > 0)) return null;\n start = Math.max(0, size - n);\n end = size - 1;\n } else {\n start = Number(startRaw);\n end = endRaw === \"\" ? size - 1 : Number(endRaw);\n }\n if (!Number.isFinite(start) || !Number.isFinite(end)) return null;\n end = Math.min(end, size - 1);\n if (start < 0 || start > end || start >= size) return null;\n return { start, end };\n}\n\nexport interface SceneServer {\n origin: string;\n /** Absolute URL of a served input asset by slot name. */\n inputUrl(slot: string): string;\n close(): Promise<void>;\n}\n\nexport interface SceneServerArgs {\n /** Directory of the built scene app (served at \"/\"). */\n staticDir: string;\n /** Slot name → absolute input file path, served under /_inputs/. */\n inputs: Record<string, string>;\n}\n\n/**\n * Ephemeral static server for one scene render: serves the built scene app plus this asset's\n * input files (with HTTP range support so the scene's <video> can seek). No writes anywhere.\n */\nexport async function startSceneServer(args: SceneServerArgs): Promise<SceneServer> {\n const staticRoot = path.resolve(args.staticDir);\n const inputByUrl = new Map<string, string>();\n const slotToUrl = new Map<string, string>();\n for (const [slot, file] of Object.entries(args.inputs)) {\n const ext = path.extname(file) || \".mp4\";\n const url = `/_inputs/${slot}${ext}`;\n inputByUrl.set(url, file);\n slotToUrl.set(slot, url);\n }\n\n const serveFile = async (\n file: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<void> => {\n let info;\n try {\n info = await stat(file);\n } catch {\n res.statusCode = 404;\n res.end();\n return;\n }\n if (info.isDirectory()) return serveFile(path.join(file, \"index.html\"), req, res);\n\n const type = mimeFor(file);\n const rangeable = type.startsWith(\"video/\");\n if (rangeable) res.setHeader(\"Accept-Ranges\", \"bytes\");\n\n const parsed = rangeable ? parseByteRange(req.headers.range, info.size) : null;\n if (parsed) {\n res.writeHead(206, {\n \"Content-Type\": type,\n \"Content-Range\": `bytes ${parsed.start}-${parsed.end}/${info.size}`,\n \"Content-Length\": parsed.end - parsed.start + 1,\n });\n createReadStream(file, { start: parsed.start, end: parsed.end }).pipe(res);\n } else {\n // No range, or malformed/unsatisfiable — serve the whole body rather than mis-claim a 206.\n res.writeHead(200, { \"Content-Type\": type, \"Content-Length\": info.size });\n createReadStream(file).pipe(res);\n }\n };\n\n const handle = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n const urlPath = decodeURIComponent((req.url ?? \"/\").split(\"?\")[0] ?? \"/\");\n\n const inputFile = inputByUrl.get(urlPath);\n if (inputFile) return serveFile(inputFile, req, res);\n\n const rel = urlPath === \"/\" ? \"index.html\" : urlPath.replace(/^\\/+/, \"\");\n const filePath = path.resolve(staticRoot, rel);\n if (filePath !== staticRoot && !filePath.startsWith(staticRoot + path.sep)) {\n res.statusCode = 403;\n res.end();\n return;\n }\n try {\n await stat(filePath);\n return serveFile(filePath, req, res);\n } catch {\n // SPA fallback so the scene app always loads.\n return serveFile(path.join(staticRoot, \"index.html\"), req, res);\n }\n };\n\n const server = http.createServer((req, res) => {\n void handle(req, res).catch(() => {\n res.statusCode = 500;\n res.end();\n });\n });\n\n await new Promise<void>((resolve) => server.listen(0, \"127.0.0.1\", () => resolve()));\n const addr = server.address();\n const port = typeof addr === \"object\" && addr ? addr.port : 0;\n const origin = `http://127.0.0.1:${port}`;\n\n return {\n origin,\n inputUrl: (slot) => {\n const rel = slotToUrl.get(slot);\n if (!rel) throw new Error(`Unknown scene input slot \"${slot}\".`); // don't silently serve the SPA\n return origin + rel;\n },\n close: () => new Promise<void>((resolve) => server.close(() => resolve())),\n };\n}\n","import path from \"node:path\";\nimport { mkdtemp } from \"node:fs/promises\";\nimport type { Browser } from \"playwright-core\";\nimport { ensureDir } from \"@/utils/fs\";\nimport type { Logger } from \"@/utils/logger\";\n\nexport interface RecordSceneArgs {\n browser: Browser;\n url: string;\n width: number;\n height: number;\n deviceScaleFactor: number;\n durationSeconds: number;\n tmpDir: string;\n logger: Logger;\n}\n\n/** The recorded .webm plus the lead time (seconds) of blank navigation/readiness before play()\n * started — Playwright records the whole context lifetime, so the caller trims this off the front. */\nexport interface RecordSceneResult {\n path: string;\n leadSeconds: number;\n}\n\n/**\n * Realtime capture: load the scene, wait until it's ready (fonts loaded and the scene has painted\n * its first frame), play it, and record the page for `durationSeconds` via Playwright's native\n * recorder. Real-time (wall-clock) — simple, but not frame-accurate; the deterministic frame-stepper\n * is the precise path. Playwright records from context creation, so we also return `leadSeconds` (the\n * blank navigation/readiness span before play()) for the caller to trim off the recording's head.\n */\nexport async function recordSceneRealtime(args: RecordSceneArgs): Promise<RecordSceneResult> {\n await ensureDir(args.tmpDir);\n const recordDir = await mkdtemp(path.join(args.tmpDir, \"scene-rec-\"));\n\n const recStart = Date.now(); // recording effectively begins at context creation\n const context = await args.browser.newContext({\n viewport: { width: args.width, height: args.height },\n deviceScaleFactor: args.deviceScaleFactor,\n // Note: Playwright records at the viewport's CSS resolution — `size` places (does not upscale)\n // the viewport into the frame, so it must equal width×height. Realtime can't supersample this\n // way; the frame-stepper is the high-fidelity path. (deviceScaleFactor still sharpens rendering.)\n recordVideo: { dir: recordDir, size: { width: args.width, height: args.height } },\n });\n const page = await context.newPage();\n const video = page.video();\n page.on(\"console\", (m) => args.logger.debug(`[scene] ${m.text()}`));\n page.on(\"pageerror\", (e) => args.logger.debug(`[scene error] ${e.message}`));\n\n let leadSeconds = 0;\n try {\n args.logger.debug(`loading scene ${args.url}`);\n await page.goto(args.url, { waitUntil: \"load\" });\n try {\n await page.waitForFunction(\n () => (globalThis as { __showcaseReady?: boolean }).__showcaseReady === true,\n undefined,\n { timeout: 30_000 },\n );\n } catch (err) {\n const diag = await page\n .evaluate(() => {\n // Runs in the browser; the node tsconfig has no DOM lib, so reach via globalThis.\n const g = globalThis as unknown as {\n __showcase?: unknown;\n document: { querySelectorAll(s: string): ArrayLike<Record<string, unknown>> };\n };\n const vids = Array.from(g.document.querySelectorAll(\"video\"));\n return {\n hasRuntime: Boolean(g.__showcase),\n videoCount: vids.length,\n videos: vids.map((v) => ({\n src: v.currentSrc || v.src,\n readyState: v.readyState,\n networkState: v.networkState,\n error: (v.error as { code?: number } | null)?.code ?? null,\n })),\n };\n })\n .catch(() => null);\n args.logger.error(`scene never became ready: ${JSON.stringify(diag)}`);\n throw err;\n }\n // Everything up to here (navigation + readiness) is blank in the recording; the scene is now\n // painted, so the head trim lands on the first real frame.\n leadSeconds = (Date.now() - recStart) / 1000;\n await page.evaluate(() =>\n (globalThis as { __showcase?: { play(): void } }).__showcase?.play(),\n );\n await page.waitForTimeout(Math.round(args.durationSeconds * 1000));\n } finally {\n await context.close(); // finalizes the webm\n }\n\n if (!video) throw new Error(\"Playwright did not record a scene video.\");\n return { path: await video.path(), leadSeconds };\n}\n","import type { Browser } from \"playwright-core\";\nimport { captureFramedVideo } from \"@/media/frame-capture\";\nimport type { Logger } from \"@/utils/logger\";\n\nexport interface FrameCaptureArgs {\n browser: Browser;\n url: string;\n width: number;\n height: number;\n deviceScaleFactor: number;\n fps: number;\n durationSeconds: number;\n crf: number;\n /** Absolute mp4 path to write. */\n outPath: string;\n /** \"ultrafast\" in draft, \"medium\" otherwise. */\n preset?: string;\n /** Intermediate frame format; \"png\" is lossless into the encoder. Default \"jpeg\". */\n frameFormat?: \"jpeg\" | \"png\";\n /** JPEG quality for intermediate frames (perf vs fidelity; ignored for png). */\n jpegQuality?: number;\n /** Parallel render workers (each its own browser context). Default 1. */\n workers?: number;\n /** Scratch dir for per-worker segments. */\n tmpDir: string;\n logger: Logger;\n /** Fractional progress (0–1) as frames complete. */\n onProgress?: (fraction: number) => void;\n /** Cancels the capture mid-flight. */\n signal?: AbortSignal;\n}\n\n/**\n * Deterministic scene capture: drives the scene-app's `window.__showcase` timeline. Each frame seeks\n * the scene to its exact time, screenshots, and pipes into ffmpeg — frame-accurate and machine-\n * independent. This is now a thin adapter over the shared {@link captureFramedVideo} harness; the only\n * scene-specific parts are the readiness wait and the per-frame `seek(t)`.\n */\nexport async function captureSceneFrames(args: FrameCaptureArgs): Promise<void> {\n await captureFramedVideo({\n browser: args.browser,\n width: args.width,\n height: args.height,\n deviceScaleFactor: args.deviceScaleFactor,\n fps: args.fps,\n durationSeconds: args.durationSeconds,\n crf: args.crf,\n outPath: args.outPath,\n preset: args.preset,\n frameFormat: args.frameFormat,\n jpegQuality: args.jpegQuality,\n workers: args.workers,\n tmpDir: args.tmpDir,\n logger: args.logger,\n onProgress: args.onProgress,\n signal: args.signal,\n prepare: async (page) => {\n await page.goto(args.url, { waitUntil: \"load\" });\n await page.waitForFunction(\n () => (globalThis as { __showcaseReady?: boolean }).__showcaseReady === true,\n undefined,\n { timeout: 30_000 },\n );\n },\n seekToFrame: async (page, t) => {\n await page.evaluate(\n (tt) =>\n (globalThis as { __showcase?: { seek(t: number): Promise<void> } }).__showcase?.seek(tt),\n t,\n );\n },\n });\n}\n","import { wallOptionsSchema, type ResolvedWallOptions } from \"@/generators/wall/options\";\nimport { renderScene } from \"@/generators/scene\";\nimport type { ResolvedSceneOptions } from \"@/generators/scene/options\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const WALL_ID = \"wall\";\n\n/** Map the friendly wall options onto the `wall` scene and render it through the scene engine. */\nasync function run(ctx: PipelineContext, o: ResolvedWallOptions): Promise<{ assets: AssetRecord[] }> {\n const sceneOptions: ResolvedSceneOptions = {\n scene: \"wall\",\n width: o.width,\n height: o.height,\n background: o.background,\n deviceScaleFactor: o.deviceScaleFactor,\n fps: o.fps,\n durationSeconds: o.durationSeconds,\n capture: o.capture,\n workers: o.workers,\n frameFormat: o.frameFormat,\n crf: o.crf,\n fileName: o.fileName,\n files: {},\n sceneOptions: {\n columns: o.columns,\n gap: o.gap,\n tileAspect: o.tileAspect,\n cornerRadius: o.cornerRadius,\n background: o.background,\n pan: o.pan,\n loops: o.loops,\n pulses: o.pulses,\n test: o.test,\n testTiles: o.testTiles,\n },\n };\n return renderScene(ctx, sceneOptions, WALL_ID);\n}\n\n/**\n * The wall declares its dependencies through its columns: every tile is an asset name, so the\n * pipeline can derive the `inputs` map (slot === asset name) instead of the author maintaining a\n * separate list. The `Wall` scene resolves a column's tiles → urls via these slots. In `test`\n * (preview) mode the tiles are faux color boxes, so there are NO real dependencies — return none\n * (column tile names need not reference real assets) and nothing has to be generated first.\n */\nfunction deriveInputs(o: ResolvedWallOptions): Record<string, string> {\n if (o.test) return {};\n const map: Record<string, string> = {};\n for (const col of o.columns) for (const name of col.tiles) map[name] = name;\n return map;\n}\n\nexport const wallGenerator: Generator<ResolvedWallOptions> = {\n id: WALL_ID,\n optionsSchema: wallOptionsSchema,\n deriveInputs,\n run,\n};\n","import {\n specimenOptionsSchema,\n type ResolvedSpecimenOptions,\n} from \"@/generators/specimen/options\";\nimport { renderScene } from \"@/generators/scene\";\nimport type { ResolvedSceneOptions } from \"@/generators/scene/options\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const SPECIMEN_ID = \"specimen\";\n\n/** Map the friendly specimen options onto the `specimen` scene and render it. */\nasync function run(\n ctx: PipelineContext,\n o: ResolvedSpecimenOptions,\n): Promise<{ assets: AssetRecord[] }> {\n // Clip length follows the composed pulses (doubled when mirrored for a seamless loop) unless\n // explicitly overridden.\n const pulsesTotal = o.pulses.reduce((sum, p) => sum + p.duration, 0);\n const durationSeconds = o.durationSeconds ?? (o.mirror ? pulsesTotal * 2 : pulsesTotal);\n\n const sceneOptions: ResolvedSceneOptions = {\n scene: \"specimen\",\n width: o.width,\n height: o.height,\n background: o.colors.background,\n deviceScaleFactor: o.deviceScaleFactor,\n fps: o.fps,\n durationSeconds,\n // The specimen's animation is a seeded, deterministic function of time (it publishes\n // __sceneSeek), so the frame-stepper renders it frame-exact: single encode, supersampling via\n // deviceScaleFactor, and a perfect loop seam — no realtime recording jitter.\n capture: \"frames\",\n frameFormat: \"jpeg\",\n crf: o.crf,\n fileName: o.fileName,\n files: { font: o.font },\n sceneOptions: {\n label: o.name,\n demo: o.demo,\n weight: o.weight,\n lines: o.lines,\n leading: o.leading,\n blacklist: o.blacklist,\n characterPool: o.characterPool,\n colors: o.colors,\n colorWeights: o.colorWeights,\n pulses: o.pulses,\n mirror: o.mirror,\n characterIntensity: o.characterIntensity,\n colorIntensity: o.colorIntensity,\n maxLineDrift: o.maxLineDrift,\n seed: o.seed,\n },\n };\n return renderScene(ctx, sceneOptions, SPECIMEN_ID);\n}\n\nexport const specimenGenerator: Generator<ResolvedSpecimenOptions> = {\n id: SPECIMEN_ID,\n optionsSchema: specimenOptionsSchema,\n // The font's CONTENT shapes the output — hash it into the cache key (edit font → regenerate).\n fileDependencies: (o) => [o.font],\n run,\n};\n","import path from \"node:path\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { imageSize } from \"image-size\";\nimport {\n paletteOptionsSchema,\n type ResolvedPaletteOptions,\n} from \"@/generators/palette/options\";\nimport { buildPaletteHtml } from \"@/generators/palette/html\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { sha256Buffer } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const PALETTE_ID = \"palette\";\n\nconst FONT_MIME: Record<string, string> = {\n \".woff2\": \"font/woff2\",\n \".woff\": \"font/woff\",\n \".ttf\": \"font/ttf\",\n \".otf\": \"font/otf\",\n};\n\n/** Read a custom font file into a base64 data URL so it can be embedded in the render (no server). */\nasync function fontDataUrl(fontFile: string): Promise<string> {\n const abs = path.isAbsolute(fontFile) ? fontFile : path.resolve(process.cwd(), fontFile);\n const mime = FONT_MIME[path.extname(abs).toLowerCase()] ?? \"font/woff2\";\n const data = await readFile(abs);\n return `data:${mime};base64,${data.toString(\"base64\")}`;\n}\n\n/** Render the palette HTML and screenshot it to a PNG (static — no animation). */\nasync function run(\n ctx: PipelineContext,\n o: ResolvedPaletteOptions,\n): Promise<{ assets: AssetRecord[] }> {\n const fileName = o.fileName ?? `${slugify(ctx.target.name)}.png`;\n const outPath = ctx.resolveOutPath(fileName);\n\n const dataUrl = o.fontFile ? await fontDataUrl(o.fontFile) : undefined;\n const html = buildPaletteHtml(o, dataUrl);\n\n ctx.logger.info(`rendering palette (${o.colors.length} colors, ${o.layout})`);\n const context = await ctx.browser.newContext({\n viewport: { width: o.width, height: o.height },\n deviceScaleFactor: o.deviceScaleFactor,\n });\n let buffer: Buffer;\n try {\n const page = await context.newPage();\n await page.setContent(html, { waitUntil: \"load\" });\n // Let a custom font finish loading before the shot so it's actually applied.\n if (dataUrl) {\n await page.evaluate(\n () =>\n (globalThis as { document?: { fonts?: { ready?: Promise<unknown> } } }).document?.fonts\n ?.ready,\n );\n }\n buffer = await page.screenshot({ type: \"png\" });\n } finally {\n await context.close();\n }\n\n await ensureDir(path.dirname(outPath));\n await writeFile(outPath, buffer);\n\n const dims = imageSize(buffer);\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: PALETTE_ID,\n sourceUrl: `palette:${o.colors.length}`,\n file: ctx.toManifestPath(outPath),\n format: \"png\",\n width: dims.width ?? o.width * o.deviceScaleFactor,\n height: dims.height ?? o.height * o.deviceScaleFactor,\n bytes: buffer.length,\n contentHash: sha256Buffer(buffer),\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n}\n\nexport const paletteGenerator: Generator<ResolvedPaletteOptions> = {\n id: PALETTE_ID,\n optionsSchema: paletteOptionsSchema,\n // A custom font's content shapes the output — hash it into the cache key.\n fileDependencies: (o) => (o.fontFile ? [o.fontFile] : []),\n run,\n};\n","import { z } from \"zod\";\nimport { normalizeHex, type FieldId } from \"@/generators/palette/color\";\n\n/** Field ids that can be placed in a swatch corner. */\nconst fieldEnum = z.enum([\"name\", \"hex\", \"rgb\", \"oklch\", \"hsl\", \"cmyk\"]);\n\nconst hexString = z.string().refine(\n (s) => {\n try {\n normalizeHex(s);\n return true;\n } catch {\n return false;\n }\n },\n { message: \"must be a hex color like #D7DBDE\" },\n);\n\n/** One color in the palette. */\nexport interface PaletteColorInput {\n /** Display name, e.g. \"Wet Grey\". */\n name: string;\n /** Hex value (#rgb or #rrggbb, with or without #). */\n hex: string;\n}\n\n/**\n * Author-facing options for the `palette` generator — a still color-palette image. Each color is a\n * swatch labeled with the fields you place in its corners (name / hex / rgb / oklch / hsl / cmyk),\n * with auto-contrasting text. Only `colors` is required.\n */\nexport interface PaletteOptionsInput {\n /** The colors to show (at least one). */\n colors: PaletteColorInput[];\n /** Swatch arrangement: full-width bands, full-height columns, or an N-wide grid. Default \"rows\". */\n layout?: \"rows\" | \"columns\" | \"grid\";\n /** Columns when `layout: \"grid\"`. Default 3. */\n gridColumns?: number;\n /** Output width in px. Default 1400. */\n width?: number;\n /** Output height in px. Default 1750 (portrait 4:5 with the default width). */\n height?: number;\n /** Render scale (2 = retina-crisp). Default 2. */\n deviceScaleFactor?: number;\n /** Page background, shown only in the gaps between swatches. Default \"#ffffff\". */\n background?: string;\n /** Gap between swatches (px). Default 0 (swatches abut). */\n gap?: number;\n /** Swatch corner radius (px). Default 0 (square). */\n cornerRadius?: number;\n /** Fields stacked in the top-left corner. Default name + hex. */\n topLeft?: FieldId[];\n /** Fields stacked in the top-right corner. Default rgb + oklch. */\n topRight?: FieldId[];\n /** Fields stacked in the bottom-left corner. Default none. */\n bottomLeft?: FieldId[];\n /** Fields stacked in the bottom-right corner. Default none. */\n bottomRight?: FieldId[];\n /** Uppercase the color names. Default false. */\n uppercase?: boolean;\n /** RGB string style. Default \"labeled\". */\n rgbStyle?: \"labeled\" | \"css\" | \"plain\";\n /** OKLCH string style. Default \"css\". */\n oklchStyle?: \"css\" | \"labeled\";\n /** Custom font file (woff2/woff/ttf/otf), embedded into the render. Omit for a system bold sans. */\n fontFile?: string;\n /** Label font size in px. Omit to derive from the width. */\n fontSize?: number;\n /** Label font weight. Default 700. */\n fontWeight?: number;\n /** Light text color, used on dark swatches (picked by contrast). Default \"#ffffff\". */\n textLight?: string;\n /** Dark text color, used on light swatches (picked by contrast). Default \"#141414\". */\n textDark?: string;\n /** Luminance above which the dark text is used (0..1). Default 0.5. */\n contrastThreshold?: number;\n /** Inset of the labels from the swatch edges (px). Omit to derive from the width. */\n padding?: number;\n /** Output filename; defaults to \"<slug(asset name)>.png\". */\n fileName?: string;\n}\n\nconst paletteObjectSchema = z\n .object({\n colors: z\n .array(\n z\n .object({\n name: z.string().min(1).describe('Display name, e.g. \"Wet Grey\".'),\n hex: hexString.describe(\"Hex value (#rgb or #rrggbb, with or without #).\"),\n })\n .strict(),\n )\n .min(1)\n .describe(\"The colors to show (at least one).\"),\n layout: z\n .enum([\"rows\", \"columns\", \"grid\"])\n .default(\"rows\")\n .describe('Swatch arrangement: full-width bands, full-height columns, or an N-wide grid. Default \"rows\".'),\n gridColumns: z\n .number()\n .int()\n .min(1)\n .max(12)\n .default(3)\n .describe('Columns when layout is \"grid\". Default 3.'),\n width: z.number().int().positive().default(1400).describe(\"Output width in px. Default 1400.\"),\n height: z\n .number()\n .int()\n .positive()\n .default(1750)\n .describe(\"Output height in px. Default 1750 (portrait 4:5 with the default width).\"),\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(2)\n .describe(\"Render scale (2 = retina-crisp). Default 2.\"),\n background: z\n .string()\n .default(\"#ffffff\")\n .describe('Page background, shown only in the gaps between swatches. Default \"#ffffff\".'),\n gap: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Gap between swatches (px). Default 0 (swatches abut).\"),\n cornerRadius: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Swatch corner radius (px). Default 0 (square).\"),\n topLeft: z\n .array(fieldEnum)\n .default([\"name\", \"hex\"])\n .describe(\"Fields stacked in the top-left corner. Default name + hex.\"),\n topRight: z\n .array(fieldEnum)\n .default([\"rgb\", \"oklch\"])\n .describe(\"Fields stacked in the top-right corner. Default rgb + oklch.\"),\n bottomLeft: z\n .array(fieldEnum)\n .default([])\n .describe(\"Fields stacked in the bottom-left corner. Default none.\"),\n bottomRight: z\n .array(fieldEnum)\n .default([])\n .describe(\"Fields stacked in the bottom-right corner. Default none.\"),\n uppercase: z.boolean().default(false).describe(\"Uppercase the color names. Default false.\"),\n rgbStyle: z\n .enum([\"labeled\", \"css\", \"plain\"])\n .default(\"labeled\")\n .describe('RGB string style. Default \"labeled\".'),\n oklchStyle: z\n .enum([\"css\", \"labeled\"])\n .default(\"css\")\n .describe('OKLCH string style. Default \"css\".'),\n fontFile: z\n .string()\n .optional()\n .describe(\"Custom font file (woff2/woff/ttf/otf), embedded into the render. Omit for a system bold sans.\"),\n fontSize: z\n .number()\n .positive()\n .optional()\n .describe(\"Label font size in px. Omit to derive from the width.\"),\n fontWeight: z\n .number()\n .int()\n .min(1)\n .max(1000)\n .default(700)\n .describe(\"Label font weight. Default 700.\"),\n textLight: z\n .string()\n .default(\"#ffffff\")\n .describe('Light text color, used on dark swatches (picked by contrast). Default \"#ffffff\".'),\n textDark: z\n .string()\n .default(\"#141414\")\n .describe('Dark text color, used on light swatches (picked by contrast). Default \"#141414\".'),\n contrastThreshold: z\n .number()\n .min(0)\n .max(1)\n .default(0.5)\n .describe(\"Luminance above which the dark text is used (0..1). Default 0.5.\"),\n padding: z\n .number()\n .nonnegative()\n .optional()\n .describe(\"Inset of the labels from the swatch edges (px). Omit to derive from the width.\"),\n fileName: z\n .string()\n .optional()\n .describe('Output filename; defaults to \"<slug(asset name)>.png\".'),\n })\n .strict();\n\nexport const paletteOptionsSchema = paletteObjectSchema;\n\n/** Author-facing input (documented for editor hover; the schema validates it at run time). */\nexport type PaletteOptions = PaletteOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedPaletteOptions = z.infer<typeof paletteObjectSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _paletteInputInSync: Exact<PaletteOptionsInput, z.input<typeof paletteObjectSchema>> = true;\nvoid _paletteInputInSync;\n","/**\n * Pure color math for the palette generator: hex parsing and conversions to RGB / OKLCH / HSL /\n * CMYK, a relative-luminance contrast pick (dark-vs-light text on a swatch), and per-field label\n * formatting. No I/O — fully unit-testable.\n */\n\nexport interface Rgb {\n r: number;\n g: number;\n b: number;\n}\n\n/** A field that can be shown on a swatch. */\nexport type FieldId = \"name\" | \"hex\" | \"rgb\" | \"oklch\" | \"hsl\" | \"cmyk\";\n\n/** Normalize `#rgb` / `rgb` / `#rrggbb` (any case) to canonical uppercase `#RRGGBB`. */\nexport function normalizeHex(hex: string): string {\n let h = hex.trim().replace(/^#/, \"\");\n if (/^[0-9a-fA-F]{3}$/.test(h)) {\n h = h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\");\n }\n if (!/^[0-9a-fA-F]{6}$/.test(h)) throw new Error(`Invalid hex color: \"${hex}\"`);\n return `#${h.toUpperCase()}`;\n}\n\nexport function hexToRgb(hex: string): Rgb {\n const h = normalizeHex(hex).slice(1);\n return {\n r: parseInt(h.slice(0, 2), 16),\n g: parseInt(h.slice(2, 4), 16),\n b: parseInt(h.slice(4, 6), 16),\n };\n}\n\nconst clamp = (n: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, n));\n\n/** sRGB channel (0..1) → linear-light. */\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n}\n\nexport interface Oklch {\n /** Lightness 0..1. */\n l: number;\n /** Chroma (~0..0.4). */\n c: number;\n /** Hue degrees 0..360. */\n h: number;\n}\n\n/**\n * sRGB (0..255) → OKLCH, via Björn Ottosson's OKLab matrices. Returns L in [0,1], C ≥ 0, H in\n * [0,360). Hue is undefined for greys (C≈0) — reported as 0.\n */\nexport function rgbToOklch({ r, g, b }: Rgb): Oklch {\n const lr = srgbToLinear(r / 255);\n const lg = srgbToLinear(g / 255);\n const lb = srgbToLinear(b / 255);\n\n const l_ = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);\n const m_ = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);\n const s_ = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);\n\n const L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_;\n const a = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_;\n const bb = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_;\n\n const C = Math.hypot(a, bb);\n let H = (Math.atan2(bb, a) * 180) / Math.PI;\n if (H < 0) H += 360;\n return { l: clamp(L, 0, 1), c: C, h: C < 1e-4 ? 0 : H };\n}\n\nexport interface Hsl {\n h: number;\n s: number;\n l: number;\n}\n\n/** sRGB (0..255) → HSL (h 0..360, s/l 0..100). */\nexport function rgbToHsl({ r, g, b }: Rgb): Hsl {\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const d = max - min;\n const l = (max + min) / 2;\n let h = 0;\n let s = 0;\n if (d !== 0) {\n s = d / (1 - Math.abs(2 * l - 1));\n switch (max) {\n case rn:\n h = ((gn - bn) / d) % 6;\n break;\n case gn:\n h = (bn - rn) / d + 2;\n break;\n default:\n h = (rn - gn) / d + 4;\n }\n h *= 60;\n if (h < 0) h += 360;\n }\n return { h: Math.round(h), s: Math.round(s * 100), l: Math.round(l * 100) };\n}\n\nexport interface Cmyk {\n c: number;\n m: number;\n y: number;\n k: number;\n}\n\n/** sRGB (0..255) → CMYK percentages (naive, profile-less). */\nexport function rgbToCmyk({ r, g, b }: Rgb): Cmyk {\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n const k = 1 - Math.max(rn, gn, bn);\n if (k >= 1 - 1e-9) return { c: 0, m: 0, y: 0, k: 100 };\n return {\n c: Math.round(((1 - rn - k) / (1 - k)) * 100),\n m: Math.round(((1 - gn - k) / (1 - k)) * 100),\n y: Math.round(((1 - bn - k) / (1 - k)) * 100),\n k: Math.round(k * 100),\n };\n}\n\n/** WCAG relative luminance (0..1) of an sRGB color. */\nfunction relativeLuminance({ r, g, b }: Rgb): number {\n return (\n 0.2126 * srgbToLinear(r / 255) +\n 0.7152 * srgbToLinear(g / 255) +\n 0.0722 * srgbToLinear(b / 255)\n );\n}\n\n/** Pick readable text on a swatch: the dark text on light backgrounds, light text on dark ones. */\nexport function pickTextColor(\n hex: string,\n opts: { light: string; dark: string; threshold?: number },\n): string {\n const lum = relativeLuminance(hexToRgb(hex));\n return lum > (opts.threshold ?? 0.5) ? opts.dark : opts.light;\n}\n\nexport interface FieldFormat {\n uppercaseName?: boolean;\n /** \"labeled\" → R:..,G:..,B:.. · \"css\" → rgb(.. .. ..) · \"plain\" → \".. .. ..\" */\n rgbStyle?: \"labeled\" | \"css\" | \"plain\";\n /** \"css\" → oklch(88% 0.012 250) · \"labeled\" → L:88% C:0.012 H:250 */\n oklchStyle?: \"css\" | \"labeled\";\n}\n\nconst round = (n: number, d = 0): number => {\n const p = 10 ** d;\n return Math.round(n * p) / p;\n};\n\n/** Render one field of a color as its display string. */\nexport function formatField(\n color: { name: string; hex: string },\n field: FieldId,\n fmt: FieldFormat = {},\n): string {\n const hex = normalizeHex(color.hex);\n const rgb = hexToRgb(hex);\n switch (field) {\n case \"name\":\n return fmt.uppercaseName ? color.name.toUpperCase() : color.name;\n case \"hex\":\n return hex;\n case \"rgb\": {\n const { r, g, b } = rgb;\n if (fmt.rgbStyle === \"css\") return `rgb(${r}, ${g}, ${b})`;\n if (fmt.rgbStyle === \"plain\") return `${r} ${g} ${b}`;\n return `R:${r},G:${g},B:${b}`;\n }\n case \"oklch\": {\n const { l, c, h } = rgbToOklch(rgb);\n const L = `${round(l * 100)}%`;\n const C = round(c, 3);\n const H = round(h, 1);\n return fmt.oklchStyle === \"labeled\" ? `L:${L} C:${C} H:${H}` : `oklch(${L} ${C} ${H})`;\n }\n case \"hsl\": {\n const { h, s, l } = rgbToHsl(rgb);\n return `H:${h},S:${s},L:${l}`;\n }\n case \"cmyk\": {\n const { c, m, y, k } = rgbToCmyk(rgb);\n return `C:${c},M:${m},Y:${y},K:${k}`;\n }\n }\n}\n","import { formatField, pickTextColor, type FieldId } from \"@/generators/palette/color\";\nimport type { ResolvedPaletteOptions } from \"@/generators/palette/options\";\n\nconst esc = (s: string): string =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n\ntype Corner = \"topLeft\" | \"topRight\" | \"bottomLeft\" | \"bottomRight\";\n\n/** One corner block of stacked field lines (empty string if no fields placed there). */\nfunction corner(\n o: ResolvedPaletteOptions,\n color: { name: string; hex: string },\n which: Corner,\n fields: FieldId[],\n): string {\n if (!fields.length) return \"\";\n const vert = which.startsWith(\"top\") ? \"top:0\" : \"bottom:0\";\n const horiz = which.endsWith(\"Left\") ? \"left:0;text-align:left\" : \"right:0;text-align:right\";\n const lines = fields\n .map(\n (f) =>\n `<div>${esc(\n formatField(color, f, {\n uppercaseName: o.uppercase,\n rgbStyle: o.rgbStyle,\n oklchStyle: o.oklchStyle,\n }),\n )}</div>`,\n )\n .join(\"\");\n return `<div class=\"cnr\" style=\"${vert};${horiz}\">${lines}</div>`;\n}\n\n/** Build a fully self-contained palette HTML document (inline CSS). Pure — unit-tested. */\nexport function buildPaletteHtml(o: ResolvedPaletteOptions, fontDataUrl?: string): string {\n const fontSize = o.fontSize ?? Math.round(o.width * 0.02);\n const pad = o.padding ?? Math.round(o.width * 0.025);\n const fontFace = fontDataUrl\n ? `@font-face{font-family:'PaletteFont';src:url(${fontDataUrl});font-weight:1 1000;font-display:block;}`\n : \"\";\n const family = fontDataUrl\n ? `'PaletteFont', \"Helvetica Neue\", Arial, sans-serif`\n : `\"Helvetica Neue\", Arial, \"Segoe UI\", system-ui, sans-serif`;\n\n // The container that arranges the swatches per layout.\n let container: string;\n if (o.layout === \"columns\") {\n container = `display:flex;flex-direction:row;gap:${o.gap}px`;\n } else if (o.layout === \"grid\") {\n container = `display:grid;grid-template-columns:repeat(${o.gridColumns},1fr);gap:${o.gap}px`;\n } else {\n container = `display:flex;flex-direction:column;gap:${o.gap}px`;\n }\n // rows/columns share their long axis equally; grid cells fill their track.\n const swatchFlex = o.layout === \"grid\" ? \"\" : \"flex:1 1 0;min-width:0;min-height:0;\";\n\n const swatches = o.colors\n .map((color) => {\n const text = pickTextColor(color.hex, {\n light: o.textLight,\n dark: o.textDark,\n threshold: o.contrastThreshold,\n });\n const corners =\n corner(o, color, \"topLeft\", o.topLeft) +\n corner(o, color, \"topRight\", o.topRight) +\n corner(o, color, \"bottomLeft\", o.bottomLeft) +\n corner(o, color, \"bottomRight\", o.bottomRight);\n return `<div class=\"sw\" style=\"background:${esc(color.hex)};color:${text};${swatchFlex}\">${corners}</div>`;\n })\n .join(\"\");\n\n return `<!doctype html><html><head><meta charset=\"utf-8\"><style>\n*{box-sizing:border-box;margin:0;padding:0}\n${fontFace}\nhtml,body{width:${o.width}px;height:${o.height}px}\nbody{background:${esc(o.background)};font-family:${family};font-weight:${o.fontWeight};\n font-size:${fontSize}px;line-height:1.12;letter-spacing:-0.01em}\n.wrap{width:${o.width}px;height:${o.height}px;${container}}\n.sw{position:relative;overflow:hidden;border-radius:${o.cornerRadius}px}\n.cnr{position:absolute;padding:${pad}px}\n.cnr>div{white-space:nowrap}\n</style></head><body><div class=\"wrap\">${swatches}</div></body></html>`;\n}\n","import { z } from \"zod\";\nimport { normalizeHex, type FieldId } from \"@/generators/palette/color\";\nimport type { PaletteColorInput } from \"@/generators/palette/options\";\n\n/** Field ids that can be revealed on a band when it expands (same set as the still palette). */\nconst fieldEnum = z.enum([\"name\", \"hex\", \"rgb\", \"oklch\", \"hsl\", \"cmyk\"]);\n\nconst hexString = z.string().refine(\n (s) => {\n try {\n normalizeHex(s);\n return true;\n } catch {\n return false;\n }\n },\n { message: \"must be a hex color like #D7DBDE\" },\n);\n\n/**\n * Author-facing options for the `palette-reel` generator — a looping reveal *video* of a color\n * palette (the moving counterpart of the still `palette` generator). The colors start as thin\n * slivers showing only their name; one at a time a sliver expands into a band that reveals its\n * configured `details` (hex / oklch / rgb …), holds, then collapses before the next opens — sweeping\n * every color and looping seamlessly. Only `colors` is required; everything else has a default.\n */\nexport interface PaletteReelOptionsInput {\n /** The colors to reveal (at least one). */\n colors: PaletteColorInput[];\n /** Sliver arrangement: horizontal bands (names upright) or full-height vertical strips. Default \"rows\". */\n orientation?: \"rows\" | \"columns\";\n /** Fields revealed when a color expands (the name is always shown, so it's ignored here). Default hex + oklch + rgb. */\n details?: FieldId[];\n\n // --- timing (seconds) ---\n /** How long each color stays fully open before handing off to the next. Default 2. */\n holdSeconds?: number;\n /** Crossfade length from one open color to the next. Default 0.7. */\n transitionSeconds?: number;\n /**\n * Ping-pong the sweep (down the list then back up) so every handoff is between neighbouring bands —\n * the open band only ever slides by one, avoiding the \"pinch\" of a last→first jump at the loop seam.\n * Off wraps directly (last→first): shorter, but crossfades non-adjacent bands at the seam. Default true.\n */\n bounce?: boolean;\n /** Easing applied to the crossfade ramp. Default \"ease-in-out\". */\n easing?: \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\";\n /** Clip length override (s). Omit to derive (count × (hold + transition)) for a clean loop. */\n durationSeconds?: number;\n\n // --- layout / sizing ---\n /** How many times a sliver's share a fully-open band takes (a collapsed sliver is the baseline). Default 12. */\n grownFlex?: number;\n /** Minimum cross-size of a sliver in px so its name stays legible. Default 0 (derive from height). */\n minCrossPx?: number;\n /** Keep the name fully visible even in a collapsed sliver (else it fades with the band). Default true. */\n nameAlwaysVisible?: boolean;\n\n // --- styling (carried from the still palette) ---\n /** Uppercase the color names. Default false. */\n uppercase?: boolean;\n /** RGB string style. Default \"labeled\". */\n rgbStyle?: \"labeled\" | \"css\" | \"plain\";\n /** OKLCH string style. Default \"css\". */\n oklchStyle?: \"css\" | \"labeled\";\n /** Light text color, used on dark bands (picked by contrast). Default \"#ffffff\". */\n textLight?: string;\n /** Dark text color, used on light bands (picked by contrast). Default \"#141414\". */\n textDark?: string;\n /** Luminance above which the dark text is used (0..1). Default 0.5. */\n contrastThreshold?: number;\n /** Custom font file (woff2/woff/ttf/otf), served into the render. Omit for a system bold sans. */\n fontFile?: string;\n /** Label font weight. Default 700. */\n fontWeight?: number;\n /** Name font size in px. Omit to derive from the frame size. */\n fontSize?: number;\n /** Detail-line font size as a fraction of the name size. Default 0.62. */\n detailFontScale?: number;\n /** Backdrop behind the bands (shown in `gap` between them). Default \"#ffffff\". */\n background?: string;\n /** Gap between bands (px). Default 0 (bands abut). */\n gap?: number;\n /** Band corner radius (px). Default 0 (square). */\n cornerRadius?: number;\n\n // --- output ---\n /** Output frame width in px. Default 1920. */\n width?: number;\n /** Output frame height in px. Default 1080. */\n height?: number;\n /** Render scale (higher = crisper capture, downscaled into the video). Default 1. */\n deviceScaleFactor?: number;\n /** Output frames per second. Default 30. */\n fps?: number;\n /** x264 quality, 0–51 (lower = better quality / larger file). Default 18. */\n crf?: number;\n /** Output filename; defaults to \"<slug(asset name)>.mp4\". */\n fileName?: string;\n}\n\nconst paletteReelObjectSchema = z\n .object({\n colors: z\n .array(\n z\n .object({\n name: z.string().min(1).describe(\"Display name shown on the color's sliver/band.\"),\n hex: hexString.describe(\"Color value as a hex string like #D7DBDE.\"),\n })\n .strict(),\n )\n .min(1)\n .describe(\"The colors to reveal (at least one).\"),\n orientation: z\n .enum([\"rows\", \"columns\"])\n .default(\"rows\")\n .describe(\n 'Sliver arrangement: horizontal bands (names upright) or full-height vertical strips. Default \"rows\".',\n ),\n details: z\n .array(fieldEnum)\n .default([\"hex\", \"oklch\", \"rgb\"])\n .describe(\n \"Fields revealed when a color expands (the name is always shown, so it's ignored here). Default hex + oklch + rgb.\",\n ),\n\n holdSeconds: z\n .number()\n .positive()\n .default(2)\n .describe(\"How long each color stays fully open before handing off to the next (s). Default 2.\"),\n transitionSeconds: z\n .number()\n .positive()\n .default(0.7)\n .describe(\"Crossfade length from one open color to the next (s). Default 0.7.\"),\n bounce: z\n .boolean()\n .default(true)\n .describe(\n \"Ping-pong the sweep so each handoff is between neighbouring bands; off wraps directly (last to first). Default true.\",\n ),\n easing: z\n .enum([\"linear\", \"ease-in\", \"ease-out\", \"ease-in-out\"])\n .default(\"ease-in-out\")\n .describe('Easing applied to the crossfade ramp. Default \"ease-in-out\".'),\n durationSeconds: z\n .number()\n .positive()\n .optional()\n .describe(\"Clip length override (s). Omit to derive (count x (hold + transition)) for a clean loop.\"),\n\n grownFlex: z\n .number()\n .min(1)\n .default(12)\n .describe(\n \"How many times a sliver's share a fully-open band takes (a collapsed sliver is the baseline). Default 12.\",\n ),\n minCrossPx: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Minimum cross-size of a sliver in px so its name stays legible. Default 0 (derive from height).\"),\n nameAlwaysVisible: z\n .boolean()\n .default(true)\n .describe(\n \"Keep the name fully visible even in a collapsed sliver (else it fades with the band). Default true.\",\n ),\n\n uppercase: z.boolean().default(false).describe(\"Uppercase the color names. Default false.\"),\n rgbStyle: z\n .enum([\"labeled\", \"css\", \"plain\"])\n .default(\"labeled\")\n .describe('RGB string style. Default \"labeled\".'),\n oklchStyle: z\n .enum([\"css\", \"labeled\"])\n .default(\"css\")\n .describe('OKLCH string style. Default \"css\".'),\n textLight: z\n .string()\n .default(\"#ffffff\")\n .describe('Light text color, used on dark bands (picked by contrast). Default \"#ffffff\".'),\n textDark: z\n .string()\n .default(\"#141414\")\n .describe('Dark text color, used on light bands (picked by contrast). Default \"#141414\".'),\n contrastThreshold: z\n .number()\n .min(0)\n .max(1)\n .default(0.5)\n .describe(\"Luminance above which the dark text is used (0..1). Default 0.5.\"),\n fontFile: z\n .string()\n .optional()\n .describe(\"Custom font file (woff2/woff/ttf/otf), served into the render. Omit for a system bold sans.\"),\n fontWeight: z\n .number()\n .int()\n .min(1)\n .max(1000)\n .default(700)\n .describe(\"Label font weight. Default 700.\"),\n fontSize: z\n .number()\n .positive()\n .optional()\n .describe(\"Name font size in px. Omit to derive from the frame size.\"),\n detailFontScale: z\n .number()\n .positive()\n .default(0.62)\n .describe(\"Detail-line font size as a fraction of the name size. Default 0.62.\"),\n background: z\n .string()\n .default(\"#ffffff\")\n .describe('Backdrop behind the bands (shown in `gap` between them). Default \"#ffffff\".'),\n gap: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Gap between bands (px). Default 0 (bands abut).\"),\n cornerRadius: z\n .number()\n .nonnegative()\n .default(0)\n .describe(\"Band corner radius (px). Default 0 (square).\"),\n\n width: z\n .number()\n .int()\n .positive()\n .default(1920)\n .describe(\"Output frame width in px. Default 1920.\"),\n height: z\n .number()\n .int()\n .positive()\n .default(1080)\n .describe(\"Output frame height in px. Default 1080.\"),\n deviceScaleFactor: z\n .number()\n .positive()\n .max(4)\n .default(1)\n .describe(\"Render scale (higher = crisper capture, downscaled into the video). Default 1.\"),\n fps: z\n .number()\n .int()\n .positive()\n .max(120)\n .default(30)\n .describe(\"Output frames per second. Default 30.\"),\n crf: z\n .number()\n .int()\n .min(0)\n .max(51)\n .default(18)\n .describe(\"x264 quality, 0-51 (lower = better quality / larger file). Default 18.\"),\n fileName: z\n .string()\n .optional()\n .describe('Output filename; defaults to \"<slug(asset name)>.mp4\".'),\n })\n .strict();\n\nexport const paletteReelOptionsSchema = paletteReelObjectSchema;\n\n/** Author-facing input (documented for editor hover; the schema validates it at run time). */\nexport type PaletteReelOptions = PaletteReelOptionsInput;\n/** Fully-resolved options after parsing. */\nexport type ResolvedPaletteReelOptions = z.infer<typeof paletteReelObjectSchema>;\n\n// Compile-time guard: the documented authoring type must stay in sync with the schema's input shape.\ntype Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : never) : never;\nconst _paletteReelInputInSync: Exact<\n PaletteReelOptionsInput,\n z.input<typeof paletteReelObjectSchema>\n> = true;\nvoid _paletteReelInputInSync;\n","import {\n paletteReelOptionsSchema,\n type ResolvedPaletteReelOptions,\n} from \"@/generators/palette-reel/options\";\nimport { formatField, normalizeHex, pickTextColor } from \"@/generators/palette/color\";\nimport { renderScene } from \"@/generators/scene\";\nimport type { ResolvedSceneOptions } from \"@/generators/scene/options\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const PALETTE_REEL_ID = \"palette-reel\";\n\n/**\n * Clip length derived from the timing knobs — must match the scene's `totalDuration(params)` exactly\n * so the last captured frame lands on the loop seam. Every color holds once and hands off once.\n * Mirrors palette-reel-timeline.ts.\n */\nfunction deriveDuration(o: ResolvedPaletteReelOptions): number {\n const n = o.colors.length;\n const stops = n <= 1 ? n : o.bounce ? 2 * (n - 1) : n;\n return stops * (o.holdSeconds + o.transitionSeconds);\n}\n\n/** Map the friendly palette-reel options onto the `palette-reel` scene and render it. */\nasync function run(\n ctx: PipelineContext,\n o: ResolvedPaletteReelOptions,\n): Promise<{ assets: AssetRecord[] }> {\n // Precompute every display string Node-side (the scene is a separate bundle that can't import the\n // color math): per color a swatch hex, its contrast text color, the name, and the detail lines.\n const fmt = { uppercaseName: o.uppercase, rgbStyle: o.rgbStyle, oklchStyle: o.oklchStyle };\n const fields = o.details.filter((f) => f !== \"name\"); // the name is always shown separately\n const items = o.colors.map((color) => {\n const hex = normalizeHex(color.hex);\n return {\n name: formatField(color, \"name\", fmt),\n hex,\n textColor: pickTextColor(hex, {\n light: o.textLight,\n dark: o.textDark,\n threshold: o.contrastThreshold,\n }),\n details: fields.map((f) => formatField(color, f, fmt)),\n };\n });\n\n const durationSeconds = o.durationSeconds ?? deriveDuration(o);\n\n const sceneOptions: ResolvedSceneOptions = {\n scene: PALETTE_REEL_ID,\n width: o.width,\n height: o.height,\n background: o.background,\n deviceScaleFactor: o.deviceScaleFactor,\n fps: o.fps,\n durationSeconds,\n // The reveal is a deterministic, closed-form function of time (it publishes __sceneSeek), so the\n // frame-stepper renders it frame-exact with a perfect loop seam — no realtime recording jitter.\n capture: \"frames\",\n frameFormat: \"jpeg\",\n crf: o.crf,\n fileName: o.fileName,\n // Only serve a font slot when one is configured — renderScene path.resolves every files entry.\n files: o.fontFile ? { font: o.fontFile } : {},\n sceneOptions: {\n items,\n orientation: o.orientation,\n holdSeconds: o.holdSeconds,\n transitionSeconds: o.transitionSeconds,\n bounce: o.bounce,\n easing: o.easing,\n grownFlex: o.grownFlex,\n minCrossPx: o.minCrossPx,\n nameAlwaysVisible: o.nameAlwaysVisible,\n fontWeight: o.fontWeight,\n fontSize: o.fontSize,\n detailFontScale: o.detailFontScale,\n gap: o.gap,\n cornerRadius: o.cornerRadius,\n },\n };\n return renderScene(ctx, sceneOptions, PALETTE_REEL_ID);\n}\n\nexport const paletteReelGenerator: Generator<ResolvedPaletteReelOptions> = {\n id: PALETTE_REEL_ID,\n optionsSchema: paletteReelOptionsSchema,\n // A custom font's content shapes the output — hash it into the cache key (edit font → regenerate).\n fileDependencies: (o) => (o.fontFile ? [o.fontFile] : []),\n run,\n};\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { copyFile, readFile, stat } from \"node:fs/promises\";\nimport { imageSize } from \"image-size\";\nimport { imageOptionsSchema, type ResolvedImageOptions } from \"@/generators/image/options\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { sha256File } from \"@/utils/hash\";\nimport { slugify } from \"@/utils/paths\";\nimport type { Generator, PipelineContext } from \"@/generators/types\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport const IMAGE_ID = \"image\";\n\n/** File extension → manifest `format` label. */\nconst EXT_FORMAT: Record<string, string> = {\n \".jpg\": \"jpg\",\n \".jpeg\": \"jpg\",\n \".png\": \"png\",\n \".webp\": \"webp\",\n \".avif\": \"avif\",\n \".gif\": \"gif\",\n};\n\n/**\n * Copy an existing image into the output dir and record it — a passthrough so real, full-resolution\n * assets (photos, exported graphics) can be used directly as scene inputs (e.g. crisp media-wall\n * tiles) instead of re-capturing them at a lower resolution.\n */\nasync function run(ctx: PipelineContext, o: ResolvedImageOptions): Promise<{ assets: AssetRecord[] }> {\n const abs = path.isAbsolute(o.src) ? o.src : path.resolve(process.cwd(), o.src);\n if (!existsSync(abs)) throw new Error(`image src not found: ${o.src} (resolved ${abs})`);\n\n const ext = path.extname(abs).toLowerCase();\n const format = EXT_FORMAT[ext] ?? ext.replace(\".\", \"\") ?? \"png\";\n const fileName = o.fileName ?? `${slugify(ctx.target.name)}${ext || \".png\"}`;\n const outPath = ctx.resolveOutPath(fileName);\n\n await ensureDir(path.dirname(outPath));\n await copyFile(abs, outPath);\n\n const [buf, stats, contentHash] = await Promise.all([\n readFile(outPath),\n stat(outPath),\n sha256File(outPath),\n ]);\n let width = 0;\n let height = 0;\n try {\n const d = imageSize(buf);\n width = d.width ?? 0;\n height = d.height ?? 0;\n } catch {\n ctx.logger.warn(`could not read dimensions for ${fileName}`);\n }\n\n const record: AssetRecord = {\n id: ctx.target.name,\n generator: IMAGE_ID,\n sourceUrl: `image:${path.basename(abs)}`,\n file: ctx.toManifestPath(outPath),\n format,\n width,\n height,\n bytes: stats.size,\n contentHash,\n createdAt: new Date().toISOString(),\n toolVersion: ctx.toolVersion,\n };\n await ctx.writeAsset(record);\n ctx.logger.success(`${ctx.target.name} → ${record.file}`);\n return { assets: [record] };\n}\n\nexport const imageGenerator: Generator<ResolvedImageOptions> = {\n id: IMAGE_ID,\n optionsSchema: imageOptionsSchema,\n // The source file's content shapes the output — hash it into the cache key (and fail early if missing).\n fileDependencies: (o) => [o.src],\n run,\n};\n","import { z } from \"zod\";\n\n/**\n * Author-facing options for the `image` generator — a passthrough that records an existing image\n * file as an asset so it can feed a scene (e.g. high-resolution photos as media-wall tiles) or be\n * tracked in the manifest. Only `src` is required.\n */\nexport interface ImageOptions {\n /** Path to the source image (relative to the cwd, or absolute). */\n src: string;\n /** Output filename; defaults to \"<slug(asset name)><ext of src>\". */\n fileName?: string;\n}\n\nexport const imageOptionsSchema = z\n .object({\n src: z.string().min(1).describe(\"Path to the source image (relative to the cwd, or absolute).\"),\n fileName: z\n .string()\n .optional()\n .describe('Output filename; defaults to \"<slug(asset name)><ext of src>\".'),\n })\n .strict();\n\nexport type ResolvedImageOptions = z.infer<typeof imageOptionsSchema>;\n","import type { Generator } from \"@/generators/types\";\nimport { scrollReelGenerator } from \"@/generators/scroll-reel\";\nimport { screenshotsGenerator } from \"@/generators/screenshots\";\nimport { wallGenerator } from \"@/generators/wall\";\nimport { specimenGenerator } from \"@/generators/specimen\";\nimport { paletteGenerator } from \"@/generators/palette\";\nimport { paletteReelGenerator } from \"@/generators/palette-reel\";\nimport { imageGenerator } from \"@/generators/image\";\n\nconst registry = new Map<string, Generator<unknown>>();\n\nfunction register<T>(gen: Generator<T>): void {\n registry.set(gen.id, gen as Generator<unknown>);\n}\n\nexport function getGenerator(id: string): Generator<unknown> | undefined {\n return registry.get(id);\n}\n\nexport function listGenerators(): Generator<unknown>[] {\n return [...registry.values()];\n}\n\nexport function generatorIds(): string[] {\n return [...registry.keys()];\n}\n\n// Register built-in generators. New asset types add a line here.\nregister(scrollReelGenerator);\nregister(screenshotsGenerator);\nregister(wallGenerator);\nregister(specimenGenerator);\nregister(paletteGenerator);\nregister(paletteReelGenerator);\nregister(imageGenerator);\n","import path from \"node:path\";\nimport { writeFile } from \"node:fs/promises\";\nimport { resolveCwd } from \"@/utils/paths\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { createLogger } from \"@/utils/logger\";\nimport { serializeConfigJsonSchema } from \"@/config/json-schema\";\n\nexport const DEFAULT_SCHEMA_FILE = \"pro-visu.schema.json\";\n\nexport interface SchemaOptions {\n cwd?: string;\n /** Output path, relative to cwd (default pro-visu.schema.json). */\n out?: string;\n}\n\n/**\n * Write a JSON Schema for `pro-visu.config.json` so editors give autocomplete + validation for a\n * dependency-free (JSON) config. Reference it from the config with `\"$schema\": \"./pro-visu.schema.json\"`.\n * Re-run after upgrading the tool to refresh the schema to the new version.\n */\nexport async function runSchema(options: SchemaOptions = {}): Promise<void> {\n const cwd = resolveCwd(options.cwd);\n const logger = createLogger(\"info\");\n const outPath = path.resolve(cwd, options.out ?? DEFAULT_SCHEMA_FILE);\n\n await ensureDir(path.dirname(outPath));\n await writeFile(outPath, serializeConfigJsonSchema(), \"utf8\");\n logger.success(`wrote ${path.relative(cwd, outPath) || DEFAULT_SCHEMA_FILE}`);\n logger.info('Reference it from your JSON config: \"$schema\": \"./pro-visu.schema.json\"');\n}\n","import path from \"node:path\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { loadConfig } from \"c12\";\nimport type { ZodError } from \"zod\";\nimport { showcaseConfigSchema, type ResolvedConfig } from \"@/config/schema\";\nimport { CONFIG_NAME } from \"@/config/defaults\";\n\nexport class ConfigNotFoundError extends Error {\n constructor(public readonly cwd: string) {\n super(\n `No pro-visu config found in ${cwd}.\\n` +\n \"Run `pro-visu init` to create one, or pass --config <path>.\",\n );\n this.name = \"ConfigNotFoundError\";\n }\n}\n\nexport class ConfigValidationError extends Error {\n constructor(\n public readonly zodError: ZodError,\n public readonly file?: string,\n ) {\n super(\"Invalid pro-visu config.\");\n this.name = \"ConfigValidationError\";\n }\n}\n\nexport interface LoadedConfig {\n config: ResolvedConfig;\n /** Absolute path of the discovered config file, if it came from a file. */\n configFile?: string;\n}\n\nexport interface LoadOptions {\n cwd: string;\n /** Explicit config path from --config. */\n configFile?: string;\n}\n\n/** rc files in the JS ecosystem (.prettierrc, .eslintrc) are JSON — treat ours the same. */\nconst RC_FILES = [\".pro-visurc.json\", \".pro-visurc\"];\n\n/**\n * Discover + load the repo config (pro-visu.config.{ts,js,mjs,cjs,json}, .pro-visurc[.json],\n * or a `pro-visu` key in package.json), then validate it with zod.\n */\nexport async function loadShowcaseConfig(opts: LoadOptions): Promise<LoadedConfig> {\n // 1. Explicit --config wins.\n if (opts.configFile) {\n const abs = path.resolve(opts.cwd, opts.configFile);\n if (!existsSync(abs)) throw new Error(`Config file not found: ${abs}`);\n if (abs.endsWith(\".json\") || RC_FILES.some((rc) => abs.endsWith(rc))) {\n return validate(readJson(abs), abs);\n }\n return loadViaC12({ ...opts, configFile: abs });\n }\n\n // 2. rc files (parsed as JSON), highest precedence among discovered files.\n const rc = findRcJson(opts.cwd);\n if (rc) return validate(rc.data, rc.file);\n\n // 3. c12 discovery: pro-visu.config.* and the package.json \"pro-visu\" key.\n return loadViaC12(opts);\n}\n\nasync function loadViaC12(opts: LoadOptions): Promise<LoadedConfig> {\n const result = await loadConfig<Record<string, unknown>>({\n cwd: opts.cwd,\n name: CONFIG_NAME,\n configFile: opts.configFile,\n rcFile: false,\n packageJson: true,\n globalRc: false,\n dotenv: false,\n });\n\n const raw = result.config ?? {};\n const hasContent = Object.keys(raw).length > 0;\n const hasFile = !!result.configFile && existsSync(result.configFile);\n const hasLayer = (result.layers ?? []).some(\n (layer) => layer.config && Object.keys(layer.config).length > 0,\n );\n\n if (!hasContent && !hasFile && !hasLayer) {\n throw new ConfigNotFoundError(opts.cwd);\n }\n return validate(raw, result.configFile);\n}\n\nfunction validate(raw: unknown, file?: string): LoadedConfig {\n const parsed = showcaseConfigSchema.safeParse(raw);\n if (!parsed.success) throw new ConfigValidationError(parsed.error, file);\n return { config: parsed.data, configFile: file };\n}\n\nfunction findRcJson(cwd: string): { data: unknown; file: string } | undefined {\n for (const name of RC_FILES) {\n const file = path.join(cwd, name);\n if (existsSync(file)) return { data: readJson(file), file };\n }\n return undefined;\n}\n\nfunction readJson(file: string): unknown {\n try {\n return JSON.parse(readFileSync(file, \"utf8\"));\n } catch {\n throw new Error(`Invalid JSON in ${path.basename(file)}`);\n }\n}\n","import type { ResolvedAssetSpec } from \"@/config/schema\";\n\n/** Resolve an author URL/route against the base (absolute inputs pass through unchanged). */\nfunction resolveAgainst(value: string, base: string): string {\n return new URL(value, base).toString();\n}\n\n/** Resolve any relative entries in a `routes` option (string or `{ url }` object forms). */\nfunction resolveRoutes(\n options: Record<string, unknown>,\n base: string,\n): Record<string, unknown> {\n const routes = options.routes;\n if (!Array.isArray(routes)) return options;\n const resolved = routes.map((route) => {\n if (typeof route === \"string\") return resolveAgainst(route, base);\n if (\n route &&\n typeof route === \"object\" &&\n typeof (route as { url?: unknown }).url === \"string\"\n ) {\n const r = route as Record<string, unknown> & { url: string };\n return { ...r, url: resolveAgainst(r.url, base) };\n }\n return route;\n });\n return { ...options, routes: resolved };\n}\n\n/**\n * With a managed server, its URL is the default base for capture targets: a url-based asset that\n * omits `url` captures the server root, and any relative `url` or `routes` entry is resolved\n * against it. Absolute URLs pass through unchanged. With no base (no managed server), assets are\n * returned unchanged — explicit absolute URLs are then required, as before. Pure (no mutation).\n */\nexport function resolveTargets(\n assets: ResolvedAssetSpec[],\n base: string | undefined,\n requiresUrl: (generatorId: string) => boolean,\n): ResolvedAssetSpec[] {\n if (!base) return assets;\n return assets.map((asset) => {\n const url =\n asset.url == null\n ? requiresUrl(asset.generator)\n ? base\n : asset.url\n : resolveAgainst(asset.url, base);\n return { ...asset, url, options: resolveRoutes(asset.options, base) };\n });\n}\n","import readline from \"node:readline\";\n\nexport interface InterruptWatch {\n /** Tear down listeners and restore the terminal. */\n dispose: () => void;\n /** Fire a stop request programmatically (1st → onStop, 2nd → onForce). The live dashboard,\n * which owns the keyboard itself, calls this from its own Esc/Ctrl+C handler. */\n trigger: () => void;\n}\n\n/**\n * Watch for a stop request — Esc or Ctrl+C on a TTY, plus SIGINT/SIGTERM. The first request calls\n * `onStop()` (graceful: finish in-flight, then tear down); a second calls `onForce()` (bail now).\n *\n * Set `keyboard: false` to watch only signals and leave the keyboard alone — used when the live\n * dashboard owns input (Ink raw mode): it calls the returned `trigger` so there's a single keypress\n * owner. (Enabling raw mode here ourselves would also suppress the terminal's Ctrl+C → SIGINT, which\n * is why on the keyboard path we detect Ctrl+C as a keypress.)\n */\nexport function watchForInterrupt(\n onStop: () => void,\n onForce: () => void,\n opts: { keyboard?: boolean } = {},\n): InterruptWatch {\n const keyboard = opts.keyboard ?? true;\n let count = 0;\n const trigger = (): void => {\n count += 1;\n if (count === 1) onStop();\n else onForce();\n };\n\n const stdin = process.stdin;\n const useKeys = keyboard && Boolean(stdin.isTTY);\n let onKey: ((str: string, key: readline.Key) => void) | undefined;\n\n if (useKeys) {\n readline.emitKeypressEvents(stdin);\n try {\n stdin.setRawMode(true);\n } catch {\n /* some pseudo-TTYs reject raw mode — fall back to signals only */\n }\n onKey = (_str, key) => {\n if (!key) return;\n if (key.name === \"escape\" || (key.ctrl && key.name === \"c\")) trigger();\n };\n stdin.on(\"keypress\", onKey);\n stdin.resume();\n }\n\n const onSignal = (): void => trigger();\n process.on(\"SIGINT\", onSignal);\n process.on(\"SIGTERM\", onSignal);\n\n const dispose = (): void => {\n process.off(\"SIGINT\", onSignal);\n process.off(\"SIGTERM\", onSignal);\n if (useKeys && onKey) {\n stdin.off(\"keypress\", onKey);\n try {\n stdin.setRawMode(false);\n } catch {\n /* ignore */\n }\n stdin.pause();\n }\n };\n\n return { dispose, trigger };\n}\n","import { spawn } from \"node:child_process\";\nimport v8 from \"node:v8\";\n\n/** Env flag set on the re-exec'd child so it doesn't try to re-exec again (infinite loop guard). */\nconst REEXEC_FLAG = \"SHOWCASE_MEM_REEXEC\";\n\n/** This process's V8 old-space (heap) limit, in MB. */\nexport function currentHeapLimitMB(): number {\n return Math.round(v8.getHeapStatistics().heap_size_limit / (1024 * 1024));\n}\n\n/**\n * Whether a re-exec is warranted: a target is set, we haven't already re-exec'd, and it's\n * meaningfully above the current limit (5% slack avoids a pointless re-exec for a near-match).\n * Pure — unit-tested.\n */\nexport function shouldReexec(\n maxMemoryMB: number | undefined,\n currentLimitMB: number,\n alreadyReexec: boolean,\n): boolean {\n if (!maxMemoryMB || maxMemoryMB <= 0) return false;\n if (alreadyReexec) return false;\n return currentLimitMB < maxMemoryMB * 0.95;\n}\n\n/**\n * Honor `settings.maxMemoryMB`: if it asks for more heap than this process has, re-exec the CLI with\n * `--max-old-space-size=<MB>` so a heavy run has room to finish instead of crashing with a V8 OOM.\n * Returns true if it re-exec'd — the child ran the whole command (stdio inherited), so the caller\n * should simply return. Signals are forwarded to the child so Esc/Ctrl+C still work.\n */\nexport async function reexecWithMemory(maxMemoryMB: number | undefined): Promise<boolean> {\n if (!shouldReexec(maxMemoryMB, currentHeapLimitMB(), process.env[REEXEC_FLAG] === \"1\")) {\n return false;\n }\n const args = [`--max-old-space-size=${maxMemoryMB}`, process.argv[1]!, ...process.argv.slice(2)];\n const child = spawn(process.execPath, args, {\n stdio: \"inherit\",\n env: { ...process.env, [REEXEC_FLAG]: \"1\" },\n });\n const forward = (sig: NodeJS.Signals): void => {\n try {\n child.kill(sig);\n } catch {\n /* already gone */\n }\n };\n process.on(\"SIGINT\", forward);\n process.on(\"SIGTERM\", forward);\n const code = await new Promise<number>((resolve) => {\n child.on(\"exit\", (c, signal) => resolve(c ?? (signal ? 1 : 0)));\n child.on(\"error\", () => resolve(1));\n });\n process.off(\"SIGINT\", forward);\n process.off(\"SIGTERM\", forward);\n process.exitCode = code;\n return true;\n}\n","import { resolveCwd, resolveOutDir } from \"@/utils/paths\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { createLogger, createReportingLogger, type Logger } from \"@/utils/logger\";\nimport { loadShowcaseConfig } from \"@/config/load\";\nimport type { ResolvedConfig } from \"@/config/schema\";\nimport { resolveTargets } from \"@/config/resolve-targets\";\nimport { getGenerator } from \"@/generators/registry\";\nimport { watchForInterrupt } from \"@/cli/interrupt\";\nimport { reexecWithMemory, currentHeapLimitMB } from \"@/cli/reexec\";\nimport v8 from \"node:v8\";\nimport { startRunState, updateRunState, clearRunState } from \"@/cli/run-state\";\nimport { ensureChromium } from \"@/browser-install/ensure-chromium\";\nimport { ensureFfmpeg } from \"@/media/ensure-ffmpeg\";\nimport {\n startManagedServer,\n resolveServerUrl,\n type ServerHandle,\n type ServerTasks,\n type TaskHandle,\n} from \"@/server/manage-server\";\nimport { runPipeline, applyDerivedInputs, type AssetOutcome } from \"@/pipeline/runner\";\nimport { expandSelection, dependenciesOf } from \"@/pipeline/graph\";\nimport { createReporter } from \"@/cli/dashboard\";\nimport type { Reporter } from \"@/pipeline/reporter\";\nimport { TOOL_VERSION } from \"@/version\";\nimport { reportConfigError, printSummary } from \"@/cli/ui\";\n\nexport interface GenerateOptions {\n cwd?: string;\n config?: string;\n asset?: string | string[];\n concurrency?: string | number;\n skipBrowser?: boolean;\n /** Skip the managed server (use an already-running site at the asset URLs). */\n skipServer?: boolean;\n /** Keep the managed server but skip its build step (fast iteration when the site is unchanged). */\n skipBuild?: boolean;\n /** Draft quality: faster, lower-fidelity renders for iteration. */\n draft?: boolean;\n /** Skip assets whose inputs+options+tool fingerprint is unchanged. */\n cache?: boolean;\n verbose?: boolean;\n}\n\nexport async function runGenerate(options: GenerateOptions = {}): Promise<void> {\n const cwd = resolveCwd(options.cwd);\n const bootstrapLog: Logger = createLogger(options.verbose ? \"debug\" : \"info\");\n\n let loaded;\n try {\n loaded = await loadShowcaseConfig({ cwd, configFile: options.config });\n } catch (err) {\n reportConfigError(bootstrapLog, err);\n process.exitCode = 1;\n return;\n }\n const { config } = loaded;\n\n // Honor settings.maxMemoryMB: if it wants more heap than this process has, re-exec with a larger\n // --max-old-space-size and let the child run the whole command. Must happen before any heavy work.\n if (await reexecWithMemory(config.settings.maxMemoryMB)) return;\n if (config.settings.maxMemoryMB) {\n bootstrapLog.info(`Node heap limit: ${currentHeapLimitMB()} MB (settings.maxMemoryMB=${config.settings.maxMemoryMB})`);\n }\n\n const level = options.verbose ? \"debug\" : config.settings.logLevel;\n // Live job tracker on an interactive TTY; per-asset logs feed each row's current step.\n const reporter = createReporter({ tty: Boolean(process.stdout.isTTY), verbose: !!options.verbose });\n const logger = reporter.isLive ? createReportingLogger(level, reporter) : createLogger(level);\n const outDir = resolveOutDir(cwd, config.settings.outDir);\n\n const requested = normalizeAssetNames(options.asset);\n if (requested) {\n const known = new Set(config.assets.map((a) => a.name));\n const unknown = requested.filter((name) => !known.has(name));\n if (unknown.length) logger.warn(`Unknown asset(s): ${unknown.join(\", \")}`);\n if (requested.every((name) => !known.has(name))) {\n logger.error(\"No matching assets to generate.\");\n process.exitCode = 1;\n return;\n }\n }\n\n // Ensure a managed Chromium unless a system channel/executable is configured, or skipped.\n const usingManaged =\n !config.settings.browser.channel && !config.settings.browser.executablePath;\n if (!options.skipBrowser && usingManaged) {\n const ready = await ensureChromium({ logger });\n if (!ready) {\n logger.error(\"Chromium is required. Run `pro-visu init` or install it manually.\");\n process.exitCode = 1;\n return;\n }\n }\n\n // Self-heal ffmpeg: the video generators shell out to it, and the bundled binary may be\n // missing/corrupt when the consumer's package manager skipped build scripts.\n if (!(await ensureFfmpeg({ logger }))) {\n logger.error(\"A working ffmpeg is required for video generators.\");\n process.exitCode = 1;\n return;\n }\n\n // Track this run on disk so `pro-visu reset` can clean up if it's killed hard, and let Esc /\n // Ctrl+C stop it gracefully (a second press bails immediately; reset mops up any orphans).\n await ensureDir(outDir);\n await startRunState(outDir);\n const abort = new AbortController();\n let interrupted = false;\n let lowMemory = false;\n // The live dashboard owns the keyboard (Ink raw mode), so the watcher only handles signals there\n // and the dashboard calls `trigger` on Esc/Ctrl+C — one keypress owner, no clash.\n const { dispose: disposeInterrupt, trigger: interruptTrigger } = watchForInterrupt(\n () => {\n interrupted = true;\n abort.abort(); // graceful: stop launching new work, let in-flight finish, then tear down\n // Acknowledge the keypress immediately: the live tracker flips to a \"cancelling…\" banner;\n // without it (non-TTY/--verbose) print a line, since in-flight renders can take seconds.\n if (reporter.isLive) reporter.cancelling();\n else logger.warn(\"Cancelling — finishing in-flight work… (press again to force-quit)\");\n },\n () => {\n try {\n process.stdin.setRawMode?.(false); // restore the terminal before bailing\n process.stdout.write(\"\\x1b[?25h\"); // and the cursor (force-quit skips Ink's own cleanup)\n } catch {\n /* ignore */\n }\n process.exit(130);\n },\n { keyboard: !reporter.isLive },\n );\n // Hand the cancel trigger to the live dashboard so its useInput handler drives the same flow.\n if (reporter.isLive) reporter.attachInput?.(interruptTrigger);\n\n // From here the live tracker owns the terminal. Plan ALL rows up front — setup (build/server)\n // then every asset, with the assets gated on setup so they read \"waiting for build\" until it\n // finishes. The build no longer dumps raw CLI output; it feeds the \"build\" row's step. The\n // tracker's footer shows the \"esc to cancel\" hint.\n reporter.begin();\n\n // Memory watchdog: if the Node heap nears its limit, stop the run gracefully (like Esc) with a clear\n // message instead of letting V8 hard-crash (\"JavaScript heap out of memory\"). Fires once; raise\n // settings.maxMemoryMB (more heap) or lower settings.concurrency to avoid it.\n const memTimer = setInterval(() => {\n if (interrupted) return;\n const limit = v8.getHeapStatistics().heap_size_limit;\n const used = process.memoryUsage().heapUsed;\n if (limit > 0 && used / limit > 0.88) {\n lowMemory = true;\n interrupted = true;\n abort.abort();\n if (reporter.isLive) reporter.cancelling(\"low memory — stopping…\");\n else\n logger.warn(\n \"Low memory — stopping early to avoid a crash (raise settings.maxMemoryMB or lower concurrency).\",\n );\n }\n }, 1500);\n memTimer.unref?.();\n\n // The managed server only matters to url-based generators (it captures web pages). If nothing in\n // the selection needs a URL — e.g. only a `test`-mode wall, or other local generators — skip it\n // automatically so previews don't pay for a site build/boot they never use. Derive option-declared\n // dependencies first (e.g. a real wall's tile producers) so the selection — and this check — see\n // them; a `test`-mode wall declares none, so it collapses to just itself.\n applyDerivedInputs(config);\n const selected = expandSelection(config.assets, requested);\n const anyNeedsServer = selected.some((s) => Boolean(getGenerator(s.generator)?.requiresUrl));\n if (config.settings.server && !options.skipServer && !anyNeedsServer) {\n logger.info(\"No selected asset needs a URL — skipping the managed server.\");\n }\n\n const baseServerCfg = options.skipServer || !anyNeedsServer ? undefined : config.settings.server;\n // --skip-build keeps the managed server but drops its one-shot build (the site is unchanged).\n const serverCfg =\n baseServerCfg && options.skipBuild ? { ...baseServerCfg, build: undefined } : baseServerCfg;\n\n // The managed server's URL is the default base: a url-based asset that omits `url` captures its\n // root, and relative `url`/`routes` entries resolve against it. (No server → assets as authored.)\n const serverBase = serverCfg ? resolveServerUrl(serverCfg) : undefined;\n const resolvedConfig: ResolvedConfig = {\n ...config,\n assets: resolveTargets(config.assets, serverBase, (id) =>\n Boolean(getGenerator(id)?.requiresUrl),\n ),\n };\n\n const gates: string[] = [];\n const tasks: ServerTasks = {};\n if (reporter.isLive && serverCfg) {\n if (serverCfg.build) {\n reporter.add({ id: \"@build\", name: \"build\", detail: \"server\", system: true });\n gates.push(\"@build\");\n tasks.build = taskHandle(reporter, \"@build\");\n }\n reporter.add({ id: \"@server\", name: \"server\", detail: \"server\", system: true });\n gates.push(\"@server\");\n tasks.server = taskHandle(reporter, \"@server\");\n }\n if (reporter.isLive) {\n for (const spec of selected) {\n reporter.add({\n id: spec.name,\n name: spec.name,\n detail: spec.generator,\n deps: dependenciesOf(spec),\n gatedBy: gates,\n });\n }\n }\n\n let server: ServerHandle | null = null;\n let outcomes: AssetOutcome[] = [];\n let setupFailed = false;\n try {\n if (serverCfg) {\n server = await startManagedServer(serverCfg, cwd, logger, tasks, abort.signal);\n await updateRunState(outDir, { serverPid: server?.pid });\n }\n\n const concurrency =\n options.concurrency != null ? Number(options.concurrency) : undefined;\n const count = requested ? requested.length : config.assets.length;\n if (!reporter.isLive) logger.start(`Generating ${count} asset(s)…`);\n\n outcomes = await runPipeline({\n config: resolvedConfig,\n outDir,\n logger,\n toolVersion: TOOL_VERSION,\n assetNames: requested,\n concurrency: Number.isFinite(concurrency) ? concurrency : undefined,\n quality: options.draft ? \"draft\" : undefined,\n cache: options.cache,\n reporter,\n signal: abort.signal,\n onResources: (r) => void updateRunState(outDir, { tmpDirs: [r.tmpDir] }),\n });\n } catch (err) {\n reporter.stop();\n if (!interrupted) {\n logger.error((err as Error).message);\n process.exitCode = 1;\n setupFailed = true;\n }\n } finally {\n clearInterval(memTimer);\n reporter.stop(); // erase the live block before the final summary\n disposeInterrupt();\n await server?.stop();\n await clearRunState(outDir);\n }\n\n if (interrupted) {\n // A graceful stop is a clean exit, not a failure — exit 0 so the shell/pnpm doesn't print a\n // scary \"command failed\" wrapper. In-flight assets are aborted mid-work, so only the ones that\n // actually completed are \"finished\".\n const finished = outcomes.filter((o) => o.status === \"ok\").length;\n if (lowMemory) {\n logger.warn(\n `Stopped early — low memory (${finished} asset(s) finished). Raise settings.maxMemoryMB or ` +\n `lower settings.concurrency, then re-run (already-finished assets are cached if --cache is on).`,\n );\n } else {\n logger.warn(`Interrupted — stopped cleanly (${finished} asset(s) finished).`);\n }\n process.exitCode = 0;\n return;\n }\n if (setupFailed) return; // error already reported; no summary to show\n\n printSummary(logger, outcomes, outDir);\n if (outcomes.some((outcome) => outcome.status === \"failed\")) {\n process.exitCode = 1;\n }\n}\n\n/** A live-tracker row handle the managed-server lifecycle drives (build/server steps). */\nfunction taskHandle(reporter: Reporter, id: string): TaskHandle {\n return {\n start: () => reporter.status(id, \"running\"),\n step: (t) => reporter.step(id, t),\n ok: () => reporter.status(id, \"ok\"),\n fail: () => reporter.status(id, \"failed\"),\n };\n}\n\nfunction normalizeAssetNames(value?: string | string[]): string[] | undefined {\n if (value == null) return undefined;\n const arr = (Array.isArray(value) ? value : [value]).map(String).filter(Boolean);\n return arr.length ? arr : undefined;\n}\n","import path from \"node:path\";\nimport { readFile, writeFile, rm } from \"node:fs/promises\";\nimport { spawn } from \"node:child_process\";\n\n/**\n * A small record of what a `pro-visu generate` run spawned, written to `<outDir>/.pro-visu-run.json`\n * and deleted on a clean exit. If the run is killed hard (Ctrl+C / crash), the file survives so\n * `pro-visu reset` can find and tear down the orphaned server + browser + temp dirs.\n */\nexport interface RunState {\n /** The `pro-visu generate` process id (used to tell \"still running\" from \"orphaned\"). */\n pid: number;\n startedAt: string;\n /** Root of the managed server's process tree (the shell we spawned) — the main orphan risk\n * (Playwright's browser is tied to this process's lifetime and exits on its own). */\n serverPid?: number;\n /** Temp working dirs to remove. */\n tmpDirs?: string[];\n}\n\nfunction runStatePath(outDir: string): string {\n return path.join(outDir, \".pro-visu-run.json\");\n}\n\nexport async function readRunState(outDir: string): Promise<RunState | null> {\n try {\n return JSON.parse(await readFile(runStatePath(outDir), \"utf8\")) as RunState;\n } catch {\n return null;\n }\n}\n\nasync function write(outDir: string, state: RunState): Promise<void> {\n try {\n await writeFile(runStatePath(outDir), `${JSON.stringify(state, null, 2)}\\n`);\n } catch {\n /* best-effort — tracking is a convenience, never fail the run over it */\n }\n}\n\nexport async function startRunState(outDir: string): Promise<void> {\n await write(outDir, { pid: process.pid, startedAt: new Date().toISOString(), tmpDirs: [] });\n}\n\n/** Merge a patch into the on-disk state (tmpDirs are appended, deduped). */\nexport async function updateRunState(outDir: string, patch: Partial<RunState>): Promise<void> {\n const cur = (await readRunState(outDir)) ?? {\n pid: process.pid,\n startedAt: new Date().toISOString(),\n };\n const tmpDirs = [...new Set([...(cur.tmpDirs ?? []), ...(patch.tmpDirs ?? [])])];\n await write(outDir, { ...cur, ...patch, tmpDirs });\n}\n\nexport async function clearRunState(outDir: string): Promise<void> {\n try {\n await rm(runStatePath(outDir), { force: true });\n } catch {\n /* already gone */\n }\n}\n\n/** Is a pid still running? */\nexport function isAlive(pid: number): boolean {\n if (!pid) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === \"EPERM\"; // exists but not ours to signal\n }\n}\n\n/** Kill a process and its descendants, cross-platform. Resolves to whether it was alive to kill. */\nexport function killTreeByPid(pid?: number): Promise<boolean> {\n return new Promise((resolve) => {\n if (!pid || !isAlive(pid)) return resolve(false);\n if (process.platform === \"win32\") {\n const killer = spawn(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], { stdio: \"ignore\" });\n killer.on(\"error\", () => resolve(false));\n killer.on(\"close\", () => resolve(true));\n return;\n }\n try {\n process.kill(-pid, \"SIGKILL\"); // detached children are group leaders → kills the tree\n } catch {\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n /* already gone */\n }\n }\n resolve(true);\n });\n}\n","import path from \"node:path\";\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport type { ResolvedServerSettings } from \"@/config/schema\";\nimport type { Logger } from \"@/utils/logger\";\n\n/** A started server we own and must tear down (null when we reused an existing one). */\nexport interface ServerHandle {\n stop: () => Promise<void>;\n /** Process id of the spawned server tree's root (for orphan cleanup after a hard kill). */\n pid?: number;\n}\n\nconst delay = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/** Resolve the readiness URL from explicit url or port. */\nexport function resolveServerUrl(server: ResolvedServerSettings): string {\n return server.url ?? `http://127.0.0.1:${server.port}`;\n}\n\n/** Single GET; resolves true if the server answered at all (any status code). */\nfunction probe(url: string): Promise<boolean> {\n return new Promise((resolve) => {\n const lib = url.startsWith(\"https:\") ? https : http;\n const req = lib.get(url, (res) => {\n res.resume();\n resolve((res.statusCode ?? 0) > 0);\n });\n req.on(\"error\", () => resolve(false));\n req.setTimeout(2000, () => {\n req.destroy();\n resolve(false);\n });\n });\n}\n\nasync function waitForUrl(url: string, timeoutMs: number, signal?: AbortSignal): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (signal?.aborted) return false;\n if (await probe(url)) return true;\n await delay(500);\n }\n return false;\n}\n\n/** A row in the live tracker the server lifecycle can drive (build / server steps). */\nexport interface TaskHandle {\n /** Mark the row active (spinner + ticking elapsed). */\n start(): void;\n step(text: string): void;\n ok(): void;\n fail(): void;\n}\n\nexport interface ServerTasks {\n build?: TaskHandle;\n server?: TaskHandle;\n}\n\n/**\n * Run a one-shot command (e.g. a build) to completion, rejecting on non-zero exit. With a\n * `task`, output is captured and the latest line feeds the tracker row (instead of streaming\n * raw to the terminal); the captured tail is included in the error on failure.\n */\nfunction runOnce(\n command: string,\n cwd: string,\n task?: TaskHandle,\n signal?: AbortSignal,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(new Error(\"Aborted.\"));\n const tracked = Boolean(task);\n const child = spawn(command, {\n cwd,\n shell: true,\n stdio: tracked ? [\"ignore\", \"pipe\", \"pipe\"] : \"inherit\",\n });\n let tail = \"\";\n const onData = (b: Buffer): void => {\n const text = b.toString();\n tail = (tail + text).slice(-4000);\n const last = text\n .split(/\\r?\\n/)\n .map((l) => l.trim())\n .filter(Boolean)\n .at(-1);\n if (last) task?.step(last);\n };\n if (tracked) {\n child.stdout?.on(\"data\", onData);\n child.stderr?.on(\"data\", onData);\n }\n const onAbort = (): void => {\n void killTree(child); // stop the build (and its tree) on a graceful interrupt\n reject(new Error(\"Aborted.\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n const settle = (fn: () => void): void => {\n signal?.removeEventListener(\"abort\", onAbort);\n fn();\n };\n child.on(\"error\", (err) => settle(() => reject(err)));\n child.on(\"close\", (code) =>\n settle(() =>\n code === 0\n ? resolve()\n : reject(\n new Error(\n `\"${command}\" failed (exit ${code}).${tracked ? `\\n${tail.slice(-1500)}` : \"\"}`,\n ),\n ),\n ),\n );\n });\n}\n\n/** Kill a process and its whole tree, cross-platform. */\nfunction killTree(child: ChildProcess): Promise<void> {\n return new Promise((resolve) => {\n if (child.pid == null || child.exitCode != null) return resolve();\n if (process.platform === \"win32\") {\n // The shell-spawned server has child processes (next → node); /T kills the tree.\n const killer = spawn(\"taskkill\", [\"/pid\", String(child.pid), \"/T\", \"/F\"], {\n stdio: \"ignore\",\n });\n killer.on(\"error\", () => resolve());\n killer.on(\"close\", () => resolve());\n } else {\n try {\n process.kill(-child.pid, \"SIGTERM\"); // negative pid = the process group\n } catch {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* already gone */\n }\n }\n resolve();\n }\n });\n}\n\n/**\n * Ensure a server is reachable for the capture. If one is already up (and reuseExisting),\n * returns null — we leave it alone. Otherwise optionally builds, starts the server, waits for\n * readiness, and returns a handle whose stop() tears it (and its children) down.\n */\nexport async function startManagedServer(\n server: ResolvedServerSettings,\n baseCwd: string,\n logger: Logger,\n tasks: ServerTasks = {},\n signal?: AbortSignal,\n): Promise<ServerHandle | null> {\n const cwd = server.cwd ? path.resolve(baseCwd, server.cwd) : baseCwd;\n const url = resolveServerUrl(server);\n\n // In live mode the rows convey progress; avoid untagged logs that would corrupt the block.\n const live = Boolean(tasks.build || tasks.server);\n\n if (server.reuseExisting && (await probe(url))) {\n if (live) {\n tasks.build?.step(\"reused\");\n tasks.build?.ok();\n tasks.server?.step(`reusing existing server at ${url}`);\n tasks.server?.ok();\n } else {\n logger.info(`Reusing the server already running at ${url}`);\n }\n return null;\n }\n\n if (server.build) {\n if (live) {\n tasks.build?.start();\n tasks.build?.step(`building (${server.build})…`);\n } else {\n logger.info(`Building: ${server.build}`);\n }\n try {\n await runOnce(server.build, cwd, tasks.build, signal);\n } catch (err) {\n tasks.build?.fail();\n throw err;\n }\n tasks.build?.ok();\n }\n\n if (live) {\n tasks.server?.start();\n tasks.server?.step(`starting (${server.command})…`);\n } else {\n logger.info(`Starting server: ${server.command}`);\n }\n // Pass the readiness port/host to the command as PORT/HOST so frameworks that honor them\n // (Next, Vite, …) bind exactly the port we probe — no need to repeat it in the command. An\n // explicit flag in the command (e.g. `next start -p 4000`) still wins.\n const probed = new URL(url);\n const child = spawn(server.command, {\n cwd,\n shell: true,\n stdio: \"ignore\",\n env: {\n ...process.env,\n PORT: probed.port || (probed.protocol === \"https:\" ? \"443\" : \"80\"),\n HOST: probed.hostname,\n },\n // POSIX: own process group so we can signal the whole tree. Windows uses taskkill /T.\n detached: process.platform !== \"win32\",\n });\n\n let exited = false;\n child.on(\"exit\", () => {\n exited = true;\n });\n\n tasks.server?.step(`waiting for ${url}…`);\n const ready = await waitForUrl(url, server.readyTimeoutMs, signal);\n if (!ready) {\n tasks.server?.fail();\n await killTree(child);\n throw new Error(\n signal?.aborted\n ? \"Aborted.\"\n : exited\n ? `Server command exited before ${url} became reachable.`\n : `Server did not become reachable at ${url} within ${server.readyTimeoutMs}ms.`,\n );\n }\n tasks.server?.step(`ready at ${url}`);\n tasks.server?.ok();\n if (!live) logger.success(`Server ready at ${url}`); // in live mode the row conveys this\n\n return {\n pid: child.pid,\n stop: async () => {\n logger.info(\"Shutting down server…\");\n await killTree(child);\n },\n };\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { mkdtemp } from \"node:fs/promises\";\nimport { launchBrowser } from \"@/pipeline/browser\";\nimport { createContext } from \"@/pipeline/context\";\nimport { buildGraph, dependenciesOf, expandSelection } from \"@/pipeline/graph\";\nimport { computeCacheKey } from \"@/pipeline/cache\";\nimport type { Reporter } from \"@/pipeline/reporter\";\nimport { getGenerator } from \"@/generators/registry\";\nimport { ManifestStore } from \"@/manifest/manifest\";\nimport { ensureDir, removeDir } from \"@/utils/fs\";\nimport { sha256File } from \"@/utils/hash\";\nimport type { ResolvedAssetSpec, ResolvedConfig } from \"@/config/schema\";\nimport type { Logger } from \"@/utils/logger\";\nimport type { AssetRecord } from \"@/manifest/schema\";\n\nexport interface AssetOutcome {\n name: string;\n generator: string;\n status: \"ok\" | \"failed\";\n records: AssetRecord[];\n error?: Error;\n /** True when the asset was served from cache (skipped, unchanged). */\n cached?: boolean;\n /** True when the asset was aborted in-flight by a cancel (not a real failure). */\n cancelled?: boolean;\n}\n\nexport interface RunOptions {\n config: ResolvedConfig;\n /** Absolute output dir. */\n outDir: string;\n logger: Logger;\n toolVersion: string;\n /** Restrict to these asset names (undefined/empty = all). */\n assetNames?: string[];\n /** Override settings.concurrency. */\n concurrency?: number;\n /** Override settings.quality. */\n quality?: \"draft\" | \"final\";\n /** Override settings.cache (skip unchanged assets). */\n cache?: boolean;\n /** Live progress sink (job tracker). Optional. */\n reporter?: Reporter;\n /** Aborts the run gracefully: stop launching new assets and let in-flight ones finish. */\n signal?: AbortSignal;\n /** Reports the run's temp working dir so the caller can track it for cleanup after a hard kill. */\n onResources?: (info: { tmpDir: string }) => void;\n}\n\n/**\n * Draft trades fidelity for iteration speed: fewer frames, no retina scale, looser quality.\n * Applied to the common video-option names shared across generators before validation.\n */\nexport function applyQuality(\n options: Record<string, unknown>,\n quality: \"draft\" | \"final\",\n): Record<string, unknown> {\n if (quality !== \"draft\") return options;\n const o = { ...options };\n if (typeof o.fps === \"number\") o.fps = Math.min(o.fps as number, 15);\n if (typeof o.deviceScaleFactor === \"number\") o.deviceScaleFactor = 1;\n if (typeof o.crf === \"number\") o.crf = Math.max(o.crf as number, 30);\n return o;\n}\n\n/**\n * Generate each selected asset, isolating failures so one bad asset never aborts the run.\n * Assets form a dependency DAG (via `inputs`): a producer runs before its consumers, and its\n * output file is exposed to them. Independent assets run up to `concurrency` at a time; one\n * shared browser + one manifest store for the whole run.\n */\nexport async function runPipeline(opts: RunOptions): Promise<AssetOutcome[]> {\n applyDerivedInputs(opts.config); // generators that declare deps via options (e.g. wall columns)\n buildGraph(opts.config.assets); // validate refs + reject cycles up front\n const specs = expandSelection(opts.config.assets, opts.assetNames);\n if (specs.length === 0) return [];\n\n await ensureDir(opts.outDir);\n const manifest = await ManifestStore.load(opts.outDir);\n const tmpRoot = await mkdtemp(path.join(os.tmpdir(), \"pro-visu-\"));\n const browser = await launchBrowser(opts.config.settings.browser);\n opts.onResources?.({ tmpDir: tmpRoot });\n const concurrency = Math.max(1, opts.concurrency ?? opts.config.settings.concurrency);\n const quality = opts.quality ?? opts.config.settings.quality;\n const cacheEnabled = opts.cache ?? opts.config.settings.cache;\n const reporter = opts.reporter;\n for (const s of specs) {\n reporter?.add({ id: s.name, name: s.name, detail: s.generator, deps: dependenciesOf(s) });\n }\n\n const outcomes = new Map<string, AssetOutcome>();\n /** Primary output (absolute path) per completed asset, for consumers' `inputs`. */\n const primaryOutput = new Map<string, string>();\n /** Primary output contentHash per completed asset, for consumers' cache keys. */\n const primaryHash = new Map<string, string>();\n\n const recordDone = (name: string, record: AssetRecord | undefined): void => {\n if (!record) return;\n primaryOutput.set(name, path.resolve(opts.outDir, record.file));\n primaryHash.set(name, record.contentHash);\n };\n\n const runSpec = async (spec: ResolvedAssetSpec): Promise<AssetOutcome> => {\n const log = opts.logger.withTag(spec.name);\n try {\n const generator = getGenerator(spec.generator);\n if (!generator) throw new Error(`Unknown generator \"${spec.generator}\".`);\n\n const resolvedInputs: Record<string, string> = {};\n const inputHashes: Record<string, string> = {};\n for (const [slot, dep] of Object.entries(spec.inputs)) {\n const file = primaryOutput.get(dep);\n if (!file) throw new Error(`Input \"${slot}\" (asset \"${dep}\") produced no file.`);\n resolvedInputs[slot] = file;\n inputHashes[slot] = primaryHash.get(dep) ?? \"\";\n }\n\n const merged = applyQuality(\n mergeGeneratorOptions(opts.config.settings.defaults, spec),\n quality,\n );\n const options = generator.optionsSchema.parse(merged);\n\n // Hash the content of declared file dependencies (e.g. fonts) into the cache key, so\n // editing the file regenerates the asset. Missing files fail here, early and clearly,\n // instead of producing a blank render from a 404'd URL later.\n let fileHashes: Record<string, string> | undefined;\n for (const dep of generator.fileDependencies?.(options) ?? []) {\n const resolved = path.isAbsolute(dep) ? dep : path.resolve(process.cwd(), dep);\n try {\n (fileHashes ??= {})[resolved] = await sha256File(resolved);\n } catch {\n throw new Error(`File dependency not found: ${dep}`);\n }\n }\n\n const cacheKey = computeCacheKey({\n generator: spec.generator,\n url: spec.url,\n options,\n inputs: inputHashes,\n files: fileHashes,\n quality,\n toolVersion: opts.toolVersion,\n });\n\n if (cacheEnabled) {\n const existing = manifest.find(spec.name);\n if (\n existing?.cacheKey === cacheKey &&\n existsSync(path.resolve(opts.outDir, existing.file))\n ) {\n log.info(\"cached — unchanged, skipped\");\n reporter?.status(spec.name, \"cached\");\n recordDone(spec.name, existing);\n return {\n name: spec.name,\n generator: spec.generator,\n status: \"ok\",\n records: [existing],\n cached: true,\n };\n }\n }\n\n reporter?.status(spec.name, \"running\");\n\n const ctx = await createContext({\n browser,\n generatorId: generator.id,\n target: { name: spec.name, url: spec.url },\n resolvedInputs,\n outDir: opts.outDir,\n tmpDir: tmpRoot,\n logger: log,\n toolVersion: opts.toolVersion,\n quality,\n manifest,\n onProgress: reporter ? (v) => reporter.progress(spec.name, v) : undefined,\n signal: opts.signal,\n });\n\n const result = await generator.run(ctx, options);\n // Stamp the cache key onto produced records (primary first) so reruns can skip.\n for (const record of result.assets) {\n record.cacheKey = cacheKey;\n await manifest.upsert(record);\n }\n recordDone(spec.name, result.assets[0]);\n reporter?.status(spec.name, \"ok\");\n return { name: spec.name, generator: spec.generator, status: \"ok\", records: result.assets };\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n // A cancelled run aborts in-flight work mid-flight: that's an expected stop, not a failure —\n // don't log a scary error or flag the row red; mark it cancelled and move on.\n if (opts.signal?.aborted) {\n return { name: spec.name, generator: spec.generator, status: \"failed\", records: [], error: err, cancelled: true };\n }\n log.error(err.message);\n reporter?.status(spec.name, \"failed\");\n return { name: spec.name, generator: spec.generator, status: \"failed\", records: [], error: err };\n }\n };\n\n try {\n await scheduleDag(specs, concurrency, outcomes, runSpec, reporter, opts.signal);\n return specs.map((s) => outcomes.get(s.name)).filter((o): o is AssetOutcome => Boolean(o));\n } finally {\n // The caller owns begin()/stop() (it spans the build/server setup rows too).\n await browser.close();\n await removeDir(tmpRoot);\n }\n}\n\n/**\n * Run a dependency DAG with a concurrency cap: a spec starts once all its dependencies have\n * completed `ok`; if any dependency failed, the spec is skipped (recorded as failed). Cycles\n * were already rejected by buildGraph, so this always drains.\n */\nasync function scheduleDag(\n specs: ResolvedAssetSpec[],\n concurrency: number,\n outcomes: Map<string, AssetOutcome>,\n runSpec: (spec: ResolvedAssetSpec) => Promise<AssetOutcome>,\n reporter?: Reporter,\n signal?: AbortSignal,\n): Promise<void> {\n const remaining = new Map(specs.map((s) => [s.name, s]));\n const inflight = new Map<string, Promise<void>>();\n\n const depState = (spec: ResolvedAssetSpec): \"ready\" | \"blocked\" | \"failed\" => {\n let ready = true;\n for (const dep of dependenciesOf(spec)) {\n const r = outcomes.get(dep);\n if (!r) ready = false;\n else if (r.status === \"failed\") return \"failed\";\n }\n return ready ? \"ready\" : \"blocked\";\n };\n\n while (remaining.size > 0 || inflight.size > 0) {\n // Graceful stop: don't launch anything new; let in-flight assets finish, then drain. Un-started\n // assets are simply omitted from the results (not marked failed).\n if (signal?.aborted) remaining.clear();\n for (const [name, spec] of [...remaining]) {\n if (inflight.size >= concurrency) break;\n const state = depState(spec);\n if (state === \"blocked\") continue;\n remaining.delete(name);\n if (state === \"failed\") {\n outcomes.set(name, {\n name,\n generator: spec.generator,\n status: \"failed\",\n records: [],\n error: new Error(\"Skipped — a dependency failed.\"),\n });\n reporter?.status(name, \"failed\");\n continue;\n }\n const p = runSpec(spec)\n .then((outcome) => {\n outcomes.set(name, outcome);\n })\n .finally(() => {\n inflight.delete(name);\n });\n inflight.set(name, p);\n }\n\n if (inflight.size > 0) {\n await Promise.race(inflight.values());\n } else if (remaining.size > 0) {\n // No work in flight and nothing became ready — only possible if every remaining spec is\n // blocked by a skipped dep. Re-loop resolves them as \"failed\"; guard against a spin.\n const stuck = [...remaining.values()].every((s) => depState(s) === \"blocked\");\n if (stuck) break;\n }\n }\n}\n\n/**\n * Merge each generator's option-derived dependencies (`deriveInputs`) into its asset's `inputs`,\n * in place, before the graph is built — so producers are ordered ahead of consumers that declared\n * their dependencies through options (e.g. a wall whose columns reference assets by name). Author-\n * declared `inputs` win on conflict. Options that fail to parse here are skipped; `runSpec`'s own\n * parse surfaces the real validation error for that asset.\n */\nexport function applyDerivedInputs(config: ResolvedConfig): void {\n for (const spec of config.assets) {\n const generator = getGenerator(spec.generator);\n if (!generator?.deriveInputs) continue;\n let options: unknown;\n try {\n options = generator.optionsSchema.parse(mergeGeneratorOptions(config.settings.defaults, spec));\n } catch {\n continue;\n }\n spec.inputs = { ...generator.deriveInputs(options), ...spec.inputs };\n }\n}\n\n/**\n * Merge a spec's options on top of the repo's per-generator defaults. Defaults are keyed\n * by generator id (the same string used in `assets[].generator`); per-asset options win.\n */\nexport function mergeGeneratorOptions(\n defaults: Record<string, Record<string, unknown>>,\n spec: ResolvedAssetSpec,\n): Record<string, unknown> {\n const generatorDefaults = defaults[spec.generator] ?? {};\n return { ...generatorDefaults, ...spec.options };\n}\n","import { chromium } from \"playwright-core\";\nimport type { Browser } from \"playwright-core\";\nimport type { ResolvedBrowserSettings } from \"@/config/schema\";\n\n/** Launch Chromium with the repo's browser settings. */\nexport async function launchBrowser(settings: ResolvedBrowserSettings): Promise<Browser> {\n return chromium.launch({\n headless: settings.headless,\n channel: settings.channel,\n executablePath: settings.executablePath,\n args: settings.args,\n timeout: settings.timeout,\n });\n}\n","import path from \"node:path\";\nimport type { Browser } from \"playwright-core\";\nimport { ensureDir } from \"@/utils/fs\";\nimport { generatorDir, relPosix } from \"@/utils/paths\";\nimport type { Logger } from \"@/utils/logger\";\nimport type { ManifestStore } from \"@/manifest/manifest\";\nimport type { AssetTarget, PipelineContext } from \"@/generators/types\";\n\nexport interface CreateContextArgs {\n browser: Browser;\n generatorId: string;\n target: AssetTarget;\n /** Absolute file paths of declared `inputs`, keyed by slot name. */\n resolvedInputs: Record<string, string>;\n /** Absolute output root. */\n outDir: string;\n tmpDir: string;\n logger: Logger;\n toolVersion: string;\n quality: \"draft\" | \"final\";\n manifest: ManifestStore;\n /** Forwarded to the live dashboard as this asset's progress (0–1). */\n onProgress?: (value: number) => void;\n /** Aborts in-flight work when the run is cancelled. */\n signal?: AbortSignal;\n}\n\n/** Build a per-(generator, target) context, ensuring the generator's output subdir exists. */\nexport async function createContext(args: CreateContextArgs): Promise<PipelineContext> {\n const genDir = generatorDir(args.outDir, args.generatorId);\n await ensureDir(genDir);\n\n return {\n browser: args.browser,\n target: args.target,\n resolvedInputs: args.resolvedInputs,\n outDir: args.outDir,\n resolveOutPath: (filename) => path.join(genDir, filename),\n toManifestPath: (absPath) => relPosix(args.outDir, absPath),\n tmpDir: args.tmpDir,\n logger: args.logger,\n toolVersion: args.toolVersion,\n quality: args.quality,\n writeAsset: (record) => args.manifest.upsert(record),\n progress: args.onProgress,\n signal: args.signal,\n };\n}\n","import type { ResolvedAssetSpec } from \"@/config/schema\";\n\n/** Unique dependency asset-names for a spec (the values of its `inputs` map). */\nexport function dependenciesOf(spec: ResolvedAssetSpec): string[] {\n return [...new Set(Object.values(spec.inputs))];\n}\n\n/**\n * Validate the asset dependency graph: every `inputs` reference must name a real asset, and\n * there must be no cycles. Throws a descriptive error otherwise. Returns the spec list by name.\n */\nexport function buildGraph(specs: ResolvedAssetSpec[]): Map<string, ResolvedAssetSpec> {\n const byName = new Map(specs.map((s) => [s.name, s]));\n\n for (const spec of specs) {\n for (const [slot, dep] of Object.entries(spec.inputs)) {\n if (!byName.has(dep)) {\n throw new Error(\n `Asset \"${spec.name}\" input \"${slot}\" references unknown asset \"${dep}\".`,\n );\n }\n if (dep === spec.name) {\n throw new Error(`Asset \"${spec.name}\" cannot depend on itself.`);\n }\n }\n }\n\n assertAcyclic(specs);\n return byName;\n}\n\n/** DFS cycle check; throws with the offending cycle path. */\nfunction assertAcyclic(specs: ResolvedAssetSpec[]): void {\n const byName = new Map(specs.map((s) => [s.name, s]));\n const state = new Map<string, \"visiting\" | \"done\">();\n const stack: string[] = [];\n\n const visit = (name: string): void => {\n const s = state.get(name);\n if (s === \"done\") return;\n if (s === \"visiting\") {\n const from = stack.indexOf(name);\n const cycle = [...stack.slice(from), name].join(\" → \");\n throw new Error(`Cyclic asset dependency: ${cycle}.`);\n }\n state.set(name, \"visiting\");\n stack.push(name);\n const spec = byName.get(name);\n if (spec) for (const dep of dependenciesOf(spec)) visit(dep);\n stack.pop();\n state.set(name, \"done\");\n };\n\n for (const spec of specs) visit(spec.name);\n}\n\n/**\n * Given a user selection of asset names, expand it to include every transitive dependency\n * (you can't build an asset without its inputs). Preserves config order. Unknown selected\n * names are ignored here (the caller warns); unknown *dependencies* are caught by buildGraph.\n */\nexport function expandSelection(\n specs: ResolvedAssetSpec[],\n names: string[] | undefined,\n): ResolvedAssetSpec[] {\n if (!names || names.length === 0) return specs;\n const byName = new Map(specs.map((s) => [s.name, s]));\n const wanted = new Set<string>();\n\n const add = (name: string): void => {\n if (wanted.has(name)) return;\n const spec = byName.get(name);\n if (!spec) return;\n wanted.add(name);\n for (const dep of dependenciesOf(spec)) add(dep);\n };\n for (const name of names) add(name);\n\n return specs.filter((s) => wanted.has(s.name));\n}\n","import { createHash } from \"node:crypto\";\n\nexport interface CacheKeyParts {\n generator: string;\n url?: string;\n /** Fully-resolved generator options. */\n options: unknown;\n /** Slot name → producing asset's output contentHash. */\n inputs: Record<string, string>;\n /**\n * Resolved path → content hash of the generator's declared file dependencies (e.g. fonts).\n * Pass `undefined` when there are none — stableStringify drops undefined entries, keeping keys\n * byte-identical for generators that don't use the feature.\n */\n files?: Record<string, string>;\n quality: string;\n toolVersion: string;\n}\n\n/** Deterministic JSON: object keys sorted recursively so the hash is stable. */\nfunction stableStringify(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`);\n return `{${entries.join(\",\")}}`;\n}\n\n/**\n * Fingerprint of everything that determines an asset's output: its generator, options, the\n * content hashes of its inputs, the quality profile, and the tool version (which covers tool\n * + scene-app changes). A stable key here means the cached output is still valid.\n */\nexport function computeCacheKey(parts: CacheKeyParts): string {\n return createHash(\"sha256\").update(stableStringify(parts)).digest(\"hex\");\n}\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { ensureDir } from \"@/utils/fs\";\nimport {\n emptyManifest,\n manifestSchema,\n type AssetRecord,\n type Manifest,\n} from \"@/manifest/schema\";\n\nfunction manifestPath(outDir: string): string {\n return path.join(outDir, \"manifest.json\");\n}\n\n/** Read + validate the manifest, returning an empty one if none exists. */\nexport async function readManifest(outDir: string): Promise<Manifest> {\n const file = manifestPath(outDir);\n if (!existsSync(file)) return emptyManifest();\n\n const text = await readFile(file, \"utf8\");\n let json: unknown;\n try {\n json = JSON.parse(text);\n } catch {\n throw new Error(`Corrupt manifest (invalid JSON): ${file}`);\n }\n const parsed = manifestSchema.safeParse(json);\n if (!parsed.success) {\n throw new Error(`Corrupt manifest (schema mismatch): ${file}`);\n }\n return parsed.data;\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** Lock errors the tmp→rename swap can hit transiently on Windows (held handles, AV scans). */\nconst TRANSIENT_RENAME_CODES = new Set([\"EPERM\", \"EACCES\", \"EBUSY\"]);\n\n/**\n * Atomically write the manifest (sorted by id for stable diffs).\n *\n * The tmp→rename swap can fail with EPERM/EACCES/EBUSY on Windows when another process holds a\n * brief handle on the target — the managed server serving `public/`, an editor, or antivirus\n * scanning the just-written file. Retry the rename a few times with backoff, then fall back to a\n * direct (non-atomic) overwrite, so a long generation run never dies on a flaky lock.\n */\nexport async function writeManifest(outDir: string, manifest: Manifest): Promise<void> {\n await ensureDir(outDir);\n const file = manifestPath(outDir);\n const tmp = `${file}.tmp`;\n const sorted: Manifest = {\n ...manifest,\n assets: [...manifest.assets].sort((a, b) => a.id.localeCompare(b.id)),\n };\n const contents = `${JSON.stringify(sorted, null, 2)}\\n`;\n await writeFile(tmp, contents, \"utf8\");\n\n const maxAttempts = 5;\n for (let attempt = 1; ; attempt++) {\n try {\n await rename(tmp, file);\n return;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (!code || !TRANSIENT_RENAME_CODES.has(code)) throw err;\n if (attempt < maxAttempts) {\n await sleep(attempt * 50); // 50,100,150,200ms backoff\n continue;\n }\n // The lock outlived our retries — overwrite in place (non-atomic) and clean up the tmp.\n await writeFile(file, contents, \"utf8\");\n await rm(tmp, { force: true });\n return;\n }\n }\n}\n\n/**\n * In-memory manifest with serialized, idempotent (by id) upserts flushed to disk.\n * Serializing through a promise chain keeps concurrent generators from racing on the file.\n */\nexport class ManifestStore {\n private chain: Promise<void> = Promise.resolve();\n\n private constructor(\n private readonly outDir: string,\n private readonly manifest: Manifest,\n ) {}\n\n static async load(outDir: string): Promise<ManifestStore> {\n return new ManifestStore(outDir, await readManifest(outDir));\n }\n\n /** Replace an existing record with the same id, or append a new one; then flush. */\n upsert(record: AssetRecord): Promise<void> {\n this.chain = this.chain.then(async () => {\n const index = this.manifest.assets.findIndex((a) => a.id === record.id);\n if (index >= 0) this.manifest.assets[index] = record;\n else this.manifest.assets.push(record);\n await writeManifest(this.outDir, this.manifest);\n });\n return this.chain;\n }\n\n get records(): readonly AssetRecord[] {\n return this.manifest.assets;\n }\n\n /** Look up the existing record for an asset id (for cache checks). */\n find(id: string): AssetRecord | undefined {\n return this.manifest.assets.find((a) => a.id === id);\n }\n}\n","import { z } from \"zod\";\n\n/** One generated asset, recorded in pro-visu/manifest.json. Keyed by `id` (the asset name). */\nconst assetRecordSchema = z.object({\n /** Unique asset id — the asset spec's `name`. */\n id: z.string(),\n generator: z.string(),\n sourceUrl: z.string(),\n /** Path relative to the output dir, forward-slashed. */\n file: z.string(),\n format: z.string(),\n width: z.number().int(),\n height: z.number().int(),\n durationMs: z.number().optional(),\n bytes: z.number().int(),\n /** sha256 of the output file. */\n contentHash: z.string(),\n createdAt: z.string(),\n toolVersion: z.string(),\n /** Inputs+options+tool fingerprint; lets `--cache` skip unchanged assets. */\n cacheKey: z.string().optional(),\n});\nexport type AssetRecord = z.infer<typeof assetRecordSchema>;\n\nexport const manifestSchema = z.object({\n version: z.literal(1),\n assets: z.array(assetRecordSchema),\n});\nexport type Manifest = z.infer<typeof manifestSchema>;\n\nexport function emptyManifest(): Manifest {\n return { version: 1, assets: [] };\n}\n","import { render, type Instance } from \"ink\";\nimport type { JobStatus, Reporter, RowInit } from \"@/pipeline/reporter\";\nimport { DashboardStore } from \"./store\";\nimport { Dashboard } from \"./Dashboard\";\n\n/** No-op reporter for non-TTY / CI / --verbose: logs print normally, nothing is rendered. */\nexport class NoopReporter implements Reporter {\n readonly isLive = false;\n begin(): void {}\n add(): void {}\n status(): void {}\n step(): void {}\n progress(): void {}\n cancelling(): void {}\n stop(): void {}\n route(): boolean {\n return false;\n }\n}\n\n/**\n * Live terminal dashboard backed by Ink (React). The {@link DashboardStore} holds run state; this\n * class drives it through the {@link Reporter} API and owns the Ink render lifecycle. Tagged\n * generator logs become a row's current step; everything else is committed above the dashboard.\n */\nexport class InkReporter implements Reporter {\n readonly isLive = true;\n private readonly store = new DashboardStore();\n private instance?: Instance;\n private stopped = false;\n private onInterrupt?: () => void;\n\n constructor(private readonly out: NodeJS.WriteStream = process.stdout) {}\n\n /** Let the dashboard own Esc/Ctrl+C; calls this trigger on each press. Call before begin(). */\n attachInput(onInterrupt: () => void): void {\n this.onInterrupt = onInterrupt;\n }\n\n begin(): void {\n if (this.instance || this.stopped) return;\n this.instance = render(<Dashboard store={this.store} onInterrupt={() => this.onInterrupt?.()} />, {\n stdout: this.out,\n // We route logs through the dashboard ourselves, so Ink shouldn't also patch console.\n patchConsole: false,\n // Esc/Ctrl+C is handled inside the dashboard via useInput; Ink must not exit on its own.\n exitOnCtrlC: false,\n });\n }\n\n add(row: RowInit): void {\n this.store.add(row);\n }\n\n status(id: string, status: JobStatus): void {\n this.store.status(id, status);\n }\n\n step(id: string, text: string): void {\n this.store.step(id, text);\n }\n\n progress(id: string, value: number): void {\n this.store.progress(id, value);\n }\n\n cancelling(reason?: string): void {\n this.store.cancelling(reason);\n }\n\n route(tag: string, type: string, message: string): boolean {\n if (this.stopped) return false; // after teardown, let logs print normally\n if (tag && this.store.has(tag)) {\n this.store.step(tag, message);\n return true;\n }\n this.store.log(type, message);\n return true;\n }\n\n stop(): void {\n if (this.stopped) return;\n this.stopped = true;\n // Erase the live box before the final summary prints (committed Static logs stay in scrollback).\n // clear() + unmount() run synchronously so the animation timer can't redraw between them.\n this.instance?.clear();\n this.instance?.unmount();\n }\n}\n\n/**\n * Pick the live dashboard on an interactive TTY (unless --verbose, which wants full logs).\n * `SHOWCASE_LIVE=1` forces it on (e.g. terminals that mis-report TTY); `=0` forces it off.\n */\nexport function createReporter(opts: { tty: boolean; verbose: boolean }): Reporter {\n const forced = process.env.SHOWCASE_LIVE;\n if (forced === \"0\") return new NoopReporter();\n if (forced === \"1\") return new InkReporter();\n return opts.tty && !opts.verbose ? new InkReporter() : new NoopReporter();\n}\n","import type { JobStatus, RowInit } from \"@/pipeline/reporter\";\n\n/** A row's live state: its init data plus where it is in the run and its current step text. */\nexport interface JobView extends RowInit {\n status: JobStatus;\n step: string;\n startedAt?: number;\n endedAt?: number;\n /** Fractional progress (0–1) while running, if the generator reports it; else undefined. */\n progress?: number;\n}\n\n/** A log line committed above the dashboard (untagged logs and logs for unknown tags). */\nexport interface LogLine {\n /** Monotonic id so React keys stay stable as the list grows. */\n key: number;\n /** consola log type (info/warn/error/success/…) — drives the line's color. */\n type: string;\n message: string;\n}\n\n/** An immutable view of the whole run, handed to React via useSyncExternalStore. */\nexport interface DashboardSnapshot {\n jobs: JobView[];\n logs: LogLine[];\n startTime: number;\n cancelRequested: boolean;\n /** Optional banner verb shown while cancelling (e.g. \"low memory — stopping…\"). */\n cancelReason?: string;\n}\n\n/**\n * The mutable run state behind the live dashboard, deliberately decoupled from React: the Ink\n * `<Dashboard>` subscribes to it with `useSyncExternalStore`, and {@link InkReporter} drives it\n * through the {@link Reporter} API. Each mutation rebuilds an immutable {@link DashboardSnapshot};\n * `getSnapshot` returns the same reference until the next change, so React's referential check\n * stays stable across spinner re-renders (which read state but never mutate it).\n */\nexport class DashboardStore {\n private jobs = new Map<string, JobView>();\n private order: string[] = [];\n private logs: LogLine[] = [];\n private logSeq = 0;\n private cancelRequested = false;\n private cancelReason?: string;\n private readonly startTime: number;\n private snap: DashboardSnapshot;\n private readonly listeners = new Set<() => void>();\n\n constructor(now: number = Date.now()) {\n this.startTime = now;\n this.snap = this.build();\n }\n\n /** Subscribe to changes (useSyncExternalStore). Returns an unsubscribe. */\n readonly subscribe = (cb: () => void): (() => void) => {\n this.listeners.add(cb);\n return () => void this.listeners.delete(cb);\n };\n\n /** The current immutable snapshot (stable between mutations). */\n readonly getSnapshot = (): DashboardSnapshot => this.snap;\n\n has(id: string): boolean {\n return this.jobs.has(id);\n }\n\n private build(): DashboardSnapshot {\n return {\n jobs: this.order\n .map((id) => this.jobs.get(id))\n .filter((v): v is JobView => Boolean(v)),\n logs: this.logs,\n startTime: this.startTime,\n cancelRequested: this.cancelRequested,\n cancelReason: this.cancelReason,\n };\n }\n\n private commit(): void {\n this.snap = this.build();\n for (const cb of this.listeners) cb();\n }\n\n add(row: RowInit): void {\n if (this.jobs.has(row.id)) return;\n // The waiting reason is derived at render time from gate/dep state, so it stays accurate.\n this.jobs.set(row.id, { ...row, deps: row.deps ?? [], status: \"waiting\", step: \"\" });\n this.order.push(row.id);\n this.commit();\n }\n\n status(id: string, status: JobStatus, now: number = Date.now()): void {\n const j = this.jobs.get(id);\n if (!j) return;\n if (status === \"running\" && j.status !== \"running\") {\n j.startedAt = now;\n if (!j.step || j.step === \"queued\" || j.step.startsWith(\"waiting\")) j.step = \"starting…\";\n }\n if (status === \"ok\" || status === \"failed\" || status === \"cached\") {\n j.endedAt = now;\n if (status === \"cached\") j.step = \"cached\";\n else if (status === \"ok\" && (j.step === \"\" || j.step === \"starting…\" || j.step === \"queued\"))\n j.step = \"done\";\n }\n j.status = status;\n this.commit();\n }\n\n step(id: string, text: string): void {\n const j = this.jobs.get(id);\n if (!j) return;\n j.step = text;\n this.commit();\n }\n\n progress(id: string, value: number): void {\n const j = this.jobs.get(id);\n if (!j) return;\n j.progress = Math.max(0, Math.min(1, value));\n this.commit();\n }\n\n /** Commit a log line above the dashboard. */\n log(type: string, message: string): void {\n this.logs = [...this.logs, { key: this.logSeq++, type, message }];\n this.commit();\n }\n\n /** Flip into the \"cancelling…\" state (idempotent). An optional reason customizes the banner verb. */\n cancelling(reason?: string): void {\n if (this.cancelRequested) return;\n this.cancelRequested = true;\n this.cancelReason = reason;\n this.commit();\n }\n}\n","import { useEffect, useState, useSyncExternalStore, type ReactElement } from \"react\";\nimport { Box, Static, Text, useInput, useStdin, useWindowSize } from \"ink\";\nimport type { DashboardStore } from \"./store\";\nimport { buildView, type DashboardVM, type RowVM, type TallyCell } from \"./view-model\";\n\n/** Spinner / progress animation cadence. */\nconst TICK_MS = 90;\n/** Don't let the dashboard box grow wider than this even on very wide terminals. */\nconst MAX_WIDTH = 100;\n\nfunction Row({ row, vm }: { row: RowVM; vm: DashboardVM }): ReactElement {\n // Fixed columns are flexShrink={0} so only the step (flexGrow) absorbs overflow and truncates —\n // otherwise Ink's flexbox shrinks the name/detail columns under a long step and they misalign.\n return (\n <Box paddingLeft={1}>\n <Box flexShrink={0} marginRight={1}>\n <Text color={row.glyph.color} dimColor={row.glyph.dim}>\n {row.glyph.text}\n </Text>\n </Box>\n <Box flexShrink={0} width={vm.nameWidth} marginRight={1}>\n <Text bold wrap=\"truncate-end\">\n {row.name}\n </Text>\n </Box>\n {vm.showDetail ? (\n <Box flexShrink={0} width={vm.detailWidth} marginRight={1}>\n <Text dimColor wrap=\"truncate-end\">\n {row.detail}\n </Text>\n </Box>\n ) : null}\n {vm.showProgress ? (\n <Box flexShrink={0} marginRight={1}>\n <Text color={row.progress.color} dimColor={row.progress.dim}>\n {row.progress.text}\n </Text>\n </Box>\n ) : null}\n <Box flexGrow={1} flexShrink={1}>\n <Text color={row.step.color} dimColor={row.step.dim} wrap=\"truncate-end\">\n {row.step.text}\n </Text>\n </Box>\n {row.elapsed ? (\n <Box flexShrink={0} marginLeft={1}>\n <Text dimColor>{row.elapsed}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction Tally({ cells }: { cells: TallyCell[] }): ReactElement {\n return (\n <Box>\n {cells.map((c, i) => (\n <Box key={c.text}>\n {i > 0 ? <Text dimColor> · </Text> : null}\n <Text color={c.color}>{c.text}</Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction Section({\n label,\n rows,\n vm,\n tally,\n before = 0,\n after = 0,\n}: {\n label: string;\n rows: RowVM[];\n vm: DashboardVM;\n tally?: TallyCell[];\n /** Rows hidden above / below this window (for the \"N more\" markers). */\n before?: number;\n after?: number;\n}): ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box justifyContent=\"space-between\">\n <Text bold dimColor>\n {label}\n </Text>\n {tally && tally.length > 0 ? <Tally cells={tally} /> : null}\n </Box>\n {before > 0 ? <Text dimColor>{` ↑ ${before} more above`}</Text> : null}\n {rows.map((r) => (\n <Row key={r.id} row={r} vm={vm} />\n ))}\n {after > 0 ? <Text dimColor>{` ↓ ${after} more below`}</Text> : null}\n </Box>\n );\n}\n\nfunction Header({ vm }: { vm: DashboardVM }): ReactElement {\n const { done, total, cached } = vm.overall;\n return (\n <Box justifyContent=\"space-between\">\n <Text bold>pro-visu</Text>\n <Box>\n <Text dimColor>{`${done}/${total} done`}</Text>\n {cached > 0 ? <Text dimColor>{` · ${cached} cached`}</Text> : null}\n <Text dimColor>{` ${vm.elapsed}`}</Text>\n </Box>\n </Box>\n );\n}\n\n/** The live run dashboard. Reads run state from the store; animates a spinner/bar on a timer. */\nexport function Dashboard({\n store,\n onInterrupt,\n}: {\n store: DashboardStore;\n onInterrupt?: () => void;\n}): ReactElement {\n const snapshot = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n const [frame, setFrame] = useState(0);\n // Manual scroll position; null = auto-follow the running rows (the default).\n const [viewStart, setViewStart] = useState<number | null>(null);\n useEffect(() => {\n const timer = setInterval(() => setFrame((f) => f + 1), TICK_MS);\n return () => clearInterval(timer);\n }, []);\n\n const { isRawModeSupported } = useStdin();\n const { columns, rows } = useWindowSize();\n const width = columns > 0 ? Math.min(columns, MAX_WIDTH) : undefined;\n const vm = buildView(\n snapshot,\n frame,\n Date.now(),\n columns > 0 ? width : undefined,\n rows > 0 ? rows : undefined,\n viewStart,\n );\n\n const scrollable = vm.assetsBefore > 0 || vm.assetsAfter > 0;\n // Ink owns keyboard input while live (one keypress owner), so the caller's watcher handles only\n // signals. Esc/Ctrl+C cancels; ↑/↓ + PgUp/PgDn scroll the asset window (which suspends auto-follow);\n // `f` returns to following the running rows. Gated on raw-mode support so a forced dashboard on a\n // non-TTY doesn't throw.\n useInput(\n (input, key) => {\n if (key.escape || (key.ctrl && input === \"c\")) {\n onInterrupt?.();\n return;\n }\n const here = viewStart ?? vm.assetsBefore; // current top, whether auto or manual\n const page = Math.max(1, vm.assets.length);\n if (key.upArrow) setViewStart(Math.max(0, here - 1));\n else if (key.downArrow) setViewStart(Math.min(vm.maxStart, here + 1));\n else if (key.pageUp) setViewStart(Math.max(0, here - page));\n else if (key.pageDown) setViewStart(Math.min(vm.maxStart, here + page));\n else if (input === \"f\") setViewStart(null);\n },\n { isActive: isRawModeSupported },\n );\n\n const manual = viewStart !== null;\n let footerText = vm.footer.text;\n if (!vm.cancelling && isRawModeSupported && scrollable) {\n footerText += manual ? \" · ↑↓ scroll · f follow\" : \" · ↑↓ scroll\";\n }\n\n return (\n <>\n {/* Committed above the live box, in scrollback — completed logs that won't change. */}\n <Static items={vm.logs}>\n {(line) => (\n <Text key={line.key} color={line.color} dimColor={line.dim}>\n {line.text}\n </Text>\n )}\n </Static>\n\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={vm.cancelling ? \"yellow\" : \"gray\"}\n borderDimColor={!vm.cancelling}\n paddingX={1}\n width={width}\n >\n <Header vm={vm} />\n\n {vm.setup.length > 0 ? <Section label=\"SETUP\" rows={vm.setup} vm={vm} /> : null}\n\n <Section\n label=\"ASSETS\"\n rows={vm.assets}\n vm={vm}\n tally={vm.tally}\n before={vm.assetsBefore}\n after={vm.assetsAfter}\n />\n\n <Box marginTop={1}>\n <Text\n color={vm.footer.tone === \"warn\" ? \"yellow\" : undefined}\n dimColor={vm.footer.tone === \"dim\"}\n wrap=\"truncate-end\"\n >\n {footerText}\n </Text>\n </Box>\n </Box>\n </>\n );\n}\n","import type { LogSink } from \"@/utils/logger\";\n\nexport type JobStatus = \"waiting\" | \"running\" | \"ok\" | \"failed\" | \"cached\";\n\nexport interface RowInit {\n /** Stable id; for assets this is the asset name (so tagged logs route to it). */\n id: string;\n /** Primary label. */\n name: string;\n /** Dim secondary column — the generator id, or \"server\" for setup rows. */\n detail: string;\n /** Ids this row waits on (assets it depends on). */\n deps?: string[];\n /** Setup row ids (build/server) that must finish before this row can start. */\n gatedBy?: string[];\n /** Setup rows (build/server) — shown, but excluded from the asset tally. */\n system?: boolean;\n}\n\n/**\n * A progress sink driven across a whole run: setup steps (build/server) and then each asset as\n * it moves through the DAG. Implemented by the live terminal tracker (and a no-op for\n * non-TTY/CI). Also a {@link LogSink} so tagged generator logs become a row's current step.\n * The caller owns begin()/stop(); rows are added incrementally with add().\n */\nexport interface Reporter extends LogSink {\n /** True for the live renderer; false for the no-op. */\n readonly isLive: boolean;\n begin(): void;\n add(row: RowInit): void;\n status(id: string, status: JobStatus): void;\n step(id: string, text: string): void;\n /** Report fractional progress (0–1) for a running row, for a determinate bar + ETA. */\n progress(id: string, value: number): void;\n /** Signal a graceful cancellation is underway, so the UI can show it's winding down. An optional\n * reason replaces the default \"cancelling…\" banner verb (e.g. \"low memory — stopping…\"). */\n cancelling(reason?: string): void;\n stop(): void;\n /**\n * Hand the live renderer a cancel trigger so it can own keyboard input (Esc/Ctrl+C) itself —\n * one keypress owner, no clash with the signal/keypress watcher. No-op renderers don't implement\n * it (the caller keeps its own keypress watching).\n */\n attachInput?(onInterrupt: () => void): void;\n}\n\n/** mm:ss for a millisecond duration (pure — unit-tested). */\nexport function formatElapsed(ms: number): string {\n const sec = Math.max(0, Math.round(ms / 1000));\n return `${Math.floor(sec / 60)}:${String(sec % 60).padStart(2, \"0\")}`;\n}\n\n/** Truncate to a visible width with an ellipsis (pure — unit-tested). */\nexport function truncate(text: string, width: number): string {\n if (width <= 0) return \"\";\n if (text.length <= width) return text;\n return `${text.slice(0, Math.max(0, width - 1))}…`;\n}\n","import { formatElapsed } from \"@/pipeline/reporter\";\nimport type { DashboardSnapshot, JobView } from \"./store\";\n\n/** Braille spinner frames for running rows. */\nexport const SPINNER = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\n\n/** Width of the bar itself, in cells. */\nconst BAR_WIDTH = 8;\n/** Full width of the progress column: bar + space + a right-aligned \"NN%\". */\nexport const PROGRESS_COL = BAR_WIDTH + 4;\nconst BAR_FULL = \"▓\";\nconst BAR_EMPTY = \"░\";\n\n/** Below these terminal widths, columns drop out so rows never wrap. */\nconst HIDE_DETAIL_BELOW = 76;\nconst HIDE_PROGRESS_BELOW = 60;\n\n/** A chalk color name Ink understands, or undefined for the terminal default. */\nexport type Tone = string | undefined;\n\n/** Text + how to paint it (Ink `<Text>` props). */\nexport interface Paint {\n text: string;\n color?: Tone;\n dim?: boolean;\n}\n\n/** A committed log line plus a stable React key. */\nexport interface LogVM extends Paint {\n key: number;\n}\n\n/** A fully-derived row, ready to lay out. */\nexport interface RowVM {\n id: string;\n glyph: Paint;\n name: string;\n detail: string;\n /** The progress column (fixed width PROGRESS_COL): bar, plus \"NN%\" when determinate. */\n progress: Paint;\n step: Paint;\n /** Elapsed, with a \"~ETA\" suffix once a determinate row is underway. */\n elapsed: string;\n}\n\nexport interface TallyCell {\n text: string;\n color: Tone;\n}\n\nexport interface DashboardVM {\n /** Total run elapsed (mm:ss), shown in the header. */\n elapsed: string;\n /** Header rollup across assets: done/total + cached. */\n overall: { done: number; total: number; cached: number };\n setup: RowVM[];\n /** The asset rows to render — a window around the active rows when they don't all fit the height. */\n assets: RowVM[];\n /** Asset rows hidden above / below the visible window (0 when everything fits). */\n assetsBefore: number;\n assetsAfter: number;\n /** Largest valid window start (0 when everything fits) — lets the UI clamp manual scrolling. */\n maxStart: number;\n /** Running / waiting / failed cells for the ASSETS section header (done lives in the rollup). */\n tally: TallyCell[];\n footer: { text: string; tone: \"dim\" | \"warn\" };\n cancelling: boolean;\n /** Column widths so setup + asset rows align. */\n nameWidth: number;\n detailWidth: number;\n /** Adaptive flags: which columns fit the current terminal width. */\n showDetail: boolean;\n showProgress: boolean;\n logs: LogVM[];\n}\n\nconst isDone = (v?: JobView): boolean =>\n Boolean(v && (v.status === \"ok\" || v.status === \"cached\"));\n\n/** Collapse newlines/tabs so routed (multi-line) output renders as one row. */\nconst oneLine = (s: string): string => s.replace(/[\\r\\n\\t]+/g, \" \");\n\n/** Live \"why is this waiting\" text: gates first (build/server), then asset deps, then queued. */\nexport function waitingReason(v: JobView, jobs: Map<string, JobView>): string {\n const label = (id: string): string => jobs.get(id)?.name ?? id;\n const gates = (v.gatedBy ?? []).filter((id) => !isDone(jobs.get(id)));\n if (gates.length) return `waiting for ${gates.map(label).join(\", \")}`;\n const deps = (v.deps ?? []).filter((id) => !isDone(jobs.get(id)));\n if (deps.length) return `waiting on ${deps.map(label).join(\", \")}`;\n return \"queued\";\n}\n\nexport function glyph(v: JobView, frame: number, cancelling: boolean): Paint {\n switch (v.status) {\n case \"running\":\n return { text: SPINNER[frame % SPINNER.length] ?? \"◐\", color: cancelling ? \"yellow\" : \"cyan\" };\n case \"ok\":\n return { text: \"✓\", color: \"green\" };\n case \"failed\":\n return { text: \"✗\", color: \"red\" };\n case \"cached\":\n return { text: \"⊙\", color: \"blue\" };\n default:\n return { text: \"·\", dim: true };\n }\n}\n\n/**\n * The progress column (fixed width {@link PROGRESS_COL} so the step column stays aligned).\n * - done/cached/failed → a full bar (green/blue/red).\n * - running with a reported fraction → a determinate bar + \"NN%\".\n * - running with no fraction → an indeterminate cell that ping-pongs across the bar.\n * - waiting → an empty bar.\n */\nexport function progressColumn(v: JobView, frame: number, cancelling: boolean): Paint {\n const w = BAR_WIDTH;\n const pad = (s: string): string => s.padEnd(PROGRESS_COL);\n const live: Tone = cancelling ? \"yellow\" : \"cyan\";\n\n switch (v.status) {\n case \"ok\":\n return { text: pad(BAR_FULL.repeat(w)), color: \"green\" };\n case \"cached\":\n return { text: pad(BAR_FULL.repeat(w)), color: \"blue\" };\n case \"failed\":\n return { text: pad(BAR_FULL.repeat(w)), color: \"red\" };\n case \"running\": {\n if (typeof v.progress === \"number\") {\n const filled = Math.max(0, Math.min(w, Math.round(v.progress * w)));\n const bar = BAR_FULL.repeat(filled) + BAR_EMPTY.repeat(w - filled);\n const pct = `${Math.min(99, Math.floor(v.progress * 100))}%`.padStart(3);\n return { text: `${bar} ${pct}`, color: live };\n }\n const span = Math.max(1, w * 2 - 2); // ping-pong period over [0, w-1]\n const m = frame % span;\n const pos = m < w ? m : span - m;\n const cells = Array.from({ length: w }, (_, i) => (i === pos ? BAR_FULL : BAR_EMPTY)).join(\"\");\n return { text: pad(cells), color: live };\n }\n default:\n return { text: pad(BAR_EMPTY.repeat(w)), dim: true };\n }\n}\n\n/** Elapsed for a row, plus a \"~ETA\" suffix once a determinate row is meaningfully underway. */\nfunction rowElapsed(v: JobView, now: number): string {\n if (v.status === \"waiting\") return \"\";\n const dur = (v.endedAt ?? now) - (v.startedAt ?? now);\n const base = formatElapsed(dur);\n if (v.status === \"running\" && typeof v.progress === \"number\" && v.progress > 0.02 && v.progress < 1) {\n const eta = (dur * (1 - v.progress)) / v.progress;\n return `${base} ~${formatElapsed(eta)}`;\n }\n return base;\n}\n\nfunction rowOf(v: JobView, jobs: Map<string, JobView>, frame: number, now: number, cancelling: boolean): RowVM {\n const rawStep = v.status === \"waiting\" ? waitingReason(v, jobs) : v.step;\n const step: Paint =\n v.status === \"failed\"\n ? { text: oneLine(rawStep), color: \"red\" }\n : v.status === \"running\"\n ? { text: oneLine(rawStep) }\n : { text: oneLine(rawStep), dim: true };\n return {\n id: v.id,\n glyph: glyph(v, frame, cancelling),\n name: v.name,\n detail: v.detail,\n progress: progressColumn(v, frame, cancelling),\n step,\n elapsed: rowElapsed(v, now),\n };\n}\n\nconst LOG_TONE: Record<string, Paint> = {\n error: { text: \"\", color: \"red\" },\n fail: { text: \"\", color: \"red\" },\n warn: { text: \"\", color: \"yellow\" },\n success: { text: \"\", color: \"green\" },\n debug: { text: \"\", dim: true },\n};\n\n/** Derive everything the `<Dashboard>` needs from a snapshot. Pure — unit-tested. */\nexport function buildView(\n snapshot: DashboardSnapshot,\n frame: number,\n now: number,\n columns?: number,\n rows?: number,\n /** Manual window start (row index) for scrolling; null/undefined = auto-follow the running rows. */\n viewStart?: number | null,\n): DashboardVM {\n const jobs = new Map(snapshot.jobs.map((j) => [j.id, j]));\n const cancelling = snapshot.cancelRequested;\n\n const nameWidth = Math.min(22, Math.max(4, ...snapshot.jobs.map((j) => j.name.length)));\n const detailWidth = Math.min(14, Math.max(6, ...snapshot.jobs.map((j) => j.detail.length)));\n\n const setup: RowVM[] = [];\n const assets: RowVM[] = [];\n for (const j of snapshot.jobs) {\n (j.system ? setup : assets).push(rowOf(j, jobs, frame, now, cancelling));\n }\n\n // Counts cover assets only; setup rows (build/server) are shown but not counted.\n const assetJobs = snapshot.jobs.filter((j) => !j.system);\n const cached = assetJobs.filter((j) => j.status === \"cached\").length;\n const done = assetJobs.filter((j) => j.status === \"ok\" || j.status === \"cached\").length;\n const running = assetJobs.filter((j) => j.status === \"running\").length;\n const waiting = assetJobs.filter((j) => j.status === \"waiting\").length;\n const failed = assetJobs.filter((j) => j.status === \"failed\").length;\n\n const tally: TallyCell[] = [];\n if (running) tally.push({ text: `${running} running`, color: \"cyan\" });\n if (waiting) tally.push({ text: `${waiting} waiting`, color: undefined });\n if (failed) tally.push({ text: `${failed} failed`, color: \"red\" });\n\n const verb = snapshot.cancelReason ?? \"cancelling…\";\n const footer: DashboardVM[\"footer\"] = cancelling\n ? { text: running ? `${verb} finishing ${running} · esc to force` : `${verb} · esc to force`, tone: \"warn\" }\n : { text: \"esc to cancel\", tone: \"dim\" };\n\n const logs: LogVM[] = snapshot.logs.map((l) => ({\n ...(LOG_TONE[l.type] ?? {}),\n key: l.key,\n text: oneLine(l.message),\n }));\n\n // Keep the live block within the terminal height. Rendering more lines than the terminal has rows\n // makes Ink's in-place redraw fight the terminal's own scroll (the rows \"jiggle\"), so when the asset\n // rows don't all fit, show a window around the active rows and mark how many are hidden above/below.\n let visibleAssets = assets;\n let assetsBefore = 0;\n let assetsAfter = 0;\n let maxStart = 0;\n if (rows && rows > 0) {\n // Fixed chrome: borders(2) + header(1) + ASSETS margin+label(2) + footer margin+line(2) = 7,\n // plus the setup block (margin+label+rows) when present, plus a 1-row safety margin.\n const chrome = 7 + (setup.length > 0 ? 2 + setup.length : 0) + 1;\n const budget = Math.max(1, rows - chrome);\n if (assets.length > budget) {\n const show = Math.max(1, budget - 2); // reserve up to two \"N more\" marker lines\n maxStart = assets.length - show;\n let start: number;\n if (typeof viewStart === \"number\") {\n // Manual scroll: honor the requested start, clamped to the list.\n start = Math.max(0, Math.min(viewStart, maxStart));\n } else {\n // Auto-follow: centre the window on the running rows (or the next waiting one).\n const runningPos: number[] = [];\n assetJobs.forEach((j, i) => {\n if (j.status === \"running\") runningPos.push(i);\n });\n let anchor: number;\n if (runningPos.length) {\n anchor = Math.floor((runningPos[0]! + runningPos[runningPos.length - 1]!) / 2);\n } else {\n const firstWaiting = assetJobs.findIndex((j) => j.status === \"waiting\");\n anchor = firstWaiting >= 0 ? firstWaiting : assetJobs.length - 1;\n }\n start = Math.max(0, Math.min(anchor - Math.floor(show / 2), maxStart));\n }\n visibleAssets = assets.slice(start, start + show);\n assetsBefore = start;\n assetsAfter = assets.length - (start + show);\n }\n }\n\n return {\n elapsed: formatElapsed(now - snapshot.startTime),\n overall: { done, total: assetJobs.length, cached },\n setup,\n assets: visibleAssets,\n assetsBefore,\n assetsAfter,\n maxStart,\n tally,\n footer,\n cancelling,\n nameWidth,\n detailWidth,\n showDetail: columns === undefined || columns >= HIDE_DETAIL_BELOW,\n showProgress: columns === undefined || columns >= HIDE_PROGRESS_BELOW,\n logs,\n };\n}\n","import type { ReactElement } from \"react\";\nimport { Box, Text, renderToString } from \"ink\";\nimport path from \"node:path\";\nimport { formatBytes } from \"@/utils/format\";\nimport type { AssetOutcome } from \"@/pipeline/runner\";\n\n/** A flattened summary line — one per produced record, or one per failed asset. */\ninterface Line {\n key: string;\n glyph: { text: string; color: string };\n name: string;\n /** dimensions / format / size, or the error message for failures. */\n meta: string;\n metaDim: boolean;\n}\n\nfunction toLines(outcomes: AssetOutcome[]): Line[] {\n const lines: Line[] = [];\n for (const o of outcomes) {\n if (o.status === \"failed\") {\n lines.push({\n key: o.name,\n glyph: { text: \"✗\", color: \"red\" },\n name: o.name,\n meta: o.error?.message ?? \"failed\",\n metaDim: false,\n });\n continue;\n }\n const glyph = o.cached ? { text: \"⊙\", color: \"blue\" } : { text: \"✓\", color: \"green\" };\n for (const r of o.records) {\n lines.push({\n key: `${o.name}:${r.file}`,\n glyph,\n name: o.name,\n meta: o.cached\n ? `${r.width}×${r.height} ${r.format} cached`\n : `${r.width}×${r.height} ${r.format} ${formatBytes(r.bytes)}`,\n metaDim: true,\n });\n }\n }\n return lines;\n}\n\nfunction Summary({ outcomes, outDir }: { outcomes: AssetOutcome[]; outDir: string }): ReactElement {\n const lines = toLines(outcomes);\n const nameWidth = Math.min(28, Math.max(4, ...lines.map((l) => l.name.length)));\n\n const ok = outcomes.filter((o) => o.status === \"ok\");\n const generated = ok.filter((o) => !o.cached).length;\n const cached = ok.filter((o) => o.cached).length;\n const failed = outcomes.length - ok.length;\n const totalBytes = ok.reduce((sum, o) => sum + o.records.reduce((s, r) => s + r.bytes, 0), 0);\n\n const totals: { text: string; color?: string }[] = [\n { text: `${generated} generated`, color: \"green\" },\n ];\n if (cached) totals.push({ text: `${cached} cached`, color: \"blue\" });\n totals.push({ text: `${failed} failed`, color: failed ? \"red\" : undefined });\n totals.push({ text: formatBytes(totalBytes), color: undefined });\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor={failed ? \"yellow\" : \"green\"} borderDimColor paddingX={1}>\n <Box justifyContent=\"space-between\">\n <Text bold>{failed ? \"done with failures\" : \"done\"}</Text>\n <Text dimColor>{outDir}</Text>\n </Box>\n <Box flexDirection=\"column\" marginTop={1}>\n {lines.map((l) => (\n <Box key={l.key}>\n <Box flexShrink={0} marginRight={1}>\n <Text color={l.glyph.color}>{l.glyph.text}</Text>\n </Box>\n <Box flexShrink={0} width={nameWidth} marginRight={1}>\n <Text bold wrap=\"truncate-end\">\n {l.name}\n </Text>\n </Box>\n <Box flexGrow={1}>\n <Text color={l.metaDim ? undefined : \"red\"} dimColor={l.metaDim} wrap=\"truncate-end\">\n {l.meta}\n </Text>\n </Box>\n </Box>\n ))}\n </Box>\n <Box marginTop={1}>\n {totals.map((c, i) => (\n <Box key={c.text}>\n {i > 0 ? <Text dimColor> · </Text> : null}\n <Text color={c.color}>{c.text}</Text>\n </Box>\n ))}\n </Box>\n </Box>\n );\n}\n\n/** Render the final results panel to a string (static — safe to print after the live dashboard stops). */\nexport function renderSummary(outcomes: AssetOutcome[], outDir: string, columns = 80): string {\n const display = path.isAbsolute(outDir) ? path.relative(process.cwd(), outDir) || outDir : outDir;\n return renderToString(<Summary outcomes={outcomes} outDir={display} />, {\n columns: Math.max(40, Math.min(columns, 100)),\n });\n}\n","/** Human-readable byte size (e.g. 2.4 MB). */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n const units = [\"KB\", \"MB\", \"GB\"];\n let value = bytes / 1024;\n let i = 0;\n while (value >= 1024 && i < units.length - 1) {\n value /= 1024;\n i += 1;\n }\n return `${value.toFixed(1)} ${units[i]}`;\n}\n","import { ConfigNotFoundError, ConfigValidationError } from \"@/config/load\";\nimport type { Logger } from \"@/utils/logger\";\nimport type { AssetOutcome } from \"@/pipeline/runner\";\nimport { renderSummary } from \"@/cli/dashboard/Summary\";\n\n/** Print a friendly, actionable message for config load/validation errors. */\nexport function reportConfigError(logger: Logger, err: unknown): void {\n if (err instanceof ConfigNotFoundError) {\n logger.error(err.message);\n return;\n }\n if (err instanceof ConfigValidationError) {\n logger.error(`Invalid config${err.file ? ` (${err.file})` : \"\"}:`);\n for (const issue of err.zodError.issues) {\n const where = issue.path.length ? issue.path.join(\".\") : \"(root)\";\n logger.error(` • ${where}: ${issue.message}`);\n }\n return;\n }\n logger.error(err instanceof Error ? err.message : String(err));\n}\n\n/** Print the final results panel (a rendered Ink summary) and a one-line note when nothing matched. */\nexport function printSummary(\n logger: Logger,\n outcomes: AssetOutcome[],\n outDir: string,\n): void {\n if (outcomes.length === 0) {\n logger.warn(\"No assets matched.\");\n return;\n }\n logger.log(\"\");\n logger.log(renderSummary(outcomes, outDir, process.stdout.columns ?? 80));\n}\n","import pc from \"picocolors\";\nimport { resolveCwd, resolveOutDir } from \"@/utils/paths\";\nimport { createLogger } from \"@/utils/logger\";\nimport { loadShowcaseConfig, ConfigNotFoundError } from \"@/config/load\";\nimport { readManifest } from \"@/manifest/manifest\";\nimport { DEFAULT_OUTDIR } from \"@/config/defaults\";\nimport { reportConfigError } from \"@/cli/ui\";\nimport { formatBytes } from \"@/utils/format\";\n\nexport interface ListOptions {\n cwd?: string;\n config?: string;\n}\n\nexport async function runList(options: ListOptions = {}): Promise<void> {\n const cwd = resolveCwd(options.cwd);\n const logger = createLogger(\"info\");\n\n let outDir: string;\n try {\n const { config } = await loadShowcaseConfig({ cwd, configFile: options.config });\n outDir = resolveOutDir(cwd, config.settings.outDir);\n } catch (err) {\n if (err instanceof ConfigNotFoundError) {\n outDir = resolveOutDir(cwd, DEFAULT_OUTDIR);\n } else {\n reportConfigError(logger, err);\n process.exitCode = 1;\n return;\n }\n }\n\n const manifest = await readManifest(outDir);\n if (manifest.assets.length === 0) {\n logger.info(\"Nothing generated yet. Run `pro-visu generate`.\");\n return;\n }\n\n logger.log(pc.bold(`Assets in ${outDir}:`));\n logger.log(\"\");\n for (const asset of manifest.assets) {\n logger.log(\n ` ${pc.bold(asset.id)} ${pc.dim(asset.generator)} ${pc.cyan(asset.file)} ` +\n `${pc.dim(`${asset.width}×${asset.height}`)} ${pc.dim(formatBytes(asset.bytes))}`,\n );\n }\n logger.log(\"\");\n logger.log(pc.dim(`${manifest.assets.length} asset(s)`));\n}\n","import path from \"node:path\";\nimport { resolveCwd, resolveOutDir } from \"@/utils/paths\";\nimport { createLogger } from \"@/utils/logger\";\nimport { loadShowcaseConfig } from \"@/config/load\";\nimport { removeDir } from \"@/utils/fs\";\nimport { readRunState, clearRunState, isAlive, killTreeByPid } from \"@/cli/run-state\";\n\nexport interface ResetOptions {\n cwd?: string;\n config?: string;\n /** Clean up even if a run still looks active. */\n force?: boolean;\n}\n\n/**\n * Tear down whatever a previous `generate` left running when it was killed hard (Ctrl+C/crash):\n * the managed server and browser process trees, plus its temp working dirs, recorded in the\n * run-state file. A clean exit removes that file, so there's normally nothing to do.\n */\nexport async function runReset(options: ResetOptions = {}): Promise<void> {\n const cwd = resolveCwd(options.cwd);\n const log = createLogger(\"info\");\n\n // The run-state file lives under the output dir; fall back to the default if config can't load.\n let outDir = path.join(cwd, \"pro-visu\");\n try {\n const { config } = await loadShowcaseConfig({ cwd, configFile: options.config });\n outDir = resolveOutDir(cwd, config.settings.outDir);\n } catch {\n /* no/invalid config — still try the default output dir */\n }\n\n const state = await readRunState(outDir);\n if (!state) {\n log.success(\"Nothing to reset — no interrupted run recorded.\");\n return;\n }\n if (isAlive(state.pid) && !options.force) {\n log.warn(\n `A run looks active (pid ${state.pid}). If it's stuck, re-run with --force to clean up anyway.`,\n );\n return;\n }\n\n let killed = 0;\n if (await killTreeByPid(state.serverPid)) {\n killed += 1;\n log.info(`Stopped orphaned server (pid ${state.serverPid}).`);\n }\n let removed = 0;\n for (const dir of state.tmpDirs ?? []) {\n await removeDir(dir);\n removed += 1;\n }\n await clearRunState(outDir);\n\n log.success(\n `Reset complete — stopped ${killed} process tree(s), removed ${removed} temp dir(s).`,\n );\n}\n"],"mappings":"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","names":["path","existsSync","readFile","writeFile","path","path","require","cli","path","existsSync","rm","spawn","createRequire","path","spawn","rm","writeFile","spawn","path","writeFile","rm","require","createRequire","existsSync","spawn","path","rm","path","stat","z","path","sleep","path","path","path","PREWARM_SETTLE_MS","path","readFile","path","path","mkdtemp","mkdtemp","path","sleep","stat","workers","path","baseName","writeFile","z","run","writeFile","z","z","z","z","z","path","copyFile","stat","path","stat","parsed","path","mkdtemp","mkdtemp","path","path","copyFile","stat","run","run","path","readFile","writeFile","imageSize","z","z","fontDataUrl","path","readFile","run","writeFile","imageSize","z","fieldEnum","z","hexString","run","path","existsSync","copyFile","readFile","stat","imageSize","z","run","path","existsSync","copyFile","readFile","stat","imageSize","path","writeFile","path","writeFile","writeFile","path","existsSync","readFile","path","existsSync","readFileSync","path","existsSync","rc","parsed","readFileSync","spawn","v8","path","readFile","writeFile","rm","spawn","path","http","spawn","os","path","existsSync","mkdtemp","chromium","path","path","createHash","path","existsSync","readFile","rename","rm","writeFile","z","path","existsSync","readFile","parsed","writeFile","rename","rm","mkdtemp","path","os","existsSync","jsx","Box","Text","path","jsx","jsxs","glyph","Box","Text","path","v8","path","path"]}
|