fix(chat): draw the built-in examples again and undo edit previews on errors

Found by the PR review:
- The built-in examples showed a finished card and an empty canvas. They
  are answered in the browser, never reach the tool handler, and relied
  on the final redraw that an earlier commit removed. The example branch
  now loads its diagram itself.
- When the request failed while an edit was streaming (a provider error,
  a lost connection), its preview stayed on the canvas. The error handler
  now restores the diagram from before the preview.
- The model picker could not scroll with the wheel or touch: the settings
  dialog blocks those events outside itself, and the picker is rendered
  outside it. The popover is modal now.
- A fetch error and the open picker stayed when switching providers.
- Editing a model id kept the old test warning and response time, which
  also hid the "may not be able to draw" hint for the new id.
This commit is contained in:
dayuan.jiang
2026-10-04 23:04:21 +09:00
parent 48128acc65
commit 2bb69b58af
4 changed files with 215 additions and 60 deletions
+16 -2
View File
@@ -41,6 +41,7 @@ import type { UrlData } from "@/lib/url-utils"
import { type FileData, useFileProcessor } from "@/lib/use-file-processor"
import { useQuotaManager } from "@/lib/use-quota-manager"
import { cn, formatXML, isRealDiagram } from "@/lib/utils"
import { prepareNewDiagram } from "@/packages/mcp-server/src/new-diagram.ts"
import { BLANK_MXFILE, hasCells } from "@/packages/mcp-server/src/pages.ts"
import type { ValidationState } from "./chat/ValidationCard"
import {
@@ -363,6 +364,13 @@ export default function ChatPanel({
await handleToolCall({ toolCall }, addToolOutput)
},
onError: (error) => {
// An edit still streaming when the request failed never reaches
// the tool handler: undo its preview. The first stored original
// is the diagram before any of them.
const [originalXml] = editDiagramOriginalXmlRef.current.values()
if (originalXml) onDisplayChart(originalXml, true)
editDiagramOriginalXmlRef.current.clear()
// Server errors are JSON: a quota limit ({type: request, token or
// tpm}), a provider error ({type: "provider", code, message}) or
// {error}. The SDK puts the response body in error.message.
@@ -778,8 +786,9 @@ export default function ChatPanel({
files.length === 1 ? files[0].name : undefined,
)
if (cached) {
// Add user message and fake assistant response to messages
// The chat-message-display useEffect will handle displaying the diagram
// Add the user message and a finished display_diagram
// answer, and load its diagram here: these messages never
// reach the tool handler
const toolCallId = `cached-${Date.now()}`
// Build user message text including any file content
@@ -816,6 +825,11 @@ export default function ChatPanel({
0,
chartXMLRef.current || BLANK_MXFILE,
)
const prepared = prepareNewDiagram(cached.xml, {
pageId: "page-1",
pageName: "Page-1",
})
if (prepared.ok) onDisplayChart(prepared.xml, true)
setInput("")
sessionStorage.removeItem(SESSION_STORAGE_INPUT_KEY)
setFiles([])
+14
View File
@@ -282,6 +282,8 @@ export function ModelConfigDialog({
const newProvider = addProvider(providerType)
setSelectedProviderId(newProvider.id)
setValidationStatus("idle")
setFetchModelsError("")
setModelPickerOpen(false)
}
// Handle provider field updates
@@ -653,6 +655,10 @@ export function ModelConfigDialog({
)
setValidationStatus("idle")
setShowApiKey(false)
// These belong to the
// provider shown before
setFetchModelsError("")
setModelPickerOpen(false)
}}
className={cn(
"group flex items-center gap-3 px-3 py-2.5 rounded-xl w-full",
@@ -957,7 +963,11 @@ export function ModelConfigDialog({
)}
</Button>
)}
{/* modal: the dialog blocks the
wheel outside itself, and the
list is rendered outside it */}
<Popover
modal
open={modelPickerOpen}
onOpenChange={
setModelPickerOpen
@@ -1310,6 +1320,10 @@ export function ModelConfigDialog({
undefined,
validationError:
undefined,
validationWarning:
undefined,
responseTime:
undefined,
},
)
}