mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-10 19:49:52 +08:00
fix(mcp-server): make edit_diagram all-or-nothing and fix preview sync races
- edit_diagram applies nothing when any operation fails, rejects invalid or multi-cell new_xml, validates only the target page, and returns the current page XML on every rejection (including stale edits) - Fix get_diagram reading the old diagram right after an AI write: the preview pushed its sync reply with a newer version than it was taken at - Keep a user edit that loses the race with an AI write in history and tell the user in the preview - Autofix removes only exact foreign tags (a stray <mxGraph/> deleted <mxGraphModel>), fixes tag case, drops orphan <mxPoint>s, and rejects unknown element names in model XML - Edit empty and compressed pages; PNG exports use the page on screen; tag download exports; reload from the server after a page export - Expand ~ in paths, tell the model when the browser sync timed out, use registerPrompt, require SDK ^1.31.0
This commit is contained in:
@@ -184,3 +184,65 @@ describe("XML serializer and strict parsing in page helpers", () => {
|
||||
).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("autoFixXml keeps valid tags", () => {
|
||||
it("removes a stray <mxGraph/> without touching <mxGraphModel>", () => {
|
||||
const r = validateAndFixXml(
|
||||
model(`<mxGraph/><mxCell id="2" vertex="1" parent="1"/>`),
|
||||
)
|
||||
expect(r.valid).toBe(true)
|
||||
expect(r.fixed).toContain("<mxGraphModel>")
|
||||
expect(r.fixed).toContain("</mxGraphModel>")
|
||||
expect(r.fixed).not.toContain("<mxGraph/>")
|
||||
expect(r.fixed).toContain('id="2"')
|
||||
})
|
||||
|
||||
it("removes a stray <a> without touching <Array> waypoints", () => {
|
||||
const edge = `<mxCell id="e" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="1" y="2"/></Array></mxGeometry></mxCell>`
|
||||
const r = validateAndFixXml(model(`<a>x</a>${edge}`))
|
||||
expect(r.valid).toBe(true)
|
||||
expect(r.fixed).toContain('<Array as="points">')
|
||||
expect(r.fixed).toContain('<mxPoint x="1" y="2"/>')
|
||||
expect(r.fixed).not.toContain("<a>")
|
||||
})
|
||||
|
||||
it("fixes a lowercase <mxcell> instead of deleting every cell", () => {
|
||||
const r = validateAndFixXml(
|
||||
model(
|
||||
`<mxcell id="3" vertex="1" parent="1"></mxcell>${BROKEN_CELL}`,
|
||||
),
|
||||
)
|
||||
expect(r.valid).toBe(true)
|
||||
expect(r.fixed).toContain('<mxCell id="3"')
|
||||
expect(r.fixed).toContain('<mxCell id="0"/>')
|
||||
expect(r.fixed).toContain('value="R&D"')
|
||||
})
|
||||
|
||||
it("keeps label text when removing a foreign tag", () => {
|
||||
const cell = `<mxCell id="4" value="<b>Bold</b>" vertex="1" parent="1"/>`
|
||||
const r = validateAndFixXml(model(`<foo/>${cell}`))
|
||||
expect(r.valid).toBe(true)
|
||||
expect(r.fixed).toContain('value="<b>Bold</b>"')
|
||||
expect(r.fixed).not.toContain("<foo/>")
|
||||
})
|
||||
})
|
||||
|
||||
describe("validateAndFixXml strict checks", () => {
|
||||
it("fixes the case of an unknown element name", () => {
|
||||
const r = validateAndFixXml(
|
||||
model(`<mxcell id="3" vertex="1" parent="1"/>`),
|
||||
)
|
||||
expect(r.valid).toBe(true)
|
||||
expect(r.fixes).toContain("Fixed tag case of <mxCell>")
|
||||
expect(r.fixed).toContain('<mxCell id="3"')
|
||||
})
|
||||
|
||||
it("removes an orphan mxPoint but keeps waypoints and named points", () => {
|
||||
const cell = `<mxCell id="e" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="5" y="5"/><mxPoint x="0" y="0" as="sourcePoint"/><Array as="points"><mxPoint x="1" y="2"/></Array></mxGeometry></mxCell>`
|
||||
const r = validateAndFixXml(model(cell))
|
||||
expect(r.valid).toBe(true)
|
||||
expect(r.fixed).not.toContain('<mxPoint x="5" y="5"/>')
|
||||
expect(r.fixed).toContain('as="sourcePoint"')
|
||||
expect(r.fixed).toContain('<mxPoint x="1" y="2"/>')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user