'What it looks like' section right under the intro: a clean full-workspace hero shot plus Composer and Engine Settings side by side, generated from the mocked tour demo (no ComfyUI/GPU needed) by a new tools/showcase/capture_clean.mjs (npm run capture:clean) - same scaffolding as capture.mjs but with the tour overlay stripped, so shots are undimmed and card-free. Images committed under docs/images/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
87 lines
3.5 KiB
JavaScript
87 lines
3.5 KiB
JavaScript
// Clean (no tour card, no spotlight dim) hero shots of the Cutting Room, using the
|
|
// same ?mode=tour mocked demo boot as capture.mjs — no ComfyUI, no models, no GPU.
|
|
// Output: out/hero-*.png. Run after capture.mjs (or standalone): `npm run capture:clean`.
|
|
|
|
import { chromium } from "playwright";
|
|
import http from "node:http";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const FRONTEND = path.resolve(__dirname, "../../movie_editor/frontend");
|
|
const OUT = path.resolve(__dirname, "out");
|
|
const VIEW = { width: 1440, height: 900 };
|
|
|
|
const MIME = {
|
|
".html": "text/html", ".js": "text/javascript", ".css": "text/css",
|
|
".json": "application/json", ".svg": "image/svg+xml", ".png": "image/png",
|
|
".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif",
|
|
".woff": "font/woff", ".woff2": "font/woff2", ".ttf": "font/ttf", ".map": "application/json",
|
|
};
|
|
|
|
function serve(dir) {
|
|
const server = http.createServer((req, res) => {
|
|
const urlPath = decodeURIComponent((req.url || "/").split("?")[0]);
|
|
const fp = path.normalize(path.join(dir, urlPath === "/" ? "/index.html" : urlPath));
|
|
if (!fp.startsWith(dir)) { res.writeHead(403); return res.end("forbidden"); }
|
|
fs.readFile(fp, (err, data) => {
|
|
if (err) { res.writeHead(404); return res.end("not found"); }
|
|
res.writeHead(200, { "content-type": MIME[path.extname(fp).toLowerCase()] || "application/octet-stream" });
|
|
res.end(data);
|
|
});
|
|
});
|
|
return new Promise((resolve) => server.listen(0, "127.0.0.1", () => resolve(server)));
|
|
}
|
|
|
|
async function main() {
|
|
fs.mkdirSync(OUT, { recursive: true });
|
|
const server = await serve(FRONTEND);
|
|
const { port } = server.address();
|
|
const browser = await chromium.launch();
|
|
const page = await browser.newPage({ viewport: VIEW, deviceScaleFactor: 2 });
|
|
await page.goto(`http://127.0.0.1:${port}/index.html?mode=tour`, { waitUntil: "load" });
|
|
await page.waitForFunction(
|
|
() => window.Store && document.getElementById("workspace"),
|
|
null, { timeout: 30000 },
|
|
);
|
|
await page.waitForTimeout(1800);
|
|
|
|
// If the tour auto-opened, strip its overlay — the mocked demo data stays.
|
|
const strip = () => page.evaluate(() => document.getElementById("tour-root")?.remove());
|
|
await strip();
|
|
|
|
// 1. Hero: the full workspace with the first generative clip selected.
|
|
await page.evaluate(() => {
|
|
const first = (window.Store.get().project?.scenes || [])[0];
|
|
if (first) window.Store.selectScene(first.id);
|
|
});
|
|
await page.waitForTimeout(600);
|
|
await strip();
|
|
await page.screenshot({ path: path.join(OUT, "hero-cutting-room.png") });
|
|
console.log("hero-cutting-room.png");
|
|
|
|
// 2. Composer: global prompt + shortcut/split libraries in the floating window.
|
|
await page.evaluate(() => { if (!window.Composer.isOpen()) window.Composer.toggle(); });
|
|
await page.waitForTimeout(700);
|
|
await strip();
|
|
await page.screenshot({ path: path.join(OUT, "hero-composer.png") });
|
|
console.log("hero-composer.png");
|
|
|
|
// 3. Engine Settings: Studio + Chain Sampler knobs without touching a graph.
|
|
await page.evaluate(() => {
|
|
if (window.Composer.isOpen()) window.Composer.toggle();
|
|
window.EngineSettingsModal.open();
|
|
});
|
|
await page.waitForTimeout(700);
|
|
await strip();
|
|
await page.screenshot({ path: path.join(OUT, "hero-engine-settings.png") });
|
|
console.log("hero-engine-settings.png");
|
|
|
|
await browser.close();
|
|
server.close();
|
|
console.log(`Done — clean shots in ${OUT}`);
|
|
}
|
|
|
|
main().catch((e) => { console.error(e); process.exit(1); });
|