diff --git a/apps/web/netlify/edge-functions/og.tsx b/apps/web/netlify/edge-functions/og.tsx index 2617c7dab7..0df0d881cb 100644 --- a/apps/web/netlify/edge-functions/og.tsx +++ b/apps/web/netlify/edge-functions/og.tsx @@ -11,7 +11,13 @@ const templateSchema = z.object({ headers: z.array(z.string()), }); -const OGSchema = z.discriminatedUnion("type", [templateSchema]); +const changelogSchema = z.object({ + type: z.literal("changelog"), + version: z.string(), + isNightly: z.boolean().optional().default(false), +}); + +const OGSchema = z.discriminatedUnion("type", [templateSchema, changelogSchema]); function parseSearchParams(url: URL): z.infer | null { const type = url.searchParams.get("type"); @@ -19,6 +25,14 @@ function parseSearchParams(url: URL): z.infer | null { return null; } + if (type === "changelog") { + const version = url.searchParams.get("version"); + const isNightly = url.searchParams.get("isNightly") === "true"; + + const result = OGSchema.safeParse({ type, version, isNightly }); + return result.success ? result.data : null; + } + const title = url.searchParams.get("title"); const headers = url.searchParams.getAll("headers"); @@ -88,10 +102,127 @@ function renderTemplate(params: z.infer) { ); } +function renderChangelogTemplate(params: z.infer) { + const background = params.isNightly + ? "linear-gradient(180deg, #03BCF1 0%, #127FE5 100%)" + : "linear-gradient(180deg, #A8A29E 0%, #57534E 100%)"; + + return ( +
+ {/* Header section */} +
+ {/* Left: Hyprnote Changelog */} +
+ Hyprnote Changelog +
+ + {/* Center: Line */} +
+ + {/* Right: Version */} +
+ {params.version} +
+
+ + {/* Image section */} +
+
+ {/* Shadow layer */} +
+ {/* Image */} + +
+
+
+ ); +} + export default async function handler(req: Request) { const url = new URL(req.url); - if (url.hostname === "localhost" || url.hostname === "127.0.0.1") { + // Disable in development to avoid WASM loading issues + if ( + url.hostname === "localhost" || + url.hostname === "127.0.0.1" || + Deno.env.get("CONTEXT") === "dev" + ) { return new Response("OG image generation disabled in dev", { status: 503, headers: { "Content-Type": "text/plain" }, @@ -109,7 +240,35 @@ export default async function handler(req: Request) { try { // https://unpic.pics/og-edge - return new ImageResponse(renderTemplate(params)); + let response; + if (params.type === "changelog") { + response = renderChangelogTemplate(params); + } else { + response = renderTemplate(params); + } + + const fonts = params.type === "changelog" + ? [ + { + name: "Lora", + data: await fetch( + "https://fonts.gstatic.com/s/lora/v37/0QI6MX1D_JOuGQbT0gvTJPa787z5vCJG.ttf" + ).then((res) => res.arrayBuffer()), + weight: 700, + style: "normal", + }, + { + name: "IBM Plex Mono", + data: await fetch( + "https://fonts.gstatic.com/s/ibmplexmono/v20/-F63fjptAgt5VM-kVkqdyU8n5ig.ttf" + ).then((res) => res.arrayBuffer()), + weight: 400, + style: "normal", + }, + ] + : undefined; + + return new ImageResponse(response, { fonts }); } catch (error) { console.error("OG image generation failed:", error); return new Response(JSON.stringify({ error: "image_generation_failed" }), { @@ -123,4 +282,5 @@ export default async function handler(req: Request) { export const config = { path: "/og", cache: "manual", + excludedPath: Deno.env.get("CONTEXT") === "dev" ? "/og" : undefined, };