import { ImageResponse } from "next/og"; import { api } from "@/lib/api"; import { fmtInt, fmtMw } from "@/lib/format"; /** * Shared Open Graph template (1200×630) — dark ink, thin world-grid texture, mark + wordmark, * title/tagline, stats strip. Entity pages reuse `renderOg({ title, subtitle, stats, eyebrow })`. * Satori subset of CSS only: flex layout, no grid, no CSS vars. */ export const OG_SIZE = { width: 1200, height: 630 }; export const OG_CONTENT_TYPE = "image/png"; const INK = "#0b0f14"; const PAPER = "#eceae5"; const MUTED = "#8a9099"; const ACCENT = "#e8763a"; const HAIR = "rgba(236,234,229,0.08)"; export interface OgStat { label: string; value: string; } export interface OgProps { eyebrow?: string; title: string; subtitle?: string; stats?: OgStat[]; footer?: string; } function Mark({ size }: { size: number }) { return ( ); } function Graticule() { const cols = Array.from({ length: 25 }, (_, i) => i * 50); const rows = Array.from({ length: 13 }, (_, i) => i * 50); return (
{cols.map((x) => (
))} {rows.map((y) => (
))} {/* meridian arcs as thin ellipses */} {[220, 420, 640].map((rx, i) => (
))}
); } export function OgTemplate({ eyebrow, title, subtitle, stats, footer }: OgProps) { return (
DataCenter Index
{eyebrow &&
{eyebrow}
}
40 ? 52 : 64, fontWeight: 600, letterSpacing: -2, lineHeight: 1.05 }}>{title}
{subtitle &&
{subtitle}
}
{(stats ?? []).map((s, i) => (
{s.value}
{s.label}
))}
{footer ?? "www.datacenterindex.io"} published figures · sourced · versioned
); } /** Live stats for the default share image, with a graceful fallback when the API is down. */ export async function ogDashboardStats(): Promise { const r = await api.dashboard(); if (!r.ok || !r.data?.stats) return [{ label: "Global coverage", value: "Facilities · MW · Countries" }]; const s = r.data.stats; return [ { label: "Facilities indexed", value: fmtInt(s.facilities) }, { label: "Known operational MW", value: fmtMw(s.knownOperationalMw) }, { label: "Under construction (≈)", value: fmtMw(s.constructionMw) }, { label: "Countries", value: fmtInt(s.countries) }, { label: "Events · 24 h", value: fmtInt(s.events24h) }, ]; } export function renderOg(props: OgProps): ImageResponse { return new ImageResponse(, { ...OG_SIZE }); }