// 관리 앱 2종(가온·두레) 전용 캡처 — 쇼케이스 카드 2컷(본컷 + 모달 -2컷)과 og.png. // capture-shots.mjs 는 CLI 크롬이라 모달을 열 수 없어 이 스크립트가 -2컷을 담당한다. import puppeteer from "puppeteer-core"; import path from "node:path"; import url from "node:url"; import { collectPageFailures, prepareStillPage, resolveChrome, routeForFile, startPublicServer, waitForStableVisual, } from "./capture-stable.mjs"; const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); const root = path.resolve(__dirname, ".."); const shots = path.join(root, "src", "assets", "shots"); const publicRoot = path.join(root, "public"); function requestedSlugs(argv) { const slugs = new Set(); for (let index = 2; index < argv.length; index += 1) { const argument = argv[index]; if (argument === "--slug") { const slug = argv[index + 1]; if (!slug) throw new Error("--slug requires an app slug"); slugs.add(slug); index += 1; } else if (argument.startsWith("--slug=")) { slugs.add(argument.slice("--slug=".length)); } else { throw new Error(`Unknown argument: ${argument}`); } } return slugs; } const requested = requestedSlugs(process.argv); const known = new Set(["gaon-lms", "dure-enrollment"]); const unknown = [...requested].filter((slug) => !known.has(slug)); if (unknown.length) throw new Error(`Unknown app slug(s): ${unknown.join(", ")}`); const selected = requested.size ? requested : known; const server = await startPublicServer(publicRoot); let browser; async function openSettled(app) { const page = await browser.newPage(); const failures = collectPageFailures(page, server.origin); await prepareStillPage(page); const file = path.join(publicRoot, "work", app, "index.html"); const response = await page.goto(server.origin + routeForFile(publicRoot, file), { waitUntil: "domcontentloaded", timeout: 30000, }); if (!response?.ok()) throw new Error(`${app} returned ${response?.status() ?? "no response"}`); await waitForStableVisual(page); if (failures.length) throw new Error(failures.join(" · ")); return page; } try { browser = await puppeteer.launch({ executablePath: resolveChrome(), headless: "new", args: ["--no-sandbox", "--use-angle=swiftshader", "--enable-unsafe-swiftshader", "--force-device-scale-factor=1"], defaultViewport: { width: 1440, height: 900, deviceScaleFactor: 1 }, }); if (selected.has("gaon-lms")) { // 가온: 대시보드 본컷 + 학적 카드(수강생 모달) -2컷 const page = await openSettled("gaon-lms"); try { await page.screenshot({ path: path.join(shots, "work-gaon-lms.png") }); await page.screenshot({ path: path.join(publicRoot, "work/gaon-lms/og.png") }); await page.evaluate(() => document.querySelector('[data-view="students"]').click()); await page.evaluate(() => document.querySelector("#stu-table [data-stu]").click()); await page.waitForSelector("#stu-dialog[open]"); await waitForStableVisual(page, { settleMs: 80 }); await page.screenshot({ path: path.join(shots, "work-gaon-lms-2.png") }); } finally { await page.close(); } } if (selected.has("dure-enrollment")) { // 두레: 과목 찾기 본컷 + 강의계획(과목 모달) -2컷 const page = await openSettled("dure-enrollment"); try { await page.screenshot({ path: path.join(shots, "work-dure-enrollment.png") }); await page.screenshot({ path: path.join(publicRoot, "work/dure-enrollment/og.png") }); await page.evaluate(() => document.querySelector(".course-row").click()); await page.waitForSelector("#course-dialog[open]"); await waitForStableVisual(page, { settleMs: 80 }); await page.screenshot({ path: path.join(shots, "work-dure-enrollment-2.png") }); } finally { await page.close(); } } } finally { if (browser) await browser.close(); await server.close(); } console.log("정착 상태 work shots + og.png 갱신 완료");