cumtanks.snowsune.net/build/generate-preview.js

163 lines
4.3 KiB
JavaScript

import puppeteer from "puppeteer";
import { createServer } from "http";
import { readFileSync, existsSync } from "fs";
import { join, dirname, extname } from "path";
import { fileURLToPath } from "url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, "..");
const distDir = join(root, "dist");
const PORT = 5174;
async function startServer() {
const httpServer = createServer((req, res) => {
const decodedUrl = decodeURIComponent(req.url || "/");
let filePath = join(distDir, decodedUrl === "/" ? "index.html" : decodedUrl);
if (!existsSync(filePath) || extname(filePath) === "") {
filePath = join(distDir, "index.html");
}
if (existsSync(filePath)) {
const content = readFileSync(filePath);
const ext = extname(filePath);
const contentType =
ext === ".html" ? "text/html" :
ext === ".js" ? "application/javascript" :
ext === ".css" ? "text/css" :
ext === ".json" ? "application/json" :
ext === ".png" ? "image/png" :
ext === ".jpg" || ext === ".jpeg" ? "image/jpeg" :
"application/octet-stream";
res.writeHead(200, { "Content-Type": contentType });
res.end(content);
} else {
res.writeHead(404);
res.end("Not found");
}
});
await new Promise((resolve, reject) => {
httpServer.listen(PORT, () => {
console.log(`Preview server running on http://localhost:${PORT}`);
resolve();
});
httpServer.on("error", reject);
});
return httpServer;
}
async function generatePreview(outputPath, uri, selector) {
const browser = await puppeteer.launch({
headless: true,
args: ["--no-sandbox", "--disable-setuid-sandbox"]
});
try {
const page = await browser.newPage();
await page.setViewport({
width: 1400,
height: 2000,
deviceScaleFactor: 2
});
await page.goto(`http://localhost:${PORT}${uri}`, {
waitUntil: "networkidle0"
});
await new Promise((resolve) => setTimeout(resolve, 1000));
await page.waitForSelector(selector, { timeout: 10000 });
// Wait for images to load
await page.evaluate(() => {
return Promise.all(
Array.from(document.images).map((img) => {
if (img.complete) return Promise.resolve();
return new Promise((resolve) => {
img.onload = resolve;
img.onerror = resolve;
setTimeout(resolve, 3000);
});
})
);
});
// For tank page, ensure stage renders at full size and wait for layers
if (uri === "/") {
await page.evaluate(() => {
const layout = document.querySelector(".layout");
if (layout) {
layout.style.width = "1400px";
layout.style.minWidth = "1200px";
}
const stage = document.querySelector(".stage");
if (stage) {
stage.style.flex = "1 1 1200px";
stage.style.width = "1200px";
stage.style.maxWidth = "1200px";
}
});
await new Promise((resolve) => setTimeout(resolve, 500));
// Wait for liquid layers to appear and have height
await page.waitForFunction(
() => {
const layers = document.querySelectorAll(".liquid-layer");
return layers.length > 0 && Array.from(layers).some(l => l.offsetHeight > 0);
},
{ timeout: 10000 }
);
}
await new Promise((resolve) => setTimeout(resolve, 500));
const element = await page.$(selector);
if (element) {
await element.screenshot({
path: outputPath,
type: "png"
});
console.log(`✓ Generated preview: ${outputPath}`);
}
} finally {
await browser.close();
}
}
export async function generateClonesPreview() {
if (!existsSync(distDir) || !existsSync(join(distDir, "index.html"))) {
return;
}
let httpServer;
try {
httpServer = await startServer();
await generatePreview(
join(distDir, "vixiclones-preview.png"),
"/vixiclones",
".clones-rows"
);
await generatePreview(
join(distDir, "main-preview.png"),
"/",
".stage"
);
} catch (error) {
console.error("Failed to generate previews:", error);
throw error;
} finally {
if (httpServer) {
await new Promise((resolve) => {
httpServer.close(() => resolve());
});
}
}
}