diff --git a/docs/assets/main-first-image.png b/docs/assets/main-first-image.png index 3b03077b..40fa006f 100644 Binary files a/docs/assets/main-first-image.png and b/docs/assets/main-first-image.png differ diff --git a/web/app/[locale]/assets/landing-image.png b/web/app/[locale]/assets/landing-image.png index 3b03077b..40fa006f 100644 Binary files a/web/app/[locale]/assets/landing-image.png and b/web/app/[locale]/assets/landing-image.png differ diff --git a/web/app/[locale]/assets/og-screenshot.png b/web/app/[locale]/assets/og-screenshot.png new file mode 100644 index 00000000..24bfb623 Binary files /dev/null and b/web/app/[locale]/assets/og-screenshot.png differ diff --git a/web/app/[locale]/blog/cmd-shift-u/page.tsx b/web/app/[locale]/blog/cmd-shift-u/page.tsx index 815d4790..e87e3fab 100644 --- a/web/app/[locale]/blog/cmd-shift-u/page.tsx +++ b/web/app/[locale]/blog/cmd-shift-u/page.tsx @@ -21,7 +21,7 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s url, }, twitter: { - card: "summary", + card: "summary_large_image", title: t("metaTitle"), description: t("metaDescription"), }, diff --git a/web/app/[locale]/blog/introducing-cmux/page.tsx b/web/app/[locale]/blog/introducing-cmux/page.tsx index ef5a7ddc..82dadc10 100644 --- a/web/app/[locale]/blog/introducing-cmux/page.tsx +++ b/web/app/[locale]/blog/introducing-cmux/page.tsx @@ -21,7 +21,7 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s url, }, twitter: { - card: "summary", + card: "summary_large_image", title: t("metaTitle"), description: t("metaDescription"), }, diff --git a/web/app/[locale]/blog/show-hn-launch/page.tsx b/web/app/[locale]/blog/show-hn-launch/page.tsx index 9783c70e..71eb3bd9 100644 --- a/web/app/[locale]/blog/show-hn-launch/page.tsx +++ b/web/app/[locale]/blog/show-hn-launch/page.tsx @@ -25,7 +25,7 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s url, }, twitter: { - card: "summary", + card: "summary_large_image", title: t("metaTitle"), description: t("metaDescription"), }, diff --git a/web/app/[locale]/blog/zen-of-cmux/page.tsx b/web/app/[locale]/blog/zen-of-cmux/page.tsx index 79439987..cff31a9c 100644 --- a/web/app/[locale]/blog/zen-of-cmux/page.tsx +++ b/web/app/[locale]/blog/zen-of-cmux/page.tsx @@ -21,7 +21,7 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s url, }, twitter: { - card: "summary", + card: "summary_large_image", title: t("metaTitle"), description: t("metaDescription"), }, diff --git a/web/app/[locale]/layout.tsx b/web/app/[locale]/layout.tsx index cc230873..32c2cc03 100644 --- a/web/app/[locale]/layout.tsx +++ b/web/app/[locale]/layout.tsx @@ -57,7 +57,7 @@ export async function generateMetadata({ type: "website", }, twitter: { - card: "summary", + card: "summary_large_image", title: t("title"), description: t("ogDescription"), }, diff --git a/web/app/[locale]/opengraph-image.tsx b/web/app/[locale]/opengraph-image.tsx new file mode 100644 index 00000000..8315d872 --- /dev/null +++ b/web/app/[locale]/opengraph-image.tsx @@ -0,0 +1,134 @@ +import { ImageResponse } from "next/og"; +import { readFile } from "fs/promises"; +import { join } from "path"; + +export const runtime = "nodejs"; +export const size = { width: 1200, height: 630 }; +export const contentType = "image/png"; +export const alt = "cmux — The terminal built for multitasking"; + +const S = 2; // render at 2x for sharper images on social platforms + +export default async function Image() { + const [logoData, screenshotData, geistRegular, geistSemiBold] = + await Promise.all([ + readFile(join(process.cwd(), "public", "logo.png")), + readFile( + join(process.cwd(), "app", "[locale]", "assets", "og-screenshot.png") + ), + fetch( + "https://fonts.gstatic.com/s/geist/v4/gyBhhwUxId8gMGYQMKR3pzfaWI_RnOM4nQ.ttf" + ).then((res) => res.arrayBuffer()), + fetch( + "https://fonts.gstatic.com/s/geist/v4/gyBhhwUxId8gMGYQMKR3pzfaWI_RQuQ4nQ.ttf" + ).then((res) => res.arrayBuffer()), + ]); + + const logoSrc = `data:image/png;base64,${logoData.toString("base64")}`; + const screenshotSrc = `data:image/png;base64,${screenshotData.toString("base64")}`; + + return new ImageResponse( + ( +
+
+ {/* Screenshot */} +
+ +
+
+ + {/* Branding bar */} +
+
+ +
+
+ cmux +
+
+ The terminal built for multitasking +
+
+
+
+
+
+ ), + { + width: size.width * S, + height: size.height * S, + fonts: [ + { name: "Geist", data: geistRegular, weight: 400, style: "normal" }, + { name: "Geist", data: geistSemiBold, weight: 600, style: "normal" }, + ], + } + ); +}