Files
olivv-cs-ComfyUI-FunPack/tools/showcase/capture_clean.mjs
T
digital-garbageandClaude Fable 5 439a85f469 readme: show the Cutting Room - hero screenshots + gallery section
'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>
2026-07-02 07:01:27 +03:00

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); });