-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrender.js
More file actions
75 lines (63 loc) · 2.64 KB
/
Copy pathrender.js
File metadata and controls
75 lines (63 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// render.js
// Turns a euromancer post page into a series of Instagram-ready PNGs.
// Usage: node render.js <post-url-path>
// Example: node render.js /euromancer/archive/0001/CityNowhen/
import { chromium } from "playwright";
import { mkdir } from "node:fs/promises";
import path from "node:path";
// ── Config ─────────────────────────────────────────────
const DEV_SERVER = "http://localhost:8080";
const IG_WIDTH = 1080;
const IG_HEIGHT = 1350;
const OUTPUT_ROOT = "slides";
// ── Parse argv ──────────────────────────────────────────
const postPath = process.argv[2];
if (!postPath) {
console.error("Usage: node render.js <post-url-path>");
console.error("Example: node render.js /euromancer/archive/0001/CityNowhen/");
process.exit(1);
}
const url = DEV_SERVER + postPath;
const slug = postPath.replace(/\/$/, "").split("/").slice(-2).join("-");
const outDir = path.join(OUTPUT_ROOT, slug);
// ── Main ────────────────────────────────────────────────
await mkdir(outDir, { recursive: true });
console.log(`→ Opening ${url}`);
const browser = await chromium.launch();
const context = await browser.newContext({
viewport: { width: IG_WIDTH, height: IG_HEIGHT },
});
const page = await context.newPage();
await page.goto(url, { waitUntil: "networkidle" });
await page.evaluate(async () => {
document.body.classList.add("render-mode");
await document.fonts.ready;
});
const slides = page.locator(".slide");
const count = await slides.count();
console.log(`→ Found ${count} slides`);
for (let i = 0; i < count; i++) {
const slide = slides.nth(i);
const filename = `slide-${String(i + 1).padStart(2, "0")}.png`;
const filepath = path.join(outDir, filename);
if (i === 0) {
// Size cover slide to fill 1350px minus header height, so header+cover = exactly 1350px
const header = page.locator("header");
const headerBox = await header.boundingBox();
const coverHeight = IG_HEIGHT - headerBox.height;
await slide.evaluate((el, h) => {
el.style.height = `${h}px`;
el.style.minHeight = `${h}px`;
}, coverHeight);
await page.screenshot({
path: filepath,
clip: { x: 0, y: 0, width: IG_WIDTH, height: IG_HEIGHT }
});
} else {
await slide.scrollIntoViewIfNeeded();
await slide.screenshot({ path: filepath });
}
console.log(` ${filename}`);
}
await browser.close();
console.log(`✓ Wrote ${count} PNGs to ${outDir}/`);