Website: 8-bit Open Graph card drawn pixel by pixel; old screenshot assets removed
CI / Typecheck, lint, tests, e2e (push) Successful in 3m22s

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>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-10 00:09:05 -04:00
1 parent 2d0bff3f98
commit b21db0e07a
17 files changed
+147 -3

No files matched your search

+2 -2
View File
@@ -20,9 +20,9 @@ export const metadata: Metadata = {
siteName: "AgentCompany",
locale: "en_US",
type: "website",
images: [{ url: "/images/og.jpg", width: 1200, height: 630, alt: "The AgentCompany desktop app" }],
images: [{ url: "/images/og.png", width: 1200, height: 630, alt: "AgentCompany in 8-bit: run an autonomous AI company on your desktop" }],
},
twitter: { card: "summary_large_image", title, description, images: ["/images/og.jpg"] },
twitter: { card: "summary_large_image", title, description, images: ["/images/og.png"] },
robots: { index: true, follow: true },
icons: {
icon: [
+2 -1
View File
@@ -9,7 +9,8 @@
"start": "next start",
"preview": "wrangler pages dev out",
"test:e2e": "next build && playwright test",
"test:unit": "node --import ./tests/unit/resolve-ts.mjs --test 'tests/unit/*.test.ts'"
"test:unit": "node --import ./tests/unit/resolve-ts.mjs --test 'tests/unit/*.test.ts'",
"og": "node scripts/og.mjs"
},
"dependencies": {
"next": "16.2.6",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

+143
View File
@@ -0,0 +1,143 @@
// 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`);