From 0265e4732f87464d2691c1f3e8e04d34f98233bb Mon Sep 17 00:00:00 2001 From: nicoalbanese <49612682+nicoalbanese@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:16:05 +0000 Subject: [PATCH 1/4] feat: add social media preview images and metadata --- apps/web/app/layout.tsx | 6 +- apps/web/app/opengraph-image.tsx | 200 +++++++++++++++++++++++++++++++ apps/web/app/twitter-image.tsx | 1 + 3 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 apps/web/app/opengraph-image.tsx create mode 100644 apps/web/app/twitter-image.tsx diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 90411b945..261841732 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -52,11 +52,15 @@ export const metadata: Metadata = { default: "Open Agents", template: "%s | Open Agents", }, - description: "Open Agents web app for managing AI coding sessions.", + description: + "Spawn coding agents that run infinitely in the cloud. Powered by AI SDK, Gateway, Sandbox, and Workflow DevKit.", icons: { icon: faviconPath, shortcut: faviconPath, }, + twitter: { + card: "summary_large_image", + }, }; export default function RootLayout({ diff --git a/apps/web/app/opengraph-image.tsx b/apps/web/app/opengraph-image.tsx new file mode 100644 index 000000000..2feee78cd --- /dev/null +++ b/apps/web/app/opengraph-image.tsx @@ -0,0 +1,200 @@ +import { ImageResponse } from "next/og"; + +export const alt = "Open Agents — Spawn coding agents that run in the cloud"; +export const size = { width: 1200, height: 630 }; +export const contentType = "image/png"; +export const runtime = "edge"; + +export default function OgImage() { + return new ImageResponse( +
+ {/* Subtle radial glow — top-left warm, bottom-right cool */} +
+ + {/* Noise-ish grain approximation with faint horizontal lines */} +
+ + {/* Border frame */} +
+ + {/* Content */} +
+ {/* Top section */} +
+ {/* Logo / icon + wordmark row */} +
+ + + + + + Open Agents + +
+ + {/* Hero heading */} +
+ Open Agents. +
+ + {/* Subtitle */} +
+ Spawn coding agents that run infinitely in the cloud. +
+
+ + {/* Bottom row — tech pills */} +
+ + + + + + {/* Spacer + domain */} +
+ + open-agents.dev + +
+
+
+
, + { ...size }, + ); +} + +function TechPill({ label }: { label: string }) { + return ( +
+ {label} +
+ ); +} diff --git a/apps/web/app/twitter-image.tsx b/apps/web/app/twitter-image.tsx new file mode 100644 index 000000000..1b687684a --- /dev/null +++ b/apps/web/app/twitter-image.tsx @@ -0,0 +1 @@ +export { default, alt, size, contentType, runtime } from "./opengraph-image"; From 257dc7d85fed3c777627f5d762a0e13359606733 Mon Sep 17 00:00:00 2001 From: nicoalbanese <49612682+nicoalbanese@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:19:19 +0000 Subject: [PATCH 2/4] refactor: reorder sandbox-lifecycle in manifest --- .../.well-known/workflow/v1/manifest.json | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/web/public/.well-known/workflow/v1/manifest.json b/apps/web/public/.well-known/workflow/v1/manifest.json index eea5512e3..7a81c28cb 100644 --- a/apps/web/public/.well-known/workflow/v1/manifest.json +++ b/apps/web/public/.well-known/workflow/v1/manifest.json @@ -6,17 +6,6 @@ "stepId": "step//workflow@4.2.0-beta.74//fetch" } }, - "app/workflows/sandbox-lifecycle.ts": { - "clearLifecycleRunIdIfOwned": { - "stepId": "step//./app/workflows/sandbox-lifecycle//clearLifecycleRunIdIfOwned" - }, - "computeLifecycleWakeDecision": { - "stepId": "step//./app/workflows/sandbox-lifecycle//computeLifecycleWakeDecision" - }, - "runLifecycleEvaluation": { - "stepId": "step//./app/workflows/sandbox-lifecycle//runLifecycleEvaluation" - } - }, "node_modules/workflow/dist/internal/builtins.js": { "__builtin_response_array_buffer": { "stepId": "__builtin_response_array_buffer" @@ -28,6 +17,17 @@ "stepId": "__builtin_response_text" } }, + "app/workflows/sandbox-lifecycle.ts": { + "clearLifecycleRunIdIfOwned": { + "stepId": "step//./app/workflows/sandbox-lifecycle//clearLifecycleRunIdIfOwned" + }, + "computeLifecycleWakeDecision": { + "stepId": "step//./app/workflows/sandbox-lifecycle//computeLifecycleWakeDecision" + }, + "runLifecycleEvaluation": { + "stepId": "step//./app/workflows/sandbox-lifecycle//runLifecycleEvaluation" + } + }, "app/workflows/chat-post-finish.ts": { "clearActiveStream": { "stepId": "step//./app/workflows/chat-post-finish//clearActiveStream" @@ -176,4 +176,4 @@ } }, "classes": {} -} +} \ No newline at end of file From eba9ea1ad7e459ee6e3b6dea15cd5abec23c8d85 Mon Sep 17 00:00:00 2001 From: nicoalbanese <49612682+nicoalbanese@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:24:46 +0000 Subject: [PATCH 3/4] style: redesign og image layout and colors --- apps/web/app/[username]/og/route.tsx | 214 ++++++++++------ .../app/shared/[shareId]/opengraph-image.tsx | 238 ++++++++++++++++++ .../.well-known/workflow/v1/manifest.json | 2 +- 3 files changed, 371 insertions(+), 83 deletions(-) create mode 100644 apps/web/app/shared/[shareId]/opengraph-image.tsx diff --git a/apps/web/app/[username]/og/route.tsx b/apps/web/app/[username]/og/route.tsx index c56f3ae40..95f886be7 100644 --- a/apps/web/app/[username]/og/route.tsx +++ b/apps/web/app/[username]/og/route.tsx @@ -26,9 +26,8 @@ export async function GET(request: Request, context: OgRouteContext) { display: "flex", position: "relative", overflow: "hidden", - background: - "linear-gradient(135deg, #060816 0%, #0f172a 38%, #111827 100%)", - color: "#f8fafc", + background: "#0a0a0a", + color: "#ffffff", fontFamily: 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif', }} @@ -37,75 +36,126 @@ export async function GET(request: Request, context: OgRouteContext) { style={{ position: "absolute", inset: 0, + display: "flex", background: - "radial-gradient(circle at top right, rgba(99, 102, 241, 0.45), transparent 28%), radial-gradient(circle at bottom left, rgba(56, 189, 248, 0.32), transparent 24%)", + "radial-gradient(ellipse 900px 500px at 15% 20%, rgba(255, 138, 61, 0.12), transparent 60%), radial-gradient(ellipse 700px 500px at 85% 80%, rgba(255, 255, 255, 0.04), transparent 60%)", }} /> + +
+
-
+
- Open Agents Wrapped + + + + - {profile.dateSelection.label} + Open Agents
-
-
- {displayName} -
-
- @{profile.user.username} + +
+ + Wrapped + +
+ {profile.dateSelection.label}
+
- Shareable output, usage, and model stats from Open Agents. + {displayName} +
+ +
+ @{profile.user.username}
@@ -115,35 +165,38 @@ export async function GET(request: Request, context: OgRouteContext) { flexDirection: "column", alignItems: "flex-end", gap: 16, + minWidth: 360, }} >
-
- Total tokens -
-
- {formatCompactNumber(profile.totals.totalTokens)} -
+ Total tokens +
+
+ {formatCompactNumber(profile.totals.totalTokens)}
+
-
+
- {label} - {value} + {label} + + {value} +
); } @@ -262,28 +311,29 @@ function FeatureCard({ flex: 1, display: "flex", flexDirection: "column", - gap: 12, - borderRadius: 28, - padding: 24, - background: "rgba(15, 23, 42, 0.82)", - border: "1px solid rgba(148, 163, 184, 0.18)", + gap: 10, + borderRadius: 16, + padding: 20, + background: "rgba(255, 255, 255, 0.03)", + border: "1px solid rgba(255, 255, 255, 0.08)", }} >
{title}
@@ -291,9 +341,9 @@ function FeatureCard({
{detail} diff --git a/apps/web/app/shared/[shareId]/opengraph-image.tsx b/apps/web/app/shared/[shareId]/opengraph-image.tsx new file mode 100644 index 000000000..398f48f46 --- /dev/null +++ b/apps/web/app/shared/[shareId]/opengraph-image.tsx @@ -0,0 +1,238 @@ +import { ImageResponse } from "next/og"; +import { eq } from "drizzle-orm"; +import { db } from "@/lib/db/client"; +import { users } from "@/lib/db/schema"; +import { getChatById } from "@/lib/db/sessions"; +import { + getSessionByIdCached, + getShareByIdCached, +} from "@/lib/db/sessions-cache"; + +export const alt = "Shared Open Agents session"; +export const size = { width: 1200, height: 630 }; +export const contentType = "image/png"; + +export default async function Image({ + params, +}: { + params: Promise<{ shareId: string }>; +}) { + const { shareId } = await params; + + const share = await getShareByIdCached(shareId); + if (!share) { + return fallbackImage(); + } + + const chat = await getChatById(share.chatId); + if (!chat) { + return fallbackImage(); + } + + const session = await getSessionByIdCached(chat.sessionId); + if (!session) { + return fallbackImage(); + } + + const [owner] = await db + .select({ + username: users.username, + name: users.name, + avatarUrl: users.avatarUrl, + }) + .from(users) + .where(eq(users.id, session.userId)) + .limit(1); + + if (!owner) { + return fallbackImage(); + } + + const displayName = owner.name?.trim() || owner.username; + const repoLabel = + session.repoOwner && session.repoName + ? `${session.repoOwner}/${session.repoName}` + : null; + + return new ImageResponse( +
+
+ +
+ +
+
+
+ + + + + + Open Agents + +
+ +
+ {chat.title || "Shared Chat"} +
+ + {repoLabel ? ( +
+ {repoLabel} +
+ ) : null} + + {session.branch ? ( +
+ Branch: {session.branch} +
+ ) : null} +
+ +
+ + Shared by {displayName} + + + open-agents.dev + +
+
+
, + { ...size }, + ); +} + +function fallbackImage() { + return new ImageResponse( +
+ Shared Open Agents session +
, + { ...size }, + ); +} diff --git a/apps/web/public/.well-known/workflow/v1/manifest.json b/apps/web/public/.well-known/workflow/v1/manifest.json index 7a81c28cb..0c0715132 100644 --- a/apps/web/public/.well-known/workflow/v1/manifest.json +++ b/apps/web/public/.well-known/workflow/v1/manifest.json @@ -176,4 +176,4 @@ } }, "classes": {} -} \ No newline at end of file +} From 1330bb8608355907d85ffa78ae565c15be254435 Mon Sep 17 00:00:00 2001 From: nicoalbanese <49612682+nicoalbanese@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:32:47 +0000 Subject: [PATCH 4/4] refactor: set twitter image runtime to edge --- apps/web/app/twitter-image.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/app/twitter-image.tsx b/apps/web/app/twitter-image.tsx index 1b687684a..ecffdd060 100644 --- a/apps/web/app/twitter-image.tsx +++ b/apps/web/app/twitter-image.tsx @@ -1 +1,3 @@ -export { default, alt, size, contentType, runtime } from "./opengraph-image"; +export { default, alt, size, contentType } from "./opengraph-image"; + +export const runtime = "edge";