Quellcode durchsuchen

Share: load server data on page load

Frank vor 8 Monaten
Ursprung
Commit
1d782dc19a
3 geänderte Dateien mit 329 neuen und 132 gelöschten Zeilen
  1. 21 0
      packages/function/src/api.ts
  2. 301 131
      packages/web/src/components/Share.tsx
  3. 7 1
      packages/web/src/pages/s/index.astro

+ 21 - 0
packages/function/src/api.ts

@@ -69,6 +69,15 @@ export class SyncServer extends DurableObject<Env> {
     return secret
   }
 
+  public async messages() {
+    const data = await this.ctx.storage.list()
+    const messages = []
+    for (const [key, content] of data.entries()) {
+      messages.push({ key, content })
+    }
+    return messages
+  }
+
   private async getSecret() {
     return this.ctx.storage.get<string>("secret")
   }
@@ -163,5 +172,17 @@ export default {
       const stub = env.SYNC_SERVER.get(env.SYNC_SERVER.idFromName(id))
       return stub.fetch(request)
     }
+
+    if (request.method === "GET" && method === "share_messages") {
+      const id = url.searchParams.get("id")
+      console.log("share_messages", id)
+      if (!id)
+        return new Response("Error: Share ID is required", { status: 400 })
+      const stub = env.SYNC_SERVER.get(env.SYNC_SERVER.idFromName(id))
+      const messages = await stub.messages()
+      return new Response(JSON.stringify({ messages }), {
+        headers: { "Content-Type": "application/json" },
+      })
+    }
   },
 }

+ 301 - 131
packages/web/src/components/Share.tsx

@@ -139,12 +139,10 @@ function flattenToolArgs(obj: any, prefix: string = ""): Array<[string, any]> {
             entries.push([arrayPath, item])
           }
         })
-      }
-      else {
+      } else {
         entries.push(...flattenToolArgs(value, path))
       }
-    }
-    else {
+    } else {
       entries.push([path, value])
     }
   }
