dsh-model-visibility 0.1.2 → 0.2.0

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
@@ -1,225 +1,237 @@
1
- window.__ModuleLoader__.load({
2
- id: "dsh-model-visibility",
3
- factory: (require) => {
4
- var module = { exports: {} };
5
- var exports = module.exports;
6
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
- let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
8
- let react = require("react");
9
- let react_jsx_runtime = require("react/jsx-runtime");
10
- //#region src/namespace.ts
11
- /**
12
- * Shared settings namespace of the model-visibility plugin. Lives in its own
13
- * module so the browser half can name the namespace without value-importing
14
- * the Host half (which would drag the settings schema and its Node-side deps
15
- * into the client bundle).
16
- */
17
- const MODEL_VISIBILITY_NS = "model-visibility";
18
- //#endregion
19
- //#region src/client/controller.ts
20
- /**
21
- * The model-visibility card's controller: React-free state over two sources —
22
- * the (already filtered) Host model catalog and the `model-visibility`
23
- * settings scope. The rendered list is their union: catalog rows are visible
24
- * models; hidden entries restate the models the filter removed, labeled by
25
- * the name snapshot each entry carries. Beyond the reference shape, the
26
- * controller exposes provider-level batch writes and a whole-list write so
27
- * the card can offer search + bulk actions.
28
- */
29
- const EMPTY = {
30
- phase: "loading",
31
- writable: false,
32
- groups: [],
33
- total: 0,
34
- visible: 0
35
- };
36
- var ModelVisibilityCardController = class {
37
- scope;
38
- catalogReader;
39
- /** Exposed: the section contribution passes the store through its face as `hooks.snapshot`. */
40
- store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(EMPTY);
41
- catalog;
42
- loadError;
43
- generation = 0;
44
- constructor(scope, catalogReader) {
45
- this.scope = scope;
46
- this.catalogReader = catalogReader;
47
- scope.subscribe(() => {
48
- this.recompute();
49
- });
1
+ window.__ModuleLoader__.load({ id: "dsh-model-visibility", factory: (require) => {
2
+ var module = { exports: {} }; var exports = module.exports;
3
+ //#region rolldown:runtime
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
21
+ value: mod,
22
+ enumerable: true
23
+ }) : target, mod));
24
+
25
+ //#endregion
26
+ const __deepseek_ai_dsh_client_store = __toESM(require("@deepseek-ai/dsh-client-store"));
27
+ const react = __toESM(require("react"));
28
+ const react_jsx_runtime = __toESM(require("react/jsx-runtime"));
29
+
30
+ //#region src/namespace.ts
31
+ const MODEL_VISIBILITY_NS = "model-visibility";
32
+
33
+ //#endregion
34
+ //#region src/client/controller.ts
35
+ const EMPTY = {
36
+ phase: "loading",
37
+ writable: false,
38
+ groups: [],
39
+ total: 0,
40
+ visible: 0
41
+ };
42
+ var ModelVisibilityCardController = class {
43
+ /** Exposed: the section contribution passes the store through its face as `hooks.snapshot`. */
44
+ store = (0, __deepseek_ai_dsh_client_store.createSnapshotStore)(EMPTY);
45
+ catalog;
46
+ loadError;
47
+ generation = 0;
48
+ unsubscribe;
49
+ constructor(form, catalogReader) {
50
+ this.form = form;
51
+ this.catalogReader = catalogReader;
52
+ this.unsubscribe = form.subscribe(() => {
53
+ this.recompute();
54
+ });
55
+ this.load();
56
+ }
57
+ /** The registration-side inject face. */
58
+ face() {
59
+ return {
60
+ hooks: { modelVisibility: this.store },
61
+ toggle: (provider, model, name$1, hidden) => {
62
+ this.toggle(provider, model, name$1, hidden);
63
+ },
64
+ toggleProvider: (provider, hidden) => {
65
+ this.toggleProvider(provider, hidden);
66
+ },
67
+ resetAll: () => {
68
+ this.resetAll();
69
+ },
70
+ reload: () => {
50
71
  this.load();
51
72
  }
52
- /** The registration-side inject face. */
53
- face() {
54
- return {
55
- hooks: { modelVisibility: this.store },
56
- toggle: (provider, model, name, hidden) => {
57
- this.toggle(provider, model, name, hidden);
58
- },
59
- toggleProvider: (provider, hidden) => {
60
- this.toggleProvider(provider, hidden);
61
- },
62
- resetAll: () => {
63
- this.resetAll();
64
- },
65
- reload: () => {
66
- this.load();
67
- }
73
+ };
74
+ }
75
+ /** Re-read the catalog; an older response never overwrites a newer one. */
76
+ async load() {
77
+ const generation = ++this.generation;
78
+ if (this.catalog === void 0) this.store.update((draft) => {
79
+ draft.phase = "loading";
80
+ });
81
+ try {
82
+ const groups = await this.catalogReader.read();
83
+ if (generation !== this.generation) return;
84
+ this.catalog = groups.map((group) => ({
85
+ id: group.id,
86
+ name: group.name,
87
+ models: group.models.map((model) => ({
88
+ id: model.id,
89
+ name: model.name
90
+ }))
91
+ }));
92
+ this.loadError = void 0;
93
+ } catch (error) {
94
+ if (generation !== this.generation) return;
95
+ this.loadError = error instanceof Error ? error.message : String(error);
96
+ }
97
+ this.recompute();
98
+ }
99
+ /** Stop following the settings form (plugin unload). */
100
+ dispose() {
101
+ this.unsubscribe();
102
+ }
103
+ /** Compute the next hidden list from the current one plus one model's flip. */
104
+ nextHidden(current, provider, model, name$1, hidden) {
105
+ const rest = current.filter((entry) => !(entry.provider === provider && entry.model === model));
106
+ return hidden ? [...rest, {
107
+ provider,
108
+ model,
109
+ name: name$1
110
+ }] : rest;
111
+ }
112
+ /** Write the next hidden list, then re-pull the catalog the filter changed. */
113
+ async write(next) {
114
+ try {
115
+ await this.form.set("hidden", next);
116
+ } catch {}
117
+ await this.load();
118
+ }
119
+ /** Flip one model's visibility (public API — the section card calls it directly). */
120
+ async toggle(provider, model, name$1, hidden) {
121
+ const current = this.form.getSnapshot().value?.hidden ?? [];
122
+ await this.write(this.nextHidden(current, provider, model, name$1, hidden));
123
+ }
124
+ /**
125
+
126
+ * Batch flip for one provider. Models are taken from the union of the
127
+
128
+ * (filtered) catalog rows and existing hidden entries so a provider with
129
+
130
+ * every model already hidden still flips back fully.
131
+
132
+ */
133
+ /** Batch flip for one provider (public API). */
134
+ async toggleProvider(provider, hidden) {
135
+ const current = this.form.getSnapshot().value?.hidden ?? [];
136
+ const providerRows = this.stateRows().find((group) => group.id === provider);
137
+ const ids = new Set([...providerRows ? providerRows.models.map((model) => model.id) : [], ...current.filter((entry) => entry.provider === provider).map((entry) => entry.model)]);
138
+ if (ids.size === 0) return;
139
+ let next = current.filter((entry) => entry.provider !== provider);
140
+ if (hidden) {
141
+ const nameOf = (id) => providerRows?.models.find((model) => model.id === id)?.name ?? id;
142
+ next = [...next, ...[...ids].map((id) => ({
143
+ provider,
144
+ model: id,
145
+ name: nameOf(id)
146
+ }))];
147
+ }
148
+ await this.write(next);
149
+ }
150
+ /** Clear the hidden list entirely (public API). */
151
+ async resetAll() {
152
+ await this.write([]);
153
+ }
154
+ /** Current union rows (catalog ∪ hidden) without touching the store. */
155
+ stateRows() {
156
+ const snapshot = this.form.getSnapshot();
157
+ const hidden = snapshot.value?.hidden ?? [];
158
+ if (this.catalog === void 0) return [];
159
+ const hiddenKeys = new Set(hidden.map((entry) => `${entry.provider}\u0000${entry.model}`));
160
+ const groups = this.catalog.map((group) => ({
161
+ id: group.id,
162
+ name: group.name,
163
+ models: group.models.map((model) => ({
164
+ id: model.id,
165
+ name: model.name,
166
+ hidden: hiddenKeys.has(`${group.id}\u0000${model.id}`)
167
+ }))
168
+ }));
169
+ const groupsById = new Map(groups.map((group) => [group.id, group]));
170
+ for (const entry of hidden) {
171
+ const group = groupsById.get(entry.provider);
172
+ if (group?.models.some((model) => model.id === entry.model)) continue;
173
+ const row = {
174
+ id: entry.model,
175
+ name: entry.name ?? entry.model,
176
+ hidden: true
177
+ };
178
+ if (group !== void 0) group.models.push(row);
179
+ else {
180
+ const created = {
181
+ id: entry.provider,
182
+ name: entry.provider,
183
+ models: [row]
68
184
  };
185
+ groupsById.set(entry.provider, created);
186
+ groups.push(created);
69
187
  }
70
- /** Re-read the catalog; an older response never overwrites a newer one. */
71
- async load() {
72
- const generation = ++this.generation;
73
- if (this.catalog === void 0) this.store.update((draft) => {
74
- draft.phase = "loading";
75
- });
76
- try {
77
- const groups = await this.catalogReader.read();
78
- if (generation !== this.generation) return;
79
- this.catalog = groups.map((group) => ({
80
- id: group.id,
81
- name: group.name,
82
- models: group.models.map((model) => ({
83
- id: model.id,
84
- name: model.name
85
- }))
86
- }));
87
- this.loadError = void 0;
88
- } catch (error) {
89
- if (generation !== this.generation) return;
90
- this.loadError = error instanceof Error ? error.message : String(error);
91
- }
92
- this.recompute();
93
- }
94
- /** Compute the next hidden list from the current one plus one model's flip. */
95
- nextHidden(current, provider, model, name, hidden) {
96
- const rest = current.filter((entry) => !(entry.provider === provider && entry.model === model));
97
- return hidden ? [...rest, {
98
- provider,
99
- model,
100
- name
101
- }] : rest;
102
- }
103
- /** Write the next hidden list, then re-pull the catalog the filter changed. */
104
- async write(next) {
105
- try {
106
- await this.scope.set("hidden", next);
107
- } catch {}
108
- await this.load();
109
- }
110
- /** Flip one model's visibility (public API — the section card calls it directly). */
111
- async toggle(provider, model, name, hidden) {
112
- const current = this.scope.getSnapshot().value?.hidden ?? [];
113
- await this.write(this.nextHidden(current, provider, model, name, hidden));
114
- }
115
- /**
116
- * Batch flip for one provider. Models are taken from the union of the
117
- * (filtered) catalog rows and existing hidden entries so a provider with
118
- * every model already hidden still flips back fully.
119
- */
120
- /** Batch flip for one provider (public API). */
121
- async toggleProvider(provider, hidden) {
122
- const current = this.scope.getSnapshot().value?.hidden ?? [];
123
- const providerRows = this.stateRows().find((group) => group.id === provider);
124
- const ids = /* @__PURE__ */ new Set([...providerRows ? providerRows.models.map((model) => model.id) : [], ...current.filter((entry) => entry.provider === provider).map((entry) => entry.model)]);
125
- if (ids.size === 0) return;
126
- let next = current.filter((entry) => entry.provider !== provider);
127
- if (hidden) {
128
- const nameOf = (id) => providerRows?.models.find((model) => model.id === id)?.name ?? id;
129
- next = [...next, ...[...ids].map((id) => ({
130
- provider,
131
- model: id,
132
- name: nameOf(id)
133
- }))];
134
- }
135
- await this.write(next);
136
- }
137
- /** Clear the hidden list entirely (public API). */
138
- async resetAll() {
139
- await this.write([]);
140
- }
141
- /** Current union rows (catalog ∪ hidden) without touching the store. */
142
- stateRows() {
143
- const hidden = this.scope.getSnapshot().value?.hidden ?? [];
144
- if (this.catalog === void 0) return [];
145
- const hiddenKeys = new Set(hidden.map((entry) => `${entry.provider}\u0000${entry.model}`));
146
- const groups = this.catalog.map((group) => ({
147
- id: group.id,
148
- name: group.name,
149
- models: group.models.map((model) => ({
150
- id: model.id,
151
- name: model.name,
152
- hidden: hiddenKeys.has(`${group.id}\u0000${model.id}`)
153
- }))
154
- }));
155
- const groupsById = new Map(groups.map((group) => [group.id, group]));
156
- for (const entry of hidden) {
157
- const group = groupsById.get(entry.provider);
158
- if (group?.models.some((model) => model.id === entry.model)) continue;
159
- const row = {
160
- id: entry.model,
161
- name: entry.name ?? entry.model,
162
- hidden: true
163
- };
164
- if (group !== void 0) group.models.push(row);
165
- else {
166
- const created = {
167
- id: entry.provider,
168
- name: entry.provider,
169
- models: [row]
170
- };
171
- groupsById.set(entry.provider, created);
172
- groups.push(created);
173
- }
174
- }
175
- return groups;
176
- }
177
- /** Union the filtered catalog with the hidden entries into render rows. */
178
- recompute() {
179
- const snapshot = this.scope.getSnapshot();
180
- if (this.catalog === void 0 && this.loadError !== void 0) {
181
- this.store.set({
182
- ...EMPTY,
183
- phase: "error",
184
- error: this.loadError,
185
- writable: snapshot.writable
186
- });
187
- return;
188
- }
189
- if (this.catalog === void 0 || snapshot.status === "loading") {
190
- this.store.set({
191
- ...EMPTY,
192
- writable: snapshot.writable
193
- });
194
- return;
195
- }
196
- const groups = this.stateRows();
197
- const total = groups.reduce((count, group) => count + group.models.length, 0);
198
- const visible = groups.reduce((count, group) => count + group.models.filter((model) => !model.hidden).length, 0);
199
- this.store.set({
200
- phase: "ready",
201
- writable: snapshot.writable,
202
- groups,
203
- total,
204
- visible
205
- });
206
- }
207
- };
208
- //#endregion
209
- //#region src/client/ModelVisibilityCard.tsx
210
- /**
211
- * The model-visibility settings section, styled to mirror the official
212
- * settings pages: big page title + muted intro, a controls row, then one
213
- * bordered rounded card per provider (like the provider cards on the
214
- * 模型 page) with pill action buttons and token-style switches.
215
- *
216
- * Consumes the `settings.section` contribution contract exactly like the
217
- * official ModelsSection: props = spread inject face, where `hooks.snapshot`
218
- * arrives as the `useSnapshot` selector hook; all three seats are undefined
219
- * while the shell has not injected yet (render null then).
220
- */
221
- const STYLE_ID = "dsh-model-visibility-styles";
222
- const STYLES = `
188
+ }
189
+ return groups;
190
+ }
191
+ /** Union the filtered catalog with the hidden entries into render rows. */
192
+ recompute() {
193
+ const snapshot = this.form.getSnapshot();
194
+ if (snapshot.status === "unavailable") {
195
+ this.store.set({
196
+ ...EMPTY,
197
+ phase: "unavailable",
198
+ writable: snapshot.writable
199
+ });
200
+ return;
201
+ }
202
+ if (this.catalog === void 0 && this.loadError !== void 0) {
203
+ this.store.set({
204
+ ...EMPTY,
205
+ phase: "error",
206
+ error: this.loadError,
207
+ writable: snapshot.writable
208
+ });
209
+ return;
210
+ }
211
+ if (this.catalog === void 0 || snapshot.status === "loading") {
212
+ this.store.set({
213
+ ...EMPTY,
214
+ writable: snapshot.writable
215
+ });
216
+ return;
217
+ }
218
+ const groups = this.stateRows();
219
+ const total = groups.reduce((count, group) => count + group.models.length, 0);
220
+ const visible = groups.reduce((count, group) => count + group.models.filter((model) => !model.hidden).length, 0);
221
+ this.store.set({
222
+ phase: "ready",
223
+ writable: snapshot.writable,
224
+ groups,
225
+ total,
226
+ visible
227
+ });
228
+ }
229
+ };
230
+
231
+ //#endregion
232
+ //#region src/client/ModelVisibilityCard.tsx
233
+ const STYLE_ID = "dsh-model-visibility-styles";
234
+ const STYLES = `
223
235
  .dsh-mvw { display:flex; flex-direction:column; gap:16px; }
224
236
  .dsh-mvw .mvw-title { font-size:20px; font-weight:700; letter-spacing:.01em; }
225
237
  .dsh-mvw .mvw-sub { font-size:13px; color:inherit; opacity:.62; margin-top:6px; max-width:64ch; line-height:1.55; }
@@ -273,286 +285,307 @@ window.__ModuleLoader__.load({
273
285
  .dsh-mvw .mvw-warn { font-size:12.5px; color:#c77700; }
274
286
  .dsh-mvw .mvw-seats-off { font-size:12.5px; opacity:.66; }
275
287
  `;
276
- function ensureStyles() {
277
- if (typeof document === "undefined") return;
278
- if (document.getElementById(STYLE_ID)) return;
279
- const el = document.createElement("style");
280
- el.id = STYLE_ID;
281
- el.textContent = STYLES;
282
- document.head.appendChild(el);
283
- }
284
- function ModelVisibilityCard(props) {
285
- const { controller, useSnapshot, t } = props;
286
- const state = (useSnapshot ?? ((_selector) => void 0))((snapshot) => snapshot);
287
- const [query, setQuery] = (0, react.useState)("");
288
- (0, react.useMemo)(() => {
289
- ensureStyles();
290
- }, []);
291
- const filteredGroups = (0, react.useMemo)(() => {
292
- const q = query.trim().toLowerCase();
293
- if (q === "") return state?.groups ?? [];
294
- return (state?.groups ?? []).map((group) => ({
295
- ...group,
296
- models: group.models.filter((model) => model.id.toLowerCase().includes(q) || model.name.toLowerCase().includes(q) || group.id.toLowerCase().includes(q))
297
- })).filter((group) => group.models.length > 0);
298
- }, [state, query]);
299
- if (controller === void 0 || useSnapshot === void 0 || t === void 0) return null;
300
- if (state === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
301
- className: "dsh-mvw",
302
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
303
- className: "mvw-note",
304
- children: t("loading")
305
- })
306
- });
307
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
308
- className: "dsh-mvw",
288
+ function ensureStyles() {
289
+ if (typeof document === "undefined") return;
290
+ if (document.getElementById(STYLE_ID)) return;
291
+ const el = document.createElement("style");
292
+ el.id = STYLE_ID;
293
+ el.textContent = STYLES;
294
+ document.head.appendChild(el);
295
+ }
296
+ function ModelVisibilityCard(props) {
297
+ const { controller, useSnapshot, t } = props;
298
+ const state = (useSnapshot ?? ((_selector) => void 0))((snapshot) => snapshot);
299
+ const [query, setQuery] = (0, react.useState)("");
300
+ (0, react.useMemo)(() => {
301
+ ensureStyles();
302
+ }, []);
303
+ const filteredGroups = (0, react.useMemo)(() => {
304
+ const q = query.trim().toLowerCase();
305
+ if (q === "") return state?.groups ?? [];
306
+ return (state?.groups ?? []).map((group) => ({
307
+ ...group,
308
+ models: group.models.filter((model) => model.id.toLowerCase().includes(q) || model.name.toLowerCase().includes(q) || group.id.toLowerCase().includes(q))
309
+ })).filter((group) => group.models.length > 0);
310
+ }, [state, query]);
311
+ if (controller === void 0 || useSnapshot === void 0 || t === void 0) return null;
312
+ if (state === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
313
+ className: "dsh-mvw",
314
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
315
+ className: "mvw-note",
316
+ children: t("loading")
317
+ })
318
+ });
319
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
320
+ className: "dsh-mvw",
321
+ children: [
322
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
323
+ className: "mvw-title",
324
+ children: t("title")
325
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
326
+ className: "mvw-sub",
327
+ children: t("intro")
328
+ })] }),
329
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
330
+ className: "mvw-controls",
309
331
  children: [
310
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
311
- className: "mvw-title",
312
- children: t("title")
313
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
314
- className: "mvw-sub",
315
- children: t("intro")
316
- })] }),
317
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
318
- className: "mvw-controls",
319
- children: [
320
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
321
- type: "search",
322
- className: "mvw-search",
323
- value: query,
324
- placeholder: t("searchPlaceholder"),
325
- onChange: (event) => {
326
- setQuery(event.target.value);
327
- }
328
- }),
329
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
332
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
333
+ type: "search",
334
+ className: "mvw-search",
335
+ value: query,
336
+ placeholder: t("searchPlaceholder"),
337
+ onChange: (event) => {
338
+ setQuery(event.target.value);
339
+ }
340
+ }),
341
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
342
+ type: "button",
343
+ className: "mvw-pill",
344
+ disabled: !state.writable || state.total === state.visible,
345
+ onClick: () => {
346
+ controller.resetAll();
347
+ },
348
+ children: t("showAllProviders")
349
+ }),
350
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
351
+ className: "mvw-count",
352
+ children: t("visibleCount", {
353
+ visible: state.visible,
354
+ total: state.total
355
+ })
356
+ })
357
+ ]
358
+ }),
359
+ state.phase === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
360
+ className: "mvw-warn",
361
+ children: [
362
+ t("loadFailed", { message: state.error ?? "" }),
363
+ " ",
364
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
365
+ type: "button",
366
+ className: "mvw-mini",
367
+ onClick: () => {
368
+ controller.load();
369
+ },
370
+ children: t("retry")
371
+ })
372
+ ]
373
+ }),
374
+ state.phase === "unavailable" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
375
+ className: "mvw-note",
376
+ children: t("unavailable")
377
+ }),
378
+ state.phase === "ready" && !state.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
379
+ className: "mvw-note",
380
+ children: t("readOnly")
381
+ }),
382
+ state.phase === "ready" && state.groups.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
383
+ className: "mvw-note",
384
+ children: t("empty")
385
+ }),
386
+ state.phase === "ready" && state.total > 0 && state.visible === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
387
+ className: "mvw-warn",
388
+ children: t("allHiddenWarning")
389
+ }),
390
+ state.phase === "ready" && filteredGroups.length === 0 && state.groups.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
391
+ className: "mvw-note",
392
+ children: t("noMatch")
393
+ }),
394
+ state.phase === "ready" && filteredGroups.map((group) => {
395
+ const groupVisible = group.models.filter((model) => !model.hidden).length;
396
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
397
+ className: "mvw-card",
398
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
399
+ className: "mvw-prov-head",
400
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
401
+ className: "mvw-prov-name",
402
+ children: group.name
403
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
404
+ className: "mvw-prov-count",
405
+ children: t("providerRow", {
406
+ visible: groupVisible,
407
+ total: group.models.length
408
+ })
409
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
410
+ className: "mvw-prov-btns",
411
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
330
412
  type: "button",
331
- className: "mvw-pill",
332
- disabled: !state.writable || state.total === state.visible,
413
+ className: "mvw-mini",
414
+ disabled: !state.writable || groupVisible === 0,
333
415
  onClick: () => {
334
- controller.resetAll();
416
+ controller.toggleProvider(group.id, true);
335
417
  },
336
- children: t("showAllProviders")
337
- }),
338
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
339
- className: "mvw-count",
340
- children: t("visibleCount", {
341
- visible: state.visible,
342
- total: state.total
343
- })
344
- })
345
- ]
346
- }),
347
- state.phase === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
348
- className: "mvw-warn",
349
- children: [
350
- t("loadFailed", { message: state.error ?? "" }),
351
- " ",
352
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
418
+ children: t("hideAll")
419
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
353
420
  type: "button",
354
421
  className: "mvw-mini",
422
+ disabled: !state.writable || groupVisible === group.models.length,
355
423
  onClick: () => {
356
- controller.load();
424
+ controller.toggleProvider(group.id, false);
357
425
  },
358
- children: t("retry")
359
- })
360
- ]
361
- }),
362
- state.phase === "ready" && !state.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
363
- className: "mvw-note",
364
- children: t("readOnly")
365
- }),
366
- state.phase === "ready" && state.groups.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
367
- className: "mvw-note",
368
- children: t("empty")
369
- }),
370
- state.phase === "ready" && state.total > 0 && state.visible === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
371
- className: "mvw-warn",
372
- children: t("allHiddenWarning")
373
- }),
374
- state.phase === "ready" && filteredGroups.length === 0 && state.groups.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
375
- className: "mvw-note",
376
- children: t("noMatch")
377
- }),
378
- state.phase === "ready" && filteredGroups.map((group) => {
379
- const groupVisible = group.models.filter((model) => !model.hidden).length;
380
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
381
- className: "mvw-card",
382
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
383
- className: "mvw-prov-head",
384
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
385
- className: "mvw-prov-name",
386
- children: group.name
387
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
388
- className: "mvw-prov-count",
389
- children: t("providerRow", {
390
- visible: groupVisible,
391
- total: group.models.length
392
- })
393
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
394
- className: "mvw-prov-btns",
395
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
396
- type: "button",
397
- className: "mvw-mini",
398
- disabled: !state.writable || groupVisible === 0,
399
- onClick: () => {
400
- controller.toggleProvider(group.id, true);
401
- },
402
- children: t("hideAll")
403
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
404
- type: "button",
405
- className: "mvw-mini",
406
- disabled: !state.writable || groupVisible === group.models.length,
407
- onClick: () => {
408
- controller.toggleProvider(group.id, false);
409
- },
410
- children: t("showAll")
411
- })]
426
+ children: t("showAll")
427
+ })]
428
+ })]
429
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
430
+ style: {
431
+ listStyle: "none",
432
+ margin: 0,
433
+ padding: 0
434
+ },
435
+ children: group.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
436
+ className: "mvw-row",
437
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
438
+ className: "mvw-name",
439
+ children: [model.name, model.name !== model.id && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
440
+ className: "mvw-id",
441
+ children: model.id
412
442
  })]
413
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
414
- style: {
415
- listStyle: "none",
416
- margin: 0,
417
- padding: 0
418
- },
419
- children: group.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
420
- className: "mvw-row",
421
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
422
- className: "mvw-name",
423
- children: [model.name, model.name !== model.id && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
424
- className: "mvw-id",
425
- children: model.id
426
- })]
427
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
428
- on: !model.hidden,
429
- disabled: !state.writable,
430
- label: model.name,
431
- onChange: (visible) => {
432
- controller.toggle(group.id, model.id, model.name, !visible);
433
- }
434
- })]
435
- }, model.id))
443
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
444
+ on: !model.hidden,
445
+ disabled: !state.writable,
446
+ label: model.name,
447
+ onChange: (visible) => {
448
+ controller.toggle(group.id, model.id, model.name, !visible);
449
+ }
436
450
  })]
437
- }, group.id);
438
- })
439
- ]
440
- });
441
- }
442
- function Switch(props) {
443
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
444
- type: "button",
445
- role: "switch",
446
- "aria-checked": props.on,
447
- "aria-label": props.label,
448
- disabled: props.disabled,
449
- onClick: () => {
450
- props.onChange(!props.on);
451
- },
452
- style: {
453
- width: 40,
454
- height: 22,
455
- borderRadius: 11,
456
- border: "none",
457
- padding: 2,
458
- cursor: props.disabled ? "not-allowed" : "pointer",
459
- background: props.on ? "#4c8bf5" : "rgba(128,128,128,.45)",
460
- opacity: props.disabled ? .5 : 1,
461
- display: "inline-flex",
462
- justifyContent: props.on ? "flex-end" : "flex-start",
463
- transition: "background .15s",
464
- flex: "none"
465
- },
466
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
467
- width: 18,
468
- height: 18,
469
- borderRadius: "50%",
470
- background: "#fff",
471
- display: "block"
472
- } })
473
- });
474
- }
475
- //#endregion
476
- //#region src/client/locales.ts
477
- const LOCALE_NS = "settings.modelVisibility";
478
- const zh = {
479
- nav: "模型可见性",
480
- title: "模型可见性",
481
- intro: "控制各模型是否出现在对话界面的模型选择菜单中。隐藏一个正在使用的模型不会中断该会话,也不会改动 Provider 配置。",
482
- loading: "正在读取模型目录…",
483
- loadFailed: "模型目录读取失败:{message}",
484
- retry: "重试",
485
- empty: "没有任何 Provider 提供模型。",
486
- allHiddenWarning: "所有模型都已隐藏,模型选择菜单将是空的。",
487
- readOnly: "设置文档不可写,开关已禁用。",
488
- visibleCount: "{visible} / {total} 可见",
489
- searchPlaceholder: "搜索模型…",
490
- noMatch: "没有匹配的模型。",
491
- hideAll: "全部隐藏",
492
- showAll: "全部显示",
493
- showAllProviders: "恢复全部",
494
- providerRow: "该渠道 {visible} / {total} 可见"
495
- };
496
- const en = {
497
- nav: "Model visibility",
498
- title: "Model visibility",
499
- intro: "Control which models appear in the conversation model selector. Hiding a model a session already uses never interrupts that session or touches your provider config.",
500
- loading: "Loading the model catalog…",
501
- loadFailed: "Failed to load the model catalog: {message}",
502
- retry: "Retry",
503
- empty: "No provider advertises any model.",
504
- allHiddenWarning: "Every model is hidden; the model selector will be empty.",
505
- readOnly: "The settings document is read-only; toggles are disabled.",
506
- visibleCount: "{visible} / {total} visible",
507
- searchPlaceholder: "Search models…",
508
- noMatch: "No model matches.",
509
- hideAll: "Hide all",
510
- showAll: "Show all",
511
- showAllProviders: "Reset all",
512
- providerRow: "{visible} / {total} visible in this provider"
451
+ }, model.id))
452
+ })]
453
+ }, group.id);
454
+ })
455
+ ]
456
+ });
457
+ }
458
+ function Switch(props) {
459
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
460
+ type: "button",
461
+ role: "switch",
462
+ "aria-checked": props.on,
463
+ "aria-label": props.label,
464
+ disabled: props.disabled,
465
+ onClick: () => {
466
+ props.onChange(!props.on);
467
+ },
468
+ style: {
469
+ width: 40,
470
+ height: 22,
471
+ borderRadius: 11,
472
+ border: "none",
473
+ padding: 2,
474
+ cursor: props.disabled ? "not-allowed" : "pointer",
475
+ background: props.on ? "#4c8bf5" : "rgba(128,128,128,.45)",
476
+ opacity: props.disabled ? .5 : 1,
477
+ display: "inline-flex",
478
+ justifyContent: props.on ? "flex-end" : "flex-start",
479
+ transition: "background .15s",
480
+ flex: "none"
481
+ },
482
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
483
+ width: 18,
484
+ height: 18,
485
+ borderRadius: "50%",
486
+ background: "#fff",
487
+ display: "block"
488
+ } })
489
+ });
490
+ }
491
+
492
+ //#endregion
493
+ //#region src/client/locales.ts
494
+ const LOCALE_NS = "settings.modelVisibility";
495
+ const zh = {
496
+ nav: "模型可见性",
497
+ title: "模型可见性",
498
+ intro: "控制各模型是否出现在对话界面的模型选择菜单中。隐藏一个正在使用的模型不会中断该会话,也不会改动 Provider 配置。",
499
+ loading: "正在读取模型目录…",
500
+ loadFailed: "模型目录读取失败:{message}",
501
+ retry: "重试",
502
+ unavailable: "此连接不共享宿主设置,模型可见性不可用。",
503
+ empty: "没有任何 Provider 提供模型。",
504
+ allHiddenWarning: "所有模型都已隐藏,模型选择菜单将是空的。",
505
+ readOnly: "设置文档不可写,开关已禁用。",
506
+ visibleCount: "{visible} / {total} 可见",
507
+ searchPlaceholder: "搜索模型…",
508
+ noMatch: "没有匹配的模型。",
509
+ hideAll: "全部隐藏",
510
+ showAll: "全部显示",
511
+ showAllProviders: "恢复全部",
512
+ providerRow: "该渠道 {visible} / {total} 可见"
513
+ };
514
+ const en = {
515
+ nav: "Model visibility",
516
+ title: "Model visibility",
517
+ intro: "Control which models appear in the conversation model selector. Hiding a model a session already uses never interrupts that session or touches your provider config.",
518
+ loading: "Loading the model catalog…",
519
+ loadFailed: "Failed to load the model catalog: {message}",
520
+ retry: "Retry",
521
+ unavailable: "Model visibility is unavailable: this connection does not share the Host settings.",
522
+ empty: "No provider advertises any model.",
523
+ allHiddenWarning: "Every model is hidden; the model selector will be empty.",
524
+ readOnly: "The settings document is read-only; toggles are disabled.",
525
+ visibleCount: "{visible} / {total} visible",
526
+ searchPlaceholder: "Search models…",
527
+ noMatch: "No model matches.",
528
+ hideAll: "Hide all",
529
+ showAll: "Show all",
530
+ showAllProviders: "Reset all",
531
+ providerRow: "{visible} / {total} visible in this provider"
532
+ };
533
+
534
+ //#endregion
535
+ //#region src/client/index.ts
536
+ const name = "dsh-model-visibility";
537
+ const inject = [
538
+ "slots",
539
+ "locale",
540
+ "configForms",
541
+ "remote",
542
+ "remote.session"
543
+ ];
544
+ function apply(ctx) {
545
+ ctx.effect(() => ctx.locale.register(LOCALE_NS, {
546
+ zh,
547
+ en
548
+ }), "model-visibility: copy dictionaries");
549
+ const form = ctx.configForms.get(MODEL_VISIBILITY_NS);
550
+ const catalog = { async read() {
551
+ const response = await ctx.remote.session.modelCatalog();
552
+ if (!response.ok) throw new Error(`${response.error.code}: ${response.error.message}`);
553
+ return response.value.groups;
554
+ } };
555
+ const controller = new ModelVisibilityCardController(form, catalog);
556
+ ctx.effect(() => () => {
557
+ controller.dispose();
558
+ }, "model-visibility: card controller");
559
+ ctx.effect(() => {
560
+ const offAdapters = ctx.remote.$on("llm/adapters-updated", () => {
561
+ controller.load();
562
+ });
563
+ const offSettings = ctx.remote.$on("settings/document-updated", (ns) => {
564
+ if (ns !== MODEL_VISIBILITY_NS) controller.load();
565
+ });
566
+ return () => {
567
+ offAdapters();
568
+ offSettings();
513
569
  };
514
- //#endregion
515
- //#region src/client/index.ts
516
- const name = "dsh-model-visibility";
517
- const inject = [
518
- "slots",
519
- "locale",
520
- "settingsScope",
521
- "remote",
522
- "remote.session"
523
- ];
524
- function apply(ctx) {
525
- ctx.locale.register(LOCALE_NS, {
526
- zh,
527
- en
528
- });
529
- const scope = ctx.settingsScope.bind({ namespace: MODEL_VISIBILITY_NS });
530
- const remote = ctx.remote;
531
- const controller = new ModelVisibilityCardController(scope, { async read() {
532
- const response = await remote.session.modelCatalog();
533
- if (!response.ok) throw new Error(`${response.error.code}: ${response.error.message}`);
534
- return response.value.groups;
535
- } });
536
- const t = ctx.locale.bind(LOCALE_NS);
537
- const injected = () => ({
538
- controller,
539
- t,
540
- hooks: { snapshot: controller.store }
541
- });
542
- ctx.slots.inject("settings.section", () => ctx.slots.register({
543
- name: "settings.section",
544
- id: "model-visibility",
545
- order: 11,
546
- label: () => t("nav"),
547
- inject: injected
548
- }, ModelVisibilityCard));
549
- }
550
- //#endregion
551
- exports.apply = apply;
552
- exports.inject = inject;
553
- exports.name = name;
554
- return module.exports;
555
- }
556
- });
570
+ }, "model-visibility: catalog invalidation");
571
+ const t = ctx.locale.bind(LOCALE_NS);
572
+ const injected = () => ({
573
+ controller,
574
+ t,
575
+ hooks: { snapshot: controller.store }
576
+ });
577
+ ctx.effect(() => ctx.configForms.whileServed([MODEL_VISIBILITY_NS], () => ctx.slots.inject("settings.section", () => ctx.slots.register({
578
+ name: "settings.section",
579
+ id: "model-visibility",
580
+ order: 11,
581
+ label: () => t("nav"),
582
+ inject: injected
583
+ }, ModelVisibilityCard))), "model-visibility: settings section");
584
+ }
557
585
 
586
+ //#endregion
587
+ exports.apply = apply;
588
+ exports.inject = inject;
589
+ exports.name = name;
590
+ return module.exports; } });
558
591
  //# sourceMappingURL=client.js.map