mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-13 04:59:51 +08:00
9c31481dd70847b8a85a962a50d8fb76f094917c
59
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9c31481dd7 | docs: revise the provider refactor plan after the multi-model review | ||
|
|
1ef9f3ea3d | docs: add the AI provider layer refactor plan | ||
|
|
5b27d30194 |
docs(mcp-server): promo video in the READMEs, a shorter MCP README with an install prompt, release 0.4.1 (#989)
* docs: MCP promo video at the top of the MCP section in the three READMEs * docs(mcp-server): the promo video in the package README * docs(mcp-server): shorter README with an install prompt for AI agents and the right VS Code config * chore(mcp-server): release 0.4.1 |
||
|
|
60105ddb51 |
refactor: remove dead code and over-engineering found by two audits (#986)
## Problem
Two audits of the repository (a workflow of finder, dedup and per-file verifier agents, run on 2026-10-11 against 1f4141e and again against
|
||
|
|
4c7b2cc0c5 |
feat(mcp-server): the preview is the web app's canvas: same-origin draw.io, version cards, get_selection (#984)
* refactor(canvas): decouple the canvas from the chat engine
The canvas components will be reused by the MCP server's browser shell,
which has no chat and no Next.js. They now read everything they need
from small contexts and the canvas store instead of the chat engine:
- components/canvas/versions-context.tsx: VersionsProvider and
useVersionsContext give the version cards, the strip and the compare
dialog a VersionsSource (versions, onCanvasId, undoneId, isBusy,
canUndo, canRedo, restore, undo, redo). The chat fills it from the
versions store and the engine in components/chat/chat-versions.tsx.
- stores/canvas-store.ts: isBusy and busyReason; the chat engine sets
them while a turn runs, SelectionAsk and the version UI read them.
- components/canvas/version-card.tsx: the visual version card and the
thumbnail, out of tool-activity.tsx; the chat's card composes it and
adds its "Show XML" link and code panel.
- compare-dialog.tsx and version-strip.tsx move to components/canvas;
the strip takes the minimum number of versions to show as a prop, the
chat panel computes it from the cards it has.
- components/canvas/locale-context.tsx: LocaleProvider and useLocale,
fed by the [lang] layout; CanvasStage no longer uses next/navigation.
The overlay (SelectionAsk) is a slot and the wait for the saved
language is a prop, so the shell can leave both out.
- lib/version-text.ts: describeChanges is now describeChangeSummary, so
it can be imported next to the MCP core's describeChanges.
* test(unit): canvas import boundary and the shared version card
The boundary test scans the canvas modules' imports and bundles the canvas components with esbuild; both fail on anything from components/chat, the tool handlers, next/navigation, next/font, next/script, next/headers or a server-only module. The card test renders VersionCard and VersionStrip on a fake VersionsSource.
* refactor(scripts): pin the draw.io release in one json file and share the zip reader
* feat(mcp-server): bundle a trimmed draw.io into dist/drawio at build time
scripts/fetch-drawio.mjs downloads the pinned draw.war into a cache
(DRAWIO_WAR_CACHE, or DRAWIO_WAR for a local file), checks its SHA-256
and extracts the files named in drawio-files.txt plus a LICENSE with the
Apache-2.0 text. scripts/check-drawio-files.mjs drives the embedded
editor in headless Chromium from a full copy, records every requested
file and checks or rewrites (--update) the list.
* feat(mcp-server): serve the bundled draw.io same-origin behind an api token
GET /drawio/<path> serves dist/drawio with a MIME table, a day of
caching and nosniff; paths are normalized and never reach WEB-INF or
META-INF. The preview embeds /drawio/index.html when the copy exists and
DRAWIO_BASE_URL is unset, else the external draw.io as before (and
start_session says so). Every /api request must carry the per-process
X-Drawio-Token the page gets in its HTML; pages send
frame-ancestors 'self' and nosniff.
* build(mcp-server): require the bundled draw.io in the package and cap the tarball at 40 MB
* fix(mcp-server): review fixes for the preview token and the draw.io static files
- The preview page fetches a fresh copy of itself and retries once when an
API request is refused with 403: another MCP process, with its own token,
now answers on this port, and the recovery logic (recoverState) needs its
polls to go through. The page is sent with Cache-Control: no-store.
- draw.io files are served with an ETag and Cache-Control: no-cache instead
of a 24 hour max-age: their names do not change between versions, so a
package upgrade must reach the browser on the next preview. HEAD and
If-None-Match (304) are answered.
- The file read stream goes through stream.pipeline, so a read error no
longer ends the MCP process and a client that leaves mid-download no
longer leaks the file handle.
* fix(mcp-server): review fixes for the draw.io file list and its guard
- The list now ships the templates of Insert > Template, the PlantUML
parser of Insert > Advanced and the template dialog's icon: the menu
items were shown but failed with 404s. 15.1 MB packed; the tarball cap
goes from 40 MB to 20 MB, where it still catches a list that grew by a
whole js/ directory.
- The guard drives both features, fails when the copy it runs against
lacks files the editor asked for or when an export does not answer,
checks that the copy is the pinned draw.io version, and runs in CI
(npm run check-drawio, one E2E shard) so a list regression cannot reach
a release.
* docs: review fixes for the draw.io version pointer and the preview's embedding
- The offline deployment guides point at packages/mcp-server/src/
drawio-version.json for the draw.war version; scripts/fetch-drawio.mjs
no longer names it.
- The MCP README says what the bundled draw.io copy includes, and that the
preview page cannot be shown inside an editor's built-in browser since it
is served with frame-ancestors 'self'.
* refactor(canvas): review fixes for the busy flag, the comparison note and the bundle size check
- busyReason had no reader: the chat engine sets isBusy through the store's
generic set, like every other flag.
- The isSameDocument comment says the two "same document" rules disagree
in both directions, so neither is a subset of the other.
- The import boundary test bundles minified with one pako (the MCP core
resolves its own copy) and caps the canvas core at 160 KB (134 KB now).
* fix(mcp-server): name exports at random and stamp the draw.io version into ETags
- Export requests carried a per-process counter. The preview page retries a
result refused with 403 against the process that took over the port, so a
late result of the old process's export could be taken for the new
process's export with the same number. The id is a random UUID now.
- The ETag of a bundled draw.io file now starts with the version the fetch
script stamps into dist/drawio/.version. An install that keeps the
archive's dates (npm does not) would otherwise answer 304 for a changed
file of the same size after an upgrade.
* docs(mcp-server): mermaid is in the bundled draw.io, which has no image proxy
- Mermaid's converter is in js/extensions.min.js, which the editor loads at
startup, so the README no longer lists it as left out; the org chart
layout (js/orgchart.min.js) is the example of a feature that is.
- Note that the bundled copy, like the web app's, has no /drawio/proxy:
images from other websites, including those in some templates, are left
out of exports and thumbnails. The guard's comment names this as a known
limitation it does not cover.
* refactor(canvas): getDrawioSrc takes the editor's source, and CanvasStage can be given one
* feat(mcp-server): canvas shell bundled from the web app's canvas with esbuild
The shell (packages/mcp-server/shell) renders DrawioFrame inside the
shared providers without the chat: its config comes from
window.__MCP_CONFIG__, the four dictionaries ship in the bundle, the
theme is kept under an mcp: localStorage key, and system fonts stand in
for the web fonts. scripts/build-shell.mjs bundles it into dist/shell
with the root's esbuild and Tailwind; check-package caps shell.js at
1.5 MB and shell.css at 300 KB.
* feat(mcp-server): serve the canvas shell at /shell/ behind DRAWIO_PREVIEW_UI
GET /shell/ fills the page template's {{CONFIG_JSON}} with the session,
the API token, where draw.io comes from and the host's editor settings,
with the classic page's security headers; /shell/<file> serves the built
files like the draw.io copy. start_session opens the shell when
DRAWIO_PREVIEW_UI=shell; the classic page stays the default.
* test(mcp-server): open the built shell with playwright
npm run test:e2e starts dist/index.js over the MCP SDK with
DRAWIO_PREVIEW_UI=shell, opens the URL start_session returns in headless
Chromium, and checks that the bundled draw.io loads without console
errors and that the theme toggle switches draw.io in place.
* ci: install the root dependencies before building the mcp package
The shell build needs the root's esbuild, Tailwind and React.
* feat(canvas): DiagramProvider exports with options and shows a transient document
requestExport(request, timeoutMs) runs one draw.io export with the
request's own parameters (a PNG's pageId and width) and resolves with its
answer (data, or xml for format "xml"); null when draw.io does not answer.
showTransient(xml) puts a document on the canvas for an export only: it is
not recorded, its autosaves are ignored, and the next loadDiagram brings
the user back to the page they were on. The MCP's canvas shell answers the
server's export requests with both.
* feat(mcp-server): the shell syncs with the server like the classic page
shell/mcp-sync-core.ts ports preview.js's protocol without the page or
React: polling GET /api/state every 2 s with the state id and version,
pushes of the user's edits with their base version and state, 409 and 413
handling, recovery of a recreated session (the tab's copy goes to History),
the token refresh after a 403, sync and export requests (PNG by page id,
SVG of another page through a transient one-page projection with autosave
ignored, the 600 ms wait for icons), thumbnails for History, and History
reads and restores that name the state the list belongs to.
A server write goes on the canvas as one undo step (loadDiagram commit,
with the change marked) when decideLoad finds it changes only the page on
screen, keeps the file variables and needs no load-only setting; the other
pages are compared with the MCP core's contentFingerprint. draw.io's own
re-serialized copy of such a write is recognized with isSameDocument and
not pushed back as an edit.
shell/use-mcp-sync.ts mounts the sync once inside DiagramProvider, reads
draw.io's autosave and load messages, and reports the connection state,
which the shell's status bar shows; notices come as toasts from the
dictionaries.
* test(mcp-server): port the preview protocol tests to the sync core
The recovery, thumbnail, size limit and History cases of
tests/unit/mcp-preview-recovery.test.ts, driven against mcp-sync-core.ts
with a stubbed server and canvas, plus the stale 409 recovery, an export
request answered once, a projection that ignores edits and restores the
document, the thumbnail of a write loaded before draw.io was ready, writes
taken in place as commits (draw.io's own copy is not pushed), and the
commit-or-load decision table of decideLoad.
* test(mcp-server): e2e of the shell's sync with the server
The shell connects, shows what create_new_diagram draws, marks an
edit_diagram change and takes it back with one Ctrl+Z (which get_diagram
then reflects), pushes a shape inserted in the editor, serves
screenshot_diagram, and shows another page only for its SVG export before
the user's page comes back.
* fix(mcp-server): review fixes for the shell's sync core
A recovery loads the server's diagram in full and waits for draw.io's
load report, as the classic page does: whether the canvas takes a write
in place is decided inside DiagramProvider, and an autosave of the
canvas being replaced went to the server as an edit when the decision
was wrong. A paper size change alone is pushed again: autosaves are
compared as documents only for draw.io's own copy of a committed write.
A projection stays on screen until draw.io reports the restore load
(5 s at most), the whole export ends in 15 s even when its result POST
hangs, and an export in flight is dropped when draw.io starts over. A
refused poll shows the tab offline. With several pages and the page on
screen unknown, a write loads in full. hasLoadOnlySettings and the
highlight of an AI change are shared with the editor bridge.
* fix(mcp-server): review fixes for the preview URL and the theme menu
The stalled-tab note names the page start_session opened (the shell
with DRAWIO_PREVIEW_UI=shell) instead of the classic page. With the
shell not built, start_session opens the classic page and the server
says so at startup, as it does for a missing dist/drawio. BROWSER=none
skips the system browser, as Vite and CRA dev servers do: the e2e tests
set it. Without a fixed DRAWIO_UI, the shell's draw.io URL carries
themes=1 so Extras > Theme is offered, as on the classic page.
* fix(mcp-server): review fixes for the shell's build
tsc -p shell checked nothing: the inherited exclude dropped every shell
file; the shell's tsconfig now excludes node_modules only. Tailwind scans
app-toaster.tsx too, so the sync notices look like the web app's toasts.
The bundle keeps its dependencies' license comments (esbuild appends them
at the end). postcss, which build-shell.mjs loads from the root, is a
declared devDependency.
* ci: run the shell's browser tests on the packed tarball, publish on shared code changes
The e2e job's first shard builds the MCP package, packs it, installs the
tarball in an empty directory and runs packages/mcp-server/tests/e2e
against the installed dist/index.js (MCP_SERVER_ENTRY), so a shell that
builds but fails in the browser, or a tarball missing a runtime
dependency, fails the PR. Its traces go with the Playwright report. The
publish workflow also triggers on the web app code the shell is bundled
from (components, contexts, hooks, lib, stores, globals.css) and the
root lockfile.
* fix(canvas): ignore autosaves of the document a pending full load replaces
An autosave that arrives after loadDiagram sent a full load, and before draw.io reports it, is of the canvas being replaced (draw.io reports each load, in order). Until now it overwrote chartXMLRef: in the MCP shell, a late autosave of a one-page projection replaced the cached document, and a remount of an external draw.io then put the projection on the canvas as the whole document.
* fix(mcp-server): review fixes for the shell's commits and export timeout
A write taken in place that leaves the canvas as it is gets no autosave from draw.io, so the marker for its copy stayed armed and ate the user's next paper-size edit: arm it only when the write changed the document. The 15 s export timeout now retires the export's sequence number, so a result POST that answers late no longer restores the document a second time.
* ci: create the tarball directory before npm pack
npm pack does not create its --pack-destination; on a fresh runner the step failed with ENOENT before the packed shell tests ran.
* feat(mcp-server): history entries name their maker, and /api/history the entry on the canvas
Each History entry records who made it (a user edit, a recovering tab's
copy, a restored copy; none for the AI's writes), when, and its page
count. GET /api/history now also returns each entry's XML, the first entry
with the same content (a restore adds a copy of an older one), the state
the ids belong to, and the entry whose content the canvas shows, by the
rule restore_version uses. The shell's version cards read these; the
classic page keeps using index, id and svg.
* feat(mcp-server): version cards in the shell from the server's History
The shell's VersionsSource (shell/node-versions-source.ts) reads GET
/api/history through the sync and shows one version per distinct content
(a restore's copy folds into the entry it copies; the blank page is none),
numbered as they first appeared, with what changed since the one before.
The entry the server says is on the canvas marks the version; undo and
redo of the newest version restore the one before it, or it again, through
POST /api/restore, so hand edits stay as versions of their own. The cards
sit in a panel beside the canvas (toggle in the header); the strip and
Compare come from the shared canvas components. The sync tells listeners
once per server version, and when History changed without one, so the
list is read again only then.
* test(mcp-server): e2e of the shell's version cards
In a session of its own: three writes give three cards; undoing the newest
restores the one before on the server too, redo brings it back, a hand edit
turns the undo into a restore, and restoring the first version keeps that
edit as a version of its own, which restore_version also finds.
* feat(mcp-server): get_selection reads the cells the user selected in the shell
The server asks the preview tab for the selection the way it asks for an export (a random request id in GET /api/state, the answer in a POST with that id, 10 s to answer). The shell answers through the editor bridge with each cell's id, label, an edge's ends, a shape's geometry and the page on screen; without a same-origin editor it says so, and the tool names the external draw.io. The classic page cannot answer, so the tool says that at once. screenshot_diagram's description now says a page selector renders that page without changing the page on screen (PNG exports by pageId already did).
* test(mcp-server): selection requests, get_selection texts, and page screenshots that leave the view alone
Unit tests for the request plumbing (random id, one answer, timeout), the tool's texts (cells, nothing selected, external draw.io) and the shell's side (one answer per request, after draw.io is up, never during a projection); the wiring test starts a session with BROWSER=none to see the no-tab message. Shell e2e: cells selected in the editor reach get_selection; a PNG of another page differs from the page on screen, which stays.
* feat(mcp-server): the canvas shell is the default preview, the classic page behind DRAWIO_PREVIEW_UI=classic
* docs: the MCP preview is the web app's canvas; get_selection, DRAWIO_BASE_URL and the classic page
* chore(mcp-server): version 0.4.0; check-package requires the draw.io version stamp
* ci: an mcp-shell job builds the package, runs its tests, the draw.io file guard and the shell e2e
* fix(mcp-server): review fixes for the version cards and History
The shell's version cards (shell/node-versions-source.ts):
- a version's change and undo target are the state it replaced, the
History entry right before its first copy, not the card before it: after
a restore those differ, and undo went to the wrong version (and not
where restore_version steps_back=1 goes)
- a card restores the newest copy of its content, as restore_version
does, so page settings the user changed (a "user" copy) are kept
- a blank page after a drawing is a clear of the canvas, a version of its
own; only the blank page before any drawing is hidden
- numbers and changes are keyed by content, not by the first copy's id,
so a version keeps them when its first copy drops out of the server's
20-entry buffer; the caches start over for another server state (the
process restarted: entry ids name other content)
The server's History (src/history.ts):
- firstCopyIds compares each entry with the first of every group only: a
bare model matches any page name, so "same content" is not transitive,
and a card could show one document and restore another
- the time and pages fields had no reader; pages parsed every XML once
more on every write
Reading History (src/http-server.ts, shell/mcp-sync-core.ts):
- GET /api/state and a push's answer carry a History key (entry count,
newest id, the entry on the canvas); the shell reads History again only
when it changes, so a hand edit no longer downloads every entry's XML and
thumbnail
- a failed History read is told again at the next poll
- a History list from a state the poll has not seen yet is dropped
* fix(mcp-server): review fixes for get_selection
Server (src/http-server.ts, src/index.ts, src/selection.ts,
src/new-diagram.ts):
- overlapping get_selection calls take turns (readSelection, one slot per
session as the export slot) instead of replacing each other's request,
which left one of them with a false "tab not in front" timeout
- an answer is taken only while a request is pending (both ids undefined
compared equal)
- the tool sees the page start_session actually opens: with dist/shell
missing the classic page is in use, which never answers, so the tool
says so instead of timing out
- the result lists at most 100 cells and counts the rest: a whole large
diagram selected would fill the model's context
- every <diagram> the model sends without an id gets one, so the page id
the shell reports exists in the server's document
Shell (shell/mcp-sync-core.ts, shell/use-mcp-sync.ts,
contexts/diagram-context.tsx, lib/drawio/editor-bridge.ts):
- an answer whose POST failed is sent again at the next poll
- no answer while a full load has yet to reach the editor: it still shows
the previous document, whose cells and pages the answer would name
- a hidden tab (the same session open twice) answers a poll later, so the
tab in front answers first; alone, it still answers within the timeout
- a cell's container is reported by the model's isLayer, not by comparing
with the default parent, which is the group the user entered
* fix(mcp-server): review fixes for the shell page
- a download button in the header opens the web app's export dialog
(.drawio, .png, .svg, .drawio.svg), which the classic page had and the
shell lacked when it became the default
- the shell asks draw.io for the custom library menu (libraries=1), as
the classic page did; the web app keeps libraries=0
- the newest card no longer shows "Rendering preview" for good: the sync
takes the thumbnail of a diagram the server recovered from its file
(saved without pictures) while the canvas kept it, and of a write whose
picture was skipped because an edit came first, once the canvas shows
the write again
- e2e: the get_selection test covers a shape in a container the user
entered; a download test saves a .drawio file
* ci: the version bump reminder watches every path publish-mcp.yml does, and compares the version field
The reminder step left out the root lockfile and scripts/, which change
what is bundled into the shell and trigger the publish workflow; and it
took any change of packages/mcp-server/package.json (a dependency bump)
for a version bump. It now compares the version field between the two
commits.
* fix(mcp-server): get_selection lists every selected id, with detail for the first 100
* fix(mcp-server): a hidden tab's delayed selection answer checks the canvas again first
* fix(mcp-server): the version cards keep only the current History in memory, picture the newest copy, and keep a clear the buffer scrolled to
* fix(mcp-server): save who made each History entry with it, so the cards look the same after a restart
* fix(mcp-server): the download waits for a page export's projection to end, and the dialog's styles are bundled
* fix(mcp-server): bundle open.html and js/open.js, the picker of Open Library from > Browser
* fix(mcp-server): the shell shows the versions once, in the side panel
* test(mcp-server): get_selection wiring test expects the classic page's answer when the shell is not built
|
||
|
|
07f3e3c2a3 |
feat(mcp-server): screenshot attached to create_new_diagram and edit_diagram results (#983)
* feat(mcp-server): optional screenshot attached to create_new_diagram and edit_diagram results Both write tools take an optional boolean `screenshot`; the default comes from DRAWIO_AUTO_SCREENSHOT. The body of screenshot_diagram moved into captureScreenshot so the write tools can append the PNG and checklist to their result, or a "Screenshot skipped" note when the preview tab is not available. The preview page delays the PNG export by 600 ms right after loading a new version so icon images finish loading. * fix(mcp-server): review fixes for screenshot attached to create and edit results * fix(mcp-server): Codex review fixes for the screenshot on write results - A cleared canvas also gets the screenshot note when one was asked for - The note for a tab that never polled says it may not have connected yet * docs: list undo and custom drawing rules among the MCP features * test(mcp-server): truncation check with named styles and compact cells after the merge |
||
|
|
d5096b9628 |
docs: restructure the README around the new promo video (#961)
* docs: restructure the README around the new promo video The three READMEs now follow the reader's path: what it is (video and two paragraphs), highlights grouped as draw / edit / use, examples, how to use it (demo, desktop app, MCP), how to self-host (local run, a one-click deploy table, models and providers), and support. Removed the table of contents, the sponsor blocks, the "How It Works" section, the duplicated demo button and the model recommendation list. The provider list now matches env.example (24 providers). The video is the new 30-second promo. Bold markers inside the HTML example table are now <strong> tags, so they render. * docs: keep the TrendShift badge under the language switch * chore: remove the sponsor material for Doubao and Atlas Cloud The about pages lose the "Sponsored by ByteDance Doubao" card, the promotional links in the provider list and the thanks paragraph; the "bring your own key" note stays. The quota toast no longer shows the Volcengine sign-up paragraph, and its messages in the four languages no longer mention a sponsor. The provider guides lose the "free tokens" promo line. The logo and invite images used only by that material are deleted. Doubao and Atlas Cloud remain supported providers. |
||
|
|
17ea4811fb |
feat(settings): reorganize the settings dialog and the model settings (#958)
Tabs Models, General, Drawing and About, each with a fixed title row. The models tab becomes three pages: the user's providers with one status each and the server's models; a searchable picker of all 24 providers in four groups; and a provider page in three steps (connection, models, test). A new provider gets its first suggested model, and a passing test offers to use the model in the chat. Bedrock asks for one way of signing in, with an optional session token for temporary access keys. The composer's model picker lists every model, marking untested and failed ones, and can open the provider picker. A chat error that offers model settings opens the provider the request went to. |
||
|
|
c513d1a0c2 |
feat(ui): canvas-first redesign with a self-hosted draw.io (#957)
The canvas now fills the window and the chat floats on its right. draw.io is served from our own origin (public/drawio, downloaded at build time and checked against the release's SHA-256), so the app can drive the editor directly: - every AI change becomes a version card with a thumbnail; compare, restore or undo it, and Ctrl+Z on the canvas takes an AI change back in one step - shapes the AI just changed are highlighted on the canvas - selected shapes appear as a chip and are sent to the model - draw.io's own simple UI: its toolbar, menus (auto layout, Mermaid, CSV import) and page tabs; the shape library starts closed - new start screen, session rename and switcher, open .drawio files - settings split into Models & keys, Appearance, Advanced and About - Bedrock in the model settings also takes an API key instead of access keys - phones switch between canvas and chat - dark mode uses draw.io's own dark mode, with neutral greys around it With an external draw.io (NEXT_PUBLIC_DRAWIO_BASE_URL) the features above that need same origin are off. The chat logic moved from the 1500-line chat panel into a chat engine plus small UI components; zustand holds canvas, versions, settings and UI state. |
||
|
|
257a4bca06 |
docs: explain how to use LM Studio with the OpenAI provider (#897)
Set the base URL to http://localhost:1234/v1 and use any non-empty API key. |
||
|
|
8639779a23 |
fix(ollama): send chat requests to the /api path (#954)
The Ollama SDK appends /chat to the base URL, and Ollama serves chat at /api/chat. The model list already added the missing /api, so with "http://localhost:11434" (the address our docs showed) models were listed but every chat request went to /chat and got a 404. An OpenAI-style ".../v1" address went to /v1/chat. ollamaApiUrl() turns the server address, ".../v1" and ".../api" into ".../api". Chat, the model list and the settings dialog's request URL hint all use it. The docs now show http://localhost:11434/api, which also works on released versions. |
||
|
|
0b63e28e5a |
feat: MCP feature parity, the web app on the MCP core, and provider fixes (#951)
* fix(chat): close credential leaks and harden the chat route
- Vertex: a client-supplied base URL only works with the client's own Vertex key
- Accept only data: URLs for file parts in every message, so the server never downloads them
- Output budget retry accounts for the thinking budget Bedrock/Anthropic add, and reads
Volcengine, DashScope, SGLang and vLLM rejections; falls back to 16000 once
- x-max-output-tokens can only lower the budget on server credentials
- On server credentials only server models or AI_MODEL entries can be used
- Drop tool results together with the invalid tool calls they belong to
- Count quota tokens as input + output (cached tokens were counted twice)
- Private-URL check for custom base URLs, end Langfuse traces on error/abort/early return
- Fix repairToolCall ordering and placeholder, align edit_diagram prompt with operations
- Panel Bedrock keys are read from ADMIN_AWS_*; forward the access code to EdgeOne
- isMinimalDiagram only treats root cells as an empty canvas
* fix(api): require access codes and limit sizes on helper routes
- Shared checkAccessCode for validate-diagram, validate-model, parse-url, verify-access-code
- parse-url: 5 MB streamed body limit; validate-diagram: 5 MB image limit
- validate-model refuses redirects when private URLs are blocked
- Admin settings state shared across module instances via globalThis
- Server model ids: unique slugs (non-ASCII names encoded), duplicates rejected
- Panel Bedrock credentials stored as ADMIN_AWS_* so the DynamoDB client keeps its own
- Locale redirect keeps basePath and query; EdgeOne function drops open CORS and checks the access code
- Providers payload reports whether .env sets a default model
* fix(chat): keep saved diagrams and pages when restoring, editing and retrying
- Restored sessions no longer replay the last display_diagram over the saved diagram
- Failed or stopped edit_diagram restores the canvas
- Message snapshots keep the full multi-page document
- "Improve with suggestions" uses the normal send path (headers, xml, retry counters)
- Editing a message keeps its file/URL sections; cached example edits work
- New chat's first autosave no longer resets the UI
- Validation retries counted per user turn; validate-diagram sends the access code
- Cached examples only match the example files on an empty canvas
- Template sends keep attachments and wait for extraction
* fix(diagram): fix autosave staleness and XML repair corrupting valid diagrams
- Autosave guard reads refs, so edits after a theme or dark mode switch are kept
- Duplicate-id check and rename run per page; repair loop no longer quadratic
- autoFixXml no longer breaks style values, rich text " or single-line cells
- extractCompleteMxCells keeps the cell after a self-closing cell
- Better truncation detection; object/UserObject wrapped cells are editable
- Exports for thumbnail, PNG and save are routed by tag instead of a shared resolver
- History stores the full document; storage errors are reported, no auto-deletion of chats
- IndexedDB connection reopens after errors; focus refresh throttled
- Keep ?session= on locale redirect, map zh-Hant to zh-tw for draw.io
* fix(chat-input): stop template dialogs from sending and fix attachment races
- Template dialogs no longer submit the outer chat form
- Sending is blocked while files or URLs are still extracting
- File and URL extraction no longer drop or resurrect entries
- IME composition Enter no longer sends
- Tool call cards show the error text; keyboard handling on cards fixed
- Template import available when empty, edit dialog resets, saved templates refresh
- Only png/jpeg/gif/webp images accepted, SVG sent as text; PDF objects released
- parse-url request sends the access code
* fix(model-config): keep model selection valid and fix admin panel edge cases
- Fall back to the default server model when a saved one disappears
- Sync model config across tabs
- Validation uses the base path and sends the access code
- Model ids edited as drafts (no empty, duplicate or padded ids)
- Credential changes reset validation; stale validation results are dropped
- Admin: generateId over HTTP, env-locked group switches, discard and toggle fixes,
clearing a secret field keeps the saved key, first provider not auto-default when .env sets AI_MODEL
- Model selector items use unique values
* fix(electron): decrypt keys after ready and harden navigation and IPC
- Apply preset env after app ready, so Windows/Linux get decrypted keys
- Never re-encrypt ciphertext; restore env when switching or removing presets
- Block navigation away from the app, open external links in the browser, check IPC senders
- Keep inherited proxy settings, default NO_PROXY for localhost
- Serialize server start/restart, kill stuck processes, follow port changes
- Atomic config writes, keep corrupt files as backups, remember the server port
- Menu and settings window stay in sync; dev script gets the decrypted preset env
- Use app.isPackaged, parse inline .env comments, drop .env files from the bundle
* fix(mcp-server): fix XSS and crashes, make XML validation strict
- 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
* 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
* feat(mcp-server): bring the web app's drawing knowledge to MCP
- Add a drawing guide adapted from the web system prompt (layout, edge
routing, styles, minimal style, editing rules), returned by
start_session, a new get_drawing_guide tool and the diagram-workflow prompt
- Add get_shape_library with the 30 icon libraries; the build copies
docs/shape-libraries into dist and CI checks the packed files
- Accept bare mxCell lists in create_new_diagram and add_page; the server
adds the wrapper and root cells
- Send server instructions, shorten create_new_diagram's description to
fit Claude Code's 2,048 character limit, and annotate every tool
- Fix dead links and the totals in docs/shape-libraries/README.md
* feat(mcp-server): add screenshot_diagram so the model can check its render
- New read-only screenshot_diagram tool returns the rendered page as a PNG
plus the web app's visual checklist (overlaps, edges crossing shapes,
readability, layout, rendering errors), replacing the web app's
separate vision model with the host model's own vision
- PNG exports use draw.io's width and pageId options: screenshots stay
under ~140,000 base64 characters and page exports no longer swap the
page on screen
- Fail fast with a clear message when the preview tab stopped polling
(browsers throttle background tabs)
- Mention the screenshot step in the drawing guide and instructions
* feat(mcp-server): auto-save each session's diagram to a .drawio file
- Save the latest diagram of every session 1 second after each change
(AI write, browser edit, history restore) to ~/.next-ai-drawio/<id>.drawio,
keep the newest 50, flush on shutdown; DRAWIO_DATA_DIR changes the folder
and "off" disables it, like the web app's IndexedDB sessions
- start_session names the file, so a resumed conversation can reopen the
diagram with load_diagram after the MCP process restarted
- Fix PNG/SVG exports randomly timing out: a previous export's 10 second
timer cleared the export in progress, and a late reply could be taken
for the current one; exports are now numbered
* refactor(mcp-server): move the preview page into src/preview
The 580-line page template in http-server.ts becomes index.html,
preview.css and preview.js, copied to dist/preview by the build and
filled at request time. The rendered page is unchanged apart from the
session id and draw.io origin now coming from a small config script.
Biome skips the folder because of the {{placeholders}}, as it never
linted the old template string either.
* feat(mcp-server): add theme menu, dark mode and editable SVG to the preview
- Pass themes=1 and dark=auto to the draw.io iframe, so the Extras menu
offers the Theme submenu and draw.io follows the system dark mode; the
header and dialogs follow it too through prefers-color-scheme
- Download dialog: new Editable SVG (.drawio.svg) format through draw.io's
xmlsvg export, default name diagram-YYYY-MM-DD, Enter saves and Escape
closes it and the history dialog
- export_diagram accepts format "drawio.svg" and detects the .drawio.svg
extension
- Use system fonts instead of Google Fonts, so the page works offline with
a self-hosted draw.io (DRAWIO_BASE_URL)
* docs(mcp-server): describe the new MCP features in all READMEs
- MCP server README: 13 tools, drawing rules and shape libraries,
screenshots, all-or-nothing edits, auto-save and how to continue a
diagram later, DRAWIO_DATA_DIR and DEBUG, offline use with a local
draw.io, and what to do when a background tab makes exports time out
- Fix two errors: History is a button at the top right of the preview
page, and exports are not limited to .drawio
- Claude Code plugin README: same tool list, formats and settings
- Root READMEs (English, Chinese, Japanese): short list of what the MCP
server can do
* fix(mcp-server): fix duplicate page exports and auto-save deleting user files
- 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
* fix: log Editable SVG saves, use local dates in file names, drop unused Electron dialogs
- /api/log-save accepted only drawio, png and svg, so saving as Editable
SVG (xmlsvg) got a 400 and was never recorded in Langfuse
- The default download name and the template export name used the UTC
date, which is the previous day on mornings in East Asia
- Remove the Electron openFile/saveFile IPC handlers, their preload
bindings and types; nothing in the app calls them
* refactor(mcp-server): make the diagram modules usable from the web app
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
* 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
* refactor(web): edit and wrap diagrams with the MCP server's code
- edit_diagram runs the MCP server's editDiagram: every new_xml is checked
first, one cell per operation, and after the edit only the target page
is checked, rejecting only errors this edit introduced. An unrelated
problem elsewhere in the document no longer blocks every edit. The
error lists each failed operation
- The streaming edit preview uses the MCP applyDiagramOperations
- Delete applyDiagramOperations (292 lines) and wrapWithMxFile from
lib/utils.ts, and the unused hand-copied scripts/test-diagram-operations.mjs
- One blank document (BLANK_MXFILE) for the web app and the MCP preview,
replacing four copies
- Saving a .drawio wraps a bare model with normalizeToMxfile
- The empty-diagram check uses hasCells, which also counts cells wrapped
in a UserObject/object
- DiagramOperation is the MCP type
- The wrapped-cell and empty-diagram tests now run against the MCP code
- New e2e test: edit_diagram changes the canvas, and a failing edit
leaves it as it was
* refactor(web): share prompt examples and the shape library with the MCP server
- The three XML examples (swimlanes, two edges, waypoints) that the web
system prompt, the display_diagram description and the MCP drawing
guide each had a copy of now live in packages/mcp-server/src/xml-examples.ts
- The shape library group list and reader come from the MCP
shape-library.ts; getShapeLibrary takes the folder, and the web route
passes docs/shape-libraries under the working directory as before.
Only the 30 known library names are read. Error texts differ slightly
- next.config.ts traces docs/shape-libraries/*.md for /api/chat, since
the read now happens in another module
- Every prompt is byte-for-byte unchanged: the web system prompt for five
model ids with and without minimal style, and the MCP drawing guide,
compared before and after; the library list and swimlane example match
the old tool description text exactly
* fix(providers): update the v6 SDK packages and fix Claude and Gemini settings
- Update ai to 6.0.300 and the @ai-sdk providers to their latest v6-line
versions. @ai-sdk/anthropic 3.0.47 did not know claude-opus-4-7/4-8
and capped their output at 32000 tokens; 3.0.127 allows 128000
- Drop the fine-grained-tool-streaming beta header for the Anthropic API:
the provider now streams tool input per tool (eager_input_streaming)
- Claude 4.7 and later reject a non-default temperature/top_p/top_k and
the extended thinking budget with a 400. A middleware retries once
without them, so TEMPERATURE and *_THINKING_BUDGET_TOKENS no longer
break those models
- Prompt caching also reaches Claude on the Anthropic API and OpenRouter;
before, only Bedrock got a cache marker
- GOOGLE_TOP_K and GOOGLE_TOP_P never reached Gemini: they were sent as
Google provider options, which drops them. They are call settings now.
GOOGLE_CANDIDATE_COUNT and GOOGLE_REASONING_EFFORT, which the provider
does not support, are removed
- Add @ai-sdk/openai-compatible as a direct dependency
* refactor: simpler streaming preview and small AI SDK cleanups
- useChat throttles streamed message updates (experimental_throttle,
150 ms), replacing the two hand-written 150 ms timers of the
display_diagram and edit_diagram previews (94 lines less). The preview
now only runs while the input streams; once it is complete the tool
handler takes over, so a queued preview can no longer redraw an edit
the handler rejected and rolled back. Measured on a streamed 60-cell
diagram: 41 redraws at least 97 ms apart, before 37 with gaps down to
48 ms
- The diagram check endpoint uses streamText with Output.object instead
of the deprecated streamObject, and returns its fixed result as a plain
text response; new route test
- Import createGateway/gateway from ai and drop the direct
@ai-sdk/gateway dependency
- The per-request message structure logs only print with
DEBUG_LLM_PAYLOAD=true
- Remove an empty onFinish callback
* refactor(chat): check the last tool part with the SDK's isToolUIPart
Drop the hand-written MessagePart and ChatMessage types that only served
this check.
* refactor(providers): one model factory for chat and the settings Test button
- getAIModel resolves credentials (client key, server env vars, the
existing SSRF rules) and createModel builds the model by SDK. The
provider-by-provider switch shrinks from 24 cases to the few that
differ (lib/ai-providers.ts 1531 -> 1106 lines)
- /api/validate-model calls getAIModel instead of its own 24-case switch
(503 -> 175 lines), which had drifted from the chat: it built Azure
with createOpenAI, Kimi and MiMo with createOpenAI instead of
createDeepSeek, and the official OpenAI endpoint with Chat Completions.
A passing test now means the chat works
- Plain OpenAI-compatible providers (SiliconFlow, SGLang, ModelScope,
GLM, Qwen, Qiniu, Novita, Atlas Cloud, EdgeOne, Doubao, MiniMax in
OpenAI mode, AIHubMix on a custom URL) use @ai-sdk/openai-compatible,
which reads reasoning_content, so their reasoning shows, and accepts
SGLang's stream as is (its 95-line stream rewrite is gone).
includeUsage keeps token usage for quotas. <think> tags in their text
become reasoning (extractReasoningMiddleware)
- SGLang without a base URL used OpenAI's endpoint; it now defaults to
http://127.0.0.1:8000/v1 like the Test button did
- Chat requests to a client base URL refuse redirects, as the Test
button already did (redirectGuardedFetch moves to lib/ssrf-protection)
- The Test button streams like the chat (the ModelScope special case is
gone), times out after 15 s, does not retry, asks the model to call a
ping tool and warns when it answers without one, and tests all models
at once. The time each test took shows on its check mark
- Unknown provider names are rejected with Object.hasOwn, and the error
texts list providers from PROVIDER_INFO instead of hand-kept lists
* feat(settings): link to each provider's key page and clean up base URLs
- A "Get API key" link next to the API Key field for the 19 providers
that have a key page (from env.example and the providers' docs). 17
answered 200 to curl; OpenAI's is behind a Cloudflare challenge and
DeepSeek's behind a regional block, both checked in Chrome
- Base URLs drop spaces, trailing slashes and a pasted endpoint path
(/chat/completions, /completions, /messages, /responses), which the
SDK would otherwise append a second time and get a 404. getAIModel does
this for the chat and the Test button; the field does it on blur and
shows the URL requests go to
* feat(errors): classify provider errors and show a hint the user can act on
- lib/llm-errors.ts sorts an error into about a dozen kinds (key
rejected, no access, unknown model, no credit, rate limited, context
too long, no image input, no tool calls, output cut off, provider down,
cannot connect, timeout): first texts that name the cause precisely,
then the HTTP status code, then general texts. It unwraps RetryError and
hides keys and Bearer tokens in the provider's message
- The chat route uses it for errors before the stream and, through
toUIMessageStreamResponse's onError, for errors in the stream. Errors
of the model's own tool call stay as they are: the same text goes back
to the model so it can fix the call
- The chat shows the hint in the user's language, then the provider's
message; a rejected key, missing access or unknown model adds an "Open
model settings" button. The Test button shows the same hints
- Fixes: our message "API key is required when using a custom base URL"
was replaced by "Authentication failed" because it contains "key"; a
provider's "Rate limit exceeded" opened this site's quota toast; an
error body like {"error": ...} was shown as raw JSON; the Test button
matched "401" in the message, where providers rarely put it
- Remove the string matching fallbacks in the chat panel
* refactor(settings): drop an unused index parameter
* feat(settings): fetch the model list from any provider and flag models that cannot draw
The "Fetch models" button asks the provider for its models (OpenAI-style
/models, Anthropic, Google, Ollama, OpenRouter, Vercel Gateway, AIHubMix)
and shows them in a searchable picker. This replaces the route that only
worked for AIHubMix.
A snapshot of models.dev (MIT) says which models support tool calls.
Models without them get a "no tool calls" badge in the picker and a hint
in the model list, since drawing needs tool calls. Refresh the snapshot
with scripts/update-model-catalog.mjs.
* fix(mcp-server): reject text between tags, which draw.io cannot open
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.
* fix(chat): clearer provider errors and no empty bubble, found with real models
- An error object sent inside the stream (OpenRouter's { code, message })
showed as "[object Object]"; its message and status code are read now.
- A problem+json "detail" is added to the message: NVIDIA only said
"Gone" for a retired model. 410 counts as model not found.
- "Cannot connect to API" from the SDK gets the connection hint.
- Text that is only whitespace (Kimi K2.6 sends a space before a tool
call) no longer shows an empty bubble.
- allowSystemInMessages stops the warning on every request. Our system
messages carry cache points; a client's own system messages are already
dropped by the empty-content filter.
* feat(providers): suggest the Claude 5 and GPT 6 models, show GPT 6 reasoning
The suggested models stopped at Claude Opus 4.8 and GPT 5.5. They now
start with Claude Opus 5.5, Sonnet 5.5 and Fable 5.1, and gpt-6.1-sol,
gpt-6-sol, gpt-6-luna and gpt-6-astra (ids checked against the provider
lists). The Bedrock list is unchanged until its ids are checked.
The reasoning summary was only turned on for model ids containing o1,
o3, o4 or gpt-5, so GPT 6 models showed no thinking. It now matches the
o-series and gpt-5 or later by version, like the OpenAI SDK.
* fix(providers): keep thinking on the newest Claude models, fix the Bedrock ids
Measured on Bedrock: Claude Opus 4.7, 4.8 and every Claude 5 model reject
a thinking budget ("thinking.type.enabled") and a temperature, and all of
them accept adaptive thinking. The retry used to drop thinking, so with
a thinking budget set these models did not think at all. It now switches
to adaptive thinking with display "summarized"; without that setting the
models think but send no thinking text to show.
The suggested Bedrock ids had no region prefix, and the newer models only
answer through an inference profile id: "anthropic.claude-sonnet-5-5"
fails with "on-demand throughput isn't supported". Each suggested id was
called once; the Claude ones now start with "global.", Llama, Pixtral and
Nova 2 Lite with "us.". Nova Premier (end of life) and the ids without a
working profile are gone. The docs example had the same problem and a
wrong date.
* fix(chat): an edit after a broken edit call no longer fails, found with Opus 5.5
- Claude Opus 5.5 sent an edit with invalid JSON, then the same edit
again. The first call's streamed preview was never undone: its input
has no operations, and the undo sat behind that check. The second edit
then started from the preview, failed on a duplicate id, and the model
had to try a third time. The undo now runs first, and an edit that
starts in the same render uses the undone diagram.
- The SDK passes an invalid tool call's error as a string, which was
wrapped as a provider error. streamErrorText keeps it as the text the
model reads.
- Bedrock's "on-demand throughput isn't supported" gets the model id hint.
- The thinking header uses the page language ("Thought for 1 second" in
English), from the dictionary entries that were already there.
* chore(mcp-server): release the new features as 0.3.0
npm has 0.2.3. The earlier commits raised the version step by step to
0.6.0, but they ship together as one release.
* style: auto-format with Biome
* 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.
* fix(mcp-server): keep both pages when get_diagram meets a page export, and more review fixes
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.
* fix(server): count quota by the key actually used, and more review fixes
Found by the PR review, each with a test that failed first:
- Quota: any key header skipped it, even one the provider never reads
(x-aws-access-key-id with OpenAI), so a request ran on the server's
key without being counted. The check now runs after the model is
resolved and uses usesServerCredentials. On main already.
- usesServerCredentials read the raw base URL; "/" cleans up to none, so
an Ollama request ran on the server's key past the server-model check.
- SGLang's default 127.0.0.1:8000 only fills the settings form. Chat and
the model list used it as a real address, so the server called its own
machine even with private URLs blocked. Now a base URL is required.
- With a user's OpenAI key and no base URL, the SDK read the server's
OPENAI_BASE_URL. The official endpoint is now passed. On main already.
- The Test button refused nothing on the server's keys (Ollama Cloud),
and a 15 s timeout reported "connected, no tool call".
- The model list for Ollama without a base URL came from ollama.com while
chat went to the server's Ollama.
- Bedrock's "Too many tokens, please wait" counted as context too long.
- On the server's keys the provider's error text stays in the server log;
it can name the server's AWS account, role or internal hosts.
- Desktop app: the preset keys are the user's own (NEXT_AI_DRAWIO_DESKTOP),
so Max Output Tokens can be raised and keyless models in settings work
again. A launch that found the remembered port taken no longer replaces
it, which hid the user's chats and settings for good.
* ci: run auto-format with the Biome version in package.json again
package.json moved to Biome 2.5.7 but the auto-format job stayed on
2.4.13. The two format some files differently, so on this PR the bot
reformatted tests/unit/log-save-route.test.ts with 2.4.13 and the lint
job, on 2.5.7, then failed on it. The pin now matches package.json, as
its comment asks, and the file is back in 2.5.7's format.
* fix(chat): keep the canvas after an unrelated error, and more review fixes
Found by the second PR review:
- After a streamed edit, an older render of the stream stored the edit's
original diagram again, and the next failed request (no quota, a lost
connection) put that old diagram back on the canvas. The tool handler
now marks its call as handled, so the preview code leaves it alone.
- An edit applied before the UI showed an earlier broken edit's error was
erased when that error undid its preview, or was built on that preview.
The handler now starts from the diagram before all unhandled previews,
and reads the diagram state that updates at once.
- A failed or stopped display_diagram left its half drawn diagram on the
canvas. Its preview is undone now, like an edit's.
- "New chat" cleared a chat that could not be saved (storage full).
- The settings dialog showed a model list, a fetch error or a test result
on the provider that was opened after the request started, and marked a
model id changed during the test as tested.
- A tool call with broken JSON was shown as cut off by the output limit.
- History entries and session thumbnails could pair with a later diagram
when draw.io answered an export late.
- A server model saved before non-ASCII provider names got into the id
was reset to the default model.
* fix(mcp-server): count a one-page view only for that page, and more review fixes
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.
* fix(server): use the keys the user sent, and more review fixes
Found by the second PR review:
- With AWS_BEARER_TOKEN_BEDROCK set on the server, a request with the
user's AWS keys ran on the server's token: the Bedrock SDK prefers it.
Checked with Bedrock: invalid user keys used to get an answer.
- An OpenAI key with the official URL filled in (the settings form does
that) went to the Responses API. Back to main's rule: a configured base
URL uses Chat Completions.
- A user's Ollama key went to the server's OLLAMA_BASE_URL, for chat and
for the model list. Like every other provider, it goes to the user's
base URL or Ollama Cloud.
- The server's keyless Ollama and EdgeOne were not counted in the quota.
- AI_MODEL models on the server's keys ran on any provider with a server
key, not only on AI_PROVIDER.
- A user's Azure key without a base URL used the server's resource name.
- The admin panel's Test button failed whenever access codes were set.
- DeepSeek's errors in the stream (plain text) were shown as they were,
without a hint and also on the server's keys. Bedrock's throttling in
the stream was not recognised as a rate limit.
- The EdgeOne function accepted text/plain; x=application/json, which
other sites can send without a CORS preflight.
- Desktop app: a launch that found the old port taken for a moment (the
previous version still quitting after an update) remembered the new
port for good. The new port is kept only when Windows reserves the old
one. A failed read of the presets file moved it aside as corrupt, and a
save could then replace the presets. Switching presets on the same port
now reloads the page. The dev launcher no longer misses a preset change
made before or during a restart.
* fix(chat): undo rejected or stopped previews, and more fixes from the third review
- A call the server runs (get_shape_library) still reaches the browser's
tool handler, and it dropped the stored diagram of an earlier broken
edit, whose preview then stayed. Only the tools that draw take it now.
- A display_diagram whose final XML fails the checks loads the diagram
from before its preview again, as a failed edit does.
- After Stop, a tool result that arrives later (a screenshot check still
running) no longer sends a new request; Stop also skips calls the tool
handler already took.
- New chat and opening another chat kept nothing of a diagram drawn
without messages when it could not be saved; now they stay on it.
- The settings dialog drops a model list or test result whose provider
credentials changed meanwhile, also in another tab.
- A saved provider this version does not know crashed the whole page on
load; it is skipped.
- The input emptied a moment after the message showed in the chat, so it
briefly appeared twice (seen as a flaky e2e test).
- The desktop app's preset switch on the same port refetches the server
models instead of reloading the page, which lost unsent attachments.
* fix(mcp-server): recover sessions in one place, and more fixes from the third review
- A session whose state expired was recovered from its auto-save file only
for the preview page; the tools built on their older copy and then
overwrote the file. They now recover it first (restoreSavedSession).
- A preview tab that missed the last AI write pushed its older diagram
over the recovered one after a restart. It now shows the recovered
diagram and keeps its own copy in History.
- An empty record of what the model has seen (after load_diagram or a
page tool on unseen changes) no longer lets one page of a multi-page
document count for all, and get_diagram counts a page only once found.
- History: a thumbnail goes only to the entry it shows, the cached image
never belongs to an older diagram, a re-serialized copy adds no entry,
and a cleared document with its own pages is kept before a restore.
- The root cell id check reads attributes one by one: rack-id="1" or id
text inside a label no longer counts.
- A compressed page counts as having cells; a saved file that could not
be read is never written over.
* fix(server): keep users' keys at their own endpoints, and more fixes from the third review
- Bedrock: a user's AWS keys no longer go to an endpoint the server sets
in AWS_ENDPOINT_URL_BEDROCK_RUNTIME / AWS_ENDPOINT_URL (read by the
upgraded SDK), and admin panel keys win over AWS_BEARER_TOKEN_BEDROCK,
as the Test button checks them. Checked with Bedrock.
- Ollama: a server key without a base URL (admin panel, OLLAMA_API_KEY)
goes to Ollama Cloud, as env.example says, instead of 127.0.0.1.
- Quota: EdgeOne counts whatever key header comes along, keyless Ollama at
a private address counts, and their provider texts stay in the log.
- An EdgeOne server model (admin panel, ai-models.json) works: the route
checked the raw provider header, which holds the name's slug.
- parse-url ends downloads it does not read (too large, PDF, errors).
- Desktop app: the port follows where the chats are (IndexedDB per
origin) instead of a remembered port, which could hide them for good;
a same-port restart tells the page to refetch the server models; a
failed preset switch no longer undoes a newer choice; a presets file
removed after a failed read can be saved again; .env values quoted from
start to end keep their inner quotes, as dotenv reads them.
* fix(security): check request sources, regions and endpoints
- Bedrock: a request's AWS region must be a region name. It becomes part
of the endpoint's host name, so a value such as
"us-east-1.attacker.example/" sent the server's bearer token or signed
request to another host.
- MCP preview server: only the preview page itself (Origin equal to the
Host) or a non-browser client may call it; a page on another localhost
port could replace the diagram with a plain text POST. History builds
its thumbnails element by element and shows only SVG data images, so a
stored value can no longer run script in the preview.
- chat, validate-model, validate-diagram, provider-models and parse-url
take JSON bodies only, so another website cannot make the user's own
server (the desktop app, a local install) run models with their keys;
the desktop app also refuses a foreign Host (DNS rebinding).
- The model list reads at most 2 MB, also through the Gateway SDK, and
answers only with its own error texts: the URL is the caller's and may
be an internal address.
- An admin panel provider with its own key and no URL no longer inherits
the global <P>_BASE_URL, which may be a proxy for another key; OpenAI
then gets the official endpoint, as its Test. Azure keeps the server's
resource.
* fix: what the third round broke, and the first batch's review
MCP preview after the server lost a session (it expired, or the MCP
process restarted):
- Every server state has an id, made when the state is created. The tab
notices a new id even when the version numbers happen to match, and
every push names the state it was based on, so one based on a lost state
is refused, also when it comes before the tab's first poll (the server
recovers the saved file first).
- The tab keeps the newest canvas XML, saved or not. When the server knows
nothing (no file) or exactly what the tab last saved, the canvas wins and
is saved, so edits made while the server was down are kept. Otherwise
the server's diagram (an AI write the tab missed, a cleared document
that was saved) is shown and the tab's copy goes to History.
- Late answers to an old state's push or poll are dropped; a failed push
says the server is unreachable; Download as .drawio saves the canvas.
Settings and server:
- Saved providers this version does not know stay in storage with their
keys, and sending no longer trips over them.
- The desktop "Ollama (Local)" preset with a key goes to local Ollama
again; a server model's Ollama URL variable is read; the admin panel
writes Ollama Cloud's URL for a key without one.
- Provider error texts show again in the desktop app and for EdgeOne.
- .env: a quoted value followed by a comment ending in a quote is read as
dotenv reads it; unquoted values are unchanged.
- Desktop app: the next launch opens the port where a chat was last
saved; a launch elsewhere that saves nothing does not move it, and a
page with no chats lets the next launch try the other port once.
- The Test button no longer stays busy after another tab changed the key.
- A completed append_diagram is no longer undone by an earlier failed
edit's preview; a file read once in vain is saved again once it is read
or gone.
From the first batch's review:
- The admin panel's Test of an entry without a URL now tests the server's
<P>_BASE_URL, where chat sends the entry's key; chat is unchanged (the
first fix rerouted working setups).
- The model list ends downloads that are too large, accepts answers
without a body, and keeps the "redirects are not allowed" explanation.
- A test covers the preview's History rendering.
* fix: older defects (batch C) and the second batch's review
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.
* fix: what the batch C review found
- A redirect followed for a custom base URL (quota on) no longer carries
the user's key or cookies to another origin, as fetch itself does, and
a private address may redirect to another private one (already counted).
- The admin Test of an Ollama or Vertex AI entry without a URL goes where
chat sends that entry's key: the environment's own URL variable, for
Ollama else the local default. The Test of an Ollama Cloud key without
a URL went to the cloud while chat went to local Ollama.
- Chat saves: each save notes the chat on screen and the order of the
reads before reading its data. A save read before switching chats no
longer writes into the chat switched to, and a copy that waited for its
thumbnail no longer replaces a newer one.
- "Continue without saving" keeps its button when a later auto-save fails,
and goes away when a new message is sent.
- A screenshot check that was waiting for its image when the user pressed
Stop stays skipped after the next message.
- MCP History: draw.io's own copy of a diagram (after get_diagram) no
longer adds an entry without a picture; a change of background is still
its own version. The tab ignores an edit's answer that arrives after a
newer AI write loaded.
- Desktop: a deleted preset is not brought back by a failed switch, and a
request naming no preset does not stop a rollback. An origin keeping
an access code counts as having settings.
- .env: a quoted value ending in a backslash ("C:\dir\") is read as dotenv
reads it.
- The tool card no longer crashes on an id that does not turn into text;
an older Test's success timer no longer ends a newer Test's spinner.
- Tests that passed without their fix now check it.
* fix: what the whole-PR review and Copilot found
- A redirect followed for a custom base URL also drops the key headers of
providers that do not use Authorization (x-api-key, x-goog-api-key,
api-key) when it goes to another origin.
- A second Enter or click while a message is being prepared (attachments
read, diagram exported) no longer sends it twice.
- The admin Test on the deployment's own endpoints (EdgeOne, the server's
keyless Ollama, an address on the server's network) counts toward the
quota like a chat; the chat and the Test share one rule for it. The
Test of an Azure entry set up by AZURE_RESOURCE_NAME only goes where chat
goes.
- EdgeOne's function is called at the site root again, as on main: EdgeOne
serves edge functions there, outside Next's base path.
- MCP History: the state before a write is kept unless the browser saved
no change of the user's since the last server write (draw.io's sync copy
of it adds no entry), and the dedupe compares the exact text again, so a
change of page size or other settings only is its own version.
- MCP: an edit keeps untouched labels as draw.io shows them (a literal line
break in an attribute is a space); a new document of empty pages the
user named is auto-saved; load_diagram reads only regular files, so a
pipe cannot hold up the other write tools; the preview does not load
back its own push still on its way (an undo made meanwhile is saved).
- Two overlapping saves of a new chat no longer reload the canvas from the
older copy.
- At most three screenshot checks per user turn, passed or failed, as
documented.
- Desktop: the main window navigates only within the app (draw.io stays in
its frame); a presets file that is not JSON and cannot be moved aside is
not overwritten.
- A last self-closing cell with a raw "<" in a value is not taken for cut
off output.
- README: Material Design shapes load their icons from fonts.gstatic.com.
* fix(chat): stop saving and exporting an idle chat every second
Each auto-save takes a thumbnail, and draw.io's SVG gets a new random id
every time, so latestSvg changed and the diagram context rendered again.
getThumbnailSvg was a new function on every render, the auto-save depends
on it, so the next save was scheduled a second later: a chat with some
diagrams was saved and exported once a second for as long as it was open.
getThumbnailSvg (and the export helper it uses) now keep one identity;
they read refs only.
Found by hand in the production build; the new e2e test fails without the
fix on a production build (as CI runs it).
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||
|
|
4984be82a1 |
feat: add MiMo (Xiaomi) as AI provider (#887)
* feat: add MiMo (Xiaomi) as AI provider * fix: correct MiMo default base URL, suggested models, and reasoning support - Default base URL was the China Token Plan endpoint (tp- keys only); switch to https://api.xiaomimimo.com/v1 which works with standard pay-as-you-go sk- keys. Token Plan users can override in settings. - Replace deprecated mimo-v2-flash suggestion with mimo-v2.5 (v2 series was deprecated on 2026-06-30). - Use createDeepSeek instead of createOpenAI so reasoning_content is passed back during multi-turn tool calls (MiMo returns 400 without it), matching the existing Kimi implementation. - Add mimo to SINGLE_SYSTEM_PROVIDERS so system messages are merged. - Fold validate-model case into the shared OpenAI-compatible group. - Drop the Bot icon special case; models.dev serves a real xiaomi logo via PROVIDER_LOGO_MAP. - Document MIMO_API_KEY/MIMO_BASE_URL in env.example and docs/{en,cn,ja}/ai-providers.md. * feat: show base URL hint for MiMo in provider settings MiMo has two endpoints tied to key type: pay-as-you-go keys (sk-...) use the default api.xiaomimimo.com/v1, while Token Plan keys (tp-...) require token-plan-cn.xiaomimimo.com/v1. Surface this under the Base URL field like the existing MiniMax hint, in all four locales. --------- Co-authored-by: mapengfei <[email protected]> Co-authored-by: dayuan.jiang <[email protected]> |
||
|
|
8e42dd9da8 |
feat: support comma-separated AI_MODEL for quick multi-model setup (#870)
Users expected setting AI_MODEL to a comma-separated list to expose multiple models in the picker, but the value was used verbatim as a single model id, leaving the picker with only the "Server Default" fallback. Add a third-priority fallback in loadEnvServerModelsConfig: when AI_MODELS_CONFIG and ai-models.json are both absent, AI_MODEL contains a comma, and AI_PROVIDER is set to a known provider, synthesize an equivalent ServerModelsConfig with the provider's models trimmed, deduplicated, and the first marked as default. Also makes getAIModel and getValidationModel pick the first comma-split value when falling back to AI_MODEL, so requests started before the client picker hydrates still resolve to a real model id. Docs (en/cn/ja) and env.example updated; tests cover the new fallback plus the no-comma / no-AI_PROVIDER negative cases. |
||
|
|
0f9699843f |
feat: add AIHubMix provider (#865)
* feat: add AIHubMix provider * feat: load AIHubMix models dynamically * feat: polish AIHubMix model setup * feat: send AIHubMix app code * docs: remove redundant AIHubMix recommendation --------- Co-authored-by: LL <[email protected]> |
||
|
|
449e4c4e26 |
feat: add file-based admin settings panel at /admin (#866)
* feat: add file-based admin settings panel at /admin
Settings saved in the panel are written to data/settings.json and
overlaid onto process.env, taking precedence over environment
variables and applying immediately without restart. Enable by setting
ADMIN_PASSWORD; on serverless platforms without persistent disk the
panel degrades to read-only.
* polish: admin panel UI improvements
- Provider logos in credential rows (shared ProviderLogo component,
extracted from model-config-dialog)
- Scroll-spy active state in the sidebar nav
- Green success state in the save bar that clears after a few seconds
- Wider content column (max-w-6xl) for less wasted space on desktop
* polish: admin panel section toggles and reorder
- Move Quota & Rate Limits to the end of the settings page
- Add enable switches to Observability and Quota sections; default off
with fields grayed out, auto-on when any field is already configured
* polish: make section enable switch more visible
Wrap the switch in a labeled pill ('Enabled'/'Disabled') with border
and background so the off state is clearly visible.
* refactor: derive admin registry from PROVIDER_INFO, simplify page state
- Provider options, labels, and base-URL placeholders now come from
PROVIDER_INFO instead of hand-copied lists (fixes SiliconFlow .com/.cn
placeholder drift; panel names now match the model-config dialog)
- Replace free-text subgroup strings + SUBGROUP_PROVIDERS reverse map
with a typed provider field on SettingDef
- Precompute SETTINGS_BY_GROUP and PROVIDER_SUBGROUPS at module level
- Merge justSaved into saveMessage, drop unused mainRef, hoist
fetchSettings out of the component, dedupe savedText logic
- Serialize from SETTINGS_REGISTRY directly; json validators in a map
instead of a hardcoded key check
- Make allowPrivateUrls a function so ALLOW_PRIVATE_URLS edits in the
admin panel apply without restart
* feat: graphical model management in admin panel
Replace the provider credential fields and raw AI_MODELS_CONFIG JSON
textarea with a Models section mirroring the in-app model settings UI:
provider instance list with logos, credential fields per provider type,
model add/remove with suggestions, per-model connectivity test, and a
default-provider star.
On save the server derives everything the runtime needs into
settings.json: credential env vars (with _2 suffixes for multiple
instances of one provider), AI_MODELS_CONFIG, and AI_PROVIDER/AI_MODEL
for the default. Secrets round-trip as masked markers and are never
sent back to the browser. The general settings registry now only
covers non-provider settings (generation, access, features,
observability, quota).
* fix: allow testing unsaved providers in admin panel
The test button previously looked up credentials by providerId in the
saved settings, so testing a newly added (unsaved) provider failed with
'Unknown provider or model'. The test endpoint now accepts the client's
current provider state; newly typed secrets are used as-is and masked
markers are resolved against the stored values, so testing works both
before and after saving.
* fix: merge env AI_MODELS_CONFIG with admin panel providers
Previously, saving in the admin panel wrote a complete AI_MODELS_CONFIG
into settings.json, which (by overlay precedence) replaced any config
from .env or ai-models.json — admins lost their env-configured models.
The panel no longer writes AI_MODELS_CONFIG. Instead its providers are
merged with the env baseline at read time in loadRawServerModelsConfig,
and panel credentials go to ADMIN_-prefixed env vars wired up via
apiKeyEnv/baseUrlEnv so they never shadow standard vars. Env-based
providers now appear read-only in the panel, name clashes are rejected,
and a panel default overrides the env default. data/ is now gitignored.
* fix: block global-credential providers already managed via env
Bedrock, Vertex AI, and Ollama credentials live in fixed env vars with
no apiKeyEnv redirection, so a panel instance of one of these would
silently override the credentials that env-configured models rely on.
The API now rejects saving such a provider when the env config already
uses that type, and the Add Provider dropdown disables it with a
'managed via env' note.
* fix: address admin panel review findings
- Security: test-model no longer resolves a stored secret when the
request's baseUrl/provider differs from the stored entry, closing a
path where a tampered baseUrl could exfiltrate a saved key
- Save failures are now visible: the save bar shows the error in red
(was masked by the persistent 'Unsaved changes' text), and per-field
validation errors from the settings API are surfaced under each field
- The Observability/Quota enable switch is now real: toggling off stages
deletion of the group's saved values, and the toggle no longer snaps
back to Enabled after saving
- Env provider's default star is hidden when a panel provider is the
active default (no more double star)
- Clearing a credential field reverts to the stored value instead of
silently deleting it; an explicit X button removes a stored secret
- Form inputs are disabled during an in-flight save
* refactor(admin): split 1549-line admin page into focused modules
Extract admin-shared.ts (types + fetch helper), setting-field.tsx
(registry-driven fields), and models-section.tsx (provider/model
manager) from page.tsx. Pure mechanical move, no behavior change.
* feat(admin): share credential fields with user dialog and localize panel
Extract ProviderCredentialsFields (display name + per-provider
credential inputs) used by both the user ModelConfigDialog and the
admin Models panel; secret input passed via renderSecret (plaintext
vs masked), test button via footer slot. Add full i18n for the admin
panel across en/zh/ja/zh-Hant, reusing modelConfig.* for shared parts.
* fix(admin): address Copilot review findings
- Reflect built-in defaults for boolean settings (ALLOW_PRIVATE_URLS
defaults on) and allow clearing a saved boolean back to default,
so the SSRF toggle matches actual runtime behavior.
- Harden JSON loading: filter settings values to strings only, and
schema-validate stored ADMIN_PROVIDERS entries, dropping malformed
ones instead of letting them reach runtime code.
- Set beforeunload returnValue so the unsaved-changes prompt shows in
all browsers; reject non-finite numbers in settings validation.
- Fix README/CN/JA docs that claimed the panel auto-generates
AI_MODELS_CONFIG (providers are merged at read time, not written).
- Add unit tests for corrupted-file value filtering and provider
schema validation.
* docs: move admin panel details to dedicated docs/{en,cn,ja}/admin-panel.md
The READMEs now carry a short blurb + link, matching the existing
per-topic docs (docker.md, ai-providers.md, ...). Removes the ~22-line
inline section and the duplicated data/settings.json mentions.
* fix(admin): address follow-up Copilot findings on the prior fixes
- loadAdminProviders now validates against a stored-shape schema where
secrets are plain strings, so a hand-edited ADMIN_PROVIDERS holding an
{isSet} marker is dropped instead of later crashing maskSecret().
- loadSettings guards against array values (typeof [] === 'object'),
which would otherwise overlay numeric keys onto process.env.
- Admin SecretInput uses the bare id so the shared component's
<Label htmlFor> stays associated (only one ProviderDetail mounts).
- Add tests: marker-secret rejection, array-values guard, bedrock
multi-secret round-trip.
|
||
|
|
a9ffd6a1de |
feat: upgrade MiniMax default model to M3 (#857)
- Add MiniMax-M3 to the model selection list (set as new default at top) - Retain MiniMax-M2.7 and MiniMax-M2.7-highspeed - Remove deprecated MiniMax-M2.5 / M2.5-highspeed - Update supportsImageInput: M3 supports image input (M2.x stay text-only) - Update unit tests to reflect new model lineup - Update example AI_MODEL in CN/EN/JA docs to MiniMax-M3 Co-authored-by: octo-patch <[email protected]> |
||
|
|
277ad83552 |
fix(anthropic): support ANTHROPIC_AUTH_TOKEN as alternative to ANTHROPIC_API_KEY (#853)
* fix(anthropic): support ANTHROPIC_AUTH_TOKEN as alternative to ANTHROPIC_API_KEY Anthropic SDK supports two mutually exclusive auth methods: apiKey (sent as x-api-key header) and authToken (sent as Authorization: Bearer header). Detect either env var during provider detection and credential validation, and pass authToken to createAnthropic when only ANTHROPIC_AUTH_TOKEN is set. * docs(anthropic): document ANTHROPIC_AUTH_TOKEN and refine error message - Add ANTHROPIC_AUTH_TOKEN to env.example and the en/cn/ja provider docs - Reword the missing-credential error to "Either ... or ..." for readability --------- Co-authored-by: duyunjie <[email protected]> Co-authored-by: dayuan.jiang <[email protected]> |
||
|
|
49bfb51b10 |
feat: Update some tool demos in english about page (#598)
* feat: update about page examples with new diagrams Replace old cloud architecture and cat examples with ResNet50, RAG, Authentication, Agile Scrum, and Open Innovation diagrams. Update all three language versions (EN, CN, JA) for consistency. * fix: correct ResNet50 prompt text and keep prompts in English Update the ResNet50 example prompt to match the actual diagram. Keep all prompt texts in English across CN and JA about pages since prompts are what users type into the AI. * docs: update README examples to match about page Replace GCP, AWS, Azure examples with ResNet50, RAG, Auth, Agile Scrum, and Open Innovation. Keep Animated Transformer and Cat Sketch. Keep prompts in English across all versions. * docs: remove ResNet50 and Agile Scrum examples from READMEs --------- Co-authored-by: dayuan.jiang <[email protected]> |
||
|
|
b9fdf9538c |
chore: remove MCP preview labels (#790)
MCP server is no longer in preview. Remove "(Preview)" headings from READMEs, the purple PREVIEW badge from the UI, and the preview i18n keys. |
||
|
|
43cc4cb657 |
feat: upgrade MiniMax default model to M2.7 (#737)
- Add MiniMax-M2.7 and MiniMax-M2.7-highspeed to model list - Set MiniMax-M2.7 as default model - Keep all previous models as alternatives - Update docs in EN/CN/JA Co-authored-by: PR Bot <[email protected]> |
||
|
|
fd84aa70db |
chore: update volcengine referral URL and sponsor info (#736)
- Replace newyear-referral URL with new Coding Plan referral URL across all files - Update model name from K2-thinking to glm-4.7 - Add volcengine invite poster to Chinese README and about page |
||
|
|
be4bc916fd |
feat: Add support for Chinese AI providers (GLM, Qwen, Kimi, MiniMax, Qiniu)
* feat: Add support for Chinese AI providers (GLM, Qwen, Kimi, MiniMax, Qiniu) - Add minimax, glm, qwen, qiniu, kimi to ProviderName type - Add provider configurations to PROVIDER_INFO with default base URLs - Add suggested models for MiniMax in SUGGESTED_MODELS - Add minimax/glm/qwen/qiniu/kimi cases to getAIModel using OpenAI-compatible SDK - Update ALLOWED_CLIENT_PROVIDERS and error messages - Add environment variable examples to env.example Fixes: MiniMax API compatibility issue (invalid chat setting 2013) * fix: Add missing providers to PROVIDER_ENV_VARS type * fix: Handle null case in PROVIDER_ENV_VARS for new providers * fix: Add minimax/glm/qwen/kimi/qiniu support to validate-model API - Add getDefaultBaseUrl helper function - Add validation cases for new providers in validate-model route * fix: Add new providers to buildProviderOptions switch case * fix: Merge multiple system messages into one for minimax/glm/qwen/kimi/qiniu MiniMax API doesn't support multiple system messages. This fix combines them into a single message for Chinese providers. * fix: Handle null provider in system message check * debug: Add logging for allMessages count * fix: Use effective provider (including env var fallback) for isSingleSystemProvider check * fix: apply biome formatting (line-wrapping) * docs: add Chinese AI providers documentation (MiniMax, GLM, Qwen, Kimi, Qiniu) - Add i18n translations for new providers in all language dictionaries - Add provider configuration documentation in en/cn/ja docs * fix: 改进 PR #722 的代码审查反馈 1. 删除重复的 getDefaultBaseUrl 函数,改用 model-config.ts 的 PROVIDER_INFO 2. validate-model 路由改用 AI SDK 的 createOpenAI + generateText 3. 修复 resolveBaseURL 回退逻辑,传入 PROVIDER_INFO 的 defaultBaseUrl 4. 删除无用的 .bak 备份文件 Co-authored-by: Shinyi <[email protected]> * fix: 修正中国 AI provider 端点配置 - qiniu: api.qiniucdn.com → api.qnaigc.com - qwen: dashscope.aliyun.com → dashscope.aliyuncs.com - 更新 env.example 文档链接 Co-authored-by: Shinyi <[email protected]> * feat: MiniMax 使用 Anthropic 兼容 API - MiniMax 改用 createAnthropic (而非 createOpenAI) - 支持 api.minimax.io/anthropic 和 api.minimaxi.com/anthropic - 合并多个 system 消息为单个 (MiniMax/GLM/Qwen/Kimi/Qiniu) - 更新默认模型为 MiniMax-M2.5 系列 - 支持 MINIMAX_BASE_URL 环境变量配置 Co-authored-by: Shinyi <[email protected]> * docs: 更新 MiniMax 文档 - 添加 Anthropic 兼容 API 说明 - 更新默认模型为 MiniMax-M2.5 - 添加国际版/中国大陆版配置示例 - 更新 env.example 注释 Co-authored-by: Shinyi <[email protected]> * fix: 完善 MiniMax 双端点支持及问题修复 - 支持 MiniMax Anthropic 兼容端点和 OpenAI 兼容端点自动切换 - 修正默认端点为 api.minimaxi.com (中国大陆可用) - 修复端点路径缺少 /v1 的问题 - 添加前端 MiniMax logo 映射 - 移除调试日志 - 修正 env.example 默认配置 * chore: clean backup artifacts and align biome formatting * fix: resolve effectiveProvider bug, deduplicate MiniMax URL logic, fix docs - Fix critical bug: effectiveProvider was empty during auto-detection, causing multi-system-message to be sent to MiniMax (which rejects it). Now uses resolved provider from getAIModel instead of re-deriving it. - Extract normalizeMiniMaxBaseURL() shared helper to eliminate duplication between ai-providers.ts and validate-model/route.ts - Add guard for undefined MiniMax baseURL to prevent hitting api.anthropic.com - Fix docs: mark China mainland URL as default (matches code behavior) - Restructure minimax/glm/qwen/kimi/qiniu validation to use shared pattern Co-Authored-By: Claude Opus 4.6 <[email protected]> * feat: document MiniMax dual API formats in docs and UI - Add hint below Base URL input when MiniMax is selected, explaining Anthropic-compatible (/anthropic) vs OpenAI-compatible (/v1) endpoints - Update all 3 ai-providers docs (en/cn/ja) to list all 4 endpoint options (China/International × Anthropic/OpenAI) - Add i18n translations for the hint in all 4 locales Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: deduplicate PROVIDER_LOGO_MAP, remove unnecessary optional chaining - Extract PROVIDER_LOGO_MAP to lib/types/model-config.ts (was duplicated in model-config-dialog.tsx and model-selector.tsx) - Remove unnecessary ?. on PROVIDER_INFO.minimax (it's a full Record) --------- Co-authored-by: msga-oc <[email protected]> Co-authored-by: Shinyi <[email protected]> Co-authored-by: dayuan.jiang <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> |
||
|
|
41dc0b2b42 |
feat: add Material Design Icons shape library (#688)
* feat: add Material Design Icons shape library (#685) Add Google Material Design Icons as a new shape library using Google's CDN. Includes top 300 most popular icons by usage, and updates system prompts to guide the AI to call get_shape_library before using any icon library. * fix: align get_shape_library guidance for non-cloud icon libraries |
||
|
|
b84b156c8c |
chore: update Volcengine referral link to new year campaign (#666)
Update the Volcengine referral URL from console.volcengine.com to the new year referral campaign URL across all documentation and code files. |
||
|
|
b23b9179a0 |
[Feature] Server-side multi-provider/model support (#583)
* [Feature] Server side multi-pvorider/model support * copilot suggesition implemented * feat: improve model selector UI and auto-select default server model - Replace emoji headers with Lucide icons (Monitor, User) - Fix transition-all to explicit properties per web guidelines - Use CSS padding instead of hardcoded space indentation - Add ModelSelectorSectionHeader component for section headers - Replace Star icon with "default" text label - Style Configure button with muted text color - Auto-select default server model when page loads - Support AI_MODELS_CONFIG env var for cloud deployments - Support custom apiKeyEnv/baseUrlEnv per provider config * docs: update server-side multi-model configuration documentation - Add AI_MODELS_CONFIG env var option for cloud deployments - Document apiKeyEnv and baseUrlEnv fields for custom env var names - Document default field for auto-selecting default model - Remove deprecated version field from examples - Add field reference table for clarity --------- Co-authored-by: dayuan.jiang <[email protected]> |
||
|
|
04290a53d0 | Update Docs | ||
|
|
0009900b1b | feat: add vertex to documentation | ||
|
|
9430618660 |
docs: fix FAQ formatting and update model recommendations (#546)
- Add missing "Problem" statement to FAQ #4 for consistency - Update vision model recommendations to latest versions (GPT-5.2, Claude 4.5 Sonnet, Gemini 3 Pro) |
||
|
|
d71fe70cbe |
docs: add FAQ documentation for common issues (#544)
- Add FAQ.md in English, Chinese, and Japanese - Link FAQ from each language README - Cover: PDF export, offline deployment, self-hosted models, image upload |
||
|
|
54fd48506d |
Feat/add modelscope support (#521)
* add ModelScope API support * update some documentation * modify some details |
||
|
|
ffcb241383 |
feat: mod readme (#522)
Co-authored-by: zoejiezhou <[email protected]> |
||
|
|
aaa2938dac |
docs: reorganize documentation into i18n folder structure (#466)
* docs: reorganize docs into en/cn/ja folders - Move documentation files into language-specific folders (en, cn, ja) - Add Chinese and Japanese translations for all docs - Extract Docker section from README to separate doc file - Update README to link to new doc locations * docs: fix links to new docs folder structure * docs: update README and provider docs * docs: fix broken import statements in cloudflare deploy guides * docs: sync CN/JA READMEs with EN structure and fix all paths |
||
|
|
ca21a5bb27 |
feat: add EdgeOne Pages as AI provider (#456)
* feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * feat: edit diagram * feat: edit diagram * feat: edit diagram * feat: edit diagram * feat: edit diagram * feat: edit diagram * feat: edit diagram * feat: add edgeone provider * feat: add edgeone provider * feat: add edgeone provider * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: add cookie * fix: add cookie * fix: add cookie * fix: add cookie * fix: add cookie * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * fix: build error * feat: validate * feat: document link --------- Co-authored-by: zoejiezhou <[email protected]> |
||
|
|
6d1e12bb39 |
feat: add doubao provider and ByteDance sponsorship (#329)
* feat: add doubao provider and ByteDance sponsorship - Add doubao provider using DeepSeek SDK with Volcengine base URL - Add ByteDance Doubao sponsorship acknowledgment to about pages - Update all README files (EN/CN/JA) with K2-thinking model info - Update ai-providers.md with doubao configuration - Keep both gateway and doubao providers after merge * style: auto-format with Biome * feat: add doubao and sglang to provider config panel * fix: add doubao and sglang to validate-model API and logo maps * docs: update ByteDance sponsorship note in all README versions * docs: add Doubao logo to sponsorship note * fix: use raw GitHub URL for Doubao logo in READMEs * fix: separate link and image in sponsorship note * fix: use PNG instead of SVG for Doubao logo * fix: use current branch for PNG URL (will update to main after merge) * docs: reorganize Deployment section and update image URLs to main --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|
|
9ac99a4690 |
[Feature] Add Cloudflare Worker as deployment option (#170)
* docs(cloudflare): add detailed Cloudflare Workers + R2 deploy guide * separated cloudflare deploy guide from readme.md * Missing R2 bucket binding for incremental cache * docs: move Cloudflare guide to docs/ and improve documentation - Move Cloudflare_Deploy.md to docs/ folder - Add 'Deploy without R2' option for simple/free deployments - Add workers.dev subdomain registration instructions - Add missing global_fetch_strictly_public flag - Add troubleshooting for common deployment issues - Update README.md link to new location * fix: conditional import for cloudflare dev and regenerate lockfile - Use dynamic import for @opennextjs/cloudflare to avoid loading workerd during builds - Regenerate package-lock.json with cross-platform dependencies * fix: use main lockfile with cloudflare deps added - Use main branch's package-lock.json as base to ensure cross-platform deps - Add @opennextjs/cloudflare and wrangler --------- Co-authored-by: dayuan.jiang <[email protected]> |
||
|
|
467561df47 |
docs(shape-libraries): add label positioning to shape library examples (#422)
- Add verticalLabelPosition=bottom, verticalAlign=top, and align=center to all shape library usage examples - Update Alibaba Cloud shape library documentation - Update Atlassian shape library documentation - Update AWS4 shape library documentation - Update Azure2 shape library documentation - Update Cisco19 shape library documentation - Update Citrix shape library documentation - Update GCP2 shape library documentation - Update Kubernetes shape library documentation - Update MSCAE shape library documentation - Update Network shape library documentation - Update OpenStack shape library documentation - Update Salesforce shape library documentation - Update SAP shape library documentation - Update VVD shape library documentation - Update WebIcons shape library documentation - Ensures consistent label positioning and alignment across all shape library examples for better visual consistency |
||
|
|
e67ab37383 |
docs: fix cross-domain configuration to offline deployment docs (#405)
* docs: add cross-domain troubleshooting to offline deployment guide * make it simple * Remove common issues section from offline deployment docs Removed common issues section regarding cross-domain configuration and rebuilding after configuration changes. |
||
|
|
f087b54ee4 |
feat: add get_shape_library tool for AI icon discovery (#335)
* feat: add get_shape_library tool for AI icon discovery - Add server-side tool that returns shape library documentation - AI can fetch icon/shape names on-demand before generating diagrams - Includes path traversal protection and input sanitization - Library index embedded in tool description for discoverability - Supports 33 libraries: AWS, Azure, GCP, Kubernetes, Cisco, etc. * fix: improve get_shape_library error handling and imports - Move fs/path imports to top of file (avoid dynamic imports per call) - Distinguish file-not-found vs other errors in catch block - Include invalid input in validation error message - Log unexpected errors for debugging * docs: add get_shape_library to system prompt tool list - Add Tool4 (get_shape_library) to available tools section - Add usage guidance in 'Choose the right tool' section - Update AWS icons note to reference get_shape_library for icon discovery * fix: display get_shape_library tool output in chat UI * fix: correct state check for get_shape_library output display * fix: make get_shape_library output respect fold state * style: auto-format with Biome --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|
|
a91bd9d1e8 |
feat: add support for custom AI Gateway base URL (#315)
* feat: add support for custom AI Gateway base URL - Add createGateway support with configurable baseURL - Allow AI_GATEWAY_BASE_URL environment variable for: * Local development with custom Gateway * Self-hosted AI Gateway deployments * Enterprise proxy configurations - Maintain backward compatibility: defaults to Vercel Gateway when not set - Update documentation with usage examples and configuration notes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <[email protected]> * fix: remove errant character in error message --------- Co-authored-by: Claude Sonnet 4.5 <[email protected]> Co-authored-by: dayuan.jiang <[email protected]> |
||
|
|
439bdd4577 |
feat: add MCP server package for npx distribution (#284)
* feat: add MCP server package for npx distribution - Self-contained MCP server with embedded HTTP server - Real-time browser preview via draw.io iframe - Tools: start_session, display_diagram, edit_diagram, get_diagram, export_diagram - Port retry limit (6002-6020) and session TTL cleanup (1 hour) - Published as @next-ai-drawio/mcp-server on npm * chore: bump version to 0.1.2 * docs: add MCP server section to README (preview feature) * docs: add multi-client installation instructions for MCP server * fix: exclude packages from Next.js build * docs: use @latest instead of -y flag for npx (match Playwright MCP style) * chore: bump version to 0.4.3 and add release notes * chore: remove release notes * feat: add MCP server notice to example panel |
||
|
|
98b890bb06 |
feat: add Vercel AI Gateway support (#274)
* feat: add Vercel AI Gateway support - Updated environment configuration to include AI_GATEWAY_API_KEY for unified access to multiple AI providers. - Added gateway provider to the list of supported AI providers in the codebase. - Enhanced documentation to explain the usage of Vercel AI Gateway and its model format. This change simplifies authentication and allows users to switch between providers seamlessly. * Update package @ai-sdk/gateway to latest version 2.0.21 |
||
|
|
a0f163fe9e |
fix: improve Azure provider auto-detection and validation (#223) (#225)
* fix: improve Azure provider auto-detection and validation (#223) - Fix detectProvider() to only detect Azure when it has complete config (both AZURE_API_KEY and AZURE_RESOURCE_NAME or AZURE_BASE_URL) - Add validation in validateProviderCredentials() for Azure to provide clear error messages when configuration is incomplete - Update docs/ai-providers.md to clarify Azure requires resource name * docs: add Azure reasoning options to documentation |
||
|
|
c42efdc702 | chore: bump version to 0.4.0 and update README features | ||
|
|
9a954ccb44 |
fix: correct NEXT_PUBLIC_DRAWIO_BASE_URL in offline deployment docs (#198)
- Fix wrong URL (http://drawio:8080 -> http://localhost:8080) - Browser cannot resolve Docker internal hostnames - Add docker-compose.yml example - Simplify documentation |
||
|
|
9d4c89ec43 | docs: improve offline deployment guide with docker compose (#191) | ||
|
|
67b0adf211 | docs: align Chinese and Japanese README with main README (#190) | ||
|
|
b3deb65584 |
chore: remove duplicate self-hosted-drawio.md (#188)
Content has been consolidated into docs/offline-deployment.md |
||
|
|
626f3a76b5 | docs: add offline deployment guide (#187) | ||
|
|
97bb350dd6 |
feat: add support for self-hosted draw.io via DRAWIO_BASE_URL environment variable (#163)
## Summary
Add support for self-hosted draw.io instances via build-time configuration.
## Problem
In some corporate environments, `embed.diagrams.net` is blocked by network policies.
Users cannot use the application without access to the default draw.io embed URL.
## Solution
- Add `NEXT_PUBLIC_DRAWIO_BASE_URL` environment variable support
- Pass the `baseUrl` prop to the `DrawIoEmbed` component
- Configure Dockerfile to accept build-time argument for the draw.io URL
## Usage
```yaml
# docker-compose.yaml
services:
drawio:
image: jgraph/drawio:latest
ports:
- "8080:8080"
next-ai-draw-io:
build:
context: .
args:
- NEXT_PUBLIC_DRAWIO_BASE_URL=http://drawio:8080
```
Or build directly:
```bash
docker build --build-arg NEXT_PUBLIC_DRAWIO_BASE_URL=http://localhost:8080 -t next-ai-draw-io .
```
**Note:** This is a build-time configuration. To change the draw.io URL, you need to rebuild the Docker image.
|