Chats:
- New Chat right after an answer saves that chat once. Saves run one at
a time and read the chat on screen when their turn comes; a save
scheduled for a chat that is no longer on screen is dropped. A chat
whose id was still on its way to the URL no longer comes back after
New Chat (the next answer went into it).
- Crossing the 768 px breakpoint keeps the chat panel: a streaming answer,
unsaved messages and attachments stay. The panel gets the sizes of each
side, and a panel collapsed on desktop opens on mobile.
- The chat's export waits for its own reply: an edit's history export
still on its way no longer answers it with the older diagram, and two
file saves at once no longer swap results.
- A second edit in one answer is previewed on the first edit's result.
- Stop also ends a running screenshot check; a chat that cannot be saved
(storage full) can be left with "Continue without saving".
- Small diagrams with shapes count as diagrams; the tool card no longer
crashes on malformed operations.
Quota and providers:
- Requests that reach the server's own endpoints count toward the quota:
EdgeOne (always its own endpoint now), a private base URL whatever key
header is sent, keyless Ollama without a URL. With the quota on, a
redirect is followed only to a public address. The output cap applies
to these requests too.
- Stop records the tokens of the steps that finished; the screenshot check
counts its tokens without counting a request.
- EdgeOne configured only by AI_PROVIDER works, also in the admin Test,
which forwards the access code. Azure set up only in the admin panel
works in chat. The Test sends a Bedrock session token.
- The admin panel's Test of an entry without a URL uses the server's URL
as the server does (no private address check for it); the admin panel
no longer writes an Ollama URL.
MCP server:
- Write tools and start_session run one at a time, so two at once never
drop each other's change; a cancelled call waiting its turn is skipped.
get_diagram and export_diagram keep the session they started with.
- Export to .drawio first gets the user's latest edits from the browser.
- History thumbnails: one that arrives after the next AI write is
dropped; a sync reply keeps the image; a version that changed only page
settings is its own entry.
- A diagram over the 10 MB limit is saved without its image, or the user
is told to download it (the server now answers 413 instead of cutting
the connection).
- Labels holding text like id='1' or parent='1' are no longer read as
attributes (a layer or a parent was deleted). A broken bare
<mxGraphModel> file is refused.
- After a sync reply the tab no longer sends its autosave copy again.
Desktop and files:
- A newer switch of the same preset is not rolled back by an older one
that failed. .env values with escaped quotes are read whole.
- MCP saved files: a file that could not be read stays protected while a
folder without permission hides it, and is saved again once deleted.
- The desktop app reports "no chats" only when the count was read and no
model settings are stored.
Found by the second PR review:
- get_diagram with a page selector, or a rejected edit's error, counted
the whole document as seen, so an edit on another page could overwrite
the user's change there. A one-page view now counts for all pages only
if the others are unchanged; otherwise the reply says to get them.
- add_page accepted shapes with the root cell ids "0" and "1" and renamed
them, breaking their edges. The check also missed ids on UserObject
wrappers and ids written with spaces around the "=".
- Root cells written over two lines were kept as an extra layer, cells
with id = "a" did not count as cells, and CDATA text before a page's
model passed the check although draw.io cannot open the page.
- Auto-save cleanup deleted the user's own files that start with mcp-.
Only names in the session id format are removed now.
- Restoring a history entry dropped edits made in the browser since the
last entry. They are added to history first.
- A session whose state expired showed a blank page, and the next change
overwrote its auto-save file. The saved file is loaded instead.
- An edit on a page export's one-page projection, made before the real
document was back, replaced the whole document.
- A late sync reply could overwrite a newer edit: each sync export is
numbered, and the server ignores replies older than the current state.
- screenshot_diagram could return another session's image after
start_session ran during its retries.
Found by the PR review, each with a test that failed first:
- get_diagram during a page export returned the one-page projection on
screen as the whole document (6 of 6 times when timed so). The preview
page no longer answers a sync while a projection shows, and syncs after
reloading, so the poll that restores the real document exports it.
- Exports are numbered on the server too: a late result of an export that
timed out was saved as the next export's file.
- In Chrome, a new_xml with a syntax error counted the <parsererror>
element as a second cell, so the web app rejected edits that auto-fix
repairs ("must contain exactly one cell").
- hasCells missed single-quoted ids, so screenshot_diagram called such a
diagram empty and auto-save never created its file.
- A literal \n directly under a <diagram> that has a model passed
validation; only text-only pages are compressed data.
- A wrapped mxCell repeating its UserObject's id took the wrapper's place
in edits, so delete and update left an empty or nested wrapper.
- Bare cells with a shape or edge id of "0" or "1" are rejected with a
clear message instead of being renamed, which broke their edges.
- DRAWIO_DATA_DIR expands ~, which JSON configs pass on as it is.
draw.io reads any text inside a page as compressed page data, so a stray
text node makes the whole page fail with an atob error. gpt-5-mini sends
new cells with a literal "\n" between the tags; the edit card said
Complete while draw.io showed the error and kept the old diagram.
Validation now reports text between tags, and auto-fix turns a literal
\n, \t or \r between tags into whitespace. Other text goes back to the
model as an error. The compressed data directly under <diagram> is fine.
The web app will reuse the MCP server's XML engine instead of its own
copy in lib/utils.ts, so these modules now run in the browser too.
- Relative imports end in .ts, rewritten to .js by tsc
(rewriteRelativeImportExtensions); Next.js resolves them directly
- Every module uses the global DOMParser/XMLSerializer: native in the
browser, linkedom in Node via installDomPolyfill. pages.ts parsed with
linkedom but serialized with the global serializer, which throws in
the browser
- The saxes syntax check moves to xml-syntax.ts, so the browser does not
pull in linkedom; it now also rejects undeclared prefixes such as
xlink:, as the browser does
- Page decompression uses pako and atob instead of node:zlib and Buffer
- hasCells moves to pages.ts, away from the file system code
- The duplicate cell id check counts UserObject/object ids
- wrapCellsInModel drops comments and text before the first cell, which
the web app accepts today
- validateAndFixXml takes { strict: false } for diagrams with user content
- Web tests run these modules with a browser DOM (jsdom)
- saxes becomes a direct dependency of the web app
- Preview page: keep an MCP export open until the server has its result.
A poll answered before that still saw the request and started the same
export again, so a parallel page export could write the previous
page's image into its file
- Auto-save only removes its own mcp-*.drawio files, so a DRAWIO_DATA_DIR
that also holds the user's diagrams keeps them
- screenshot_diagram captures a page that has no id attribute by loading
just that page, like export_diagram
- An empty <Array as="points"/> no longer hides orphan mxPoints that
come after it
- POST /api/state refuses a push without xml, which used to wipe the
stored diagram
- Clear exportOptions when an export ends, reuse hasCells for the empty
diagram check, and reword two log lines
- 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
- Validate and escape the mcp session id; only serve localhost Host/Origin
- Malformed URLs and session ids return errors instead of crashing the process
- Strict XML syntax check with saxes (linkedom never reports parse errors)
- autoFixXml no longer corrupts valid XML; attribute newlines serialized as entities
- Sessions stay alive while polled; browser pushes carry a base version (409 on conflict)
- Page tools respect the edit gate; UTF-8 bodies decoded correctly
- Export replies matched to requests and serialized; xml sync export handled
- UserObject/object cells addressable by id; history restored by stable id; logs off stdout