@fonlan/dsh-model-router 0.3.5 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -4,16 +4,37 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ //#region \0rolldown/runtime.js
8
+ var __create = Object.create;
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __getProtoOf = Object.getPrototypeOf;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
16
+ key = keys[i];
17
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
18
+ get: ((k) => from[k]).bind(null, key),
19
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
+ });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
25
+ value: mod,
26
+ enumerable: true
27
+ }) : target, mod));
28
+ //#endregion
7
29
  let react = require("react");
8
30
  let react_jsx_runtime = require("react/jsx-runtime");
9
31
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
32
+ _deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
10
33
  //#region src/client/locales.ts
11
34
  const LOCALE_NS = "model-router";
12
35
  const zh = {
13
36
  settingsTitle: "模型路由",
14
- settingsCardDescription: "模型聚合、提供商切换与优先级顺序",
15
- expand: "展开",
16
- collapse: "收起",
37
+ sectionSub: "模型聚合、提供商切换与优先级顺序",
17
38
  readOnly: "本部署的设置为只读。",
18
39
  loading: "加载中…",
19
40
  loadFailed: "加载失败:{message}",
@@ -64,9 +85,7 @@ window.__ModuleLoader__.load({
64
85
  };
65
86
  const en = {
66
87
  settingsTitle: "Model Router",
67
- settingsCardDescription: "Model aggregation, provider switching and preference order",
68
- expand: "Expand",
69
- collapse: "Collapse",
88
+ sectionSub: "Model aggregation, provider switching and preference order",
70
89
  readOnly: "This deployment stores settings read-only.",
71
90
  loading: "Loading…",
72
91
  loadFailed: "Load failed: {message}",
@@ -156,9 +175,9 @@ window.__ModuleLoader__.load({
156
175
  setModelOrder: (order) => call("set-model-order", { order })
157
176
  };
158
177
  //#endregion
159
- //#region \0dsh-css:/home/runner/work/dsh-model-router/dsh-model-router/src/client/settings-card.css.mjs
160
- const css$1 = "/* Model Router settings card — chrome aligned with the built-in plugin cards\n (设置 → 插件 → 插件配置), theme-consistent via DSW alias variables. */\n.mr-settings-card {\n border: 1px solid var(--dsw-alias-border-l2, #2a3346);\n background: var(--dsw-alias-bg-layer-3, #0d1017);\n border-radius: 12px;\n list-style: none;\n transition: border-color .16s, background .16s;\n}\n\n.mr-settings-card:hover {\n border-color: var(--dsw-alias-label-dimmed, #4a5468);\n}\n\n.mr-settings-card[data-open] {\n background: var(--dsw-alias-bg-layer-2, #141a26);\n border-color: var(--dsw-alias-label-dimmed, #4a5468);\n}\n\n.mr-settings-head {\n appearance: none;\n width: 100%;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: transparent;\n border: 0;\n border-radius: 12px;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n display: flex;\n}\n\n.mr-settings-head:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary, #4f8cff);\n outline-offset: -2px;\n}\n\n.mr-settings-head-text {\n flex-direction: column;\n flex: 1;\n gap: 4px;\n min-width: 0;\n display: flex;\n}\n\n.mr-settings-title {\n color: var(--dsw-alias-label-primary, #e6e6e6);\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n.mr-settings-sub {\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n font-size: 13px;\n line-height: 1.5;\n}\n\n.mr-settings-chevron {\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n flex: none;\n transition: transform .16s;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.mr-settings-chevron[data-open] {\n transform: rotate(180deg);\n}\n\n.mr-settings-body {\n border-top: 1px solid var(--dsw-alias-border-l2, #2a3346);\n margin: 0 16px;\n padding: 12px 0 8px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.mr-settings-readonly {\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n}\n\n/* ── card body: the router controls ─────────────────────────────────────── */\n\n.mr-root {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 0 0 4px;\n}\n\n.mr-quick-switch {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 8px 10px;\n border: 1px solid var(--dsw-alias-border-inverted);\n border-radius: 10px;\n background: var(--dsw-alias-bg-module-platform);\n cursor: pointer;\n}\n\n.mr-quick-switch input[type=\"checkbox\"] {\n margin-top: 2px;\n accent-color: var(--dsw-alias-accent);\n}\n\n.mr-quick-switch-copy {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.mr-quick-switch-label {\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.mr-quick-switch-desc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.mr-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.mr-count {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.mr-button {\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n background: var(--dsw-alias-interactive-bg-hover);\n border: 1px solid var(--dsw-alias-border-l3);\n border-radius: 8px;\n font-size: 12px;\n line-height: 18px;\n padding: 3px 10px;\n}\n\n.mr-button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n filter: brightness(1.08);\n}\n\n.mr-button:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.mr-error {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n color: var(--dsw-alias-state-error-primary);\n border-radius: 8px;\n padding: 7px 8px;\n font-size: 12px;\n line-height: 18px;\n}\n\n.mr-empty {\n color: var(--dsw-alias-label-tertiary);\n padding: 16px 0;\n font-size: 13px;\n line-height: 20px;\n}\n\n.mr-models {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.mr-card {\n border: 1px solid var(--dsw-alias-border-inverted);\n border-radius: 12px;\n padding: 10px 12px;\n background: var(--dsw-alias-bg-module-platform);\n transition: opacity 0.12s;\n}\n\n.mr-card-busy {\n opacity: 0.6;\n pointer-events: none;\n}\n\n.mr-card-header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.mr-model-name {\n color: var(--dsw-alias-label-primary);\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n\n.mr-model-id {\n color: var(--dsw-alias-label-tertiary);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px;\n line-height: 18px;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n min-width: 0;\n flex: 1;\n}\n\n.mr-provider-count {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n flex: none;\n}\n\n.mr-drag-hint {\n margin: 2px 0 6px;\n color: var(--dsw-alias-label-caption);\n font-size: 11px;\n line-height: 16px;\n}\n\n.mr-providers {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.mr-provider {\n display: flex;\n align-items: center;\n gap: 6px;\n border: 1px solid transparent;\n border-radius: 10px;\n padding: 2px 8px 2px 4px;\n}\n\n.mr-provider-active {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l3);\n}\n\n.mr-provider-nocred {\n opacity: 0.55;\n}\n\n.mr-provider-drop {\n border-color: var(--dsw-alias-border-brand);\n}\n\n.mr-grip {\n color: var(--dsw-alias-label-caption);\n cursor: grab;\n font-size: 12px;\n line-height: 20px;\n flex: none;\n user-select: none;\n}\n\n.mr-grip:active {\n cursor: grabbing;\n}\n\n.mr-provider-button {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: baseline;\n gap: 6px;\n color: var(--dsw-alias-label-primary);\n background: none;\n border: none;\n text-align: left;\n cursor: pointer;\n font: inherit;\n padding: 4px 0;\n}\n\n.mr-provider-button:disabled {\n cursor: default;\n}\n\n.mr-provider-name {\n font-size: 13px;\n line-height: 20px;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.mr-provider-nocred-label {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n flex: none;\n}\n\n.mr-active-badge {\n flex: none;\n color: var(--dsw-alias-label-inverted, #fff);\n background: var(--dsw-alias-border-brand, #4d6bfe);\n border-radius: 999px;\n font-size: 11px;\n line-height: 16px;\n padding: 0 8px;\n}\n\n/* ── model display order ────────────────────────────────────────────────── */\n\n.mr-sort {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 10px;\n border: 1px solid var(--dsw-alias-border-inverted);\n border-radius: 10px;\n background: var(--dsw-alias-bg-module-platform);\n}\n\n.mr-sort-head {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.mr-sort-title {\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.mr-sort-desc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.mr-sort-modes {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.mr-sort-mode {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n border: 1px solid transparent;\n border-radius: 8px;\n padding: 5px 6px;\n cursor: pointer;\n}\n\n.mr-sort-mode:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mr-sort-mode input[type=\"radio\"] {\n margin-top: 2px;\n accent-color: var(--dsw-alias-accent);\n}\n\n.mr-sort-mode-copy {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n}\n\n.mr-sort-mode-label {\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.mr-sort-mode-desc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.mr-sort-custom {\n display: flex;\n flex-direction: column;\n gap: 6px;\n border-top: 1px solid var(--dsw-alias-border-l2, #2a3346);\n padding-top: 8px;\n}\n\n.mr-sort-hint {\n color: var(--dsw-alias-label-caption);\n margin: 0;\n font-size: 11px;\n line-height: 16px;\n}\n\n.mr-sort-list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: 280px;\n overflow-y: auto;\n}\n\n.mr-sort-item {\n display: flex;\n align-items: center;\n gap: 6px;\n border: 1px solid var(--dsw-alias-border-l2, #2a3346);\n border-radius: 8px;\n padding: 4px 8px 4px 4px;\n background: var(--dsw-alias-bg-layer-1, #0d1017);\n}\n\n.mr-sort-item-drop {\n border-color: var(--dsw-alias-border-brand);\n}\n\n.mr-sort-item-busy {\n opacity: 0.6;\n pointer-events: none;\n}\n\n.mr-sort-model-name {\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 20px;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n min-width: 0;\n}\n\n.mr-sort-model-id {\n color: var(--dsw-alias-label-tertiary);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n line-height: 16px;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n flex: 1;\n text-align: right;\n}\n";
161
- const tagId$1 = "@fonlan/dsh-model-router/settings-card.css";
178
+ //#region \0dsh-css:/home/runner/work/dsh-model-router/dsh-model-router/src/client/settings-section.css.mjs
179
+ const css$1 = "/* Model Router settings page — the page owns one entry in the settings\n sidebar and renders its content in the panel's content column. Styles are\n theme-consistent via DSW alias variables; the page draws its own static\n header (title plus a one-line summary) over the always-visible controls. */\n\n/* page layout */\n\n.mr-page {\n display: flex;\n flex-direction: column;\n gap: 16px;\n max-width: 720px;\n}\n\n.mr-page-head {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.mr-page-title {\n margin: 0;\n color: var(--dsw-alias-label-primary, #e6e6e6);\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n.mr-page-sub {\n margin: 0;\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n font-size: 13px;\n line-height: 1.5;\n}\n\n/* body layout */\n\n.mr-body {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.mr-settings-readonly {\n color: var(--dsw-alias-label-tertiary, #8b93a7);\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n}\n\n/* ── page body: the router controls ─────────────────────────────────────── */\n\n.mr-root {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 0 0 4px;\n}\n\n.mr-quick-switch {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding: 8px 10px;\n border: 1px solid var(--dsw-alias-border-inverted);\n border-radius: 10px;\n background: var(--dsw-alias-bg-module-platform);\n cursor: pointer;\n}\n\n.mr-quick-switch input[type=\"checkbox\"] {\n margin-top: 2px;\n accent-color: var(--dsw-alias-accent);\n}\n\n.mr-quick-switch-copy {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.mr-quick-switch-label {\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.mr-quick-switch-desc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.mr-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.mr-count {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.mr-button {\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n background: var(--dsw-alias-interactive-bg-hover);\n border: 1px solid var(--dsw-alias-border-l3);\n border-radius: 8px;\n font-size: 12px;\n line-height: 18px;\n padding: 3px 10px;\n}\n\n.mr-button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n filter: brightness(1.08);\n}\n\n.mr-button:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.mr-error {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n color: var(--dsw-alias-state-error-primary);\n border-radius: 8px;\n padding: 7px 8px;\n font-size: 12px;\n line-height: 18px;\n}\n\n.mr-empty {\n color: var(--dsw-alias-label-tertiary);\n padding: 16px 0;\n font-size: 13px;\n line-height: 20px;\n}\n\n.mr-models {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.mr-card {\n border: 1px solid var(--dsw-alias-border-inverted);\n border-radius: 12px;\n padding: 10px 12px;\n background: var(--dsw-alias-bg-module-platform);\n transition: opacity 0.12s;\n}\n\n.mr-card-busy {\n opacity: 0.6;\n pointer-events: none;\n}\n\n.mr-card-header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n}\n\n.mr-model-name {\n color: var(--dsw-alias-label-primary);\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n\n.mr-model-id {\n color: var(--dsw-alias-label-tertiary);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px;\n line-height: 18px;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n min-width: 0;\n flex: 1;\n}\n\n.mr-provider-count {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n flex: none;\n}\n\n.mr-drag-hint {\n margin: 2px 0 6px;\n color: var(--dsw-alias-label-caption);\n font-size: 11px;\n line-height: 16px;\n}\n\n.mr-providers {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.mr-provider {\n display: flex;\n align-items: center;\n gap: 6px;\n border: 1px solid transparent;\n border-radius: 10px;\n padding: 2px 8px 2px 4px;\n}\n\n.mr-provider-active {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l3);\n}\n\n.mr-provider-nocred {\n opacity: 0.55;\n}\n\n.mr-provider-drop {\n border-color: var(--dsw-alias-border-brand);\n}\n\n.mr-grip {\n color: var(--dsw-alias-label-caption);\n cursor: grab;\n font-size: 12px;\n line-height: 20px;\n flex: none;\n user-select: none;\n}\n\n.mr-grip:active {\n cursor: grabbing;\n}\n\n.mr-provider-button {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: baseline;\n gap: 6px;\n color: var(--dsw-alias-label-primary);\n background: none;\n border: none;\n text-align: left;\n cursor: pointer;\n font: inherit;\n padding: 4px 0;\n}\n\n.mr-provider-button:disabled {\n cursor: default;\n}\n\n.mr-provider-name {\n font-size: 13px;\n line-height: 20px;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.mr-provider-nocred-label {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n flex: none;\n}\n\n.mr-active-badge {\n flex: none;\n color: var(--dsw-alias-label-inverted, #fff);\n background: var(--dsw-alias-border-brand, #4d6bfe);\n border-radius: 999px;\n font-size: 11px;\n line-height: 16px;\n padding: 0 8px;\n}\n\n/* ── model display order ────────────────────────────────────────────────── */\n\n.mr-sort {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 8px 10px;\n border: 1px solid var(--dsw-alias-border-inverted);\n border-radius: 10px;\n background: var(--dsw-alias-bg-module-platform);\n}\n\n.mr-sort-head {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.mr-sort-title {\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.mr-sort-desc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.mr-sort-modes {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.mr-sort-mode {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n border: 1px solid transparent;\n border-radius: 8px;\n padding: 5px 6px;\n cursor: pointer;\n}\n\n.mr-sort-mode:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mr-sort-mode input[type=\"radio\"] {\n margin-top: 2px;\n accent-color: var(--dsw-alias-accent);\n}\n\n.mr-sort-mode-copy {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n}\n\n.mr-sort-mode-label {\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.mr-sort-mode-desc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.mr-sort-custom {\n display: flex;\n flex-direction: column;\n gap: 6px;\n border-top: 1px solid var(--dsw-alias-border-l2, #2a3346);\n padding-top: 8px;\n}\n\n.mr-sort-hint {\n color: var(--dsw-alias-label-caption);\n margin: 0;\n font-size: 11px;\n line-height: 16px;\n}\n\n.mr-sort-list {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: 280px;\n overflow-y: auto;\n}\n\n.mr-sort-item {\n display: flex;\n align-items: center;\n gap: 6px;\n border: 1px solid var(--dsw-alias-border-l2, #2a3346);\n border-radius: 8px;\n padding: 4px 8px 4px 4px;\n background: var(--dsw-alias-bg-layer-1, #0d1017);\n}\n\n.mr-sort-item-drop {\n border-color: var(--dsw-alias-border-brand);\n}\n\n.mr-sort-item-busy {\n opacity: 0.6;\n pointer-events: none;\n}\n\n.mr-sort-model-name {\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n line-height: 20px;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n min-width: 0;\n}\n\n.mr-sort-model-id {\n color: var(--dsw-alias-label-tertiary);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11px;\n line-height: 16px;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n flex: 1;\n text-align: right;\n}\n";
180
+ const tagId$1 = "@fonlan/dsh-model-router/settings-section.css";
162
181
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
163
182
  const tag = document.createElement("style");
164
183
  tag.dataset.plugin = "@fonlan/dsh-model-router";
@@ -167,23 +186,23 @@ window.__ModuleLoader__.load({
167
186
  document.head.appendChild(tag);
168
187
  }
169
188
  //#endregion
170
- //#region src/client/settings-card.tsx
189
+ //#region src/client/settings-section.tsx
171
190
  /**
172
- * The Model Router Settings Card (设置 → 插件 → 插件配置 → 模型路由).
191
+ * The Model Router settings page (设置 → 侧栏「模型路由」).
173
192
  *
174
- * Renders its own expandable chrome aligned with the built-in plugin cards
175
- * (external plugins cannot import PluginCard): a header naming the plugin and
176
- * what its settings govern, disclosing the router controls in place. All
177
- * reads/mutations go through the plugin's fenced API; the server persists
178
- * through the `model-router` settings namespace, so a switch here is live for
179
- * the next request globally.
193
+ * Registers into the settings.section slot, so the page owns one entry in the
194
+ * settings sidebar and renders its content in the panel's content column. The
195
+ * page draws its own static header (title plus a one-line summary) over the
196
+ * router controls: every routed model with its providers, their order and the
197
+ * provider the model currently routes to. All reads/mutations go through the
198
+ * plugin's fenced API; the server persists into this plugin's loader-entry
199
+ * config (dsh >= 0.1.7), so a switch here is live for the next request
200
+ * globally.
180
201
  *
181
- * The bound settings scope (the same `model-router` namespace the host half
182
- * registers) supplies the card's dispatch state: while the namespace is merely
183
- * loading the card stays mounted, and when it is unavailable (deployment
184
- * without the host half) nothing renders — matching the built-in cards. A
185
- * read-only deployment shows the built-in card's banner and disables the
186
- * mutation controls.
202
+ * The page needs no settings service: whether the deployment can persist is
203
+ * reported by the API itself (`state.writable`), so the page registers and
204
+ * mounts everywhere the host half serves its API. A read-only deployment
205
+ * shows the banner and disables the mutation controls.
187
206
  */
188
207
  function arrayMove(list, from, to) {
189
208
  const next = [...list];
@@ -208,7 +227,7 @@ window.__ModuleLoader__.load({
208
227
  }, [ctx]);
209
228
  return (0, react.useSyncExternalStore)(subscribe, getSnapshot);
210
229
  }
211
- function makeSettingsCard(ctx) {
230
+ function makeSettingsSection(ctx) {
212
231
  const t = (() => {
213
232
  try {
214
233
  return ctx.locale.bind(LOCALE_NS);
@@ -216,11 +235,8 @@ window.__ModuleLoader__.load({
216
235
  return (key) => key;
217
236
  }
218
237
  })();
219
- return function ModelRouterSettingsCard(props) {
220
- const { scope } = props;
221
- const snapshot = (0, react.useSyncExternalStore)((listener) => scope.subscribe(listener), () => scope.getSnapshot());
238
+ return function ModelRouterSettingsSection() {
222
239
  useLocaleRevision(ctx);
223
- const [open, setOpen] = (0, react.useState)(false);
224
240
  const [state, setState] = (0, react.useState)(null);
225
241
  const [error, setError] = (0, react.useState)(null);
226
242
  const [loading, setLoading] = (0, react.useState)(true);
@@ -233,7 +249,8 @@ window.__ModuleLoader__.load({
233
249
  const load = (0, react.useCallback)(async () => {
234
250
  setLoading(true);
235
251
  try {
236
- setState(await api.state());
252
+ const next = await api.state();
253
+ setState(next);
237
254
  setError(null);
238
255
  } catch (cause) {
239
256
  setError(cause instanceof Error ? cause.message : String(cause));
@@ -367,48 +384,22 @@ window.__ModuleLoader__.load({
367
384
  setSortBusy(false);
368
385
  }
369
386
  }, [state, t]);
370
- if (snapshot.status === "unavailable") return null;
371
- const writable = snapshot.writable;
387
+ const writable = state?.writable ?? true;
372
388
  const models = (0, react.useMemo)(() => state?.models ?? [], [state]);
373
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
374
- className: "mr-settings-card",
375
- "data-open": open ? "" : void 0,
376
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
377
- type: "button",
378
- className: "mr-settings-head",
379
- "aria-expanded": open,
380
- "aria-label": (open ? t("collapse") : t("expand")) + ":" + t("settingsTitle"),
381
- onClick: () => setOpen(!open),
382
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
383
- className: "mr-settings-head-text",
384
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
385
- className: "mr-settings-title",
386
- children: t("settingsTitle")
387
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
388
- className: "mr-settings-sub",
389
- children: t("settingsCardDescription")
390
- })]
391
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
392
- className: "mr-settings-chevron",
393
- "data-open": open ? "" : void 0,
394
- "aria-hidden": "true",
395
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
396
- width: "14",
397
- height: "14",
398
- viewBox: "0 0 14 14",
399
- fill: "none",
400
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
401
- d: "M3.5 5.25L7 8.75L10.5 5.25",
402
- stroke: "currentColor",
403
- strokeWidth: "1.5",
404
- strokeLinecap: "round",
405
- strokeLinejoin: "round"
406
- })
407
- })
389
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
390
+ className: "mr-page",
391
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
392
+ className: "mr-page-head",
393
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
394
+ className: "mr-page-title",
395
+ children: t("settingsTitle")
396
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
397
+ className: "mr-page-sub",
398
+ children: t("sectionSub")
408
399
  })]
409
- }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
410
- className: "mr-settings-body",
411
- children: [!writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
400
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
401
+ className: "mr-body",
402
+ children: [!loading && state !== null && !writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
412
403
  className: "mr-settings-readonly",
413
404
  role: "status",
414
405
  children: t("readOnly")
@@ -694,6 +685,84 @@ window.__ModuleLoader__.load({
694
685
  });
695
686
  }
696
687
  //#endregion
688
+ //#region src/client/icons.tsx
689
+ /** The runtime export table, read by capability instead of by one hard-coded name. */
690
+ const table = _deepseek_ai_dsh_client_ui_primitives;
691
+ /** First name the runtime actually provides (a missing export is `undefined`). */
692
+ function firstDefined(...names) {
693
+ for (const name of names) {
694
+ let candidate;
695
+ try {
696
+ candidate = table[name];
697
+ } catch {
698
+ continue;
699
+ }
700
+ if (typeof candidate === "function") return candidate;
701
+ }
702
+ }
703
+ /** One-path stroked glyph fallback (16×16 viewBox — the 0.1.7 artwork). */
704
+ function glyph(path, defaultSize) {
705
+ return function Glyph({ size = defaultSize, className }) {
706
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
707
+ width: size,
708
+ height: size,
709
+ className,
710
+ viewBox: "0 0 16 16",
711
+ fill: "none",
712
+ xmlns: "http://www.w3.org/2000/svg",
713
+ "aria-hidden": "true",
714
+ strokeWidth: 1,
715
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
716
+ d: path,
717
+ stroke: "currentColor"
718
+ })
719
+ });
720
+ };
721
+ }
722
+ /** Warning circle fallback (the 0.1.7 artwork: ring, stem, dot). */
723
+ function WarningFallback({ size = 16, className }) {
724
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
725
+ width: size,
726
+ height: size,
727
+ className,
728
+ viewBox: "0 0 16 16",
729
+ fill: "none",
730
+ xmlns: "http://www.w3.org/2000/svg",
731
+ "aria-hidden": "true",
732
+ strokeWidth: 1,
733
+ children: [
734
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
735
+ d: "M8 14.5C11.5899 14.5 14.5 11.5899 14.5 8C14.5 4.41015 11.5899 1.5 8 1.5C4.41015 1.5 1.5 4.41015 1.5 8C1.5 11.5899 4.41015 14.5 8 14.5Z",
736
+ stroke: "currentColor"
737
+ }),
738
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
739
+ d: "M8 4.29199V9.79199",
740
+ stroke: "currentColor"
741
+ }),
742
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
743
+ d: "M8 10.708V11.708",
744
+ stroke: "currentColor"
745
+ })
746
+ ]
747
+ });
748
+ }
749
+ /**
750
+ * Shell icons used by the model seat.
751
+ *
752
+ * DSH 0.1.7 renamed every size-suffixed icon export (`IconCheckOutline16`,
753
+ * `IconChevronDownOutline14`, …) into stroke-weight variants (`…OutlineRegular`
754
+ * 1px / `…OutlineMedium` 1.3px). A stale named import resolves to `undefined`
755
+ * and React then throws "Element type is invalid" while rendering the slot
756
+ * entry — the whole seat is dropped from the conversation input instead of
757
+ * degrading (this is exactly what the 0.1.7 upgrade did to this plugin).
758
+ * Resolve the names at runtime, newest first, so the next rename lands on
759
+ * another shipped variant or on the inlined artwork below.
760
+ */
761
+ const CheckIcon = firstDefined("IconCheckOutlineRegular", "IconCheckOutlineMedium", "IconCheckOutline16") ?? glyph("M2.25 8.5L5.49732 11.7473C5.90519 12.1552 6.57263 12.1344 6.95426 11.7018L13.75 4", 16);
762
+ const ChevronDownIcon = firstDefined("IconChevronDownOutlineRegular", "IconChevronDownOutlineMedium", "IconChevronDownOutline14") ?? glyph("M4 6L7.29289 9.29289C7.68342 9.68342 8.31658 9.68342 8.70711 9.29289L12 6", 14);
763
+ const ChevronRightIcon = firstDefined("IconChevronRightOutlineRegular", "IconChevronRightOutlineMedium", "IconChevronRightOutline14") ?? glyph("M6 12L9.29289 8.70711C9.68342 8.31658 9.68342 7.68342 9.29289 7.29289L6 4", 14);
764
+ const WarningIcon = firstDefined("IconWarningOutlineRegular", "IconWarningOutlineMedium", "IconWarningOutline16") ?? WarningFallback;
765
+ //#endregion
697
766
  //#region \0dsh-css:/home/runner/work/dsh-model-router/dsh-model-router/src/client/model-seat.css.mjs
