mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-11 12:09:53 +08:00
fix(chat): the AI reads and draws the page the user is viewing
The model always got the first page and display_diagram replaced the whole file with one page, so drawing on page 3 of a multi-page file deleted the other pages (#241), and edits went to page 1 while the user looked at another page. Now a turn records the page the user is viewing when the message is sent. That page's model goes to the model (also as previousXml, on regenerate and in edit_diagram error messages), selected shapes are sent on any page, display_diagram and append_diagram replace only that page (a drawing that brings several pages still replaces the file), edit_diagram targets it, and the streaming preview draws on it. Compact cells and default styles take the layer and the edges of that page, in the preview and in editDiagram. The editor bridge replaces the current page in place when the other pages are unchanged, so Ctrl+Z undoes an AI change on a multi-page file in one step; when a full load is needed, the user's page is shown again afterwards. Version summaries and the compare dialog count the page the change was made on.
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import pako from "pako"
|
||||
import { describe, expect, it } from "vitest"
|
||||
import {
|
||||
cn,
|
||||
extractCompleteMxCells,
|
||||
extractDiagramXML,
|
||||
isMxCellXmlComplete,
|
||||
isRealDiagram,
|
||||
replaceNodes,
|
||||
} from "@/lib/utils"
|
||||
import { BLANK_MXFILE } from "@/packages/mcp-server/src/pages.ts"
|
||||
|
||||
@@ -134,3 +137,63 @@ describe("extractCompleteMxCells", () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const pageBox = (id: string) => `<mxCell id="${id}" vertex="1" parent="1"/>`
|
||||
const pageModel = (cells: string) =>
|
||||
`<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/>${cells}</root></mxGraphModel>`
|
||||
const pageOf = (id: string, cells: string) =>
|
||||
`<diagram id="${id}" name="${id}">${pageModel(cells)}</diagram>`
|
||||
/** Ids of the shapes on a page (not the root cells) */
|
||||
const shapesOn = (xml: string, pageId: string) =>
|
||||
Array.from(
|
||||
new DOMParser()
|
||||
.parseFromString(xml, "text/xml")
|
||||
.querySelectorAll(`diagram[id="${pageId}"] mxCell`),
|
||||
)
|
||||
.map((cell) => cell.getAttribute("id"))
|
||||
.filter((id) => id !== "0" && id !== "1")
|
||||
|
||||
describe("replaceNodes", () => {
|
||||
const two = `<mxfile>${pageOf("a", pageBox("A"))}${pageOf("b", pageBox("B"))}</mxfile>`
|
||||
|
||||
it("replaces the first page's cells by default", () => {
|
||||
const out = replaceNodes(two, pageBox("N"))
|
||||
expect(shapesOn(out, "a")).toEqual(["N"])
|
||||
expect(shapesOn(out, "b")).toEqual(["B"])
|
||||
})
|
||||
|
||||
it("replaces the given page's cells and keeps the others", () => {
|
||||
const out = replaceNodes(two, pageBox("N"), "b")
|
||||
expect(shapesOn(out, "a")).toEqual(["A"])
|
||||
expect(shapesOn(out, "b")).toEqual(["N"])
|
||||
})
|
||||
|
||||
it("uses the first page when the given page is not there", () => {
|
||||
const out = replaceNodes(two, pageBox("N"), "zzz")
|
||||
expect(shapesOn(out, "a")).toEqual(["N"])
|
||||
expect(shapesOn(out, "b")).toEqual(["B"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("extractDiagramXML", () => {
|
||||
const packed = (model: string) =>
|
||||
Buffer.from(pako.deflateRaw(encodeURIComponent(model))).toString(
|
||||
"base64",
|
||||
)
|
||||
const file = `<mxfile><diagram id="a" name="a">${packed(pageModel(pageBox("A")))}</diagram><diagram id="b" name="b">${packed(pageModel(pageBox("B")))}</diagram></mxfile>`
|
||||
const escapeAttr = (s: string) =>
|
||||
s
|
||||
.replace(/&/g, "&")
|
||||
.replace(/"/g, """)
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" content="${escapeAttr(file)}"></svg>`
|
||||
const dataUrl = `data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`
|
||||
|
||||
it("returns the first page by default and a page by its id", () => {
|
||||
expect(extractDiagramXML(dataUrl)).toContain('id="A"')
|
||||
expect(extractDiagramXML(dataUrl, "b")).toContain('id="B"')
|
||||
expect(extractDiagramXML(dataUrl, "b")).not.toContain('id="A"')
|
||||
expect(extractDiagramXML(dataUrl, "zzz")).toContain('id="A"')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user