@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 +158 -88
- package/lib/client.js.map +1 -1
- package/lib/index.js +70 -47
- package/lib/types/index.d.ts +7 -6
- package/lib/types/server/service.d.ts +27 -4
- package/lib/types/shared/config.d.ts +2 -0
- package/package.json +8 -8
- package/src/client/api.ts +5 -0
- package/src/client/icons.tsx +97 -0
- package/src/client/index.tsx +45 -28
- package/src/client/locales.ts +3 -7
- package/src/client/model-seat.tsx +10 -15
- package/src/client/{settings-card.css → settings-section.css} +19 -59
- package/src/client/settings-section.tsx +431 -0
- package/src/index.ts +11 -8
- package/src/server/service.ts +67 -57
- package/src/shared/config.ts +32 -2
- package/src/client/settings-card.tsx +0 -469
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
|
-
|
|
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
|
-
|
|
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-
|
|
160
|
-
const css$1 = "/* Model Router settings
|
|
161
|
-
const tagId$1 = "@fonlan/dsh-model-router/settings-
|
|
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-
|
|
189
|
+
//#region src/client/settings-section.tsx
|
|
171
190
|
/**
|
|
172
|
-
* The Model Router
|
|
191
|
+
* The Model Router settings page (设置 → 侧栏「模型路由」).
|
|
173
192
|
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* the
|
|
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
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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)("
|
|
374
|
-
className: "mr-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
}),
|
|
410
|
-
className: "mr-
|
|
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)(
|
|
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)(
|
|
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)(
|
|
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)(
|
|
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)(
|
|
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)(
|
|
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)(
|
|
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)(
|
|
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
|
|
1313
|
-
const
|
|
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
|
|
1338
|
-
const
|
|
1339
|
-
const
|
|
1340
|
-
|
|
1341
|
-
name: "settings.
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
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
|