Add previews!
This commit is contained in:
parent
a08692bef0
commit
432bf550ff
|
|
@ -0,0 +1,162 @@
|
|||
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());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -9,6 +9,7 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.4.0"
|
||||
"vite": "^5.4.0",
|
||||
"puppeteer": "^24.15.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
src/tank.js
15
src/tank.js
|
|
@ -2,6 +2,7 @@ import "./tank.css";
|
|||
|
||||
export function renderTank(app) {
|
||||
document.title = "cumtanks.snowsune.net";
|
||||
updateMetaTags();
|
||||
app.classList.remove("page-clones");
|
||||
app.innerHTML = `
|
||||
<section class="layout">
|
||||
|
|
@ -161,3 +162,17 @@ function mixColor(hours) {
|
|||
const channel = Math.round(255 + (136 - 255) * t);
|
||||
return `rgb(${channel},${channel},${channel})`;
|
||||
}
|
||||
|
||||
function updateMetaTags() {
|
||||
const ogImage = document.querySelector('meta[property="og:image"]');
|
||||
const twitterImage = document.querySelector('meta[name="twitter:image"]');
|
||||
const ogTitle = document.querySelector('meta[property="og:title"]');
|
||||
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
|
||||
|
||||
const previewUrl = "/main-preview.png";
|
||||
|
||||
if (ogImage) ogImage.setAttribute("content", previewUrl);
|
||||
if (twitterImage) twitterImage.setAttribute("content", previewUrl);
|
||||
if (ogTitle) ogTitle.setAttribute("content", "cumtanks.snowsune.net");
|
||||
if (twitterTitle) twitterTitle.setAttribute("content", "cumtanks.snowsune.net");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ const DEFAULT_CLONES = [
|
|||
|
||||
export async function renderClones(app) {
|
||||
document.title = "Vixi Clones";
|
||||
updateMetaTags();
|
||||
app.classList.add("page-clones");
|
||||
app.innerHTML = `
|
||||
<header class="clones-header">
|
||||
|
|
@ -96,6 +97,20 @@ function renderClone(clone) {
|
|||
`;
|
||||
}
|
||||
|
||||
function updateMetaTags() {
|
||||
const ogImage = document.querySelector('meta[property="og:image"]');
|
||||
const twitterImage = document.querySelector('meta[name="twitter:image"]');
|
||||
const ogTitle = document.querySelector('meta[property="og:title"]');
|
||||
const twitterTitle = document.querySelector('meta[name="twitter:title"]');
|
||||
|
||||
const previewUrl = "/vixiclones-preview.png";
|
||||
|
||||
if (ogImage) ogImage.setAttribute("content", previewUrl);
|
||||
if (twitterImage) twitterImage.setAttribute("content", previewUrl);
|
||||
if (ogTitle) ogTitle.setAttribute("content", "Vixi Clones");
|
||||
if (twitterTitle) twitterTitle.setAttribute("content", "Vixi Clones");
|
||||
}
|
||||
|
||||
function formatName(slug) {
|
||||
if (!slug) return "";
|
||||
return slug
|
||||
|
|
|
|||
|
|
@ -1,7 +1,21 @@
|
|||
import { defineConfig } from "vite";
|
||||
import { generateClonesPreview } from "./build/generate-preview.js";
|
||||
|
||||
export default defineConfig({
|
||||
server: {
|
||||
host: true
|
||||
}
|
||||
export default defineConfig(({ command }) => {
|
||||
return {
|
||||
server: {
|
||||
host: true
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
name: "generate-clones-preview",
|
||||
async closeBundle() {
|
||||
// Only generate previews during build, not in dev/serve mode
|
||||
if (command === "build") {
|
||||
await generateClonesPreview();
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue