summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--app/packages/web/src/app.tsx227
1 files changed, 207 insertions, 20 deletions
diff --git a/app/packages/web/src/app.tsx b/app/packages/web/src/app.tsx
index 371f37c88..015d44c44 100644
--- a/app/packages/web/src/app.tsx
+++ b/app/packages/web/src/app.tsx
@@ -1,15 +1,41 @@
-import { createSignal, onCleanup, onMount, Show, For } from "solid-js"
+import { createSignal, onCleanup, onMount, Show, For, createMemo } from "solid-js"
import { useParams } from "@solidjs/router"
+type MessagePart = {
+ type: string
+ text?: string
+ [key: string]: any
+}
+
+type MessageContent = {
+ role?: string
+ parts?: MessagePart[]
+ metadata?: {
+ time?: {
+ created?: number
+ }
+ }
+ [key: string]: any
+}
+
type Message = {
key: string
content: string
}
+type SessionInfo = {
+ tokens?: {
+ input?: number
+ output?: number
+ reasoning?: number
+ }
+}
+
export default function App() {
const params = useParams<{ id: string }>()
const [messages, setMessages] = createSignal<Message[]>([])
const [connectionStatus, setConnectionStatus] = createSignal("Disconnected")
+ const [sessionInfo, setSessionInfo] = createSignal<SessionInfo | null>(null)
onMount(() => {
// Get the API URL from environment
@@ -62,20 +88,33 @@ export default function App() {
console.log("WebSocket message received")
try {
const data = JSON.parse(event.data) as Message
- setMessages((prev) => {
- // Check if message with this key already exists
- const existingIndex = prev.findIndex(msg => msg.key === data.key)
-
- if (existingIndex >= 0) {
- // Update existing message
- const updated = [...prev]
- updated[existingIndex] = data
- return updated
- } else {
- // Add new message
- return [...prev, data]
+
+ // Check if this is a session info message
+ if (data.key.startsWith("session/info/")) {
+ try {
+ const infoContent = JSON.parse(data.content) as SessionInfo;
+ setSessionInfo(infoContent);
+ console.log("Session info updated:", infoContent);
+ } catch (e) {
+ console.error("Error parsing session info:", e);
}
- })
+ } else {
+ // For all other messages
+ setMessages((prev) => {
+ // Check if message with this key already exists
+ const existingIndex = prev.findIndex(msg => msg.key === data.key)
+
+ if (existingIndex >= 0) {
+ // Update existing message
+ const updated = [...prev]
+ updated[existingIndex] = data
+ return updated
+ } else {
+ // Add new message
+ return [...prev, data]
+ }
+ })
+ }
} catch (error) {
console.error("Error parsing WebSocket message:", error)
}
@@ -125,6 +164,32 @@ export default function App() {
</p>
<h3>Live Updates</h3>
+
+ <Show when={sessionInfo()}>
+ <div
+ style={{
+ backgroundColor: "#f8f9fa",
+ padding: "1rem",
+ borderRadius: "0.5rem",
+ marginBottom: "1rem",
+ border: "1px solid #dee2e6"
+ }}
+ >
+ <h4 style={{ margin: "0 0 0.75rem 0" }}>Session Information</h4>
+ <div style={{ display: "flex", gap: "1.5rem" }}>
+ <div>
+ <strong>Input Tokens:</strong> {sessionInfo()?.tokens?.input || 0}
+ </div>
+ <div>
+ <strong>Output Tokens:</strong> {sessionInfo()?.tokens?.output || 0}
+ </div>
+ <div>
+ <strong>Reasoning Tokens:</strong> {sessionInfo()?.tokens?.reasoning || 0}
+ </div>
+ </div>
+ </div>
+ </Show>
+
<div
style={{
border: "1px solid #ccc",
@@ -143,18 +208,140 @@ export default function App() {
{(msg) => (
<li
style={{
- padding: "0.5rem",
- margin: "0.5rem 0",
+ padding: "0.75rem",
+ margin: "0.75rem 0",
backgroundColor: "#f5f5f5",
- borderRadius: "0.25rem",
+ borderRadius: "0.5rem",
+ boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
}}
>
<div>
<strong>Key:</strong> {msg.key}
</div>
- <div>
- <strong>Content:</strong> {msg.content}
- </div>
+
+ {(() => {
+ try {
+ const parsed = JSON.parse(msg.content) as MessageContent;
+ const createdTime = parsed.metadata?.time?.created
+ ? new Date(parsed.metadata.time.created).toLocaleString()
+ : 'Unknown time';
+
+ return (
+ <>
+ <div style={{ marginTop: "0.5rem" }}>
+ <strong>Full Content:</strong>
+ <pre
+ style={{
+ backgroundColor: "#f0f0f0",
+ padding: "0.5rem",
+ borderRadius: "0.25rem",
+ overflow: "auto",
+ maxHeight: "150px",
+ whiteSpace: "pre-wrap",
+ wordBreak: "break-word",
+ fontSize: "0.85rem",
+ }}
+ >
+ {JSON.stringify(parsed, null, 2)}
+ </pre>
+ </div>
+
+ {parsed.parts && parsed.parts.length > 0 && (
+ <div style={{ marginTop: "0.75rem" }}>
+ <div style={{
+ display: "flex",
+ justifyContent: "space-between",
+ alignItems: "center",
+ padding: "0.25rem 0.5rem",
+ backgroundColor: "#e9ecef",
+ borderRadius: "0.25rem",
+ marginBottom: "0.5rem"
+ }}>
+ <strong>Role: {parsed.role || 'Unknown'}</strong>
+ <span style={{ fontSize: "0.8rem", color: "#6c757d" }}>
+ {createdTime}
+ </span>
+ </div>
+
+ <div style={{
+ backgroundColor: "#fff",
+ padding: "0.75rem",
+ borderRadius: "0.25rem",
+ border: "1px solid #dee2e6"
+ }}>
+ <For each={parsed.parts}>
+ {(part, index) => (
+ <div style={{ marginBottom: index() < parsed.parts!.length - 1 ? "0.75rem" : "0" }}>
+ {part.type === "text" ? (
+ <pre style={{
+ whiteSpace: "pre-wrap",
+ wordBreak: "break-word",
+ fontFamily: "inherit",
+ margin: 0,
+ padding: 0,
+ backgroundColor: "transparent",
+ border: "none",
+ fontSize: "inherit",
+ overflow: "visible"
+ }}>
+ {part.text}
+ </pre>
+ ) : (
+ <div>
+ <div style={{
+ fontSize: "0.85rem",
+ fontWeight: "bold",
+ marginBottom: "0.25rem",
+ color: "#495057"
+ }}>
+ Part type: {part.type}
+ </div>
+ <pre
+ style={{
+ backgroundColor: "#f8f9fa",
+ padding: "0.5rem",
+ borderRadius: "0.25rem",
+ overflow: "auto",
+ maxHeight: "200px",
+ whiteSpace: "pre-wrap",
+ wordBreak: "break-word",
+ fontSize: "0.85rem",
+ margin: 0
+ }}
+ >
+ {JSON.stringify(part, null, 2)}
+ </pre>
+ </div>
+ )}
+ </div>
+ )}
+ </For>
+ </div>
+ </div>
+ )}
+ </>
+ );
+ } catch (e) {
+ return (
+ <div>
+ <strong>Content:</strong>
+ <pre
+ style={{
+ backgroundColor: "#f0f0f0",
+ padding: "0.5rem",
+ borderRadius: "0.25rem",
+ overflow: "auto",
+ maxHeight: "200px",
+ whiteSpace: "pre-wrap",
+ wordBreak: "break-word",
+ }}
+ >
+ {msg.content}
+ </pre>
+ </div>
+ );
+ }
+ })()}
</li>
)}
</For>