feat: strengthen design skill and refresh all showcases
Some checks failed
ci / build (push) Has been cancelled

This commit is contained in:
Yun Chan 2026-09-12 18:16:33 +09:00
parent 69e5cc1efd
commit e09d4efb1c
257 changed files with 8196 additions and 2374 deletions

View file

@ -1,12 +1,21 @@
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import puppeteer from "puppeteer-core";
import {
collectPageFailures,
prepareStillPage,
resolveChrome,
routeForFile,
startPublicServer,
waitForStableVisual,
} from "./capture-stable.mjs";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(__dirname, "..");
const chromePath = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";
const shotsDir = path.join(rootDir, "src", "assets", "shots");
const publicRoot = path.join(rootDir, "public");
function requestedSlugs(argv) {
const slugs = new Set();
@ -110,6 +119,24 @@ const targets = [
altUrl: path.join(rootDir, "public", "work", "dure-enrollment", "index.html"),
altOut: path.join(shotsDir, "work-dure-enrollment-2.png"),
},
{
url: path.join(rootDir, "public", "work", "cheongon-care", "index.html"),
out: path.join(shotsDir, "work-cheongon-care.png"),
altUrl: path.join(rootDir, "public", "work", "cheongon-care", "protocol", "index.html"),
altOut: path.join(shotsDir, "work-cheongon-care-2.png"),
},
{
url: path.join(rootDir, "public", "work", "cask-archive", "index.html"),
out: path.join(shotsDir, "work-cask-archive.png"),
altUrl: path.join(rootDir, "public", "work", "cask-archive", "docent", "index.html"),
altOut: path.join(shotsDir, "work-cask-archive-2.png"),
},
{
url: path.join(rootDir, "public", "work", "materia-interior", "index.html"),
out: path.join(shotsDir, "work-materia-interior.png"),
altUrl: path.join(rootDir, "public", "work", "materia-interior", "projects", "hannam-duplex", "index.html"),
altOut: path.join(shotsDir, "work-materia-interior-2.png"),
},
];
const requested = requestedSlugs(process.argv);
@ -119,39 +146,49 @@ const unknown = [...requested].filter((slug) => !known.has(slug));
if (unknown.length) throw new Error(`Unknown showcase slug(s): ${unknown.join(", ")}`);
const selectedTargets = requested.size ? targets.filter((target) => requested.has(slugOf(target))) : targets;
console.log("📸 Capturing high-resolution screenshots...");
console.log("📸 Capturing settled, high-resolution screenshots...");
const shot = (t, url, out) => {
const flags = t.webgl
? ["--no-sandbox", "--use-angle=swiftshader"] // --disable-gpu 는 WebGL 을 죽인다
: ["--disable-gpu", "--no-sandbox"];
execFileSync(chromePath, [
"--headless=new",
"--hide-scrollbars",
...flags,
"--force-device-scale-factor=1",
"--window-size=1440,900",
`--screenshot=${out}`,
`file://${url}`,
]);
};
const server = await startPublicServer(publicRoot);
let browser;
for (const t of selectedTargets) {
console.log(`- Capturing ${path.basename(t.out)}`);
async function shot(file, output) {
const page = await browser.newPage();
const failures = collectPageFailures(page, server.origin);
try {
shot(t, t.url, t.out);
} catch (e) {
console.error(`Error capturing ${t.out}:`, e.message);
}
if (t.altOut) {
console.log(`- Capturing ${path.basename(t.altOut)}`);
try {
shot(t, t.altUrl, t.altOut);
} catch (e) {
console.error(`Error capturing ${t.altOut}:`, e.message);
}
await prepareStillPage(page);
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
const route = routeForFile(publicRoot, file);
const response = await page.goto(server.origin + route, { waitUntil: "domcontentloaded", timeout: 30000 });
if (!response?.ok()) throw new Error(`${route} returned ${response?.status() ?? "no response"}`);
await waitForStableVisual(page);
if (failures.length) throw new Error(failures.join(" · "));
await page.screenshot({ path: output, type: "png", fullPage: false });
const size = Math.round(fs.statSync(output).size / 1024);
console.log(`CAPTURE ${path.basename(output)} (${size}KB)`);
} finally {
await page.close();
}
}
console.log("✅ All screenshots captured successfully!");
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 },
});
for (const target of selectedTargets) {
await shot(target.url, target.out);
if (target.altOut) await shot(target.altUrl, target.altOut);
}
} finally {
if (browser) await browser.close();
await server.close();
}
console.log("✅ All requested screenshots captured after visual settling.");