698
767
  const css = "/*\n * Composer model seat (shadows the host ModelSelect). Styles mirror the host\n * ModelSelect.module.css exactly (same tokens, sizes and spacing) so the\n * replacement is visually indistinguishable, plus the provider pane's title\n * and hint rows.\n */\n\n.mr-seat-root {\n min-width: 0;\n position: relative;\n}\n\n.mr-seat-trigger {\n min-width: 0;\n max-width: 220px;\n height: 28px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n background: transparent;\n border: none;\n border-radius: 24px;\n outline: none;\n align-items: center;\n gap: 4px;\n padding: 0 4px 0 8px;\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n display: flex;\n}\n\n.mr-seat-trigger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mr-seat-trigger:focus-visible {\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n.mr-seat-trigger:disabled {\n color: var(--dsw-alias-label-dimmed);\n cursor: default;\n}\n\n.mr-seat-triggerLabel {\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n overflow: hidden;\n}\n\n.mr-seat-triggerEffort {\n color: var(--dsw-alias-label-caption);\n flex: none;\n}\n\n.mr-seat-chevron {\n color: var(--dsw-alias-label-caption);\n flex: none;\n transition: transform 0.12s;\n}\n\n.mr-seat-chevronOpen {\n transform: rotate(180deg);\n}\n\n.mr-seat-menu {\n z-index: 20;\n border: 1px solid var(--dsw-alias-border-inverted);\n background: var(--dsw-specific-menu);\n width: min(240px, 100vw - 32px);\n max-height: min(360px, 100vh - 96px);\n box-shadow: var(--dsw-shadow-lv3);\n color: var(--dsw-alias-label-primary);\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);\n border-radius: 12px;\n flex-direction: column;\n padding: 4px;\n display: flex;\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n overflow: hidden;\n}\n\n.mr-seat-status,\n.mr-seat-empty {\n color: var(--dsw-alias-label-tertiary);\n padding: 10px;\n font-size: 13px;\n line-height: 20px;\n}\n\n.mr-seat-error,\n.mr-seat-warning {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n color: var(--dsw-alias-state-error-primary);\n border-radius: 8px;\n justify-content: space-between;\n align-items: flex-start;\n gap: 8px;\n margin-bottom: 4px;\n padding: 7px 8px;\n font-size: 12px;\n line-height: 18px;\n display: flex;\n}\n\n.mr-seat-warning {\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-state-warn-label);\n}\n\n.mr-seat-retry {\n color: inherit;\n font: inherit;\n cursor: pointer;\n background: transparent;\n border: none;\n flex: none;\n padding: 0;\n font-weight: 600;\n}\n\n.mr-seat-groups {\n min-height: 0;\n overflow-y: auto;\n}\n\n.mr-seat-group + .mr-seat-group {\n margin-top: 4px;\n}\n\n.mr-seat-groupTitle {\n z-index: 1;\n background: var(--dsw-specific-menu);\n color: var(--dsw-alias-label-tertiary);\n padding: 5px 8px 3px;\n font-size: 12px;\n font-weight: 500;\n line-height: 18px;\n position: sticky;\n top: 0;\n}\n\n/* Provider pane: non-sticky title row above the provider list. */\n.mr-seat-title {\n color: var(--dsw-alias-label-tertiary);\n padding: 5px 8px 3px;\n font-size: 12px;\n font-weight: 500;\n line-height: 18px;\n}\n\n.mr-seat-option {\n width: 100%;\n min-height: 38px;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: transparent;\n border: none;\n border-radius: 10px;\n outline: none;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n display: flex;\n}\n\n.mr-seat-option:hover:not(:disabled),\n.mr-seat-option:focus-visible {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mr-seat-selected {\n background: transparent;\n}\n\n.mr-seat-option:disabled {\n color: var(--dsw-alias-label-dimmed);\n cursor: default;\n}\n\n.mr-seat-optionCopy {\n flex-direction: column;\n flex: 1;\n min-width: 0;\n display: flex;\n}\n\n.mr-seat-modelName {\n color: inherit;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n overflow: hidden;\n}\n\n.mr-seat-description {\n color: var(--dsw-alias-label-tertiary);\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n line-height: 18px;\n overflow: hidden;\n}\n\n.mr-seat-check {\n color: var(--dsw-alias-label-primary);\n flex: 0 0 18px;\n place-items: center;\n display: grid;\n}\n\n.mr-seat-cell {\n width: 100%;\n height: 40px;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n text-align: left;\n background: transparent;\n border: none;\n border-radius: 10px;\n align-items: center;\n gap: 8px;\n padding: 0 10px;\n font-size: 14px;\n line-height: 22px;\n display: flex;\n}\n\n.mr-seat-cell:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.mr-seat-cellLabel {\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: auto;\n min-width: 0;\n overflow: hidden;\n}\n\n.mr-seat-cellValue {\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n color: var(--dsw-alias-label-tertiary);\n flex: 0 auto;\n overflow: hidden;\n}\n\n.mr-seat-cellChevron {\n color: var(--dsw-alias-label-tertiary);\n flex: none;\n}\n\n/* Global hint footer of the provider pane. */\n.mr-seat-hint {\n color: var(--dsw-alias-label-tertiary);\n padding: 5px 8px 3px;\n font-size: 12px;\n line-height: 18px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin-top: 4px;\n}\n\n/*\n * Mobile portrait: the composer row is an inline-size container. With the\n * quick route switcher gone from the right tool row, let the model seat\n * absorb the remaining row width (same rule the old switcher enforced).\n * The breakpoint matches DSH's own narrow-composer rule (<=460px).\n */\n@container (width <= 460px) {\n [data-slot=\"conversation.input.model\"] > * {\n flex: 1 1 0;\n min-width: 0;\n max-width: 100%;\n }\n\n [data-slot=\"conversation.input.model\"] [aria-haspopup=\"menu\"] {\n width: 100%;\n max-width: 100%;\n }\n}\n";
