Files
AgentCompany/landing/scripts/og.mjs
T
ZacharyZhang-NYandClaude Opus 5.5 b21db0e07a
CI / Typecheck, lint, tests, e2e (push) Successful in 3m22s
Website: 8-bit Open Graph card drawn pixel by pixel; old screenshot assets removed
landing/scripts/og.mjs paints a 240x126 NES-palette scene with a hand-placed 5x7 font and writes public/images/og.png at x5 (1200x630). npm run og regenerates it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:09:05 -04:00

144 lines
6.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Draws the 8-bit Open Graph card at 240×126 and writes it ×5 as public/images/og.png.
import { writeFileSync } from "node:fs";
import { deflateSync } from "node:zlib";
const W = 240;
const H = 126;
const SCALE = 5;
const PAL = [
"#0f0f1b", "#1b1b3a", "#2a2a5e", "#3c3c86", "#565aa8", "#fcfcfc", "#f8d878", "#f0a030",
"#a8e4fc", "#58d854", "#d82800",
].map((hex) => [1, 3, 5].map((i) => Number.parseInt(hex.slice(i, i + 2), 16)));
const [VOID, NIGHT, DUSK, HAZE, ROOF, WHITE, LIGHT, GOLD, ICE, LEAF, RED] = PAL.keys();
const px = new Uint8Array(W * H);
const set = (x, y, c) => { if (x >= 0 && y >= 0 && x < W && y < H) px[y * W + x] = c; };
const rect = (x, y, w, h, c) => { for (let j = y; j < y + h; j++) for (let i = x; i < x + w; i++) set(i, j, c); };
const FONT = {
A: "01110 10001 10001 11111 10001 10001 10001", H: "10001 10001 10001 11111 10001 10001 10001",
C: "01111 10000 10000 10000 10000 10000 01111", D: "11110 10001 10001 10001 10001 10001 11110",
E: "11111 10000 11110 10000 10000 10000 11111", G: "01111 10000 10000 10111 10001 10001 01111",
I: "11111 00100 00100 00100 00100 00100 11111", K: "10001 10010 11100 10010 10001 10001 10001",
L: "10000 10000 10000 10000 10000 10000 11111", M: "10001 11011 10101 10101 10001 10001 10001",
N: "10001 11001 10101 10011 10001 10001 10001", O: "01110 10001 10001 10001 10001 10001 01110",
P: "11110 10001 10001 11110 10000 10000 10000", R: "11110 10001 10001 11110 10100 10010 10001",
S: "01111 10000 01110 00001 00001 00001 11110", T: "11111 00100 00100 00100 00100 00100 00100",
U: "10001 10001 10001 10001 10001 10001 01110", V: "10001 10001 10001 10001 01010 01010 00100",
W: "10001 10001 10001 10101 10101 11011 10001", X: "10001 01010 00100 00100 01010 10001 10001",
Y: "10001 01010 00100 00100 00100 00100 00100", 1: "00100 01100 00100 00100 00100 00100 01110",
5: "11111 10000 11110 00001 00001 10001 01110", ".": "00000 00000 00000 00000 00000 00000 00100",
"-": "00000 00000 00000 11111 00000 00000 00000", "/": "00001 00010 00010 00100 01000 01000 10000",
"·": "00000 00000 00000 00100 00000 00000 00000", "▶": "10000 11000 11100 11110 11100 11000 10000",
"!": "00100 00100 00100 00100 00100 00000 00100", " ": "00000 00000 00000 00000 00000 00000 00000",
};
function text(str, x, y, c, s = 1, shadow = -1) {
[...str].forEach((ch, n) => {
const rows = FONT[ch];
if (!rows) throw new Error(`No glyph for "${ch}".`);
rows.split(" ").forEach((row, j) => [...row].forEach((bit, i) => {
if (bit !== "1") return;
const gx = x + n * 6 * s + i * s;
const gy = y + j * s;
if (shadow >= 0) rect(gx + s, gy + s, s, s, shadow);
rect(gx, gy, s, s, c);
}));
});
}
const width = (str, s = 1) => str.length * 6 * s - s;
function sprite(art, x, y, map) {
art.trim().split("\n").forEach((row, j) => [...row.trim()].forEach((ch, i) => { if (ch !== ".") set(x + i, y + j, map[ch]); }));
}
// Night sky in dithered bands, stars and a crescent moon.
const bands = [NIGHT, NIGHT, DUSK, DUSK, HAZE];
for (let y = 0; y < 92; y++) {
const f = (y / 92) * (bands.length - 1);
const lo = Math.floor(f);
for (let x = 0; x < W; x++) set(x, y, f - lo > ((x + y * 2) % 4) / 4 + 0.12 ? bands[lo + 1] : bands[lo]);
}
let seed = 7;
const rand = () => (seed = (seed * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff;
for (let n = 0; n < 70; n++) set(Math.floor(rand() * W), Math.floor(rand() * 60), rand() > 0.85 ? LIGHT : WHITE);
for (let y = -9; y <= 9; y++) for (let x = -9; x <= 9; x++) {
if (x * x + y * y <= 81 && (x - 4) ** 2 + (y - 3) ** 2 > 64) set(206 + x, 18 + y, LIGHT);
}
// Skyline with lit windows.
for (let x = 0, n = 0; x < W; n++) {
const w = 10 + Math.floor(rand() * 14);
const h = x < 150 ? 8 + Math.floor(rand() * 12) : 18 + Math.floor(rand() * 26);
rect(x, 104 - h, w, h, n % 2 ? VOID : NIGHT);
for (let wy = 104 - h + 3; wy < 101; wy += 4) for (let wx = x + 2; wx < x + w - 2; wx += 3) if (rand() > 0.55) set(wx, wy, rand() > 0.2 ? LIGHT : GOLD);
x += w;
}
rect(0, 104, W, 22, VOID);
// The tower and the CEO on its roof.
rect(160, 58, 34, 46, NIGHT);
rect(158, 56, 38, 3, ROOF);
for (let y = 62; y < 102; y += 5) for (let x = 163; x < 192; x += 5) rect(x, y, 3, 3, (x + y) % 3 ? LIGHT : GOLD);
sprite(`
..G.G.G..
..GGGGG..
..SSSSS..
..KKSKK..
..SSSSS..
.WBBRBBW.
.SBBRBBS.
..BBBBB..
..BB.BB..
..KK.KK..
`, 172, 44, { G: GOLD, S: LIGHT, K: VOID, W: WHITE, B: DUSK, R: RED });
// Speech bubble from the CEO.
rect(150, 26, 47, 12, WHITE);
for (let i = 0; i < 4; i++) rect(174 + i, 38 + i, 4 - i, 1, WHITE);
text("HIRING!", 153, 28, VOID);
// Logotype, tagline and the dialog box.
text("AGENT", 10, 12, GOLD, 3, VOID);
text("COMPANY", 10, 38, WHITE, 3, VOID);
text("RUN AN AUTONOMOUS AI", 11, 66, ICE, 1, VOID);
text("COMPANY ON YOUR DESKTOP.", 11, 76, ICE, 1, VOID);
rect(6, 98, 228, 24, VOID);
rect(7, 99, 226, 22, WHITE);
rect(8, 100, 224, 20, NIGHT);
text("V1.5.1 · MACOS · WINDOWS · LINUX", 12, 103, WHITE);
text("▶", 12, 112, GOLD);
text("AGENT-COMPANY.DEV", 20, 112, LIGHT);
const free = "NO ACCOUNT";
text(free, 228 - width(free), 112, LEAF);
// PNG encoder: ×SCALE nearest-neighbour, RGB, no filter.
const rowLen = W * SCALE * 3 + 1;
const raw = Buffer.alloc(rowLen * H * SCALE);
for (let y = 0; y < H * SCALE; y++) {
raw[y * rowLen] = 0;
for (let x = 0; x < W * SCALE; x++) {
const [r, g, b] = PAL[px[Math.floor(y / SCALE) * W + Math.floor(x / SCALE)]];
raw.set([r, g, b], y * rowLen + 1 + x * 3);
}
}
const crcTable = Array.from({ length: 256 }, (_, n) => { let c = n; for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; return c >>> 0; });
const crc = (buf) => { let c = 0xffffffff; for (const byte of buf) c = crcTable[(c ^ byte) & 255] ^ (c >>> 8); return (c ^ 0xffffffff) >>> 0; };
const chunk = (type, data) => {
const out = Buffer.alloc(12 + data.length);
out.writeUInt32BE(data.length, 0);
out.write(type, 4, "ascii");
data.copy(out, 8);
out.writeUInt32BE(crc(out.subarray(4, 8 + data.length)), 8 + data.length);
return out;
};
const ihdr = Buffer.alloc(13);
ihdr.writeUInt32BE(W * SCALE, 0);
ihdr.writeUInt32BE(H * SCALE, 4);
ihdr.set([8, 2, 0, 0, 0], 8);
const png = Buffer.concat([Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]), chunk("IHDR", ihdr), chunk("IDAT", deflateSync(raw, { level: 9 })), chunk("IEND", Buffer.alloc(0))]);
writeFileSync(new URL("../public/images/og.png", import.meta.url), png);
console.log(`og.png ${W * SCALE}×${H * SCALE}, ${png.length} bytes`);