import { FileDiff, Message, Model, Part, Session, SessionStatus, UserMessage } from "@opencode-ai/sdk" 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, useParams } from "@solidjs/router" import { createEffect, createMemo, ErrorBoundary, For, Match, Show, Switch } 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 { createDefaultOptions } from "@opencode-ai/ui/pierre" 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]" import { Tabs } from "@opencode-ai/ui/tabs" import { preloadMultiFileDiff, PreloadMultiFileDiffResult } from "@pierre/precision-diffs/ssr" const SessionDataMissingError = NamedError.create( "SessionDataMissingError", z.object({ sessionID: z.string(), message: z.string().optional(), }), ) const getData = query(async (shareID) => { "use server" const share = await Share.get(shareID) if (!share) throw new SessionDataMissingError({ sessionID: shareID }) const data = await Share.data(shareID) const result: { sessionID: string session: Session[] session_diff: { [sessionID: string]: FileDiff[] } session_diff_preload: { [sessionID: string]: PreloadMultiFileDiffResult[] } session_status: { [sessionID: string]: SessionStatus } message: { [sessionID: string]: Message[] } part: { [messageID: string]: Part[] } model: { [sessionID: string]: Model[] } } = { sessionID: share.sessionID, session: [], session_diff: { [share.sessionID]: [], }, session_diff_preload: { [share.sessionID]: [], }, session_status: { [share.sessionID]: { type: "idle", }, }, message: {}, part: {}, model: {}, } for (const item of data) { switch (item.type) { case "session": result.session.push(item.data) break case "session_diff": result.session_diff[share.sessionID] = item.data result.session_diff_preload[share.sessionID] = await Promise.all( item.data.map(async (diff) => preloadMultiFileDiff({ oldFile: { name: diff.file, contents: diff.before }, newFile: { name: diff.file, contents: diff.after }, options: createDefaultOptions("unified"), // annotations, }), ), ) break case "message": result.message[item.data.sessionID] = result.message[item.data.sessionID] ?? [] result.message[item.data.sessionID].push(item.data) break case "part": result.part[item.data.messageID] = result.part[item.data.messageID] ?? [] result.part[item.data.messageID].push(item.data) break case "model": result.model[share.sessionID] = item.data break } } const match = Binary.search(result.session, share.sessionID, (s) => s.id) if (!match.found) throw new SessionDataMissingError({ sessionID: share.sessionID }) return result }, "getShareData") export default function () { const params = useParams() const data = createAsync(async () => { if (!params.shareID) throw new Error("Missing shareID") return getData(params.shareID) }) createEffect(() => { console.log(data()) }) return ( { return ( ) }} > {(data) => { 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]) 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) } } 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 title = () => (
v{info().version}
{model()?.name ?? modelID()}
{DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")}
{info().title}
) const turns = () => (
{title()}
{(message) => ( )}
) const wide = createMemo(() => diffs().length === 0) const columnPadding = () => (wide() ? "px-6" : "px-21 @4xl:px-6") return (
0}> Session 5 Files Changed {turns()}
{turns()}
) })}
) }}
) }