699
768
  const tagId = "@fonlan/dsh-model-router/model-seat.css";
@@ -972,7 +1041,7 @@ window.__ModuleLoader__.load({
972
1041
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
973
1042
  className: `mr-seat-chevron${open ? " mr-seat-chevronOpen" : ""}`,
974
1043
  "aria-hidden": true,
975
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
1044
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDownIcon, {})
976
1045
  })
977
1046
  ]
978
1047
  }),
@@ -1002,7 +1071,7 @@ window.__ModuleLoader__.load({
1002
1071
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1003
1072
  className: "mr-seat-cellChevron",
1004
1073
  "aria-hidden": true,
1005
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
1074
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronRightIcon, {})
1006
1075
  })
1007
1076
  ]
1008
1077
  }),
@@ -1024,7 +1093,7 @@ window.__ModuleLoader__.load({
1024
1093
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1025
1094
  className: "mr-seat-cellChevron",
1026
1095
  "aria-hidden": true,
1027
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
1096
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronRightIcon, {})
1028
1097
  })
1029
1098
  ]
1030
1099
  }),
@@ -1046,7 +1115,7 @@ window.__ModuleLoader__.load({
1046
1115
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1047
1116
  className: "mr-seat-cellChevron",
1048
1117
  "aria-hidden": true,
1049
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
1118
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronRightIcon, {})
1050
1119
  })
