mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-12 20:49:53 +08:00
refactor(web): validate and repair diagram XML with the MCP server's engine
- Delete the web app's own copy of the XML checks and repairs from lib/utils.ts (1,074 lines). loadDiagram now uses the MCP server's validateAndFixXml without the strict checks, because the XML may hold the user's own diagram - display_diagram and append_diagram prepare the model's XML with the new shared prepareNewDiagram, also used by the MCP create_new_diagram: wrap, validate strictly and auto-fix while it is still a bare model (where duplicate ids are renamed), then turn it into an mxfile - The streaming preview of display_diagram no longer redraws the model's raw cells after the tool handler loaded the checked diagram, and drops a queued preview once the input is complete. That redraw lost auto-fixes and UserObject/object wrappers, so a linked cell lost its label; it also showed a second error toast - The web repair regression tests now run against the MCP functions - New e2e test checks the canvas content after display_diagram - Fix the e2e upload tests, whose file input locator also matched the template import input
This commit is contained in:
@@ -45,6 +45,7 @@ import {
|
||||
} from "./http-server.ts"
|
||||
import { parseDrawioFileContent } from "./load-diagram.ts"
|
||||
import { log } from "./logger.ts"
|
||||
import { prepareNewDiagram } from "./new-diagram.ts"
|
||||
import {
|
||||
addPageToDoc,
|
||||
deletePageFromDoc,
|
||||
@@ -341,44 +342,21 @@ Rules: cells are siblings (never nested), ids are unique per page and start from
|
||||
}
|
||||
}
|
||||
|
||||
// Bare cells get the wrapper and root cells first: the strict
|
||||
// parser rejects several top-level elements. Then validate and
|
||||
// auto-fix (works for both mxfile and mxGraphModel inputs).
|
||||
let xml = wrapCellsInModel(inputXml)
|
||||
const { valid, error, fixed, fixes } = validateAndFixXml(xml)
|
||||
if (fixed) {
|
||||
xml = fixed
|
||||
log.info(`XML auto-fixed: ${fixes.join(", ")}`)
|
||||
}
|
||||
if (!valid && error) {
|
||||
log.error(`XML validation failed: ${error}`)
|
||||
const prepared = prepareNewDiagram(inputXml)
|
||||
if (!prepared.ok) {
|
||||
log.error(prepared.error)
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error: XML validation failed - ${error}`,
|
||||
},
|
||||
{ type: "text", text: `Error: ${prepared.error}` },
|
||||
],
|
||||
isError: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Normalise to the canonical mxfile shape so every later tool can
|
||||
// assume "session.xml is always an mxfile". Bare <mxGraphModel>
|
||||
// inputs are wrapped into a single-page mxfile here.
|
||||
const normalized = normalizeToMxfile(xml)
|
||||
if (!normalized) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: XML must be the mxCell elements of one page, a <mxGraphModel>, or an <mxfile> with one or more <diagram> children.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
}
|
||||
if (prepared.fixes.length > 0) {
|
||||
log.info(`XML auto-fixed: ${prepared.fixes.join(", ")}`)
|
||||
}
|
||||
xml = normalized
|
||||
// Every later tool can assume session.xml is an mxfile
|
||||
const xml = prepared.xml
|
||||
|
||||
log.info(`Setting diagram content, ${xml.length} chars`)
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* A whole new diagram written by the model, for the create_new_diagram tool
|
||||
* and the web app's display_diagram tool.
|
||||
*/
|
||||
import { normalizeToMxfile, wrapCellsInModel } from "./pages.ts"
|
||||
import { validateAndFixXml } from "./xml-validation.ts"
|
||||
|
||||
export type NewDiagram =
|
||||
| { ok: true; xml: string; fixes: string[] }
|
||||
| { ok: false; error: string }
|
||||
|
||||
/**
|
||||
* Bare cells get the wrapper and root cells first, since the strict parser
|
||||
* rejects several top-level elements. Then the XML is validated and
|
||||
* auto-fixed while it is still a bare model, where duplicate ids are
|
||||
* renamed, and finally turned into an <mxfile>.
|
||||
*/
|
||||
export function prepareNewDiagram(
|
||||
input: string,
|
||||
page: { pageId?: string; pageName?: string } = {},
|
||||
): NewDiagram {
|
||||
let xml = wrapCellsInModel(input)
|
||||
const { valid, error, fixed, fixes } = validateAndFixXml(xml)
|
||||
if (fixed) xml = fixed
|
||||
if (!valid) {
|
||||
return { ok: false, error: `XML validation failed - ${error}` }
|
||||
}
|
||||
const normalized = normalizeToMxfile(xml, page)
|
||||
if (!normalized) {
|
||||
return {
|
||||
ok: false,
|
||||
error: "XML must be the mxCell elements of one page, a <mxGraphModel>, or an <mxfile> with one or more <diagram> children.",
|
||||
}
|
||||
}
|
||||
return { ok: true, xml: normalized, fixes }
|
||||
}
|
||||
Reference in New Issue
Block a user