import { useEffect, useRef } from "react"; import { useStore } from "../store"; import type { ChatMessage, TraceItem } from "../store"; export function ChatThread() { const messages = useStore((s) => s.messages); const error = useStore((s) => s.error); const resetChat = useStore((s) => s.resetChat); const sessionId = useStore((s) => s.sessionId); const endRef = useRef(null); useEffect(() => { endRef.current?.scrollIntoView({ behavior: "smooth", block: "end" }); }, [messages]); if (messages.length === 0 && !error) return null; return (

Conversation

{sessionId && ( session {sessionId.slice(0, 8)} )} {messages.length > 0 && ( )}
{error && (
{error}
)}
{messages.map((m) => ( ))}
); } function Bubble({ msg }: { msg: ChatMessage }) { if (msg.role === "user") { return (
{msg.text}
); } return (
GeoForce agent {msg.running ? "streaming…" : msg.stopReason ? `stop: ${msg.stopReason}` : `${msg.trace.length} events`}
{msg.trace.length > 0 && (
{msg.trace.map((item, i) => ( ))}
)} {msg.finalText && (
{msg.finalText}
)} {msg.running && msg.trace.length === 0 && (

Thinking…

)}
); } function TraceRow({ item }: { item: TraceItem }) { if (item.kind === "tool") { const short = item.name.replace(/^mcp__geoforce__/, ""); return (
tool {short}
); } return (

{item.text}

); } function TruncInput({ input }: { input: Record }) { let s = ""; try { s = JSON.stringify(input); } catch { s = String(input); } if (s.length > 120) s = s.slice(0, 120) + "…"; return ( {s} ); }