1051
1120
  ]
1052
1121
  })
@@ -1115,7 +1184,7 @@ window.__ModuleLoader__.load({
1115
1184
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1116
1185
  className: "mr-seat-check",
1117
1186
  "aria-hidden": true,
1118
- children: selected ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, {}) : null
1187
+ children: selected ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckIcon, {}) : null
1119
1188
  })]
1120
1189
  }, model.id);
1121
1190
  })]
@@ -1170,7 +1239,7 @@ window.__ModuleLoader__.load({
1170
1239
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1171
1240
  className: "mr-seat-check",
1172
1241
  "aria-hidden": true,
1173
- children: selected ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, {}) : null
1242
+ children: selected ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckIcon, {}) : null
1174
1243
  })]
1175
1244
  }, provider.id);
1176
1245
  })
@@ -1211,14 +1280,14 @@ window.__ModuleLoader__.load({
1211
1280
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1212
1281
  className: "mr-seat-check",
1213
1282
  "aria-hidden": true,
1214
- children: effectiveEffort === level.effort ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, {}) : null
1283
+ children: effectiveEffort === level.effort ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckIcon, {}) : null
1215
1284
  })]
1216
1285
  }, level.key))] })
1217
1286
  ]
1218
1287
  }),
1219
1288
  toast !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
