From 530b75a92a93de88ada372ba074d96dfd4920264 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 10 Dec 2025 22:15:13 -0500 Subject: ignore --- packages/enterprise/src/routes/[shareID].tsx | 1 + 1 file changed, 1 insertion(+) create mode 120000 packages/enterprise/src/routes/[shareID].tsx (limited to 'packages/enterprise/src') diff --git a/packages/enterprise/src/routes/[shareID].tsx b/packages/enterprise/src/routes/[shareID].tsx new file mode 120000 index 000000000..d4a653132 --- /dev/null +++ b/packages/enterprise/src/routes/[shareID].tsx @@ -0,0 +1 @@ +./share/[shareID].tsx \ No newline at end of file -- cgit v1.2.3 From d14462f7a707fc0b6c5c75d99c40d2e5da358716 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Wed, 10 Dec 2025 23:19:20 -0500 Subject: fix --- packages/enterprise/src/routes/[shareID].tsx | 1 - 1 file changed, 1 deletion(-) delete mode 120000 packages/enterprise/src/routes/[shareID].tsx (limited to 'packages/enterprise/src') diff --git a/packages/enterprise/src/routes/[shareID].tsx b/packages/enterprise/src/routes/[shareID].tsx deleted file mode 120000 index d4a653132..000000000 --- a/packages/enterprise/src/routes/[shareID].tsx +++ /dev/null @@ -1 +0,0 @@ -./share/[shareID].tsx \ No newline at end of file -- cgit v1.2.3 From 4309c078fb83e81859a2ba72003a3e0c75e0a7c5 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 11 Dec 2025 00:11:40 -0500 Subject: domain --- infra/enterprise.ts | 5 ++--- infra/stage.ts | 6 ++++++ packages/enterprise/src/routes/index.tsx | 3 +++ 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 packages/enterprise/src/routes/index.tsx (limited to 'packages/enterprise/src') diff --git a/infra/enterprise.ts b/infra/enterprise.ts index 316d6384d..e3b61c36d 100644 --- a/infra/enterprise.ts +++ b/infra/enterprise.ts @@ -1,14 +1,13 @@ import { SECRET } from "./secret" -import { domain } from "./stage" +import { domain, shortDomain } from "./stage" const storage = new sst.cloudflare.Bucket("EnterpriseStorage") const enterprise = new sst.cloudflare.x.SolidStart("Enterprise", { - domain: "enterprise." + domain, + domain: shortDomain, path: "packages/enterprise", buildCommand: "bun run build:cloudflare", environment: { - OPENCODE_BASE_URL: "/t", OPENCODE_STORAGE_ADAPTER: "r2", OPENCODE_STORAGE_ACCOUNT_ID: sst.cloudflare.DEFAULT_ACCOUNT_ID, OPENCODE_STORAGE_ACCESS_KEY_ID: SECRET.R2AccessKey.value, diff --git a/infra/stage.ts b/infra/stage.ts index 729422905..f9a6fd755 100644 --- a/infra/stage.ts +++ b/infra/stage.ts @@ -11,3 +11,9 @@ new cloudflare.RegionalHostname("RegionalHostname", { regionKey: "us", zoneId: zoneID, }) + +export const shortDomain = (() => { + if ($app.stage === "production") return "opncd.ai" + if ($app.stage === "dev") return "dev.opncd.ai" + return `${$app.stage}.dev.opncd.ai` +})() diff --git a/packages/enterprise/src/routes/index.tsx b/packages/enterprise/src/routes/index.tsx new file mode 100644 index 000000000..5a743b039 --- /dev/null +++ b/packages/enterprise/src/routes/index.tsx @@ -0,0 +1,3 @@ +export default function () { + return
Hello World
+} -- cgit v1.2.3 From 40f121c3e8f917bacc144455d63b8da44722a68c Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 11 Dec 2025 00:54:15 -0500 Subject: prevent indexing of share page --- packages/enterprise/src/routes/share/[shareID].tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'packages/enterprise/src') diff --git a/packages/enterprise/src/routes/share/[shareID].tsx b/packages/enterprise/src/routes/share/[shareID].tsx index 1c593ca87..6c63cf7f3 100644 --- a/packages/enterprise/src/routes/share/[shareID].tsx +++ b/packages/enterprise/src/routes/share/[shareID].tsx @@ -23,6 +23,7 @@ import { preloadMultiFileDiff, PreloadMultiFileDiffResult } from "@pierre/precis import { Diff as SSRDiff } from "@opencode-ai/ui/diff-ssr" import { clientOnly } from "@solidjs/start" import { type IconName } from "@opencode-ai/ui/icons/provider" +import { Meta } from "@solidjs/meta" const ClientOnlyDiff = clientOnly(() => import("@opencode-ai/ui/diff").then((m) => ({ default: m.Diff }))) @@ -153,6 +154,7 @@ export default function () { ) }} > + {(data) => { const match = createMemo(() => Binary.search(data().session, data().sessionID, (s) => s.id)) -- cgit v1.2.3 From 85c01e869434ec7ae52b3f3682d02cdcdef99e11 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 11 Dec 2025 01:01:27 -0500 Subject: enterprise: add social card meta tags to share pages - Add og:image and twitter:image meta tags for better social sharing - Generate dynamic social card URLs with session title, models, and version - Include description meta tag for search engines --- bun.lock | 1 + packages/enterprise/package.json | 1 + packages/enterprise/src/routes/share/[shareID].tsx | 453 +++++++++++---------- 3 files changed, 246 insertions(+), 209 deletions(-) (limited to 'packages/enterprise/src') diff --git a/bun.lock b/bun.lock index 338fb49cb..0d9a9116d 100644 --- a/bun.lock +++ b/bun.lock @@ -179,6 +179,7 @@ "aws4fetch": "^1.0.20", "hono": "catalog:", "hono-openapi": "catalog:", + "js-base64": "3.7.7", "luxon": "catalog:", "nitro": "3.0.1-alpha.1", "solid-js": "catalog:", diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 5a01dfb0b..35844bdc2 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -20,6 +20,7 @@ "@solidjs/meta": "catalog:", "hono": "catalog:", "hono-openapi": "catalog:", + "js-base64": "3.7.7", "luxon": "catalog:", "nitro": "3.0.1-alpha.1", "solid-js": "catalog:", diff --git a/packages/enterprise/src/routes/share/[shareID].tsx b/packages/enterprise/src/routes/share/[shareID].tsx index 6c63cf7f3..fee66b478 100644 --- a/packages/enterprise/src/routes/share/[shareID].tsx +++ b/packages/enterprise/src/routes/share/[shareID].tsx @@ -24,6 +24,7 @@ import { Diff as SSRDiff } from "@opencode-ai/ui/diff-ssr" import { clientOnly } from "@solidjs/start" import { type IconName } from "@opencode-ai/ui/icons/provider" import { Meta } from "@solidjs/meta" +import { Base64 } from "js-base64" const ClientOnlyDiff = clientOnly(() => import("@opencode-ai/ui/diff").then((m) => ({ default: m.Diff }))) @@ -42,6 +43,7 @@ const getData = query(async (shareID) => { const data = await Share.data(shareID) const result: { sessionID: string + shareID: string session: Session[] session_diff: { [sessionID: string]: FileDiff[] @@ -66,6 +68,7 @@ const getData = query(async (shareID) => { } } = { sessionID: share.sessionID, + shareID, session: [], session_diff: { [share.sessionID]: [], @@ -160,239 +163,271 @@ export default function () { const match = createMemo(() => Binary.search(data().session, data().sessionID, (s) => s.id)) if (!match().found) throw new Error(`Session ${data().sessionID} not found`) const info = createMemo(() => data().session[match().index]) + const ogImage = createMemo(() => { + const models = new Set() + const messages = data().message[data().sessionID] ?? [] + for (const msg of messages) { + if (msg.role === "assistant" && msg.modelID) { + models.add(msg.modelID) + } + } + const modelIDs = Array.from(models) + const encodedTitle = encodeURIComponent(Base64.encode(encodeURIComponent(info().title.substring(0, 700)))) + let modelParam: string + if (modelIDs.length === 1) { + modelParam = modelIDs[0] + } else if (modelIDs.length === 2) { + modelParam = encodeURIComponent(`${modelIDs[0]} & ${modelIDs[1]}`) + } else if (modelIDs.length > 2) { + modelParam = encodeURIComponent(`${modelIDs[0]} & ${modelIDs.length - 1} others`) + } else { + modelParam = "unknown" + } + const version = `v${info().version}` + return `https://social-cards.sst.dev/opencode-share/${encodedTitle}.png?model=${modelParam}&version=${version}&id=${data().shareID}` + }) return ( - - - {iife(() => { - const [store, setStore] = createStore({ - messageId: undefined as string | undefined, - }) - const messages = createMemo(() => - data().sessionID - ? (data().message[data().sessionID]?.filter((m) => m.role === "user") ?? []).sort( - (a, b) => b.time.created - a.time.created, - ) - : [], - ) - const firstUserMessage = createMemo(() => messages().at(0)) - const activeMessage = createMemo( - () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(), - ) - function setActiveMessage(message: UserMessage | undefined) { - if (message) { - setStore("messageId", message.id) - } else { - setStore("messageId", undefined) + <> + + + + + + {iife(() => { + const [store, setStore] = createStore({ + messageId: undefined as string | undefined, + }) + const messages = createMemo(() => + data().sessionID + ? (data().message[data().sessionID]?.filter((m) => m.role === "user") ?? []).sort( + (a, b) => b.time.created - a.time.created, + ) + : [], + ) + const firstUserMessage = createMemo(() => messages().at(0)) + const activeMessage = createMemo( + () => messages().find((m) => m.id === store.messageId) ?? firstUserMessage(), + ) + function setActiveMessage(message: UserMessage | undefined) { + if (message) { + setStore("messageId", message.id) + } else { + setStore("messageId", undefined) + } } - } - const provider = createMemo(() => activeMessage()?.model?.providerID) - const modelID = createMemo(() => activeMessage()?.model?.modelID) - const model = createMemo(() => data().model[data().sessionID]?.find((m) => m.id === modelID())) - const diffs = createMemo(() => { - const diffs = data().session_diff[data().sessionID] ?? [] - const preloaded = data().session_diff_preload[data().sessionID] ?? [] - return diffs.map((diff) => ({ - ...diff, - preloaded: preloaded.find((d) => d.newFile.name === diff.file), - })) - }) - const splitDiffs = createMemo(() => { - const diffs = data().session_diff[data().sessionID] ?? [] - const preloaded = data().session_diff_preload_split[data().sessionID] ?? [] - return diffs.map((diff) => ({ - ...diff, - preloaded: preloaded.find((d) => d.newFile.name === diff.file), - })) - }) + const provider = createMemo(() => activeMessage()?.model?.providerID) + const modelID = createMemo(() => activeMessage()?.model?.modelID) + const model = createMemo(() => data().model[data().sessionID]?.find((m) => m.id === modelID())) + const diffs = createMemo(() => { + const diffs = data().session_diff[data().sessionID] ?? [] + const preloaded = data().session_diff_preload[data().sessionID] ?? [] + return diffs.map((diff) => ({ + ...diff, + preloaded: preloaded.find((d) => d.newFile.name === diff.file), + })) + }) + const splitDiffs = createMemo(() => { + const diffs = data().session_diff[data().sessionID] ?? [] + const preloaded = data().session_diff_preload_split[data().sessionID] ?? [] + return diffs.map((diff) => ({ + ...diff, + preloaded: preloaded.find((d) => d.newFile.name === diff.file), + })) + }) - const title = () => ( -
-
-
- -
v{info().version}
-
-
- -
{model()?.name ?? modelID()}
-
-
- {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} + const title = () => ( +
+
+
+ +
v{info().version}
+
+
+ +
{model()?.name ?? modelID()}
+
+
+ {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} +
+
{info().title}
-
{info().title}
-
- ) + ) - const turns = () => ( -
-
{title()}
-
- - {(message) => ( - - )} - -
-
- + const turns = () => ( +
+
{title()}
+
+ + {(message) => ( + + )} + +
+
+ +
-
- ) + ) - const wide = createMemo(() => diffs().length === 0) + const wide = createMemo(() => diffs().length === 0) - return ( -
-
-
- - - -
-
- - -
-
-
-
+ return ( +
+
+
+ + + +
+
+ + +
+
+
1, - "px-6": !wide() && messages().length === 1, + "@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full": true, + "mx-auto max-w-146": !wide(), }} > - {title()} -
-
- - 1 ? "pr-6 pl-18" : "px-6"), +
1, + "px-6": !wide() && messages().length === 1, }} > -
- -
- -
-
- 0}> - -
- +
+ -
- - -
- - 0}> - - - - Session - - - {diffs().length} Files Changed - - - - {turns()} - - - - - -
- {turns()}
-
-
+ 0}> + +
+ +
+
+
+
+ + 0}> + + + + Session + + + {diffs().length} Files Changed + + + + {turns()} + + + + + +
+ {turns()} +
+
+
+
-
- ) - })} - - + ) + })} + + + ) }} -- cgit v1.2.3 From 2d9c76baaea4a485aae00b141366aea641edc74d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 11 Dec 2025 01:02:31 -0500 Subject: enterprise: add default social card images to HTML head - Add og:image and twitter:image meta tags to entry-server - Provide fallback social card image for pages without specific social cards --- packages/enterprise/src/entry-server.tsx | 2 -- 1 file changed, 2 deletions(-) (limited to 'packages/enterprise/src') diff --git a/packages/enterprise/src/entry-server.tsx b/packages/enterprise/src/entry-server.tsx index 68f4325c8..fbe5e6e0b 100644 --- a/packages/enterprise/src/entry-server.tsx +++ b/packages/enterprise/src/entry-server.tsx @@ -11,8 +11,6 @@ export default createHandler(() => ( OpenCode - - {assets} -- cgit v1.2.3