← Files SkillquiverARCHIVED FILE
skills/design-ui/scripts/capture-static-page.cjs
4.86 KB · Oct 5, 2026 · 18:31 UTC
const fs = require('node:fs');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const { pathToFileURL } = require('node:url');
function parseWidths(values) {
const widths = values.map(value => Number(value));
if (widths.length === 0 || widths.some(width =>
!Number.isInteger(width) || width < 240 || width > 3840)) {
throw new Error('Viewport widths must be integers between 240 and 3840.');
}
return widths;
}
function browserCandidates(env = process.env, platform = process.platform) {
const candidates = [env.CHROME_PATH].filter(Boolean);
if (platform === 'win32') {
for (const root of [env.ProgramFiles, env['ProgramFiles(x86)'], env.LOCALAPPDATA]) {
if (!root) continue;
candidates.push(
path.join(root, 'Google', 'Chrome', 'Application', 'chrome.exe'),
path.join(root, 'Microsoft', 'Edge', 'Application', 'msedge.exe')
);
}
} else if (platform === 'darwin') {
candidates.push(
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'
);
} else {
candidates.push(
'/usr/bin/google-chrome',
'/usr/bin/google-chrome-stable',
'/usr/bin/chromium',
'/usr/bin/chromium-browser',
'/usr/bin/microsoft-edge'
);
}
return [...new Set(candidates)];
}
function findBrowser() {
return browserCandidates().find(candidate => fs.existsSync(candidate));
}
function captureArguments(pagePath, outputPath, width) {
return [
'--headless=new',
'--disable-gpu',
'--hide-scrollbars',
`--screenshot=${outputPath}`,
`--window-size=${width},900`,
pathToFileURL(pagePath).href
];
}
function inspectionFrameHtml(capturePath, width) {
return `<!doctype html><meta name="viewport" content="width=device-width"><style>` +
`html,body{margin:0;min-height:100%;background:#dce8e0}` +
`img{display:block;width:${width}px;height:900px;margin:0 auto}` +
`</style><img src="${pathToFileURL(capturePath).href}" alt="">`;
}
function captureStaticPage(pagePath, outputDirectory, widths, dependencies = {}) {
const resolvedPage = path.resolve(pagePath);
if (!fs.existsSync(resolvedPage)) throw new Error(`Page not found: ${resolvedPage}`);
const findBrowserImpl = dependencies.findBrowser || findBrowser;
const spawnSyncImpl = dependencies.spawnSync || spawnSync;
const browser = findBrowserImpl();
if (!browser) throw new Error('No installed Chrome, Chromium, or Edge executable was found.');
const resolvedOutput = path.resolve(outputDirectory);
fs.mkdirSync(resolvedOutput, { recursive: true });
const pageName = path.basename(resolvedPage, path.extname(resolvedPage));
return widths.map(width => {
const outputPath = path.join(resolvedOutput, `${pageName}-${width}.png`);
const result = spawnSyncImpl(
browser,
captureArguments(resolvedPage, outputPath, width),
{ encoding: 'utf8', windowsHide: true }
);
if (result.error || result.status !== 0 || !fs.existsSync(outputPath) ||
fs.statSync(outputPath).size === 0) {
const detail = result.error?.message || result.stderr?.trim() || `exit ${result.status}`;
throw new Error(`Capture failed at ${width}px: ${detail}`);
}
let inspectionPath = null;
if (width < 480) {
inspectionPath = path.join(resolvedOutput, `${pageName}-${width}-inspect.png`);
const framePath = path.join(resolvedOutput, `.${pageName}-${width}-inspect.html`);
fs.writeFileSync(framePath, inspectionFrameHtml(outputPath, width));
try {
const inspection = spawnSyncImpl(
browser,
captureArguments(framePath, inspectionPath, 720),
{ encoding: 'utf8', windowsHide: true }
);
if (inspection.error || inspection.status !== 0 ||
!fs.existsSync(inspectionPath) || fs.statSync(inspectionPath).size === 0) {
const detail = inspection.error?.message || inspection.stderr?.trim() ||
`exit ${inspection.status}`;
throw new Error(`Inspection frame failed at ${width}px: ${detail}`);
}
} finally {
fs.rmSync(framePath, { force: true });
}
}
return { width, height: 900, outputPath, inspectionPath, browser };
});
}
if (require.main === module) {
try {
const [pagePath, outputDirectory, ...widthValues] = process.argv.slice(2);
if (!pagePath || !outputDirectory) {
throw new Error('Usage: capture-static-page.cjs <page.html> <output-dir> <width...>');
}
const captures = captureStaticPage(pagePath, outputDirectory, parseWidths(widthValues));
process.stdout.write(`${JSON.stringify({ captures })}\n`);
} catch (error) {
process.stderr.write(`${error.message}\n`);
process.exitCode = 1;
}
}
module.exports = {
browserCandidates,
captureArguments,
captureStaticPage,
findBrowser,
inspectionFrameHtml,
parseWidths
};
SHA-256: 0cb88e28bf78c908ee351a34a35da31de4d6f0bcaf6f758b77c4354e1c78af30