@@ -360,7 +358,9 @@ function TerminalPart(props: TerminalPartProps) {
       {...rest}
     >
       <div data-section="body">
-        <div data-section="header"><span>{local.desc}</span></div>
+        <div data-section="header">
+          <span>{local.desc}</span>
+        </div>
         <div data-section="content">
           <CodeBlock
             lang="ansi"
@@ -384,25 +384,26 @@ function TerminalPart(props: TerminalPartProps) {
 }
 
 function ToolFooter(props: { time: number }) {
-  return (
-    props.time > MIN_DURATION
-      ? <span data-part-footer title={`${props.time}ms`}>
-        {formatDuration(props.time)}
-      </span>
-      : <div data-part-footer="spacer"></div>
+  return props.time > MIN_DURATION ? (
+    <span data-part-footer title={`${props.time}ms`}>
+      {formatDuration(props.time)}
+    </span>
+  ) : (
+    <div data-part-footer="spacer"></div>
   )
 }
 
-export default function Share(props: { api: string }) {
+export default function Share(props: {
+  api: string
+  data: { key: string; content: SessionMessage | SessionInfo }[]
+}) {
   let params = new URLSearchParams(document.location.search)
   const id = params.get("id")
 
   const [store, setStore] = createStore<{
     info?: SessionInfo
     messages: Record<string, SessionMessage>
-  }>({
-    messages: {},
-  })
+  }>({ messages: {} })
   const messages = createMemo(() =>
     Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id)),
   )
@@ -410,6 +411,19 @@ export default function Share(props: { api: string }) {
     [Status, string?]
   >(["disconnected", "Disconnected"])
 
+  const processDatum = (d: any) => {
+    const [root, type, ...splits] = d.key.split("/")
+    if (root !== "session") return
+    if (type === "info") {
+      setStore("info", reconcile(d.content))
+      return
+    }
+    if (type === "message") {
+      const [, messageID] = splits
+      setStore("messages", messageID, reconcile(d.content))
+    }
+  }
+
   onMount(() => {
     const apiUrl = props.api
 
@@ -424,6 +438,10 @@ export default function Share(props: { api: string }) {
       return
     }
 
+    for (const datum of props.data) {
+      processDatum(datum)
+    }
+
     let reconnectTimer: number | undefined
     let socket: WebSocket | null = null
 
@@ -454,17 +472,7 @@ export default function Share(props: { api: string }) {
       socket.onmessage = (event) => {
         console.log("WebSocket message received")
         try {
-          const data = JSON.parse(event.data)
-          const [root, type, ...splits] = data.key.split("/")
-          if (root !== "session") return
-          if (type === "info") {
-            setStore("info", reconcile(data.content))
-            return
-          }
-          if (type === "message") {
-            const [, messageID] = splits
-            setStore("messages", messageID, reconcile(data.content))
-          }
+          processDatum(JSON.parse(event.data))
         } catch (error) {
           console.error("Error parsing WebSocket message:", error)
         }
@@ -540,16 +548,15 @@ export default function Share(props: { api: string }) {
         result.tokens.output += assistant.tokens.output
         result.tokens.reasoning += assistant.tokens.reasoning
 
-        result.models.push([
-          assistant.providerID,
-          assistant.modelID,
-        ])
+        result.models.push([assistant.providerID, assistant.modelID])
       }
     }
     return result
   })
   const [showingSystemPrompt, showSystemPrompt] = createSignal(false)
 
+  console.log(data())
+
   return (
     <main class={`${styles.root} not-content`}>
       <div class={styles.header}>
@@ -563,9 +570,9 @@ export default function Share(props: { api: string }) {
                     data().created || 0,
                   ).toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)}
                 >
-                  {DateTime.fromMillis(
-                    data().created || 0,
-                  ).toLocaleString(DateTime.DATE_MED)}
+                  {DateTime.fromMillis(data().created || 0).toLocaleString(
+                    DateTime.DATE_MED,
+                  )}
                 </span>
               ) : (
                 <span data-element-label data-placeholder>
@@ -575,7 +582,9 @@ export default function Share(props: { api: string }) {
             </div>
             <p data-section="status">
               <span data-status={connectionStatus()[0]}>&#9679;</span>
-              <span data-element-label>{getStatusText(connectionStatus())}</span>
+              <span data-element-label>
+                {getStatusText(connectionStatus())}
+              </span>
             </p>
           </div>
         </div>
@@ -645,11 +654,9 @@ export default function Share(props: { api: string }) {
                 onClick={() => showSystemPrompt((e) => !e)}
               >
                 <span>
-                  {
-                    showingSystemPrompt()
-                      ? "Hide system prompt"
-                      : "Show system prompt"
-                  }
+                  {showingSystemPrompt()
+                    ? "Hide system prompt"
+                    : "Show system prompt"}
                 </span>
                 <span data-button-icon>
                   <Show
@@ -825,27 +832,40 @@ export default function Share(props: { api: string }) {
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() =>
-                              msg.metadata?.tool[part().toolInvocation.toolCallId]
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
                             )
                             const args = part().toolInvocation.args
-                            const result = part().toolInvocation.state === "result" && part().toolInvocation.result
+                            const result =
+                              part().toolInvocation.state === "result" &&
+                              part().toolInvocation.result
                             const matches = metadata()?.matches
 
                             const { pattern, ...rest } = args
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
-                              <div data-section="part" data-part-type="tool-grep">
+                              <div
+                                data-section="part"
+                                data-part-type="tool-grep"
+                              >
                                 <div data-section="decoration">
                                   <div title="Grep files">
                                     <IconDocumentMagnifyingGlass
-                                      width={18} height={18}
+                                      width={18}
+                                      height={18}
                                     />
                                   </div>
                                   <div></div>
@@ -873,13 +893,16 @@ export default function Share(props: { api: string }) {
                                       <Match when={matches > 0}>
                                         <div data-part-tool-result>
                                           <ResultsButton
-                                            showCopy={matches === 1
-                                              ? "1 match"
-                                              : `${matches} matches`
+                                            showCopy={
+                                              matches === 1
+                                                ? "1 match"
+                                                : `${matches} matches`
                                             }
                                             hideCopy="Hide matches"
                                             results={results()}
-                                            onClick={() => showResults((e) => !e)}
+                                            onClick={() =>
+                                              showResults((e) => !e)
+                                            }
                                           />
                                           <Show when={results()}>
                                             <TextPart
@@ -919,25 +942,40 @@ export default function Share(props: { api: string }) {
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() =>
-                              msg.metadata?.tool[part().toolInvocation.toolCallId]
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
                             )
                             const args = part().toolInvocation.args
-                            const result = part().toolInvocation.state === "result" && part().toolInvocation.result
+                            const result =
+                              part().toolInvocation.state === "result" &&
+                              part().toolInvocation.result
                             const count = metadata()?.count
                             const pattern = args.pattern
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
-                              <div data-section="part" data-part-type="tool-glob">
+                              <div
+                                data-section="part"
+                                data-part-type="tool-glob"
+                              >
                                 <div data-section="decoration">
                                   <div title="Glob files">
-                                    <IconMagnifyingGlass width={18} height={18} />
+                                    <IconMagnifyingGlass
+                                      width={18}
+                                      height={18}
+                                    />
                                   </div>
                                   <div></div>
                                 </div>
@@ -951,12 +989,15 @@ export default function Share(props: { api: string }) {
                                       <Match when={count > 0}>
                                         <div data-part-tool-result>
                                           <ResultsButton
-                                            showCopy={count === 1
-                                              ? "1 result"
-                                              : `${count} results`
+                                            showCopy={
+                                              count === 1
+                                                ? "1 result"
+                                                : `${count} results`
                                             }
                                             results={results()}
-                                            onClick={() => showResults((e) => !e)}
+                                            onClick={() =>
+                                              showResults((e) => !e)
+                                            }
                                           />
                                           <Show when={results()}>
                                             <TextPart
@@ -996,23 +1037,36 @@ export default function Share(props: { api: string }) {
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() =>
-                              msg.metadata?.tool[part().toolInvocation.toolCallId]
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
                             )
                             const args = part().toolInvocation.args
                             const path = args.path
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
-                              <div data-section="part" data-part-type="tool-list">
+                              <div
+                                data-section="part"
+                                data-part-type="tool-list"
+                              >
                                 <div data-section="decoration">
                                   <div title="List files">
-                                    <IconRectangleStack width={18} height={18} />
+                                    <IconRectangleStack
+                                      width={18}
+                                      height={18}
+                                    />
                                   </div>
                                   <div></div>
                                 </div>
@@ -1026,21 +1080,25 @@ export default function Share(props: { api: string }) {
                                       <Match
                                         when={
                                           part().toolInvocation.state ===
-                                          "result" &&
+                                            "result" &&
                                           part().toolInvocation.result
                                         }
                                       >
                                         <div data-part-tool-result>
                                           <ResultsButton
                                             results={results()}
-                                            onClick={() => showResults((e) => !e)}
+                                            onClick={() =>
+                                              showResults((e) => !e)
+                                            }
                                           />
                                           <Show when={results()}>
                                             <TextPart
                                               expand
                                               data-size="sm"
                                               data-color="dimmed"
-                                              text={part().toolInvocation.result}
+                                              text={
+                                                part().toolInvocation.result
+                                              }
                                             />
                                           </Show>
                                         </div>
@@ -1063,21 +1121,35 @@ export default function Share(props: { api: string }) {
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() => msg.metadata?.tool[part().toolInvocation.toolCallId])
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
+                            )
                             const args = part().toolInvocation.args
                             const filePath = args.filePath
                             const hasError = metadata()?.error
                             const preview = metadata()?.preview
-                            const result = part().toolInvocation.state === "result" && part().toolInvocation.result
+                            const result =
+                              part().toolInvocation.state === "result" &&
+                              part().toolInvocation.result
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
-                              <div data-section="part" data-part-type="tool-read">
+                              <div
+                                data-section="part"
+                                data-part-type="tool-read"
+                              >
                                 <div data-section="decoration">
                                   <div title="Read file">
                                     <IconDocument width={18} height={18} />
@@ -1107,7 +1179,9 @@ export default function Share(props: { api: string }) {
                                             showCopy="Show preview"
                                             hideCopy="Hide preview"
                                             results={results()}
-                                            onClick={() => showResults((e) => !e)}
+                                            onClick={() =>
+                                              showResults((e) => !e)
+                                            }
                                           />
                                           <Show when={results()}>
                                             <div data-part-tool-code>
@@ -1123,7 +1197,9 @@ export default function Share(props: { api: string }) {
                                         <div data-part-tool-result>
                                           <ResultsButton
                                             results={results()}
-                                            onClick={() => showResults((e) => !e)}
+                                            onClick={() =>
+                                              showResults((e) => !e)
+                                            }
                                           />
                                           <Show when={results()}>
                                             <TextPart
@@ -1153,21 +1229,35 @@ export default function Share(props: { api: string }) {
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() => msg.metadata?.tool[part().toolInvocation.toolCallId])
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
+                            )
                             const args = part().toolInvocation.args
                             const filePath = args.filePath
                             const content = args.content
                             const hasError = metadata()?.error
-                            const result = part().toolInvocation.state === "result" && part().toolInvocation.result
+                            const result =
+                              part().toolInvocation.state === "result" &&
+                              part().toolInvocation.result
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
-                              <div data-section="part" data-part-type="tool-write">
+                              <div
+                                data-section="part"
+                                data-part-type="tool-write"
+                              >
                                 <div data-section="decoration">
                                   <div title="Write file">
                                     <IconDocumentPlus width={18} height={18} />
@@ -1197,7 +1287,9 @@ export default function Share(props: { api: string }) {
                                             showCopy="Show contents"
                                             hideCopy="Hide contents"
                                             results={results()}
-                                            onClick={() => showResults((e) => !e)}
+                                            onClick={() =>
+                                              showResults((e) => !e)
+                                            }
                                           />
                                           <Show when={results()}>
                                             <div data-part-tool-code>
@@ -1227,14 +1319,23 @@ export default function Share(props: { api: string }) {
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() => msg.metadata?.tool[part().toolInvocation.toolCallId])
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
+                            )
                             const args = part().toolInvocation.args
                             const filePath = args.filePath
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
@@ -1278,17 +1379,29 @@ export default function Share(props: { api: string }) {
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() => msg.metadata?.tool[part().toolInvocation.toolCallId])
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
+                            )
 
                             const command = part().toolInvocation.args.command
                             const desc = part().toolInvocation.args.description
                             const stdout = metadata()?.stdout
-                            const result = stdout || (part().toolInvocation.state === "result" && part().toolInvocation.result)
+                            const result =
+                              stdout ||
+                              (part().toolInvocation.state === "result" &&
+                                part().toolInvocation.result)
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
@@ -1307,7 +1420,9 @@ export default function Share(props: { api: string }) {
                                     <TerminalPart
                                       desc={desc}
                                       data-size="sm"
-                                      text={command + (result ? `\n${result}` : "")}
+                                      text={
+                                        command + (result ? `\n${result}` : "")
+                                      }
                                     />
                                   </div>
                                   <ToolFooter time={duration()} />
@@ -1321,17 +1436,27 @@ export default function Share(props: { api: string }) {
                           when={
                             msg.role === "assistant" &&
                             part.type === "tool-invocation" &&
-                            part.toolInvocation.toolName === "opencode_todoread" &&
+                            part.toolInvocation.toolName ===
+                              "opencode_todoread" &&
                             part
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() => msg.metadata?.tool[part().toolInvocation.toolCallId])
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
+                            )
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
@@ -1362,24 +1487,39 @@ export default function Share(props: { api: string }) {
                           when={
                             msg.role === "assistant" &&
                             part.type === "tool-invocation" &&
-                            part.toolInvocation.toolName === "opencode_todowrite" &&
+                            part.toolInvocation.toolName ===
+                              "opencode_todowrite" &&
                             part
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() => msg.metadata?.tool[part().toolInvocation.toolCallId])
-
-                            const todos = createMemo(() => sortTodosByStatus(
-                              part().toolInvocation.args.todos
-                            ))
-                            const starting = todos().every(t => t.status === "pending")
-                            const finished = todos().every(t => t.status === "completed")
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
+                            )
 
+                            const todos = createMemo(() =>
+                              sortTodosByStatus(
+                                part().toolInvocation.args.todos,
+                              ),
+                            )
+                            const starting = todos().every(
+                              (t) => t.status === "pending",
+                            )
+                            const finished = todos().every(
+                              (t) => t.status === "completed",
+                            )
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
@@ -1408,12 +1548,12 @@ export default function Share(props: { api: string }) {
                                     <Show when={todos().length > 0}>
                                       <ul class={styles.todos}>
                                         <For each={todos()}>
-                                          {({ status, content }) =>
+                                          {({ status, content }) => (
                                             <li data-status={status}>
                                               <span></span>
                                               {content}
                                             </li>
-                                          }
+                                          )}
                                         </For>
                                       </ul>
                                     </Show>
@@ -1429,26 +1569,41 @@ export default function Share(props: { api: string }) {
                           when={
                             msg.role === "assistant" &&
                             part.type === "tool-invocation" &&
-                            part.toolInvocation.toolName === "opencode_webfetch" &&
+                            part.toolInvocation.toolName ===
+                              "opencode_webfetch" &&
                             part
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() => msg.metadata?.tool[part().toolInvocation.toolCallId])
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
+                            )
                             const args = part().toolInvocation.args
                             const url = args.url
                             const format = args.format
                             const hasError = metadata()?.error
-                            const result = part().toolInvocation.state === "result" && part().toolInvocation.result
+                            const result =
+                              part().toolInvocation.state === "result" &&
+                              part().toolInvocation.result
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
-                              <div data-section="part" data-part-type="tool-fetch">
+                              <div
+                                data-section="part"
+                                data-part-type="tool-fetch"
+                              >
                                 <div data-section="decoration">
                                   <div title="Web fetch">
                                     <IconGlobeAlt width={18} height={18} />
@@ -1476,7 +1631,9 @@ export default function Share(props: { api: string }) {
                                         <div data-part-tool-result>
                                           <ResultsButton
                                             results={results()}
-                                            onClick={() => showResults((e) => !e)}
+                                            onClick={() =>
+                                              showResults((e) => !e)
+                                            }
                                           />
                                           <Show when={results()}>
                                             <div data-part-tool-code>
@@ -1505,12 +1662,21 @@ export default function Share(props: { api: string }) {
                           }
                         >
                           {(part) => {
-                            const metadata = createMemo(() => msg.metadata?.tool[part().toolInvocation.toolCallId])
+                            const metadata = createMemo(
+                              () =>
+                                msg.metadata?.tool[
+                                  part().toolInvocation.toolCallId
+                                ],
+                            )
 
                             const duration = createMemo(() =>
-                              DateTime.fromMillis(metadata()?.time.end || 0).diff(
-                                DateTime.fromMillis(metadata()?.time.start || 0),
-                              ).toMillis(),
+                              DateTime.fromMillis(metadata()?.time.end || 0)
+                                .diff(
+                                  DateTime.fromMillis(
+                                    metadata()?.time.start || 0,
+                                  ),
+                                )
+                                .toMillis(),
                             )
 
                             return (
@@ -1551,21 +1717,25 @@ export default function Share(props: { api: string }) {
                                       <Match
                                         when={
                                           part().toolInvocation.state ===
-                                          "result" &&
+                                            "result" &&
                                           part().toolInvocation.result
                                         }
                                       >
                                         <div data-part-tool-result>
                                           <ResultsButton
                                             results={results()}
-                                            onClick={() => showResults((e) => !e)}
+                                            onClick={() =>
+                                              showResults((e) => !e)
+                                            }
                                           />
                                           <Show when={results()}>
                                             <TextPart
                                               expand
                                               data-size="sm"
                                               data-color="dimmed"
-                                              text={part().toolInvocation.result}
+                                              text={
+                                                part().toolInvocation.result
+                                              }
                                             />
                                           </Show>
                                         </div>

+ 7 - 1
packages/web/src/pages/s/index.astro

@@ -4,6 +4,12 @@ import config from "virtual:starlight/user-config";
 import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
 import Share from "../../components/Share.tsx";
 
+const apiUrl = import.meta.env.VITE_API_URL;
+
+const id = Astro.url.searchParams.get('id')
+const res = await fetch(`${apiUrl}/share_messages?id=${id}`);
+const data = await res.json();
+
 ---
 
 <StarlightPage
@@ -15,7 +21,7 @@ import Share from "../../components/Share.tsx";
     tableOfContents: false,
   }}
 >
-  <Share api={import.meta.env.VITE_API_URL} client:only="solid" />
+  <Share api={apiUrl} data={data.messages} client:only="solid" />
 </StarlightPage>
 
 <style is:global>