app
Hero Studio
Public Made by Adomby adom
A live playground for the hero image v2 definition: the lower third of every hero is a true-text overlay (type pill, trending score, stars, title, author, wiki slug) rendered by the surface, never bak
// Renders the studio's ?render=1 view to a PNG. Usage: node render-hero.mjs <url> <out.png>
// Needs puppeteer-core (PUPPETEER_CORE env or a known location) and Chrome (CHROME env or
// the puppeteer cache). Exits nonzero with a plain message when either is missing.
import { existsSync, readdirSync } from 'fs';
import { homedir } from 'os';
const [,, url, out] = process.argv;
async function loadPuppeteer() {
const candidates = [
process.env.PUPPETEER_CORE,
'/home/adom/project/node_modules/puppeteer-core/lib/puppeteer/puppeteer-core.js',
homedir() + '/node_modules/puppeteer-core/lib/puppeteer/puppeteer-core.js',
].filter(Boolean);
for (const c of candidates) {
try { return (await import(c)).default; } catch {}
}
try { return (await import('puppeteer-core')).default; } catch {}
throw new Error('puppeteer-core not found. Set PUPPETEER_CORE to its puppeteer-core.js path.');
}
function findChrome() {
if (process.env.CHROME && existsSync(process.env.CHROME)) return process.env.CHROME;
const base = homedir() + '/.cache/puppeteer/chrome';
if (existsSync(base)) {
for (const d of readdirSync(base).sort().reverse()) {
const p = base + '/' + d + '/chrome-linux64/chrome';
if (existsSync(p)) return p;
}
}
throw new Error('Chrome not found. Set CHROME to a Chrome/Chromium binary path.');
}
const puppeteer = await loadPuppeteer();
const browser = await puppeteer.launch({
executablePath: findChrome(), headless: 'new',
args: ['--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars',
'--font-render-hinting=none', '--force-color-profile=srgb'],
});
try {
const page = await browser.newPage();
await page.setViewport({ width: 1680, height: 1080, deviceScaleFactor: 2 });
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
await new Promise(r => setTimeout(r, 1200)); // fonts + images settle
const hero = await page.$('#hero');
const box = await hero.boundingBox();
await page.screenshot({ path: out, clip: box });
console.log('OK ' + out);
} finally {
await browser.close();
}