Commit Graph
289 Commits
Author SHA1 Message Date
dayuan.jiang c75f74a5a0 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.
2026-10-05 17:33:36 +09:00
dayuan.jiang 4731394f32 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.
2026-10-05 17:02:06 +09:00
dayuan.jiang 49468f9478 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.
2026-10-05 13:04:12 +09:00
dayuan.jiang 2e84e02666 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.
2026-10-05 10:52:03 +09:00
dayuan.jiang 2bb69b58af 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.
2026-10-04 23:04:21 +09:00
dayuan.jiang 2ac4c54eb2 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.
2026-10-04 21:18:54 +09:00
dayuan.jiang 6a99915446 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.
2026-10-04 20:15:51 +09:00
dayuan.jiang 6236124338 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.
2026-10-04 14:02:54 +09:00
dayuan.jiang ed1722ee9d refactor(settings): drop an unused index parameter 2026-10-04 13:49:57 +09:00
dayuan.jiang ab1a58f999 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
2026-10-04 13:49:34 +09:00
dayuan.jiang 99890e9e37 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
2026-10-04 13:37:39 +09:00
dayuan.jiang c24aae6de0 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
2026-10-04 13:31:47 +09:00
dayuan.jiang b7c26916cd 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.
2026-10-04 13:18:34 +09:00
dayuan.jiang 22a1d3f03b 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
2026-10-04 13:16:38 +09:00
dayuan.jiang bf4e95dcfb 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
2026-10-04 12:51:20 +09:00
dayuan.jiang e49e9f0c47 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
2026-10-04 12:44:01 +09:00
dayuan.jiang 289e6bc25f 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
2026-10-04 11:52:53 +09:00
dayuan.jiang 3193d20e00 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
2026-10-03 17:45:41 +09:00
dayuan.jiang 87edf2e19d 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
2026-10-03 17:45:41 +09:00
dayuan.jiang 79b4c52741 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
2026-10-03 17:45:41 +09:00
Dayuan Jiang 155ef4f7ac fix: raise the output budget so reasoning models reach the tool call (#927)
* fix: raise the output budget so reasoning models reach the tool call

A reasoning model spends the output budget in order: thinking first, then prose,
then the tool call. With 16000 the thinking alone can consume all of it, so the
turn ends with finishReason "length" before display_diagram is ever called. The
canvas stays empty and nothing surfaces in the UI, because no tool call means no
tool error, and the client never reads finishReason.

Measured on openrouter deepseek/deepseek-v4-flash, the model from the report:
- max_tokens=800 with reasoning on returns reasoning_tokens=800, empty content,
  finish_reason length. So reasoning is billed against this budget, not exempt.
- refining an existing diagram (19k chars of XML in the input) produced 49142
  chars of reasoning, zero tool calls, finishReason "length" at 16000
- the same request at 40000 finished and called edit_diagram with 12 operations

64000 cannot just be sent to every model: bedrock claude-3-haiku caps at 4096,
nova-lite at 10000, and the openrouter deepseek-r1 endpoint counts input and
output against one 64000 ceiling. All three name the real limit in the 400, so
parse it and retry once. Verified: nova-lite logs "64000 rejected, retrying with
10000" and then completes its tool call.

Also expose the budget in Settings. It is sent as a header rather than read from
env only, so desktop users can raise it themselves without an env file.

vercel.json goes back to the 300s it had before #238 traded it for $2-4/month.
That is now Vercel's own default, and billing pauses while the function waits on
the model, so the saving that motivated 120s no longer applies. edgeone.json is
left alone: its 120 may be that platform's actual ceiling.

* fix: only reinterpret an error as a budget rejection when it says so

Review of the first commit found the retry could fire on errors that have
nothing to do with the budget, which would replace a readable provider error
with a truncated response: exactly the symptom this PR exists to remove.

- Drop the generic "lower than N" pattern. For the Bedrock message it was dead
  code, since "model limit of N" matches first with the same number. Left live,
  it would read a number out of any message shaped like "must be lower than 2".
- Skip errors whose status is not 400 or 422, so auth and rate-limit failures
  are never reinterpreted.
- Require the parsed ceiling to be at least 1024. Below that a diagram cannot
  come out whole, so retrying would hide the error behind broken XML.
- Validate MAX_OUTPUT_TOKENS from env the same way as the header, so a stray
  "-1" falls back instead of reaching the provider.

Adds tests for the retry wrapper itself, which had none: it retries once with
the named ceiling, leaves a 401 alone, does not retry when the ceiling is not
smaller, propagates a second rejection, and preserves the other call options.

Re-verified against the live APIs: bedrock nova-lite still logs "64000 rejected,
retrying with 10000" and completes its tool call, and deepseek-v4-flash still
finishes normally at 64000.
2026-08-22 20:47:10 +09:00
NgoQuocViet2001anddayuan.jiang fd758b9e87 fix: preserve multi-page diagrams after export (#895)
* fix: preserve multi-page diagrams after export

* fix: keep chartXML sourced from autosave to preserve multi-page state

The export event's data.xml (xmlsvg format) contains compressed <diagram>
payloads, which would break applyDiagramOperations/replaceNodes consumers
that need plain <root> elements. Instead of writing export results into
chartXML, stop overwriting it entirely: autosave already delivers the full
uncompressed multi-page document, and loadDiagram covers AI-driven updates.

Also stop overwriting chartXMLRef with the page-only export before sending
a chat message, so session persistence never sees single-page XML.

Keep the data.xml preference for .drawio file downloads (compressed pages
are a valid drawio format).

---------

Co-authored-by: dayuan.jiang <[email protected]>
2026-08-08 12:30:22 +09:00
Dayuan Jiang 6493652ff0 fix: prevent model selector label overflow (#910)
* fix: constrain model selector label width

* ci: ignore generated SVG assets in Biome
2026-08-08 10:31:46 +09:00
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]>
2026-07-12 09:10:12 +09:00
YOYO-doandLL 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]>
2026-06-15 13:54:18 +09:00
Dayuan Jiang 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.
2026-06-15 00:40:35 +09:00
果子anddayuan.jiang 4e223b6237 feat: add all Draw.io themes to settings panel (#835)
* feat: add all Draw.io themes to settings panel

Add all available Draw.io themes (kennedy, atlas, dark, min, sketch, simple)
to the settings panel dropdown. Previously only min and sketch were available
as a toggle button.

Changes:
- Replace the Draw.io style toggle button with a dropdown selector
- Expand theme type from "min" | "sketch" to include all 6 themes
- Update localStorage validation to accept all themes
- Update handler from toggle to direct theme selection

Closes #499

* fix: localize theme labels, tighten DrawioTheme typing, sync dark param

- Move DRAWIO_THEMES + DrawioTheme to lib/drawio-themes.ts; reuse in
  page.tsx, chat-panel.tsx and settings-dialog.tsx instead of `string`
- Localize theme dropdown labels (Dark/Minimal/Sketch/Simple) in
  en/zh/ja/zh-Hant; keep proper-noun themes (Kennedy/Atlas) as-is
- Drop trailing colon from drawioStyleDescription and remove dead
  switchTo/minimal/sketch keys in all 4 dictionaries
- Auto-sync drawio dark URL param when ui="dark" is selected
- Add aria-label to drawio-style SelectTrigger

* fix: use kennedy as default theme and label it "Default"

---------

Co-authored-by: dayuan.jiang <[email protected]>
2026-05-15 23:14:20 +09:00
Octopusandocto-patch 5c8ae4d6d7 fix: always re-fetch access code config when settings dialog opens (#816)
When ACCESS_CODE_LIST is configured on the server, the settings dialog
was not showing the access code input field in two cases:

1. Stale localStorage cache: if a user had previously visited without
   ACCESS_CODE_LIST enabled, the cached value of accessCodeRequired=false
   would be used indefinitely, hiding the password input.

2. Race condition on first visit: the dialog could open triggered
   by an auth error before the async fetch to /api/config completed,
   showing a blank settings dialog with no access code field.

Fix by re-fetching /api/config whenever the dialog opens (on open
change) instead of only once on mount with a cache guard. The cached
value in localStorage is still updated on success, keeping the fast
initial render intact while ensuring the dialog always reflects the
server configuration.

Fixes #811

Co-authored-by: octo-patch <[email protected]>
2026-05-15 10:52:51 +09:00
LaaraibAhmedandLaaraib Ahmed 73eefc7aa6 fix: CSS/UI issue in model-config-dialog (#818)
Co-authored-by: Laaraib Ahmed <[email protected]>
2026-05-07 22:14:46 +09:00
Dayuan Jiang 0cd1260172 fix: show all enabled panels on chat lobby instead of only templates (#802) 2026-04-10 14:33:56 +09:00
Dayuan Jiang ccd9c1f48e fix: reduce CPU usage during large XML streaming (#801)
* fix: reduce CPU usage during large XML streaming

Skip Prism syntax highlighting while tool call is streaming — use plain
<pre> during streaming and only run Prism once at completion. Also throttle
scrollIntoView to once per 150ms to avoid layout thrashing.

Profiled with Playwright + CDP: for 200-cell diagrams the longest
browser task dropped from 6.7s to ~450ms and total long-task time
fell from ~10.9s to ~1.1s.

* fix: add trailing edge to scroll throttle

Ensures the chat scrolls to the bottom after streaming ends, even if
the last messages update arrives during the 150ms throttle window.
2026-04-10 11:12:50 +09:00
Dayuan Jiang 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.
2026-04-06 10:17:38 +09:00
f5ea5a0edd feat: add personal My Templates library alongside Quick Examples (#773)
* 增加了ralph自动化编程梳理

* feat: US-001 - 为模板库建立独立的 IndexedDB 存储层

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* feat: US-002 - 在空聊天状态用我的模板库替换官方示例

- 将 ChatLobby 中的 Quick Examples 替换为 TemplatePanel
- 当没有历史会话时,展示完整的模板库面板
- 当有历史会话时,展示可折叠的 "My Templates" 区域
- 使用 TemplatePanel 组件展示用户的个人模板库

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* feat: US-004 - Provide template creation flow

- Create TemplateCreateDialog component with form fields for prompt, title, description, tags, and pinned
- Add i18n translations for template creation UI in en, zh, zh-Hant, ja
- Update TemplatePanel to integrate the create dialog
- Support initialPrompt prop for pre-filling from current input
- Validate required prompt field (empty prompt not allowed)
- Auto-generate default title from first 20 chars of  Pin templates appear at top of list

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* feat: US-005 - 为模板卡片提供编辑、删除和复制操作

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* feat: US-006 - Send template directly on click and record usage statistics

- Implement click-to-send template functionality with confirmation dialog
- Add clickCount and runCount increment logic
- Display runCount and lastUsedAt on template card
- Add i18n translations for confirmation dialog (en, zh, zh-Hant, ja)
- Pass onSendTemplate and currentInput props through component hierarchy

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* feat: US-007 - Support template search, pin and default sorting

- Add search bar to TemplatePanel with real-time filtering by title, description, and tags
- Add pin/unpin toggle button on template cards (uses Bookmark icon with fill indicator)
- Search uses existing searchTemplates function from template-storage
- Sort uses existing sortTemplates function (pinned desc, runCount desc, lastUsedAt desc, updatedAt desc)
- Show empty state with Search icon when search returns no results
- List re-sorts immediately after pin/unpin toggle
- Add i18n keys: searchPlaceholder, searchNoResults, pin, unpin for all 4 languages

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* feat: US-008 - Support saving current input as template

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* feat: US-009 - Support saving historical user message as template

- Add "Save as Template" button to user messages
- Pre-fill prompt with original user message text
- Only show on user messages,- Dialog opens TemplateCreateDialog on click
- Template appears in list immediately after saving

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* feat: US-010 - Support template import and export

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* chore: remove local-only dirs from git tracking (.agents, .cursor, scripts, screenshots)

These directories contain local IDE configs, agent scripts, and
dev tooling that should not be part of the upstream repository.
Added them to .gitignore to prevent future accidental commits.

* chore: remove AGENTS.md from git tracking

* fix: add missing i18n keys for template export/import (en/zh/zh-Hant/ja)

* fix: review fixes for my-templates PR

- Fix fragile querySelector("form") with id-based lookup
- Add objectStoreNames.contains guards for IndexedDB upgrades
- Remove duplicate TemplateSchema, import from template-storage
- Revert contributor-specific .gitignore additions
- Fix broken i18n placeholders and missing translations (zh/ja/zh-Hant)
- Remove unused setFiles prop from ChatLobby
- Remove tags feature (unnecessary complexity)
- Improve template card layout: overlay icons on hover, align stats
- Add break-all and overflow-hidden for long prompt text in dialogs
- Move incrementClickCount into sendTemplate for accurate tracking
- Use Intl.RelativeTimeFormat for locale-aware relative time

* feat: restore Quick Examples panel and add lobby panel visibility settings

Bring back the ExamplePanel as a third collapsible section in ChatLobby
alongside Recent Chats and My Templates. Add toggle switches in Settings
to show/hide each lobby panel, persisted via localStorage.

* fix: template send race condition, import defaults, and empty title bug

- Use flushSync instead of setTimeout(0) in handleSendTemplate to
  ensure React state is flushed before form submission
- Explicitly validate and default all fields in importTemplates to
  prevent undefined counters from malformed import JSON
- Fall back to existing title in edit dialog instead of writing undefined

* fix: address Copilot review comments and remove PRD file

- Fix fallback formatLastUsed returning "Not used yet" for recent usage
- Remove dead mounted flag in TemplatePanel useEffect
- Respect panel visibility settings in no-history lobby state
- Reject empty/whitespace titles in import validation
- Trim title/prompt in importTemplates with default title fallback
- Remove tasks/prd-template-library-replaces-examples.md from repo

* fix: remove double sort, dead code, redundant stats, and break-all CSS

- Remove redundant sortTemplates call in loadTemplates (already sorted by getAllTemplates)
- Remove unused createEmptyTemplateInput and sortTemplates import
- Show "Not used yet" only once for unused templates instead of twice
- Use break-words instead of break-all on prompt textareas

---------

Co-authored-by: 杜雷 <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: dayuan.jiang <[email protected]>
2026-04-03 12:16:01 +09:00
zongxi1115anddayuan.jiang cb8127920c feat: add xmlsvg export option (#761)
* feat: add xmlsvg export option

* fix: avoid atob for xmlsvg export to prevent UTF-8 corruption

Pass the data URL directly (like PNG export) instead of decoding
with atob(). atob() + Blob([string]) double-encodes non-ASCII
characters (Chinese, Japanese, emoji), corrupting the output file.

---------

Co-authored-by: dayuan.jiang <[email protected]>
2026-04-03 08:50:57 +09:00
Dayuan Jiang 16801f69b4 Merge pull request #743 from Biki-dev/some-ui-fixes
Fix: Keep settings dialog within the viewport and change the scrollbar property from hidden to thin
2026-03-30 19:29:06 +09:00
Dayuan Jiang 4f41cd1f01 Merge pull request #648 from DayuanJiang/fix/idb-closing-retry
fix: recover IDB closing + restore diagram-only sessions
2026-03-30 19:27:58 +09:00
Biki KalitaandCopilot c3ff41ce72 Apply suggestion from @Copilot
Co-authored-by: Copilot <[email protected]>
2026-03-24 17:43:53 +05:30
Biki Kalita 9efec68cbc change scrollbar-hidden to scrollbar-thin in ModelSelector and AI Model Configuration Right Panel 2026-03-24 12:11:36 +00:00
Biki Kalita 9b9a291e2b scrollbar hidden to thin 2026-03-20 12:31:16 +00:00
Biki Kalita ada2c840b9 fix: keep settings dialog within viewport with hidden scrollbar 2026-03-20 11:52:23 +00:00
Dayuan Jiang 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
2026-03-16 20:48:39 +09:00
Dayuan Jiang e7453e86a6 feat: add custom system message setting for AI personalization (#728)
* feat: add custom system message setting for AI personalization

Allow users to enter custom instructions via a textarea in Settings
that get appended to the AI's system prompt. Includes server-side
validation (type check + 5000 char limit), localStorage persistence,
and i18n support for all 4 locales.

* fix: add accessibility htmlFor/id pairing on custom system message textarea
2026-03-07 19:07:54 +09:00
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]>
2026-03-07 18:53:47 +09:00
Bryon Nevisanddayuan.jiang 69bd13bc93 feat: Turn off certain features of quota popup for self-hosting (#703)
* feat: Turn off certain features of quota popup for self-hosting

This commit introduces a new variable, NEXT_PUBLIC_SELFHOSTED,
that alters the behavior of the quota popup. Downstream
consumers of the application may have their own quota-checking
logic, and the front-end reacts to the 429 error by displaying
the quota popup.  In the case of a self-hosted version of the app,
it is inappropriate to ask for sponsorship or provide a
hyperlink to the public version of the tool to apply for an
increased quota. An alternative string translation is provided
with an empty message for adopter customization.

To use this feature, compile with NEXT_PUBLIC_SELFHOSTED=true
and those parts of the quota popup will be omitted.
The downstream consumer is still expected to customize
the internationalized strings for the popup content
to be appropriate to their organization on their local forks.

Signed-off-by: Bryon Nevis <[email protected]>

* refactor: improve readability and provide sensible selfhosted defaults

- Extract nested ternary expressions into quotaMessage and tipHtml variables
- Combine two separate !isSelfHosted conditional blocks into one
- Replace null tipSelfHosted with meaningful default strings across all locales

---------

Signed-off-by: Bryon Nevis <[email protected]>
Co-authored-by: dayuan.jiang <[email protected]>
2026-02-28 00:34:05 +09:00
Bryon Nevis dd9d79d2d6 fix: Changes to make npm run check GH status check pass (#710)
The Lint & Unit Tests PR status check is failing
at the "Run lint" step over a half-dozen issues.
This is causing all PR's to fail the Lint & Unit tets check.
This fix resolves those issues.

Signed-off-by: Bryon Nevis <[email protected]>
2026-02-28 00:24:24 +09:00
Marvelous Ikponmwosaanddayuan.jiang a5d1554c3f Add Ollama Cloud support with Base URL and API Key configuration (#692)
* Add Ollama Cloud support with Base URL and API Key configuration

* implemented feedback

* fix: use OLLAMA_BASE_URL env fallback in validate-model endpoint

* Remove dedicated Ollama configuration block

* security(ollama): prevent API key leak to client-controlled URLs

* added test

* fix: security hardening and Ollama Cloud default URL

- Add server OLLAMA_API_KEY fallback to validate-model endpoint with
  SSRF guard mirroring ai-providers.ts
- Tighten top-level SSRF exemption: only exempt Ollama when no server
  OLLAMA_API_KEY is configured
- Update Electron config to support OLLAMA_API_KEY env var
- Change default Ollama URL from localhost:11434 to ollama.com/api
  (Ollama Cloud) for web UI users
- Add tests for server env combo, API-key-only, and SSRF guard scenarios

---------

Co-authored-by: dayuan.jiang <[email protected]>
2026-02-26 21:55:21 +09:00
Khairil Rahman HakikiandCopilot 7fbc857d3a feat(ui): conditional model selector shadow logic (#681)
* feat(ui): conditional model selector shadow logic (#678)

- Update ModelSelectorList to conditionally render shadow based on scroll state
- Update CommandList to forward ref for scroll detection
- Resolves #678

* Update components/ui/command.tsx

Co-authored-by: Copilot <[email protected]>

* fix(ui): update listRef type to match CommandList forwarded ref

---------

Co-authored-by: Copilot <[email protected]>
2026-02-07 12:13:56 +09:00
Vishakha Agrawal d752524851 make show unvalidated models clickable (#680) 2026-02-07 10:36:13 +09:00
Gideon Ayenianddayuan.jiang ecce0a96c7 enhancement: Pin Configure Models button to bottom of model selector … (#665)
* enhancement: Pin Configure Models button to bottom of model selector (#637)

Keep "Configure Models..." and info text fixed at bottom of dropdown when the model list scrolls. Wire button to open Model Config dialog.

* Address PR review: use ModelSelectorItem, z-10 footer, padding on wrapper

* fix: reduce spacing between Configure Models button and info text

---------

Co-authored-by: dayuan.jiang <[email protected]>
2026-02-02 20:08:34 +09:00
Dayuan Jiang 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.
2026-01-30 14:05:16 +09:00