({ args })
| 588 | }, |
| 589 | }, |
| 590 | async run({ args }) { |
| 591 | const project = resolveProject(args.dir); |
| 592 | const frames = parseInt(args.frames as string, 10) || 5; |
| 593 | const timeout = parseInt(args.timeout as string, 10) || 5000; |
| 594 | const atTimestamps = args.at |
| 595 | ? String(args.at) |
| 596 | .split(",") |
| 597 | .map((s) => parseFloat(s.trim())) |
| 598 | .filter((n) => !isNaN(n)) |
| 599 | : undefined; |
| 600 | // Gemini frame analysis runs by default (silently skipped if |
| 601 | // GEMINI_API_KEY is not set). `--describe "custom question"` overrides |
| 602 | // the default prompt with a targeted question. `--describe false` opts |
| 603 | // out entirely. |
| 604 | const describeArg = |
| 605 | args.describe === undefined |
| 606 | ? "true" |
| 607 | : String(args.describe) === "false" |
| 608 | ? null |
| 609 | : String(args.describe); |
| 610 | |
| 611 | const camera = args.angle ? parseAngle(String(args.angle)) : undefined; |
| 612 | |
| 613 | const label = atTimestamps |
| 614 | ? `${atTimestamps.length} frames at [${atTimestamps.map((t) => t.toFixed(1) + "s").join(", ")}]` |
| 615 | : `${frames} frames`; |
| 616 | const angleLabel = |
| 617 | camera && (camera.yaw !== 0 || camera.pitch !== 0) |
| 618 | ? ` ${c.dim(`(angle yaw ${camera.yaw}° pitch ${camera.pitch}°)`)}` |
| 619 | : ""; |
| 620 | console.log(`${c.accent("◆")} Capturing ${label} from ${c.accent(project.name)}${angleLabel}`); |
| 621 | |
| 622 | try { |
| 623 | const snapshotDir = args.output |
| 624 | ? resolve(String(args.output)) |
| 625 | : join(project.dir, "snapshots"); |
| 626 | const paths = await captureSnapshots(project.dir, { |
| 627 | frames, |
| 628 | timeout, |
| 629 | at: atTimestamps, |
| 630 | outputDir: snapshotDir, |
| 631 | angle: camera, |
| 632 | includeEnd: args.end !== false, |
| 633 | }); |
| 634 | |
| 635 | if (paths.length === 0) { |
| 636 | console.log( |
| 637 | `\n${c.error("✗")} Could not determine composition duration — no frames captured`, |
| 638 | ); |
| 639 | process.exit(1); |
| 640 | } |
| 641 | |
| 642 | console.log( |
| 643 | `\n${c.success("◇")} ${paths.length} snapshots saved to ${args.output ? snapshotDir : "snapshots/"}`, |
| 644 | ); |
| 645 | for (const p of paths) { |
| 646 | console.log(` ${p}`); |
| 647 | } |
nothing calls this directly
no test coverage detected