From 3218ccc909a64df4fc07654c14bef468c64589df Mon Sep 17 00:00:00 2001 From: Dayuan Jiang <34411969+DayuanJiang@users.noreply.github.com> Date: Wed, 24 Dec 2025 09:29:29 +0900 Subject: [PATCH] feat: add dev XML streaming simulator for UI debugging (#385) --- components/chat-panel.tsx | 187 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index 6bcfb77..32931c3 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -164,6 +164,28 @@ export default function ChatPanel({ const [showNewChatDialog, setShowNewChatDialog] = useState(false) const [minimalStyle, setMinimalStyle] = useState(false) + // Dev simulation state (only used in development) + const [devXml, setDevXml] = useState("") + const [isSimulating, setIsSimulating] = useState(false) + const [devIntervalMs, setDevIntervalMs] = useState(20) + const [devChunkSize, setDevChunkSize] = useState(5) + const devStopRef = useRef(false) + const devXmlInitializedRef = useRef(false) + + // Restore dev XML from localStorage on mount (after hydration) + useEffect(() => { + const saved = localStorage.getItem("dev-xml-simulator") + if (saved) setDevXml(saved) + devXmlInitializedRef.current = true + }, []) + + // Save dev XML to localStorage (only after initial load) + useEffect(() => { + if (devXmlInitializedRef.current) { + localStorage.setItem("dev-xml-simulator", devXml) + } + }, [devXml]) + // Restore input from sessionStorage on mount (when ChatPanel remounts due to key change) useEffect(() => { const savedInput = sessionStorage.getItem(SESSION_STORAGE_INPUT_KEY) @@ -1190,6 +1212,85 @@ Continue from EXACTLY where you stopped.`, sendChatMessage(newParts, savedXml, previousXml, sessionId) } + // Dev: Simulate display_diagram streaming + const handleDevSimulate = async () => { + if (!devXml.trim() || isSimulating) return + + setIsSimulating(true) + devStopRef.current = false + const toolCallId = `dev-sim-${Date.now()}` + const xml = devXml.trim() + + // Add user message and initial assistant message with empty XML + const userMsg = { + id: `user-${Date.now()}`, + role: "user" as const, + parts: [ + { + type: "text" as const, + text: "[Dev] Simulating XML streaming", + }, + ], + } + const assistantMsg = { + id: `assistant-${Date.now()}`, + role: "assistant" as const, + parts: [ + { + type: "tool-display_diagram" as const, + toolCallId, + state: "input-streaming" as const, + input: { xml: "" }, + }, + ], + } + setMessages((prev) => [...prev, userMsg, assistantMsg] as any) + + // Stream characters progressively + for (let i = 0; i < xml.length; i += devChunkSize) { + if (devStopRef.current) { + setIsSimulating(false) + return + } + + const chunk = xml.slice(0, i + devChunkSize) + + setMessages((prev) => { + const updated = [...prev] + const lastMsg = updated[updated.length - 1] as any + if (lastMsg?.role === "assistant" && lastMsg.parts?.[0]) { + lastMsg.parts[0].input = { xml: chunk } + } + return updated + }) + + await new Promise((r) => setTimeout(r, devIntervalMs)) + } + + if (devStopRef.current) { + setIsSimulating(false) + return + } + + // Finalize: set state to output-available + setMessages((prev) => { + const updated = [...prev] + const lastMsg = updated[updated.length - 1] as any + if (lastMsg?.role === "assistant" && lastMsg.parts?.[0]) { + lastMsg.parts[0].state = "output-available" + lastMsg.parts[0].output = "Successfully displayed the diagram." + lastMsg.parts[0].input = { xml } + } + return updated + }) + + // Display the final diagram + const fullXml = wrapWithMxFile(xml) + onDisplayChart(fullXml) + + setIsSimulating(false) + } + // Collapsed view (desktop only) if (!isVisible && !isMobile) { return ( @@ -1330,6 +1431,7 @@ Continue from EXACTLY where you stopped.`, setFiles={handleFileChange} processedToolCallsRef={processedToolCallsRef} editDiagramOriginalXmlRef={editDiagramOriginalXmlRef} + partialXmlRef={partialXmlRef} sessionId={sessionId} onRegenerate={handleRegenerate} status={status} @@ -1337,6 +1439,91 @@ Continue from EXACTLY where you stopped.`, /> + {/* Dev XML Streaming Simulator - only in development */} + {DEBUG && ( +
+
+ + Dev: XML Streaming Simulator + +
+