diff options
| author | Adam <[email protected]> | 2025-11-24 11:56:00 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-11-24 15:10:40 -0600 |
| commit | acf1dd85006a0bb4009ef601ae7f7a60eb36af4d (patch) | |
| tree | c8c7c0ed92e580016b968a64f066ea32bad17e2f /packages/enterprise | |
| parent | 3fb57044d120618c92214b734d1fc831955e39a2 (diff) | |
| download | opencode-acf1dd85006a0bb4009ef601ae7f7a60eb36af4d.tar.gz opencode-acf1dd85006a0bb4009ef601ae7f7a60eb36af4d.zip | |
wip(share): more styling
Diffstat (limited to 'packages/enterprise')
| -rw-r--r-- | packages/enterprise/src/app.css | 17 | ||||
| -rw-r--r-- | packages/enterprise/src/app.tsx | 17 | ||||
| -rw-r--r-- | packages/enterprise/src/routes/share/[sessionID].tsx | 279 |
3 files changed, 175 insertions, 138 deletions
diff --git a/packages/enterprise/src/app.css b/packages/enterprise/src/app.css index 01c61e62c..4af87bca6 100644 --- a/packages/enterprise/src/app.css +++ b/packages/enterprise/src/app.css @@ -1,18 +1 @@ @import "@opencode-ai/ui/styles/tailwind"; - -:root { - --background-rgb: 214, 219, 220; - --foreground-rgb: 0, 0, 0; -} - -@media (prefers-color-scheme: dark) { - :root { - --background-rgb: 0, 0, 0; - --foreground-rgb: 255, 255, 255; - } -} - -body { - /* background: rgb(var(--background-rgb)); */ - /* color: rgb(var(--foreground-rgb)); */ -} diff --git a/packages/enterprise/src/app.tsx b/packages/enterprise/src/app.tsx index fd10899f2..77e9d8677 100644 --- a/packages/enterprise/src/app.tsx +++ b/packages/enterprise/src/app.tsx @@ -1,6 +1,5 @@ import { Router } from "@solidjs/router" import { FileRoutes } from "@solidjs/start/router" -import { Suspense } from "solid-js" import { Fonts } from "@opencode-ai/ui/fonts" import { MetaProvider } from "@solidjs/meta" import { MarkedProvider } from "@opencode-ai/ui/context/marked" @@ -10,16 +9,12 @@ export default function App() { return ( <Router root={(props) => ( - <> - <Suspense> - <MarkedProvider> - <MetaProvider> - <Fonts /> - {props.children} - </MetaProvider> - </MarkedProvider> - </Suspense> - </> + <MarkedProvider> + <MetaProvider> + <Fonts /> + {props.children} + </MetaProvider> + </MarkedProvider> )} > <FileRoutes /> diff --git a/packages/enterprise/src/routes/share/[sessionID].tsx b/packages/enterprise/src/routes/share/[sessionID].tsx index a2365aa1d..96a651391 100644 --- a/packages/enterprise/src/routes/share/[sessionID].tsx +++ b/packages/enterprise/src/routes/share/[sessionID].tsx @@ -3,15 +3,26 @@ import { SessionTurn } from "@opencode-ai/ui/session-turn" import { SessionReview } from "@opencode-ai/ui/session-review" import { DataProvider } from "@opencode-ai/ui/context" import { createAsync, query, RouteDefinition, useParams } from "@solidjs/router" -import { createMemo, Show } from "solid-js" +import { createMemo, ErrorBoundary, Show } from "solid-js" import { Share } from "~/core/share" import { Logo, Mark } from "@opencode-ai/ui/logo" import { IconButton } from "@opencode-ai/ui/icon-button" import { iife } from "@opencode-ai/util/iife" import { Binary } from "@opencode-ai/util/binary" +import { NamedError } from "@opencode-ai/util/error" import { DateTime } from "luxon" import { MessageNav } from "@opencode-ai/ui/message-nav" import { createStore } from "solid-js/store" +import z from "zod" +import NotFound from "../[...404]" + +const SessionDataMissingError = NamedError.create( + "SessionDataMissingError", + z.object({ + sessionID: z.string(), + message: z.string().optional(), + }), +) const getData = query(async (sessionID) => { const data = await Share.data(sessionID) @@ -70,6 +81,8 @@ const getData = query(async (sessionID) => { break } } + const match = Binary.search(result.session, sessionID!, (s) => s.id) + if (!match.found) throw new SessionDataMissingError({ sessionID }) return result }, "getShareData") @@ -80,126 +93,172 @@ export const route = { export default function () { const params = useParams() const data = createAsync(async () => { - if (!params.sessionID) return + if (!params.sessionID) throw new Error("Missing sessionID") return getData(params.sessionID) }) return ( - <Show when={data()}> - {(data) => ( - <DataProvider data={data()}> - {iife(() => { - const [store, setStore] = createStore({ - messageId: undefined as string | undefined, - }) - const match = createMemo(() => Binary.search(data().session, params.sessionID!, (s) => s.id)) - if (!match().found) throw new Error(`Session ${params.sessionID} not found`) - const info = createMemo(() => data().session[match().index]) - const messages = createMemo(() => - params.sessionID ? (data().message[params.sessionID]?.filter((m) => m.role === "user") ?? []) : [], - ) - 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) + <ErrorBoundary + fallback={(e) => { + return ( + <Show when={e.message === "SessionDataMissingError"}> + <NotFound /> + </Show> + ) + }} + > + <Show when={data()}> + {(data) => ( + <DataProvider data={data()}> + {iife(() => { + const [store, setStore] = createStore({ + messageId: undefined as string | undefined, + }) + const match = createMemo(() => Binary.search(data().session, params.sessionID!, (s) => s.id)) + if (!match().found) throw new Error(`Session ${params.sessionID} not found`) + const info = createMemo(() => data().session[match().index]) + const messages = createMemo(() => + params.sessionID + ? (data().message[params.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[params.sessionID!]?.find((m) => m.id === modelID())) - const diffs = createMemo(() => data().session_diff[params.sessionID!] ?? []) + const provider = createMemo(() => activeMessage()?.model?.providerID) + const modelID = createMemo(() => activeMessage()?.model?.modelID) + const model = createMemo(() => data().model[params.sessionID!]?.find((m) => m.id === modelID())) + const diffs = createMemo(() => data().session_diff[params.sessionID!] ?? []) - return ( - <div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col"> - <header class="h-12 px-6 py-2 flex items-center justify-between self-stretch bg-background-base border-b border-border-weak-base"> - <div class=""> - <a href="https://opencode.ai"> - <Mark /> - </a> - </div> - <div class="flex gap-3 items-center"> - <IconButton - as={"a"} - href="https://github.com/sst/opencode" - target="_blank" - icon="github" - variant="ghost" - /> - <IconButton - as={"a"} - href="https://opencode.ai/discord" - target="_blank" - icon="discord" - variant="ghost" - /> + const title = ( + <div class="flex flex-col gap-4 shrink-0"> + <div class="h-8 flex gap-4 items-center justify-start self-stretch"> + <div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base"> + <Mark class="shrink-0 w-3 my-0.5" /> + <div class="text-12-mono text-text-base">v{info().version}</div> + </div> + <div class="flex gap-2 items-center"> + <img src={`https://models.dev/logos/${provider()}.svg`} class="size-3.5 shrink-0 dark:invert" /> + <div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div> + </div> + <div class="text-12-regular text-text-weaker"> + {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} + </div> </div> - </header> - <div class="select-text flex flex-col flex-1 min-h-0"> - <div class="w-full flex-1 min-h-0 flex"> - <div - classList={{ - "@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full mx-auto": true, - "px-21 @4xl:px-6 max-w-2xl": diffs().length > 0, - "px-6 max-w-2xl": diffs().length === 0, - }} - > - <div class="flex flex-col gap-4 shrink-0"> - <div class="h-8 flex gap-4 items-center justify-start self-stretch"> - <div class="pl-[2.5px] pr-2 flex items-center gap-1.75 bg-surface-strong shadow-xs-border-base"> - <Mark class="shrink-0 w-3 my-0.5" /> - <div class="text-12-mono text-text-base">v{info().version}</div> - </div> - <div class="flex gap-2 items-center"> - <img - src={`https://models.dev/logos/${provider()}.svg`} - class="size-4 shrink-0 dark:invert" - /> - <div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div> - </div> - <div class="text-12-regular text-text-weaker"> - {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} - </div> + <div class="text-left text-16-medium text-text-strong">{info().title}</div> + </div> + ) + + return ( + <div class="relative bg-background-stronger w-screen h-screen overflow-hidden flex flex-col"> + <header class="h-12 px-6 py-2 flex items-center justify-between self-stretch bg-background-base border-b border-border-weak-base"> + <div class=""> + <a href="https://opencode.ai"> + <Mark /> + </a> + </div> + <div class="flex gap-3 items-center"> + <IconButton + as={"a"} + href="https://github.com/sst/opencode" + target="_blank" + icon="github" + variant="ghost" + /> + <IconButton + as={"a"} + href="https://opencode.ai/discord" + target="_blank" + icon="discord" + variant="ghost" + /> + </div> + </header> + <div class="select-text flex flex-col flex-1 min-h-0"> + <div class="w-full flex-1 min-h-0 flex"> + <div + classList={{ + "@container relative shrink-0 pt-14 flex flex-col gap-10 min-h-0 w-full mx-auto": true, + "px-21 @4xl:px-6 max-w-2xl": diffs().length > 0, + "px-6 max-w-2xl": diffs().length === 0, + }} + > + {title} + <div class="flex items-start justify-start h-full min-h-0"> + <Show when={messages().length > 1}> + <> + <div + classList={{ + "xl:hidden": true, + "absolute right-[90%]": diffs().length > 0, + "absolute right-full": diffs().length === 0, + }} + > + <MessageNav + classList={{ + "mt-0.5 mr-8": diffs().length === 0, + "mt-2.5 mr-3": diffs().length > 0, + }} + messages={messages()} + current={activeMessage()} + onMessageSelect={setActiveMessage} + size={!diffs().length ? "normal" : "compact"} + /> + </div> + <div + classList={{ + "hidden xl:block": true, + "absolute right-[90%]": diffs().length > 0, + "absolute right-full": diffs().length === 0, + }} + > + <MessageNav + classList={{ + "mt-0.5 mr-8": diffs().length === 0, + "mt-2.5 mr-3": diffs().length > 0, + }} + messages={messages()} + current={activeMessage()} + onMessageSelect={setActiveMessage} + size={!diffs().length ? "normal" : "compact"} + /> + </div> + </> + </Show> + <SessionTurn + sessionID={params.sessionID!} + messageID={store.messageId ?? firstUserMessage()!.id!} + classes={{ root: "grow", content: "flex flex-col justify-between", container: "pb-20" }} + > + <div class="flex items-center justify-center pb-8 shrink-0"> + <Logo class="w-58.5 opacity-12" /> + </div> + </SessionTurn> </div> - <div class="text-left text-16-medium text-text-strong">{info().title}</div> - </div> - <div class="flex items-start justify-start h-full min-h-0"> - <Show when={messages().length > 1}> - <MessageNav - classList={{ "mt-2 mr-3": true }} - messages={messages()} - current={activeMessage()} - onMessageSelect={setActiveMessage} - size={!diffs().length ? "normal" : "compact"} - /> - </Show> - <SessionTurn - sessionID={params.sessionID!} - messageID={store.messageId ?? firstUserMessage()!.id!} - classes={{ root: "grow", content: "flex flex-col justify-between", container: "pb-20" }} - > - <div class="flex items-center justify-center pb-8 shrink-0"> - <Logo class="w-58.5 opacity-12" /> - </div> - </SessionTurn> </div> + <Show when={diffs().length}> + <div class="relative grow px-6 pt-14 flex-1 min-h-0 border-l border-border-weak-base"> + <SessionReview diffs={diffs()} class="pb-20" /> + </div> + </Show> </div> - <Show when={diffs().length}> - <div class="relative grow px-6 pt-14 flex-1 min-h-0 border-l border-border-weak-base"> - <SessionReview diffs={diffs()} class="pb-20" /> - </div> - </Show> </div> </div> - </div> - ) - })} - </DataProvider> - )} - </Show> + ) + })} + </DataProvider> + )} + </Show> + </ErrorBoundary> ) } |