1220
1289
  text: toast.text,
1221
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {}),
1290
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WarningIcon, {}),
1222
1291
  anchor: rootRef.current?.closest("[data-composer-card]") ?? null,
1223
1292
  onDone: () => setToast(null)
1224
1293
  }, toast.seq)
@@ -1309,8 +1378,8 @@ window.__ModuleLoader__.load({
1309
1378
  }
1310
1379
  //#endregion
1311
1380
  //#region src/client/index.tsx
1312
- /** The settings namespace this card edits (must match the host half). */
1313
- const ROUTER_NS = "model-router";
1381
+ /** The settings sidebar entry this page owns (must stay stable). */
1382
+ const SECTION_ID = "model-router";
1314
1383
  /** Services required before mounting (provided by the client runtime).
1315
1384
  * `remote.session` is included like the host ui-model-selection declares it:
1316
1385
  * the seat's `directoryFor` depends on the namespace, which mounts
@@ -1320,7 +1389,6 @@ window.__ModuleLoader__.load({
1320
1389
  "locale",
1321
1390
  "modelDirectories",
1322
1391
  "sessions",
1323
- "settingsScope",
1324
1392
  "connection",
1325
1393
  "remote",
1326
1394
  "remote.session"
@@ -1334,14 +1402,16 @@ window.__ModuleLoader__.load({
1334
1402
  });
1335
1403
  return () => off();
1336
1404
  }, "model-router: dictionaries");
1337
- const SettingsCard = makeSettingsCard(ctx);
1338
- const services = ctx;
1339
- const scope = services.settingsScope.bind({ namespace: ROUTER_NS });
1340
- services.slots.inject("settings.plugin.item", () => services.slots.register({
1341
- name: "settings.plugin.item",
1342
- key: ROUTER_NS,
1343
- inject: () => ({ scope })
1344
- }, SettingsCard));
1405
+ const t = ctx.locale.bind(LOCALE_NS);
1406
+ const SettingsSection = makeSettingsSection(ctx);
1407
+ const slots = ctx.slots;
1408
+ slots.inject("settings.section", () => slots.register({
1409
+ name: "settings.section",
1410
+ id: SECTION_ID,
1411
+ order: 300,
1412
+ label: () => t("settingsTitle"),
1413
+ locale: LOCALE_NS
1414
+ }, SettingsSection));
1345
1415
  registerModelRouterSeat(ctx);
1346
1416
  }
1347
1417
  //#endregion