dsh-model-visibility 0.1.1 → 0.1.2
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/LICENSE +21 -21
- package/README.en.md +45 -45
- package/README.md +45 -45
- package/cordis.patch.yml +6 -6
- package/lib/client.js +34 -20
- package/lib/client.js.map +1 -1
- package/lib/index.js +12 -12
- package/lib/types/client/ModelVisibilityCard.d.ts +1 -1
- package/lib/types/client/controller.d.ts +40 -4
- package/lib/types/client/index.d.ts +3 -9
- package/lib/types/index.d.ts +6 -5
- package/lib/types/namespace.d.ts +7 -0
- package/package.json +67 -65
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Lzh3070
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lzh3070
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
# dsh-model-visibility
|
|
2
|
-
|
|
3
|
-
**English · [中文](README.md)**
|
|
4
|
-
|
|
5
|
-
Model visibility for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): control which models appear in the conversation model selector.
|
|
6
|
-
|
|
7
|
-
Installs a dedicated **Model visibility** settings section (right after 模型 / Models in the left nav):
|
|
8
|
-
|
|
9
|
-
- 🔍 **Search** — live filter by model name / id / provider
|
|
10
|
-
- 🎚️ **Per-model switches** — hide a model and it disappears from the selector instantly; flip back to restore
|
|
11
|
-
- 📦 **Provider-level batch** — hide or show every model of one provider in one click
|
|
12
|
-
- ♻️ **Reset all** — clear the hidden list
|
|
13
|
-
- 🛡️ **Zero intrusion** — writes only the plugin's own hidden list, never your provider config; hiding a model a session already uses never interrupts that session
|
|
14
|
-
- 🆕 **New models visible by default** — the plugin remembers what to hide, not what to show
|
|
15
|
-
|
|
16
|
-
## Preview
|
|
17
|
-
|
|
18
|
-

|
|
19
|
-
|
|
1
|
+
# dsh-model-visibility
|
|
2
|
+
|
|
3
|
+
**English · [中文](README.md)**
|
|
4
|
+
|
|
5
|
+
Model visibility for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): control which models appear in the conversation model selector.
|
|
6
|
+
|
|
7
|
+
Installs a dedicated **Model visibility** settings section (right after 模型 / Models in the left nav):
|
|
8
|
+
|
|
9
|
+
- 🔍 **Search** — live filter by model name / id / provider
|
|
10
|
+
- 🎚️ **Per-model switches** — hide a model and it disappears from the selector instantly; flip back to restore
|
|
11
|
+
- 📦 **Provider-level batch** — hide or show every model of one provider in one click
|
|
12
|
+
- ♻️ **Reset all** — clear the hidden list
|
|
13
|
+
- 🛡️ **Zero intrusion** — writes only the plugin's own hidden list, never your provider config; hiding a model a session already uses never interrupts that session
|
|
14
|
+
- 🆕 **New models visible by default** — the plugin remembers what to hide, not what to show
|
|
15
|
+
|
|
16
|
+
## Preview
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
20
|
## Requirements
|
|
21
21
|
|
|
22
|
-
- DeepSeek Harness ≥ 0.1.
|
|
22
|
+
- **DeepSeek Harness ≥ 0.1.5-rc.1** (0.1.5 rebuilt the client module system; use 0.1.1 on older releases)
|
|
23
23
|
- Node.js 22+
|
|
24
|
-
|
|
25
|
-
## Install
|
|
26
|
-
|
|
27
|
-
```sh
|
|
28
|
-
dsh plugin --profile web add dsh-model-visibility
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Restart `dsh web` to activate.
|
|
32
|
-
|
|
33
|
-
## How it works
|
|
34
|
-
|
|
35
|
-
- **Host half** (`lib/index.js`): owns the `model-visibility` settings section (the hidden list) and wraps `ctx.llm.listModels` at its source, so both
|
|
36
|
-
- **Browser half** (`lib/client.js`): registers a `settings.section` page; page state is the union of the filtered catalog and the hidden entries, each hidden entry labeled by the display-name snapshot captured when it was hidden.
|
|
37
|
-
|
|
38
|
-
## Uninstall
|
|
39
|
-
|
|
40
|
-
```sh
|
|
41
|
-
dsh plugin --profile web remove dsh-model-visibility
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Uninstalling restores everything: the plugin never writes provider config, and the hidden list dies with the package.
|
|
45
|
-
|
|
46
|
-
## License
|
|
47
|
-
|
|
48
|
-
MIT
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
dsh plugin --profile web add dsh-model-visibility
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Restart `dsh web` to activate.
|
|
32
|
+
|
|
33
|
+
## How it works
|
|
34
|
+
|
|
35
|
+
- **Host half** (`lib/index.js`): owns the `model-visibility` settings section through `ctx.settings.register` (the hidden list) and wraps `ctx.llm.listModels` at its source, so both the conversation selector and the settings page receive the filtered catalog. Catalog membership is advisory in the harness, so hiding a model a session already uses never breaks that session's dispatch.
|
|
36
|
+
- **Browser half** (`lib/client.js`): registers a `settings.section` page; the catalog arrives through 0.1.5's `ctx.remote.session.modelCatalog()`, and page state is the union of the filtered catalog and the hidden entries, each hidden entry labeled by the display-name snapshot captured when it was hidden.
|
|
37
|
+
|
|
38
|
+
## Uninstall
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
dsh plugin --profile web remove dsh-model-visibility
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Uninstalling restores everything: the plugin never writes provider config, and the hidden list dies with the package.
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
MIT
|
package/README.md
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
# dsh-model-visibility
|
|
2
|
-
|
|
3
|
-
**[English](README.en.md) · 中文**
|
|
4
|
-
|
|
5
|
-
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的模型可见性插件:控制哪些模型出现在对话界面的模型选择菜单里。
|
|
6
|
-
|
|
7
|
-
安装后在设置左侧导航提供一个独立的「**模型可见性**」分区页(紧跟「模型」):
|
|
8
|
-
|
|
9
|
-
- 🔍 **搜索** —— 按模型名/ID/渠道名实时过滤
|
|
10
|
-
- 🎚️ **逐模型开关** —— 隐藏的模型立即从选择器消失,显示即恢复
|
|
11
|
-
- 📦 **渠道级批量** —— 一键隐藏/显示某个 Provider 的全部模型
|
|
12
|
-
- ♻️ **一键恢复全部** —— 清空隐藏列表
|
|
13
|
-
- 🛡️ **零侵入** —— 只写插件自己的隐藏列表,不改你的 Provider 配置;隐藏正在使用的模型不会中断该会话
|
|
14
|
-
- 🆕 **新模型自动可见** —— 插件只记录"要隐藏什么",Provider 新广告的模型默认出现
|
|
15
|
-
|
|
16
|
-
## 预览
|
|
17
|
-
|
|
18
|
-

|
|
19
|
-
|
|
1
|
+
# dsh-model-visibility
|
|
2
|
+
|
|
3
|
+
**[English](README.en.md) · 中文**
|
|
4
|
+
|
|
5
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的模型可见性插件:控制哪些模型出现在对话界面的模型选择菜单里。
|
|
6
|
+
|
|
7
|
+
安装后在设置左侧导航提供一个独立的「**模型可见性**」分区页(紧跟「模型」):
|
|
8
|
+
|
|
9
|
+
- 🔍 **搜索** —— 按模型名/ID/渠道名实时过滤
|
|
10
|
+
- 🎚️ **逐模型开关** —— 隐藏的模型立即从选择器消失,显示即恢复
|
|
11
|
+
- 📦 **渠道级批量** —— 一键隐藏/显示某个 Provider 的全部模型
|
|
12
|
+
- ♻️ **一键恢复全部** —— 清空隐藏列表
|
|
13
|
+
- 🛡️ **零侵入** —— 只写插件自己的隐藏列表,不改你的 Provider 配置;隐藏正在使用的模型不会中断该会话
|
|
14
|
+
- 🆕 **新模型自动可见** —— 插件只记录"要隐藏什么",Provider 新广告的模型默认出现
|
|
15
|
+
|
|
16
|
+
## 预览
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
20
|
## 要求
|
|
21
21
|
|
|
22
|
-
- DeepSeek Harness ≥ 0.1.
|
|
22
|
+
- **DeepSeek Harness ≥ 0.1.5-rc.1**(0.1.5 重构了客户端模块体系,旧版本请使用 0.1.1)
|
|
23
23
|
- Node.js 22+(构建与运行)
|
|
24
|
-
|
|
25
|
-
## 安装
|
|
26
|
-
|
|
27
|
-
```sh
|
|
28
|
-
dsh plugin --profile web add dsh-model-visibility
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
重启 `dsh web` 后生效。
|
|
32
|
-
|
|
33
|
-
## 工作原理
|
|
34
|
-
|
|
35
|
-
- **宿主半**(`lib/index.js
|
|
36
|
-
- **浏览器半**(`lib/client.js`):向设置壳注册一个 `settings.section`
|
|
37
|
-
|
|
38
|
-
## 卸载
|
|
39
|
-
|
|
40
|
-
```sh
|
|
41
|
-
dsh plugin --profile web remove dsh-model-visibility
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
卸载即恢复原状:插件不写入 Provider 配置,隐藏列表随包删除自动失效。
|
|
45
|
-
|
|
46
|
-
## License
|
|
47
|
-
|
|
48
|
-
MIT
|
|
24
|
+
|
|
25
|
+
## 安装
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
dsh plugin --profile web add dsh-model-visibility
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
重启 `dsh web` 后生效。
|
|
32
|
+
|
|
33
|
+
## 工作原理
|
|
34
|
+
|
|
35
|
+
- **宿主半**(`lib/index.js`):通过 `ctx.settings.register` 持有 `model-visibility` 设置段(隐藏列表),并在源头包装 `ctx.llm.listModels`——会话选择器与设置页拿到的目录都是过滤后的。目录成员资格在 harness 中是"建议性"的,因此隐藏一个正在使用的模型不影响其调度。
|
|
36
|
+
- **浏览器半**(`lib/client.js`):向设置壳注册一个 `settings.section` 分区页;目录经 0.1.5 的 `ctx.remote.session.modelCatalog()` 读取,页面状态来自"过滤后目录 ∪ 隐藏条目"的并集,隐藏条目携带隐藏时抓取的显示名快照。
|
|
37
|
+
|
|
38
|
+
## 卸载
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
dsh plugin --profile web remove dsh-model-visibility
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
卸载即恢复原状:插件不写入 Provider 配置,隐藏列表随包删除自动失效。
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
MIT
|
package/cordis.patch.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Layer applied when a profile lists this bundle: mount the host half.
|
|
2
|
-
# The client half is served automatically by the client module system
|
|
3
|
-
# (the package declares dsh.client and exports ./client).
|
|
4
|
-
- insert:
|
|
5
|
-
- id: model-visibility
|
|
6
|
-
name: dsh-model-visibility
|
|
1
|
+
# Layer applied when a profile lists this bundle: mount the host half.
|
|
2
|
+
# The client half is served automatically by the client module system
|
|
3
|
+
# (the package declares dsh.client and exports ./client).
|
|
4
|
+
- insert:
|
|
5
|
+
- id: model-visibility
|
|
6
|
+
name: dsh-model-visibility
|
package/lib/client.js
CHANGED
|
@@ -4,9 +4,18 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
-
let
|
|
7
|
+
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
8
8
|
let react = require("react");
|
|
9
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
|
|
10
19
|
//#region src/client/controller.ts
|
|
11
20
|
/**
|
|
12
21
|
* The model-visibility card's controller: React-free state over two sources —
|
|
@@ -24,24 +33,17 @@ window.__ModuleLoader__.load({
|
|
|
24
33
|
total: 0,
|
|
25
34
|
visible: 0
|
|
26
35
|
};
|
|
27
|
-
/** Mint one wire id (the brand is client-side nominal typing only). */
|
|
28
|
-
function rpcRequest(payload) {
|
|
29
|
-
return {
|
|
30
|
-
rpcId: crypto.randomUUID(),
|
|
31
|
-
payload
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
36
|
var ModelVisibilityCardController = class {
|
|
35
37
|
scope;
|
|
36
|
-
|
|
38
|
+
catalogReader;
|
|
37
39
|
/** Exposed: the section contribution passes the store through its face as `hooks.snapshot`. */
|
|
38
|
-
store = (0,
|
|
40
|
+
store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(EMPTY);
|
|
39
41
|
catalog;
|
|
40
42
|
loadError;
|
|
41
43
|
generation = 0;
|
|
42
|
-
constructor(scope,
|
|
44
|
+
constructor(scope, catalogReader) {
|
|
43
45
|
this.scope = scope;
|
|
44
|
-
this.
|
|
46
|
+
this.catalogReader = catalogReader;
|
|
45
47
|
scope.subscribe(() => {
|
|
46
48
|
this.recompute();
|
|
47
49
|
});
|
|
@@ -72,12 +74,17 @@ window.__ModuleLoader__.load({
|
|
|
72
74
|
draft.phase = "loading";
|
|
73
75
|
});
|
|
74
76
|
try {
|
|
75
|
-
const
|
|
77
|
+
const groups = await this.catalogReader.read();
|
|
76
78
|
if (generation !== this.generation) return;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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;
|
|
81
88
|
} catch (error) {
|
|
82
89
|
if (generation !== this.generation) return;
|
|
83
90
|
this.loadError = error instanceof Error ? error.message : String(error);
|
|
@@ -510,15 +517,22 @@ window.__ModuleLoader__.load({
|
|
|
510
517
|
const inject = [
|
|
511
518
|
"slots",
|
|
512
519
|
"locale",
|
|
513
|
-
"
|
|
514
|
-
"
|
|
520
|
+
"settingsScope",
|
|
521
|
+
"remote",
|
|
522
|
+
"remote.session"
|
|
515
523
|
];
|
|
516
524
|
function apply(ctx) {
|
|
517
525
|
ctx.locale.register(LOCALE_NS, {
|
|
518
526
|
zh,
|
|
519
527
|
en
|
|
520
528
|
});
|
|
521
|
-
const
|
|
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
|
+
} });
|
|
522
536
|
const t = ctx.locale.bind(LOCALE_NS);
|
|
523
537
|
const injected = () => ({
|
|
524
538
|
controller,
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["createSnapshotStore","useState","useMemo"],"sources":["../src/client/controller.ts","../src/client/ModelVisibilityCard.tsx","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/**\n * The model-visibility card's controller: React-free state over two sources —\n * the (already filtered) Host model catalog and the `model-visibility`\n * settings scope. The rendered list is their union: catalog rows are visible\n * models; hidden entries restate the models the filter removed, labeled by\n * the name snapshot each entry carries. Beyond the reference shape, the\n * controller exposes provider-level batch writes and a whole-list write so\n * the card can offer search + bulk actions.\n */\nimport { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { IApiClient, ModelProviderGroup, RpcRequest } from '@deepseek-ai/dsh-client-connection/client'\nimport type { Config, HiddenModel } from '../index.ts'\n\n/** One toggle row. */\nexport interface ModelRow {\n id: string\n name: string\n hidden: boolean\n}\n\n/** One provider group of rows. */\nexport interface ProviderGroupRow {\n id: string\n name: string\n models: ModelRow[]\n}\n\n/** What the card renders. */\nexport interface ModelVisibilityState {\n phase: 'loading' | 'ready' | 'error'\n error?: string\n /** Whether the Host settings document accepts writes. */\n writable: boolean\n groups: ProviderGroupRow[]\n /** Union count and how many of them remain visible. */\n total: number\n visible: number\n}\n\n/** The face the slot registration injects into the card component. */\nexport interface ModelVisibilityFace {\n hooks: {\n /** Card snapshot bound by the renderer as useModelVisibility. */\n modelVisibility: SnapshotStore<ModelVisibilityState>\n }\n /** Flip one model's visibility. */\n toggle: (provider: string, model: string, name: string, hidden: boolean) => void\n /** Hide or show every model of one provider. */\n toggleProvider: (provider: string, hidden: boolean) => void\n /** Clear the hidden list entirely (everything visible again). */\n resetAll: () => void\n /** Re-read the catalog after a failure. */\n reload: () => void\n}\n\nconst EMPTY: ModelVisibilityState = { phase: 'loading', writable: false, groups: [], total: 0, visible: 0 }\n\n/** Mint one wire id (the brand is client-side nominal typing only). */\nfunction rpcRequest<P>(payload: P): RpcRequest<P> {\n return { rpcId: crypto.randomUUID(), payload } as RpcRequest<P>\n}\n\nexport class ModelVisibilityCardController {\n /** Exposed: the section contribution passes the store through its face as `hooks.snapshot`. */\n readonly store = createSnapshotStore<ModelVisibilityState>(EMPTY)\n private catalog: ModelProviderGroup[] | undefined\n private loadError: string | undefined\n private generation = 0\n\n constructor(\n private readonly scope: SettingsScope<Config>,\n private readonly api: Pick<IApiClient, 'llm'>,\n ) {\n scope.subscribe(() => { this.recompute() })\n void this.load()\n }\n\n /** The registration-side inject face. */\n face(): ModelVisibilityFace {\n return {\n hooks: { modelVisibility: this.store },\n toggle: (provider, model, name, hidden) => { void this.toggle(provider, model, name, hidden) },\n toggleProvider: (provider, hidden) => { void this.toggleProvider(provider, hidden) },\n resetAll: () => { void this.resetAll() },\n reload: () => { void this.load() },\n }\n }\n\n /** Re-read the catalog; an older response never overwrites a newer one. */\n async load(): Promise<void> {\n const generation = ++this.generation\n if (this.catalog === undefined) this.store.update((draft) => { draft.phase = 'loading' })\n try {\n const response = await this.api.llm.models(rpcRequest({}))\n if (generation !== this.generation) return\n if (response.result.ok) {\n this.catalog = response.result.value.groups\n this.loadError = undefined\n } else {\n this.loadError = response.result.error.message\n }\n } catch (error: unknown) {\n if (generation !== this.generation) return\n this.loadError = error instanceof Error ? error.message : String(error)\n }\n this.recompute()\n }\n\n /** Compute the next hidden list from the current one plus one model's flip. */\n private nextHidden(current: HiddenModel[], provider: string, model: string, name: string, hidden: boolean): HiddenModel[] {\n const rest = current.filter(entry => !(entry.provider === provider && entry.model === model))\n return hidden ? [...rest, { provider, model, name }] : rest\n }\n\n /** Write the next hidden list, then re-pull the catalog the filter changed. */\n private async write(next: HiddenModel[]): Promise<void> {\n try {\n await this.scope.set('hidden', next)\n } catch {\n // The scope already reloads Host state after a failed latest write;\n // recompute below re-renders whatever the Host now reports.\n }\n await this.load()\n }\n\n /** Flip one model's visibility (public API — the section card calls it directly). */\n async toggle(provider: string, model: string, name: string, hidden: boolean): Promise<void> {\n const current = this.scope.getSnapshot().value?.hidden ?? []\n await this.write(this.nextHidden(current, provider, model, name, hidden))\n }\n\n /**\n * Batch flip for one provider. Models are taken from the union of the\n * (filtered) catalog rows and existing hidden entries so a provider with\n * every model already hidden still flips back fully.\n */\n /** Batch flip for one provider (public API). */\n async toggleProvider(provider: string, hidden: boolean): Promise<void> {\n const current = this.scope.getSnapshot().value?.hidden ?? []\n const providerRows = this.stateRows().find(group => group.id === provider)\n const ids = new Set<string>([\n ...providerRows ? providerRows.models.map(model => model.id) : [],\n ...current.filter(entry => entry.provider === provider).map(entry => entry.model),\n ])\n if (ids.size === 0) return\n let next = current.filter(entry => entry.provider !== provider)\n if (hidden) {\n const nameOf = (id: string): string => providerRows?.models.find(model => model.id === id)?.name ?? id\n next = [...next, ...[...ids].map(id => ({ provider, model: id, name: nameOf(id) }))]\n }\n await this.write(next)\n }\n\n /** Clear the hidden list entirely (public API). */\n async resetAll(): Promise<void> {\n await this.write([])\n }\n\n /** Current union rows (catalog ∪ hidden) without touching the store. */\n private stateRows(): ProviderGroupRow[] {\n const snapshot = this.scope.getSnapshot()\n const hidden = snapshot.value?.hidden ?? []\n if (this.catalog === undefined) return []\n const hiddenKeys = new Set(hidden.map(entry => `${entry.provider}\\u0000${entry.model}`))\n const groups: ProviderGroupRow[] = this.catalog.map(group => ({\n id: group.id,\n name: group.name,\n models: group.models.map(model => ({\n id: model.id,\n name: model.name,\n hidden: hiddenKeys.has(`${group.id}\\u0000${model.id}`),\n })),\n }))\n const groupsById = new Map(groups.map(group => [group.id, group]))\n for (const entry of hidden) {\n const group = groupsById.get(entry.provider)\n if (group?.models.some(model => model.id === entry.model)) continue\n const row: ModelRow = { id: entry.model, name: entry.name ?? entry.model, hidden: true }\n if (group !== undefined) {\n group.models.push(row)\n } else {\n const created: ProviderGroupRow = { id: entry.provider, name: entry.provider, models: [row] }\n groupsById.set(entry.provider, created)\n groups.push(created)\n }\n }\n return groups\n }\n\n /** Union the filtered catalog with the hidden entries into render rows. */\n private recompute(): void {\n const snapshot = this.scope.getSnapshot()\n if (this.catalog === undefined && this.loadError !== undefined) {\n this.store.set({ ...EMPTY, phase: 'error', error: this.loadError, writable: snapshot.writable })\n return\n }\n if (this.catalog === undefined || snapshot.status === 'loading') {\n this.store.set({ ...EMPTY, writable: snapshot.writable })\n return\n }\n const groups = this.stateRows()\n const total = groups.reduce((count, group) => count + group.models.length, 0)\n const visible = groups.reduce(\n (count, group) => count + group.models.filter(model => !model.hidden).length, 0)\n this.store.set({\n phase: 'ready',\n writable: snapshot.writable,\n groups,\n total,\n visible,\n })\n }\n}\n","/**\n * The model-visibility settings section, styled to mirror the official\n * settings pages: big page title + muted intro, a controls row, then one\n * bordered rounded card per provider (like the provider cards on the\n * 模型 page) with pill action buttons and token-style switches.\n *\n * Consumes the `settings.section` contribution contract exactly like the\n * official ModelsSection: props = spread inject face, where `hooks.snapshot`\n * arrives as the `useSnapshot` selector hook; all three seats are undefined\n * while the shell has not injected yet (render null then).\n */\nimport { useMemo, useState } from 'react'\nimport type { CSSProperties } from 'react'\nimport type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { ModelVisibilityCardController, ModelVisibilityState } from './controller.ts'\nimport type {} from './locales.ts'\n\n/** Composed props: spread face fields + the `useSnapshot` hook from hooks.snapshot. */\nexport interface ModelVisibilityCardProps {\n controller: ModelVisibilityCardController | undefined\n useSnapshot: SnapshotSelectorHook<ModelVisibilityState> | undefined\n t: TSeat | undefined\n}\n\n/** Localized text seat bound to this card's namespace. */\ntype TSeat = (key: string, params?: Record<string, unknown>) => string\n\nconst STYLE_ID = 'dsh-model-visibility-styles'\n\nconst STYLES = `\n.dsh-mvw { display:flex; flex-direction:column; gap:16px; }\n.dsh-mvw .mvw-title { font-size:20px; font-weight:700; letter-spacing:.01em; }\n.dsh-mvw .mvw-sub { font-size:13px; color:inherit; opacity:.62; margin-top:6px; max-width:64ch; line-height:1.55; }\n.dsh-mvw .mvw-controls { display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-top:4px; }\n.dsh-mvw .mvw-search {\n flex:1 1 220px; min-width:160px; height:38px; padding:0 14px;\n font-size:13px; border-radius:10px;\n border:1px solid rgba(128,128,128,.38);\n background:transparent; color:inherit; outline:none;\n}\n.dsh-mvw .mvw-search:focus { border-color: rgba(76,139,245,.85); }\n.dsh-mvw .mvw-pill {\n height:36px; padding:0 16px; font-size:13px; cursor:pointer;\n border-radius:999px; border:1px solid rgba(128,128,128,.38);\n background:transparent; color:inherit;\n}\n.dsh-mvw .mvw-pill:hover:not(:disabled) { background:rgba(128,128,128,.08); }\n.dsh-mvw .mvw-pill:disabled { opacity:.45; cursor:not-allowed; }\n.dsh-mvw .mvw-count { font-size:12.5px; opacity:.62; margin-left:auto; }\n.dsh-mvw .mvw-card {\n border:1px solid rgba(128,128,128,.30); border-radius:16px;\n padding:6px 18px 2px;\n}\n.dsh-mvw .mvw-prov-head {\n display:flex; align-items:center; justify-content:space-between; gap:10px;\n padding:12px 0 10px;\n}\n.dsh-mvw .mvw-prov-name { font-size:15px; font-weight:650; }\n.dsh-mvw .mvw-prov-count { font-size:12px; opacity:.55; margin-left:10px; font-weight:400; }\n.dsh-mvw .mvw-prov-btns { display:flex; gap:8px; }\n.dsh-mvw .mvw-mini {\n height:30px; padding:0 13px; font-size:12.5px; cursor:pointer;\n border-radius:999px; border:1px solid rgba(128,128,128,.38);\n background:transparent; color:inherit;\n}\n.dsh-mvw .mvw-mini:hover:not(:disabled) { background:rgba(128,128,128,.08); }\n.dsh-mvw .mvw-mini:disabled { opacity:.4; cursor:not-allowed; }\n.dsh-mvw .mvw-row {\n display:flex; align-items:center; justify-content:space-between; gap:12px;\n min-height:46px; border-top:1px solid rgba(128,128,128,.16);\n}\n.dsh-mvw .mvw-row:first-of-type { border-top:none; }\n.dsh-mvw .mvw-name {\n font-size:14px;\n min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;\n}\n.dsh-mvw .mvw-id {\n opacity:.5; margin-left:8px; padding-right:6px; font-size:12px;\n}\n.dsh-mvw .mvw-note { font-size:12.5px; opacity:.66; }\n.dsh-mvw .mvw-warn { font-size:12.5px; color:#c77700; }\n.dsh-mvw .mvw-seats-off { font-size:12.5px; opacity:.66; }\n`\n\nfunction ensureStyles(): void {\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID)) return\n const el = document.createElement('style')\n el.id = STYLE_ID\n el.textContent = STYLES\n document.head.appendChild(el)\n}\n\nexport function ModelVisibilityCard(props: ModelVisibilityCardProps) {\n const { controller, useSnapshot, t } = props\n\n // Hooks stay unconditional regardless of injection timing.\n const state = (useSnapshot ?? ((_selector: (s: ModelVisibilityState) => unknown) => undefined))(\n (snapshot: ModelVisibilityState) => snapshot,\n ) as ModelVisibilityState | undefined\n const [query, setQuery] = useState('')\n\n // Styles must exist before first paint of this subtree; guarded, so idempotent.\n useMemo(() => { ensureStyles() }, [])\n\n const filteredGroups = useMemo(() => {\n const q = query.trim().toLowerCase()\n if (q === '') return state?.groups ?? []\n return (state?.groups ?? [])\n .map(group => ({\n ...group,\n models: group.models.filter(model =>\n model.id.toLowerCase().includes(q) || model.name.toLowerCase().includes(q) || group.id.toLowerCase().includes(q)),\n }))\n .filter(group => group.models.length > 0)\n }, [state, query])\n\n if (controller === undefined || useSnapshot === undefined || t === undefined) return null\n if (state === undefined) {\n return (\n <section className=\"dsh-mvw\">\n <div className=\"mvw-note\">{t('loading')}</div>\n </section>\n )\n }\n\n return (\n <section className=\"dsh-mvw\">\n <div>\n <div className=\"mvw-title\">{t('title')}</div>\n <p className=\"mvw-sub\">{t('intro')}</p>\n </div>\n\n <div className=\"mvw-controls\">\n <input\n type=\"search\"\n className=\"mvw-search\"\n value={query}\n placeholder={t('searchPlaceholder')}\n onChange={event => { setQuery(event.target.value) }}\n />\n <button\n type=\"button\"\n className=\"mvw-pill\"\n disabled={!state.writable || state.total === state.visible}\n onClick={() => { controller.resetAll() }}\n >\n {t('showAllProviders')}\n </button>\n <span className=\"mvw-count\">{t('visibleCount', { visible: state.visible, total: state.total })}</span>\n </div>\n\n {state.phase === 'error' && (\n <div className=\"mvw-warn\">\n {t('loadFailed', { message: state.error ?? '' })}{' '}\n <button type=\"button\" className=\"mvw-mini\" onClick={() => { controller.load() }}>{t('retry')}</button>\n </div>\n )}\n\n {state.phase === 'ready' && !state.writable && <div className=\"mvw-note\">{t('readOnly')}</div>}\n {state.phase === 'ready' && state.groups.length === 0 && <div className=\"mvw-note\">{t('empty')}</div>}\n {state.phase === 'ready' && state.total > 0 && state.visible === 0 && (\n <div className=\"mvw-warn\">{t('allHiddenWarning')}</div>\n )}\n {state.phase === 'ready' && filteredGroups.length === 0 && state.groups.length > 0 && (\n <div className=\"mvw-note\">{t('noMatch')}</div>\n )}\n\n {state.phase === 'ready' && filteredGroups.map(group => {\n const groupVisible = group.models.filter(model => !model.hidden).length\n return (\n <div key={group.id} className=\"mvw-card\">\n <div className=\"mvw-prov-head\">\n <div>\n <span className=\"mvw-prov-name\">{group.name}</span>\n <span className=\"mvw-prov-count\">\n {t('providerRow', { visible: groupVisible, total: group.models.length })}\n </span>\n </div>\n <div className=\"mvw-prov-btns\">\n <button\n type=\"button\"\n className=\"mvw-mini\"\n disabled={!state.writable || groupVisible === 0}\n onClick={() => { controller.toggleProvider(group.id, true) }}\n >\n {t('hideAll')}\n </button>\n <button\n type=\"button\"\n className=\"mvw-mini\"\n disabled={!state.writable || groupVisible === group.models.length}\n onClick={() => { controller.toggleProvider(group.id, false) }}\n >\n {t('showAll')}\n </button>\n </div>\n </div>\n <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>\n {group.models.map(model => (\n <li key={model.id} className=\"mvw-row\">\n <span className=\"mvw-name\">\n {model.name}\n {model.name !== model.id && <span className=\"mvw-id\">{model.id}</span>}\n </span>\n <Switch\n on={!model.hidden}\n disabled={!state.writable}\n label={model.name}\n onChange={visible => { controller.toggle(group.id, model.id, model.name, !visible) }}\n />\n </li>\n ))}\n </ul>\n </div>\n )\n })}\n </section>\n )\n}\n\nfunction Switch(props: { on: boolean; disabled: boolean; label: string; onChange: (next: boolean) => void }) {\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={props.on}\n aria-label={props.label}\n disabled={props.disabled}\n onClick={() => { props.onChange(!props.on) }}\n style={{\n width: 40,\n height: 22,\n borderRadius: 11,\n border: 'none',\n padding: 2,\n cursor: props.disabled ? 'not-allowed' : 'pointer',\n background: props.on ? '#4c8bf5' : 'rgba(128,128,128,.45)',\n opacity: props.disabled ? 0.5 : 1,\n display: 'inline-flex',\n justifyContent: props.on ? 'flex-end' : 'flex-start',\n transition: 'background .15s',\n flex: 'none',\n }}\n >\n <span style={{ width: 18, height: 18, borderRadius: '50%', background: '#fff', display: 'block' }} />\n </button>\n )\n}\n","/**\n * Locale dictionaries for the model-visibility settings card.\n * The namespace is merged into the slot system's LocaleNamespaceMap so the\n * register site can declare `locale:` and receive the typed `t` seat.\n */\n\n/** Dictionary keys of this card's copy. */\nexport type ModelVisibilityLocaleKey =\n | 'nav'\n | 'title'\n | 'intro'\n | 'loading'\n | 'loadFailed'\n | 'retry'\n | 'empty'\n | 'allHiddenWarning'\n | 'readOnly'\n | 'visibleCount'\n | 'searchPlaceholder'\n | 'noMatch'\n | 'hideAll'\n | 'showAll'\n | 'showAllProviders'\n | 'providerRow'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** The model-visibility card's copy. */\n 'settings.modelVisibility': ModelVisibilityLocaleKey\n }\n}\n\nexport const LOCALE_NS = 'settings.modelVisibility' as const\n\nexport const zh: Record<ModelVisibilityLocaleKey, string> = {\n nav: '模型可见性',\n title: '模型可见性',\n intro: '控制各模型是否出现在对话界面的模型选择菜单中。隐藏一个正在使用的模型不会中断该会话,也不会改动 Provider 配置。',\n loading: '正在读取模型目录…',\n loadFailed: '模型目录读取失败:{message}',\n retry: '重试',\n empty: '没有任何 Provider 提供模型。',\n allHiddenWarning: '所有模型都已隐藏,模型选择菜单将是空的。',\n readOnly: '设置文档不可写,开关已禁用。',\n visibleCount: '{visible} / {total} 可见',\n searchPlaceholder: '搜索模型…',\n noMatch: '没有匹配的模型。',\n hideAll: '全部隐藏',\n showAll: '全部显示',\n showAllProviders: '恢复全部',\n providerRow: '该渠道 {visible} / {total} 可见',\n}\n\nexport const en: Record<ModelVisibilityLocaleKey, string> = {\n nav: 'Model visibility',\n title: 'Model visibility',\n 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.',\n loading: 'Loading the model catalog…',\n loadFailed: 'Failed to load the model catalog: {message}',\n retry: 'Retry',\n empty: 'No provider advertises any model.',\n allHiddenWarning: 'Every model is hidden; the model selector will be empty.',\n readOnly: 'The settings document is read-only; toggles are disabled.',\n visibleCount: '{visible} / {total} visible',\n searchPlaceholder: 'Search models…',\n noMatch: 'No model matches.',\n hideAll: 'Hide all',\n showAll: 'Show all',\n showAllProviders: 'Reset all',\n providerRow: '{visible} / {total} visible in this provider',\n}\n","/**\n * dsh-model-visibility — browser half.\n *\n * Registers the model-visibility card into the plugin settings section\n * (`settings.plugin.item`) and its locale dictionaries. Cross-plugin\n * collaboration goes through cordis services only: the slot key's\n * declaration, the settings scope, and the wire handle arrive as type-only\n * merges, never value imports (the loader bundle-purity rule).\n */\nimport type { Context } from '@deepseek-ai/cordis'\n// Type-only merges: ctx.settingsScope, ctx.locale, ctx.slots, and the\n// `settings.plugin.item` slot declaration.\nimport type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-runtime/client'\nimport type { Config } from '../index.ts'\nimport { ModelVisibilityCardController } from './controller.ts'\nimport { ModelVisibilityCard } from './ModelVisibilityCard.tsx'\nimport { LOCALE_NS, en, zh } from './locales.ts'\n\n// The published /client types omit the browser-side merge for\n// `ctx.connection` (the ConnectionHandle the connection plugin provides);\n// restate it locally until the package ships it.\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n connection: ConnectionHandle\n }\n}\n\nexport const name = 'dsh-model-visibility'\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope'] as const\n\nexport function apply(ctx: Context): void {\n ctx.locale.register(LOCALE_NS, { zh, en })\n\n const scope = ctx.settingsScope.bind<Config>({ namespace: 'model-visibility' })\n const controller = new ModelVisibilityCardController(scope, ctx.connection.api)\n // The section card consumes the locale seat through the inject face\n // (mirroring the official Models page, whose face carries `t`).\n const t = ctx.locale.bind(LOCALE_NS)\n\n // Register as a top-level settings SECTION (nav entry 「模型可见性」, ordered\n // right after the official 模型 page at order=10) instead of a tab inside\n // the Plugins section — the card is a whole-page surface (search + batch\n // controls read better with room to breathe). The label thunk re-binds the\n // locale each call so a host language switch shows fresh text.\n //\n // Face contract for `settings.section` contributions (ui-slots): every\n // field of the injected object spreads onto the component as props, and\n // each `hooks` entry becomes a `use<Name>` snapshot-selector hook. The\n // component destructures { controller, useSnapshot, t } exactly like the\n // official ModelsSection does.\n const injected = () => ({\n controller,\n t,\n hooks: { snapshot: controller.store },\n })\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'model-visibility',\n order: 11,\n label: () => t('nav'),\n inject: injected,\n } as never, ModelVisibilityCard as never))\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;EAwDA,MAAM,QAA8B;GAAE,OAAO;GAAW,UAAU;GAAO,QAAQ,CAAC;GAAG,OAAO;GAAG,SAAS;EAAE;;EAG1G,SAAS,WAAc,SAA2B;GAChD,OAAO;IAAE,OAAO,OAAO,WAAW;IAAG;GAAQ;EAC/C;EAEA,IAAa,gCAAb,MAA2C;GAQtB;GACA;;GAPnB,SAAS,GAAQA,uCAAAA,oBAAAA,CAA0C,KAAK;GAChE;GACA;GACA,aAAqB;GAErB,YACE,OACA,KACA;IAFiB,KAAA,QAAA;IACA,KAAA,MAAA;IAEjB,MAAM,gBAAgB;KAAE,KAAK,UAAU;IAAE,CAAC;IAC1C,KAAU,KAAK;GACjB;;GAGA,OAA4B;IAC1B,OAAO;KACL,OAAO,EAAE,iBAAiB,KAAK,MAAM;KACrC,SAAS,UAAU,OAAO,MAAM,WAAW;MAAE,KAAU,OAAO,UAAU,OAAO,MAAM,MAAM;KAAE;KAC7F,iBAAiB,UAAU,WAAW;MAAE,KAAU,eAAe,UAAU,MAAM;KAAE;KACnF,gBAAgB;MAAE,KAAU,SAAS;KAAE;KACvC,cAAc;MAAE,KAAU,KAAK;KAAE;IACnC;GACF;;GAGA,MAAM,OAAsB;IAC1B,MAAM,aAAa,EAAE,KAAK;IAC1B,IAAI,KAAK,YAAY,KAAA,GAAW,KAAK,MAAM,QAAQ,UAAU;KAAE,MAAM,QAAQ;IAAU,CAAC;IACxF,IAAI;KACF,MAAM,WAAW,MAAM,KAAK,IAAI,IAAI,OAAO,WAAW,CAAC,CAAC,CAAC;KACzD,IAAI,eAAe,KAAK,YAAY;KACpC,IAAI,SAAS,OAAO,IAAI;MACtB,KAAK,UAAU,SAAS,OAAO,MAAM;MACrC,KAAK,YAAY,KAAA;KACnB,OACE,KAAK,YAAY,SAAS,OAAO,MAAM;IAE3C,SAAS,OAAgB;KACvB,IAAI,eAAe,KAAK,YAAY;KACpC,KAAK,YAAY,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IACxE;IACA,KAAK,UAAU;GACjB;;GAGA,WAAmB,SAAwB,UAAkB,OAAe,MAAc,QAAgC;IACxH,MAAM,OAAO,QAAQ,QAAO,UAAS,EAAE,MAAM,aAAa,YAAY,MAAM,UAAU,MAAM;IAC5F,OAAO,SAAS,CAAC,GAAG,MAAM;KAAE;KAAU;KAAO;IAAK,CAAC,IAAI;GACzD;;GAGA,MAAc,MAAM,MAAoC;IACtD,IAAI;KACF,MAAM,KAAK,MAAM,IAAI,UAAU,IAAI;IACrC,QAAQ,CAGR;IACA,MAAM,KAAK,KAAK;GAClB;;GAGA,MAAM,OAAO,UAAkB,OAAe,MAAc,QAAgC;IAC1F,MAAM,UAAU,KAAK,MAAM,YAAY,CAAC,CAAC,OAAO,UAAU,CAAC;IAC3D,MAAM,KAAK,MAAM,KAAK,WAAW,SAAS,UAAU,OAAO,MAAM,MAAM,CAAC;GAC1E;;;;;;;GAQA,MAAM,eAAe,UAAkB,QAAgC;IACrE,MAAM,UAAU,KAAK,MAAM,YAAY,CAAC,CAAC,OAAO,UAAU,CAAC;IAC3D,MAAM,eAAe,KAAK,UAAU,CAAC,CAAC,MAAK,UAAS,MAAM,OAAO,QAAQ;IACzE,MAAM,sBAAM,IAAI,IAAY,CAC1B,GAAG,eAAe,aAAa,OAAO,KAAI,UAAS,MAAM,EAAE,IAAI,CAAC,GAChE,GAAG,QAAQ,QAAO,UAAS,MAAM,aAAa,QAAQ,CAAC,CAAC,KAAI,UAAS,MAAM,KAAK,CAClF,CAAC;IACD,IAAI,IAAI,SAAS,GAAG;IACpB,IAAI,OAAO,QAAQ,QAAO,UAAS,MAAM,aAAa,QAAQ;IAC9D,IAAI,QAAQ;KACV,MAAM,UAAU,OAAuB,cAAc,OAAO,MAAK,UAAS,MAAM,OAAO,EAAE,CAAC,EAAE,QAAQ;KACpG,OAAO,CAAC,GAAG,MAAM,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,KAAI,QAAO;MAAE;MAAU,OAAO;MAAI,MAAM,OAAO,EAAE;KAAE,EAAE,CAAC;IACrF;IACA,MAAM,KAAK,MAAM,IAAI;GACvB;;GAGA,MAAM,WAA0B;IAC9B,MAAM,KAAK,MAAM,CAAC,CAAC;GACrB;;GAGA,YAAwC;IAEtC,MAAM,SADW,KAAK,MAAM,YACN,CAAC,CAAC,OAAO,UAAU,CAAC;IAC1C,IAAI,KAAK,YAAY,KAAA,GAAW,OAAO,CAAC;IACxC,MAAM,aAAa,IAAI,IAAI,OAAO,KAAI,UAAS,GAAG,MAAM,SAAS,QAAQ,MAAM,OAAO,CAAC;IACvF,MAAM,SAA6B,KAAK,QAAQ,KAAI,WAAU;KAC5D,IAAI,MAAM;KACV,MAAM,MAAM;KACZ,QAAQ,MAAM,OAAO,KAAI,WAAU;MACjC,IAAI,MAAM;MACV,MAAM,MAAM;MACZ,QAAQ,WAAW,IAAI,GAAG,MAAM,GAAG,QAAQ,MAAM,IAAI;KACvD,EAAE;IACJ,EAAE;IACF,MAAM,aAAa,IAAI,IAAI,OAAO,KAAI,UAAS,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;IACjE,KAAK,MAAM,SAAS,QAAQ;KAC1B,MAAM,QAAQ,WAAW,IAAI,MAAM,QAAQ;KAC3C,IAAI,OAAO,OAAO,MAAK,UAAS,MAAM,OAAO,MAAM,KAAK,GAAG;KAC3D,MAAM,MAAgB;MAAE,IAAI,MAAM;MAAO,MAAM,MAAM,QAAQ,MAAM;MAAO,QAAQ;KAAK;KACvF,IAAI,UAAU,KAAA,GACZ,MAAM,OAAO,KAAK,GAAG;UAChB;MACL,MAAM,UAA4B;OAAE,IAAI,MAAM;OAAU,MAAM,MAAM;OAAU,QAAQ,CAAC,GAAG;MAAE;MAC5F,WAAW,IAAI,MAAM,UAAU,OAAO;MACtC,OAAO,KAAK,OAAO;KACrB;IACF;IACA,OAAO;GACT;;GAGA,YAA0B;IACxB,MAAM,WAAW,KAAK,MAAM,YAAY;IACxC,IAAI,KAAK,YAAY,KAAA,KAAa,KAAK,cAAc,KAAA,GAAW;KAC9D,KAAK,MAAM,IAAI;MAAE,GAAG;MAAO,OAAO;MAAS,OAAO,KAAK;MAAW,UAAU,SAAS;KAAS,CAAC;KAC/F;IACF;IACA,IAAI,KAAK,YAAY,KAAA,KAAa,SAAS,WAAW,WAAW;KAC/D,KAAK,MAAM,IAAI;MAAE,GAAG;MAAO,UAAU,SAAS;KAAS,CAAC;KACxD;IACF;IACA,MAAM,SAAS,KAAK,UAAU;IAC9B,MAAM,QAAQ,OAAO,QAAQ,OAAO,UAAU,QAAQ,MAAM,OAAO,QAAQ,CAAC;IAC5E,MAAM,UAAU,OAAO,QACpB,OAAO,UAAU,QAAQ,MAAM,OAAO,QAAO,UAAS,CAAC,MAAM,MAAM,CAAC,CAAC,QAAQ,CAAC;IACjF,KAAK,MAAM,IAAI;KACb,OAAO;KACP,UAAU,SAAS;KACnB;KACA;KACA;IACF,CAAC;GACH;EACF;;;;;;;;;;;;;;EC1LA,MAAM,WAAW;EAEjB,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuDf,SAAS,eAAqB;GAC5B,IAAI,OAAO,aAAa,aAAa;GACrC,IAAI,SAAS,eAAe,QAAQ,GAAG;GACvC,MAAM,KAAK,SAAS,cAAc,OAAO;GACzC,GAAG,KAAK;GACR,GAAG,cAAc;GACjB,SAAS,KAAK,YAAY,EAAE;EAC9B;EAEA,SAAgB,oBAAoB,OAAiC;GACnE,MAAM,EAAE,YAAY,aAAa,MAAM;GAGvC,MAAM,SAAS,iBAAiB,cAAoD,KAAA,GAAA,EACjF,aAAmC,QACtC;GACA,MAAM,CAAC,OAAO,aAAA,GAAYC,MAAAA,SAAAA,CAAS,EAAE;GAGrC,CAAA,GAAA,MAAA,QAAA,OAAc;IAAE,aAAa;GAAE,GAAG,CAAC,CAAC;GAEpC,MAAM,kBAAA,GAAiBC,MAAAA,QAAAA,OAAc;IACnC,MAAM,IAAI,MAAM,KAAK,CAAC,CAAC,YAAY;IACnC,IAAI,MAAM,IAAI,OAAO,OAAO,UAAU,CAAC;IACvC,QAAQ,OAAO,UAAU,CAAC,EAAA,CACvB,KAAI,WAAU;KACb,GAAG;KACH,QAAQ,MAAM,OAAO,QAAO,UAC1B,MAAM,GAAG,YAAY,CAAC,CAAC,SAAS,CAAC,KAAK,MAAM,KAAK,YAAY,CAAC,CAAC,SAAS,CAAC,KAAK,MAAM,GAAG,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC;IACpH,EAAE,CAAC,CACF,QAAO,UAAS,MAAM,OAAO,SAAS,CAAC;GAC5C,GAAG,CAAC,OAAO,KAAK,CAAC;GAEjB,IAAI,eAAe,KAAA,KAAa,gBAAgB,KAAA,KAAa,MAAM,KAAA,GAAW,OAAO;GACrF,IAAI,UAAU,KAAA,GACZ,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;IAAS,WAAU;IACjB,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAU;KAAY,UAAA,EAAE,SAAS;IAAO,CAAA;GACtC,CAAA;GAIb,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;IAAS,WAAU;IAAnB,UAAA;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAa,UAAA,EAAE,OAAO;KAAO,CAAA,GAC5C,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAU;MAAW,UAAA,EAAE,OAAO;KAAK,CAAA,CACnC,EAAA,CAAA;KAEL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;QACE,MAAK;QACL,WAAU;QACV,OAAO;QACP,aAAa,EAAE,mBAAmB;QAClC,WAAU,UAAS;SAAE,SAAS,MAAM,OAAO,KAAK;QAAE;OACnD,CAAA;OACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QACE,MAAK;QACL,WAAU;QACV,UAAU,CAAC,MAAM,YAAY,MAAM,UAAU,MAAM;QACnD,eAAe;SAAE,WAAW,SAAS;QAAE;QAEtC,UAAA,EAAE,kBAAkB;OACf,CAAA;OACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAa,UAAA,EAAE,gBAAgB;SAAE,SAAS,MAAM;SAAS,OAAO,MAAM;QAAM,CAAC;OAAQ,CAAA;MAClG;;KAEJ,MAAM,UAAU,WACf,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACG,EAAE,cAAc,EAAE,SAAS,MAAM,SAAS,GAAG,CAAC;OAAG;OAClD,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,MAAK;QAAS,WAAU;QAAW,eAAe;SAAE,WAAW,KAAK;QAAE;QAAI,UAAA,EAAE,OAAO;OAAU,CAAA;MAClG;;KAGN,MAAM,UAAU,WAAW,CAAC,MAAM,YAAY,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAY,UAAA,EAAE,UAAU;KAAO,CAAA;KAC5F,MAAM,UAAU,WAAW,MAAM,OAAO,WAAW,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAY,UAAA,EAAE,OAAO;KAAO,CAAA;KACnG,MAAM,UAAU,WAAW,MAAM,QAAQ,KAAK,MAAM,YAAY,KAC/D,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAY,UAAA,EAAE,kBAAkB;KAAO,CAAA;KAEvD,MAAM,UAAU,WAAW,eAAe,WAAW,KAAK,MAAM,OAAO,SAAS,KAC/E,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAY,UAAA,EAAE,SAAS;KAAO,CAAA;KAG9C,MAAM,UAAU,WAAW,eAAe,KAAI,UAAS;MACtD,MAAM,eAAe,MAAM,OAAO,QAAO,UAAS,CAAC,MAAM,MAAM,CAAC,CAAC;MACjE,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAoB,WAAU;OAA9B,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAU;QAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAU;SAAiB,UAAA,MAAM;QAAW,CAAA,GAClD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAU;SACb,UAAA,EAAE,eAAe;UAAE,SAAS;UAAc,OAAO,MAAM,OAAO;SAAO,CAAC;QACnE,CAAA,CACH,EAAA,CAAA,GACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UACE,MAAK;UACL,WAAU;UACV,UAAU,CAAC,MAAM,YAAY,iBAAiB;UAC9C,eAAe;WAAE,WAAW,eAAe,MAAM,IAAI,IAAI;UAAE;UAE1D,UAAA,EAAE,SAAS;SACN,CAAA,GACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;UACE,MAAK;UACL,WAAU;UACV,UAAU,CAAC,MAAM,YAAY,iBAAiB,MAAM,OAAO;UAC3D,eAAe;WAAE,WAAW,eAAe,MAAM,IAAI,KAAK;UAAE;UAE3D,UAAA,EAAE,SAAS;SACN,CAAA,CACL;QACF,CAAA,CAAA;OACL,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;QAAI,OAAO;SAAE,WAAW;SAAQ,QAAQ;SAAG,SAAS;QAAE;QACnD,UAAA,MAAM,OAAO,KAAI,UAChB,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;SAAmB,WAAU;SAA7B,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;UAAM,WAAU;UAAhB,UAAA,CACG,MAAM,MACN,MAAM,SAAS,MAAM,MAAM,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,WAAU;WAAU,UAAA,MAAM;UAAS,CAAA,CACjE;SACN,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;UACE,IAAI,CAAC,MAAM;UACX,UAAU,CAAC,MAAM;UACjB,OAAO,MAAM;UACb,WAAU,YAAW;WAAE,WAAW,OAAO,MAAM,IAAI,MAAM,IAAI,MAAM,MAAM,CAAC,OAAO;UAAE;SACpF,CAAA,CACC;QAXK,GAAA,MAAM,EAWX,CACL;OACC,CAAA,CACD;MA3CK,GAAA,MAAM,EA2CX;KAET,CAAC;IACM;;EAEb;EAEA,SAAS,OAAO,OAA6F;GAC3G,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;IACE,MAAK;IACL,MAAK;IACL,gBAAc,MAAM;IACpB,cAAY,MAAM;IAClB,UAAU,MAAM;IAChB,eAAe;KAAE,MAAM,SAAS,CAAC,MAAM,EAAE;IAAE;IAC3C,OAAO;KACL,OAAO;KACP,QAAQ;KACR,cAAc;KACd,QAAQ;KACR,SAAS;KACT,QAAQ,MAAM,WAAW,gBAAgB;KACzC,YAAY,MAAM,KAAK,YAAY;KACnC,SAAS,MAAM,WAAW,KAAM;KAChC,SAAS;KACT,gBAAgB,MAAM,KAAK,aAAa;KACxC,YAAY;KACZ,MAAM;IACR;IAEA,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,OAAO;KAAE,OAAO;KAAI,QAAQ;KAAI,cAAc;KAAO,YAAY;KAAQ,SAAS;IAAQ,EAAI,CAAA;GAC9F,CAAA;EAEZ;;;ECxNA,MAAa,YAAY;EAEzB,MAAa,KAA+C;GAC1D,KAAK;GACL,OAAO;GACP,OAAO;GACP,SAAS;GACT,YAAY;GACZ,OAAO;GACP,OAAO;GACP,kBAAkB;GAClB,UAAU;GACV,cAAc;GACd,mBAAmB;GACnB,SAAS;GACT,SAAS;GACT,SAAS;GACT,kBAAkB;GAClB,aAAa;EACf;EAEA,MAAa,KAA+C;GAC1D,KAAK;GACL,OAAO;GACP,OAAO;GACP,SAAS;GACT,YAAY;GACZ,OAAO;GACP,OAAO;GACP,kBAAkB;GAClB,UAAU;GACV,cAAc;GACd,mBAAmB;GACnB,SAAS;GACT,SAAS;GACT,SAAS;GACT,kBAAkB;GAClB,aAAa;EACf;;;ECvCA,MAAa,OAAO;EACpB,MAAa,SAAS;GAAC;GAAS;GAAU;GAAc;EAAe;EAEvE,SAAgB,MAAM,KAAoB;GACxC,IAAI,OAAO,SAAS,WAAW;IAAE;IAAI;GAAG,CAAC;GAGzC,MAAM,aAAa,IAAI,8BADT,IAAI,cAAc,KAAa,EAAE,WAAW,mBAAmB,CACxB,GAAO,IAAI,WAAW,GAAG;GAG9E,MAAM,IAAI,IAAI,OAAO,KAAK,SAAS;GAanC,MAAM,kBAAkB;IACtB;IACA;IACA,OAAO,EAAE,UAAU,WAAW,MAAM;GACtC;GACA,IAAI,MAAM,OAAO,0BAA0B,IAAI,MAAM,SAAS;IAC5D,MAAM;IACN,IAAI;IACJ,OAAO;IACP,aAAa,EAAE,KAAK;IACpB,QAAQ;GACV,GAAY,mBAA4B,CAAC;EAC3C"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["createSnapshotStore","useState","useMemo"],"sources":["../src/namespace.ts","../src/client/controller.ts","../src/client/ModelVisibilityCard.tsx","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/**\n * Shared settings namespace of the model-visibility plugin. Lives in its own\n * module so the browser half can name the namespace without value-importing\n * the Host half (which would drag the settings schema and its Node-side deps\n * into the client bundle).\n */\nexport const MODEL_VISIBILITY_NS = 'model-visibility'\n","/**\n * The model-visibility card's controller: React-free state over two sources —\n * the (already filtered) Host model catalog and the `model-visibility`\n * settings scope. The rendered list is their union: catalog rows are visible\n * models; hidden entries restate the models the filter removed, labeled by\n * the name snapshot each entry carries. Beyond the reference shape, the\n * controller exposes provider-level batch writes and a whole-list write so\n * the card can offer search + bulk actions.\n */\nimport { createSnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { Config, HiddenModel } from '../index.ts'\n\r\n/** One toggle row. */\r\nexport interface ModelRow {\r\n id: string\r\n name: string\r\n hidden: boolean\r\n}\r\n\r\n/** One provider group of rows. */\r\nexport interface ProviderGroupRow {\r\n id: string\r\n name: string\r\n models: ModelRow[]\r\n}\r\n\r\n/** What the card renders. */\nexport interface ModelVisibilityState {\n phase: 'loading' | 'ready' | 'error'\r\n error?: string\r\n /** Whether the Host settings document accepts writes. */\r\n writable: boolean\r\n groups: ProviderGroupRow[]\r\n /** Union count and how many of them remain visible. */\r\n total: number\r\n visible: number\n}\n\n/**\n * One catalog group as the Host's `remote.session.modelCatalog()` advertises\n * it. The 0.1.5 published Remote types mangle the catalog interface name away,\n * so this plugin restates the wire shape it actually consumes.\n */\nexport interface CatalogModelGroup {\n id: string\n name: string\n models: readonly { id: string; name: string }[]\n}\n\n/** Host model catalog outcome carried by the Remote result envelope. */\nexport type ModelCatalogOutcome =\n | { ok: true; value: { groups: readonly CatalogModelGroup[] } }\n | { ok: false; error: { code: string; message: string } }\n\n/** The assembled Client Remote face this plugin consumes. */\nexport interface ModelCatalogRemote {\n session: {\n modelCatalog(): Promise<ModelCatalogOutcome>\n }\n}\n\n/** Injected catalog reader: decouples the controller from the Remote wire shape. */\nexport interface ModelCatalogReader {\n read(): Promise<readonly CatalogModelGroup[]>\n}\n\n/** The face the slot registration injects into the card component. */\nexport interface ModelVisibilityFace {\n hooks: {\r\n /** Card snapshot bound by the renderer as useModelVisibility. */\r\n modelVisibility: SnapshotStore<ModelVisibilityState>\r\n }\r\n /** Flip one model's visibility. */\r\n toggle: (provider: string, model: string, name: string, hidden: boolean) => void\r\n /** Hide or show every model of one provider. */\r\n toggleProvider: (provider: string, hidden: boolean) => void\r\n /** Clear the hidden list entirely (everything visible again). */\r\n resetAll: () => void\r\n /** Re-read the catalog after a failure. */\r\n reload: () => void\r\n}\n\nconst EMPTY: ModelVisibilityState = { phase: 'loading', writable: false, groups: [], total: 0, visible: 0 }\n\nexport class ModelVisibilityCardController {\n /** Exposed: the section contribution passes the store through its face as `hooks.snapshot`. */\n readonly store = createSnapshotStore<ModelVisibilityState>(EMPTY)\n private catalog: CatalogModelGroup[] | undefined\n private loadError: string | undefined\n private generation = 0\n\n constructor(\n private readonly scope: SettingsScope<Config>,\n private readonly catalogReader: ModelCatalogReader,\n ) {\n scope.subscribe(() => { this.recompute() })\n void this.load()\n }\r\n\r\n /** The registration-side inject face. */\r\n face(): ModelVisibilityFace {\r\n return {\r\n hooks: { modelVisibility: this.store },\r\n toggle: (provider, model, name, hidden) => { void this.toggle(provider, model, name, hidden) },\r\n toggleProvider: (provider, hidden) => { void this.toggleProvider(provider, hidden) },\r\n resetAll: () => { void this.resetAll() },\r\n reload: () => { void this.load() },\r\n }\r\n }\r\n\r\n /** Re-read the catalog; an older response never overwrites a newer one. */\r\n async load(): Promise<void> {\n const generation = ++this.generation\n if (this.catalog === undefined) this.store.update((draft) => { draft.phase = 'loading' })\n try {\n const groups = await this.catalogReader.read()\n if (generation !== this.generation) return\n this.catalog = groups.map(group => ({\n id: group.id,\n name: group.name,\n models: group.models.map(model => ({ id: model.id, name: model.name })),\n }))\n this.loadError = undefined\n } catch (error: unknown) {\n if (generation !== this.generation) return\n this.loadError = error instanceof Error ? error.message : String(error)\n }\r\n this.recompute()\r\n }\r\n\r\n /** Compute the next hidden list from the current one plus one model's flip. */\r\n private nextHidden(current: HiddenModel[], provider: string, model: string, name: string, hidden: boolean): HiddenModel[] {\r\n const rest = current.filter(entry => !(entry.provider === provider && entry.model === model))\r\n return hidden ? [...rest, { provider, model, name }] : rest\r\n }\r\n\r\n /** Write the next hidden list, then re-pull the catalog the filter changed. */\r\n private async write(next: HiddenModel[]): Promise<void> {\r\n try {\r\n await this.scope.set('hidden', next)\r\n } catch {\r\n // The scope already reloads Host state after a failed latest write;\r\n // recompute below re-renders whatever the Host now reports.\r\n }\r\n await this.load()\r\n }\r\n\r\n /** Flip one model's visibility (public API — the section card calls it directly). */\r\n async toggle(provider: string, model: string, name: string, hidden: boolean): Promise<void> {\r\n const current = this.scope.getSnapshot().value?.hidden ?? []\r\n await this.write(this.nextHidden(current, provider, model, name, hidden))\r\n }\r\n\r\n /**\r\n * Batch flip for one provider. Models are taken from the union of the\r\n * (filtered) catalog rows and existing hidden entries so a provider with\r\n * every model already hidden still flips back fully.\r\n */\r\n /** Batch flip for one provider (public API). */\r\n async toggleProvider(provider: string, hidden: boolean): Promise<void> {\r\n const current = this.scope.getSnapshot().value?.hidden ?? []\r\n const providerRows = this.stateRows().find(group => group.id === provider)\r\n const ids = new Set<string>([\r\n ...providerRows ? providerRows.models.map(model => model.id) : [],\r\n ...current.filter(entry => entry.provider === provider).map(entry => entry.model),\r\n ])\r\n if (ids.size === 0) return\r\n let next = current.filter(entry => entry.provider !== provider)\r\n if (hidden) {\r\n const nameOf = (id: string): string => providerRows?.models.find(model => model.id === id)?.name ?? id\r\n next = [...next, ...[...ids].map(id => ({ provider, model: id, name: nameOf(id) }))]\r\n }\r\n await this.write(next)\r\n }\r\n\r\n /** Clear the hidden list entirely (public API). */\r\n async resetAll(): Promise<void> {\r\n await this.write([])\r\n }\r\n\r\n /** Current union rows (catalog ∪ hidden) without touching the store. */\r\n private stateRows(): ProviderGroupRow[] {\r\n const snapshot = this.scope.getSnapshot()\r\n const hidden = snapshot.value?.hidden ?? []\r\n if (this.catalog === undefined) return []\r\n const hiddenKeys = new Set(hidden.map(entry => `${entry.provider}\\u0000${entry.model}`))\r\n const groups: ProviderGroupRow[] = this.catalog.map(group => ({\r\n id: group.id,\r\n name: group.name,\r\n models: group.models.map(model => ({\r\n id: model.id,\r\n name: model.name,\r\n hidden: hiddenKeys.has(`${group.id}\\u0000${model.id}`),\r\n })),\r\n }))\r\n const groupsById = new Map(groups.map(group => [group.id, group]))\r\n for (const entry of hidden) {\r\n const group = groupsById.get(entry.provider)\r\n if (group?.models.some(model => model.id === entry.model)) continue\r\n const row: ModelRow = { id: entry.model, name: entry.name ?? entry.model, hidden: true }\r\n if (group !== undefined) {\r\n group.models.push(row)\r\n } else {\r\n const created: ProviderGroupRow = { id: entry.provider, name: entry.provider, models: [row] }\r\n groupsById.set(entry.provider, created)\r\n groups.push(created)\r\n }\r\n }\r\n return groups\r\n }\r\n\r\n /** Union the filtered catalog with the hidden entries into render rows. */\r\n private recompute(): void {\r\n const snapshot = this.scope.getSnapshot()\r\n if (this.catalog === undefined && this.loadError !== undefined) {\r\n this.store.set({ ...EMPTY, phase: 'error', error: this.loadError, writable: snapshot.writable })\r\n return\r\n }\r\n if (this.catalog === undefined || snapshot.status === 'loading') {\r\n this.store.set({ ...EMPTY, writable: snapshot.writable })\r\n return\r\n }\r\n const groups = this.stateRows()\r\n const total = groups.reduce((count, group) => count + group.models.length, 0)\r\n const visible = groups.reduce(\r\n (count, group) => count + group.models.filter(model => !model.hidden).length, 0)\r\n this.store.set({\r\n phase: 'ready',\r\n writable: snapshot.writable,\r\n groups,\r\n total,\r\n visible,\r\n })\r\n }\r\n}\r\n","/**\r\n * The model-visibility settings section, styled to mirror the official\r\n * settings pages: big page title + muted intro, a controls row, then one\r\n * bordered rounded card per provider (like the provider cards on the\r\n * 模型 page) with pill action buttons and token-style switches.\r\n *\r\n * Consumes the `settings.section` contribution contract exactly like the\r\n * official ModelsSection: props = spread inject face, where `hooks.snapshot`\r\n * arrives as the `useSnapshot` selector hook; all three seats are undefined\r\n * while the shell has not injected yet (render null then).\r\n */\r\nimport { useMemo, useState } from 'react'\nimport type { CSSProperties } from 'react'\nimport type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-store'\nimport type { ModelVisibilityCardController, ModelVisibilityState } from './controller.ts'\r\nimport type {} from './locales.ts'\r\n\r\n/** Composed props: spread face fields + the `useSnapshot` hook from hooks.snapshot. */\r\nexport interface ModelVisibilityCardProps {\r\n controller: ModelVisibilityCardController | undefined\r\n useSnapshot: SnapshotSelectorHook<ModelVisibilityState> | undefined\r\n t: TSeat | undefined\r\n}\r\n\r\n/** Localized text seat bound to this card's namespace. */\r\ntype TSeat = (key: string, params?: Record<string, unknown>) => string\r\n\r\nconst STYLE_ID = 'dsh-model-visibility-styles'\r\n\r\nconst STYLES = `\r\n.dsh-mvw { display:flex; flex-direction:column; gap:16px; }\r\n.dsh-mvw .mvw-title { font-size:20px; font-weight:700; letter-spacing:.01em; }\r\n.dsh-mvw .mvw-sub { font-size:13px; color:inherit; opacity:.62; margin-top:6px; max-width:64ch; line-height:1.55; }\r\n.dsh-mvw .mvw-controls { display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-top:4px; }\r\n.dsh-mvw .mvw-search {\r\n flex:1 1 220px; min-width:160px; height:38px; padding:0 14px;\r\n font-size:13px; border-radius:10px;\r\n border:1px solid rgba(128,128,128,.38);\r\n background:transparent; color:inherit; outline:none;\r\n}\r\n.dsh-mvw .mvw-search:focus { border-color: rgba(76,139,245,.85); }\r\n.dsh-mvw .mvw-pill {\r\n height:36px; padding:0 16px; font-size:13px; cursor:pointer;\r\n border-radius:999px; border:1px solid rgba(128,128,128,.38);\r\n background:transparent; color:inherit;\r\n}\r\n.dsh-mvw .mvw-pill:hover:not(:disabled) { background:rgba(128,128,128,.08); }\r\n.dsh-mvw .mvw-pill:disabled { opacity:.45; cursor:not-allowed; }\r\n.dsh-mvw .mvw-count { font-size:12.5px; opacity:.62; margin-left:auto; }\r\n.dsh-mvw .mvw-card {\r\n border:1px solid rgba(128,128,128,.30); border-radius:16px;\r\n padding:6px 18px 2px;\r\n}\r\n.dsh-mvw .mvw-prov-head {\r\n display:flex; align-items:center; justify-content:space-between; gap:10px;\r\n padding:12px 0 10px;\r\n}\r\n.dsh-mvw .mvw-prov-name { font-size:15px; font-weight:650; }\r\n.dsh-mvw .mvw-prov-count { font-size:12px; opacity:.55; margin-left:10px; font-weight:400; }\r\n.dsh-mvw .mvw-prov-btns { display:flex; gap:8px; }\r\n.dsh-mvw .mvw-mini {\r\n height:30px; padding:0 13px; font-size:12.5px; cursor:pointer;\r\n border-radius:999px; border:1px solid rgba(128,128,128,.38);\r\n background:transparent; color:inherit;\r\n}\r\n.dsh-mvw .mvw-mini:hover:not(:disabled) { background:rgba(128,128,128,.08); }\r\n.dsh-mvw .mvw-mini:disabled { opacity:.4; cursor:not-allowed; }\r\n.dsh-mvw .mvw-row {\r\n display:flex; align-items:center; justify-content:space-between; gap:12px;\r\n min-height:46px; border-top:1px solid rgba(128,128,128,.16);\r\n}\r\n.dsh-mvw .mvw-row:first-of-type { border-top:none; }\r\n.dsh-mvw .mvw-name {\r\n font-size:14px;\r\n min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;\r\n}\r\n.dsh-mvw .mvw-id {\r\n opacity:.5; margin-left:8px; padding-right:6px; font-size:12px;\r\n}\r\n.dsh-mvw .mvw-note { font-size:12.5px; opacity:.66; }\r\n.dsh-mvw .mvw-warn { font-size:12.5px; color:#c77700; }\r\n.dsh-mvw .mvw-seats-off { font-size:12.5px; opacity:.66; }\r\n`\r\n\r\nfunction ensureStyles(): void {\r\n if (typeof document === 'undefined') return\r\n if (document.getElementById(STYLE_ID)) return\r\n const el = document.createElement('style')\r\n el.id = STYLE_ID\r\n el.textContent = STYLES\r\n document.head.appendChild(el)\r\n}\r\n\r\nexport function ModelVisibilityCard(props: ModelVisibilityCardProps) {\r\n const { controller, useSnapshot, t } = props\r\n\r\n // Hooks stay unconditional regardless of injection timing.\r\n const state = (useSnapshot ?? ((_selector: (s: ModelVisibilityState) => unknown) => undefined))(\r\n (snapshot: ModelVisibilityState) => snapshot,\r\n ) as ModelVisibilityState | undefined\r\n const [query, setQuery] = useState('')\r\n\r\n // Styles must exist before first paint of this subtree; guarded, so idempotent.\r\n useMemo(() => { ensureStyles() }, [])\r\n\r\n const filteredGroups = useMemo(() => {\r\n const q = query.trim().toLowerCase()\r\n if (q === '') return state?.groups ?? []\r\n return (state?.groups ?? [])\r\n .map(group => ({\r\n ...group,\r\n models: group.models.filter(model =>\r\n model.id.toLowerCase().includes(q) || model.name.toLowerCase().includes(q) || group.id.toLowerCase().includes(q)),\r\n }))\r\n .filter(group => group.models.length > 0)\r\n }, [state, query])\r\n\r\n if (controller === undefined || useSnapshot === undefined || t === undefined) return null\r\n if (state === undefined) {\r\n return (\r\n <section className=\"dsh-mvw\">\r\n <div className=\"mvw-note\">{t('loading')}</div>\r\n </section>\r\n )\r\n }\r\n\r\n return (\r\n <section className=\"dsh-mvw\">\r\n <div>\r\n <div className=\"mvw-title\">{t('title')}</div>\r\n <p className=\"mvw-sub\">{t('intro')}</p>\r\n </div>\r\n\r\n <div className=\"mvw-controls\">\r\n <input\r\n type=\"search\"\r\n className=\"mvw-search\"\r\n value={query}\r\n placeholder={t('searchPlaceholder')}\r\n onChange={event => { setQuery(event.target.value) }}\r\n />\r\n <button\r\n type=\"button\"\r\n className=\"mvw-pill\"\r\n disabled={!state.writable || state.total === state.visible}\r\n onClick={() => { controller.resetAll() }}\r\n >\r\n {t('showAllProviders')}\r\n </button>\r\n <span className=\"mvw-count\">{t('visibleCount', { visible: state.visible, total: state.total })}</span>\r\n </div>\r\n\r\n {state.phase === 'error' && (\r\n <div className=\"mvw-warn\">\r\n {t('loadFailed', { message: state.error ?? '' })}{' '}\r\n <button type=\"button\" className=\"mvw-mini\" onClick={() => { controller.load() }}>{t('retry')}</button>\r\n </div>\r\n )}\r\n\r\n {state.phase === 'ready' && !state.writable && <div className=\"mvw-note\">{t('readOnly')}</div>}\r\n {state.phase === 'ready' && state.groups.length === 0 && <div className=\"mvw-note\">{t('empty')}</div>}\r\n {state.phase === 'ready' && state.total > 0 && state.visible === 0 && (\r\n <div className=\"mvw-warn\">{t('allHiddenWarning')}</div>\r\n )}\r\n {state.phase === 'ready' && filteredGroups.length === 0 && state.groups.length > 0 && (\r\n <div className=\"mvw-note\">{t('noMatch')}</div>\r\n )}\r\n\r\n {state.phase === 'ready' && filteredGroups.map(group => {\r\n const groupVisible = group.models.filter(model => !model.hidden).length\r\n return (\r\n <div key={group.id} className=\"mvw-card\">\r\n <div className=\"mvw-prov-head\">\r\n <div>\r\n <span className=\"mvw-prov-name\">{group.name}</span>\r\n <span className=\"mvw-prov-count\">\r\n {t('providerRow', { visible: groupVisible, total: group.models.length })}\r\n </span>\r\n </div>\r\n <div className=\"mvw-prov-btns\">\r\n <button\r\n type=\"button\"\r\n className=\"mvw-mini\"\r\n disabled={!state.writable || groupVisible === 0}\r\n onClick={() => { controller.toggleProvider(group.id, true) }}\r\n >\r\n {t('hideAll')}\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"mvw-mini\"\r\n disabled={!state.writable || groupVisible === group.models.length}\r\n onClick={() => { controller.toggleProvider(group.id, false) }}\r\n >\r\n {t('showAll')}\r\n </button>\r\n </div>\r\n </div>\r\n <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>\r\n {group.models.map(model => (\r\n <li key={model.id} className=\"mvw-row\">\r\n <span className=\"mvw-name\">\r\n {model.name}\r\n {model.name !== model.id && <span className=\"mvw-id\">{model.id}</span>}\r\n </span>\r\n <Switch\r\n on={!model.hidden}\r\n disabled={!state.writable}\r\n label={model.name}\r\n onChange={visible => { controller.toggle(group.id, model.id, model.name, !visible) }}\r\n />\r\n </li>\r\n ))}\r\n </ul>\r\n </div>\r\n )\r\n })}\r\n </section>\r\n )\r\n}\r\n\r\nfunction Switch(props: { on: boolean; disabled: boolean; label: string; onChange: (next: boolean) => void }) {\r\n return (\r\n <button\r\n type=\"button\"\r\n role=\"switch\"\r\n aria-checked={props.on}\r\n aria-label={props.label}\r\n disabled={props.disabled}\r\n onClick={() => { props.onChange(!props.on) }}\r\n style={{\r\n width: 40,\r\n height: 22,\r\n borderRadius: 11,\r\n border: 'none',\r\n padding: 2,\r\n cursor: props.disabled ? 'not-allowed' : 'pointer',\r\n background: props.on ? '#4c8bf5' : 'rgba(128,128,128,.45)',\r\n opacity: props.disabled ? 0.5 : 1,\r\n display: 'inline-flex',\r\n justifyContent: props.on ? 'flex-end' : 'flex-start',\r\n transition: 'background .15s',\r\n flex: 'none',\r\n }}\r\n >\r\n <span style={{ width: 18, height: 18, borderRadius: '50%', background: '#fff', display: 'block' }} />\r\n </button>\r\n )\r\n}\r\n","/**\r\n * Locale dictionaries for the model-visibility settings card.\r\n * The namespace is merged into the slot system's LocaleNamespaceMap so the\r\n * register site can declare `locale:` and receive the typed `t` seat.\r\n */\r\n\r\n/** Dictionary keys of this card's copy. */\r\nexport type ModelVisibilityLocaleKey =\r\n | 'nav'\r\n | 'title'\r\n | 'intro'\r\n | 'loading'\r\n | 'loadFailed'\r\n | 'retry'\r\n | 'empty'\r\n | 'allHiddenWarning'\r\n | 'readOnly'\r\n | 'visibleCount'\r\n | 'searchPlaceholder'\r\n | 'noMatch'\r\n | 'hideAll'\r\n | 'showAll'\r\n | 'showAllProviders'\r\n | 'providerRow'\r\n\r\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\r\n interface LocaleNamespaceMap {\r\n /** The model-visibility card's copy. */\r\n 'settings.modelVisibility': ModelVisibilityLocaleKey\r\n }\r\n}\r\n\r\nexport const LOCALE_NS = 'settings.modelVisibility' as const\r\n\r\nexport const zh: Record<ModelVisibilityLocaleKey, string> = {\r\n nav: '模型可见性',\r\n title: '模型可见性',\r\n intro: '控制各模型是否出现在对话界面的模型选择菜单中。隐藏一个正在使用的模型不会中断该会话,也不会改动 Provider 配置。',\r\n loading: '正在读取模型目录…',\r\n loadFailed: '模型目录读取失败:{message}',\r\n retry: '重试',\r\n empty: '没有任何 Provider 提供模型。',\r\n allHiddenWarning: '所有模型都已隐藏,模型选择菜单将是空的。',\r\n readOnly: '设置文档不可写,开关已禁用。',\r\n visibleCount: '{visible} / {total} 可见',\r\n searchPlaceholder: '搜索模型…',\r\n noMatch: '没有匹配的模型。',\r\n hideAll: '全部隐藏',\r\n showAll: '全部显示',\r\n showAllProviders: '恢复全部',\r\n providerRow: '该渠道 {visible} / {total} 可见',\r\n}\r\n\r\nexport const en: Record<ModelVisibilityLocaleKey, string> = {\r\n nav: 'Model visibility',\r\n title: 'Model visibility',\r\n 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.',\r\n loading: 'Loading the model catalog…',\r\n loadFailed: 'Failed to load the model catalog: {message}',\r\n retry: 'Retry',\r\n empty: 'No provider advertises any model.',\r\n allHiddenWarning: 'Every model is hidden; the model selector will be empty.',\r\n readOnly: 'The settings document is read-only; toggles are disabled.',\r\n visibleCount: '{visible} / {total} visible',\r\n searchPlaceholder: 'Search models…',\r\n noMatch: 'No model matches.',\r\n hideAll: 'Hide all',\r\n showAll: 'Show all',\r\n showAllProviders: 'Reset all',\r\n providerRow: '{visible} / {total} visible in this provider',\r\n}\r\n","/**\n * dsh-model-visibility — browser half.\n *\n * Registers the model-visibility card as a top-level settings section\n * (`settings.section`) and its locale dictionaries. Cross-plugin\n * collaboration goes through cordis services only: the slot key's\n * declaration, the settings scope, and the wire handle arrive as type-only\n * merges, never value imports (the loader bundle-purity rule).\n */\nimport type { Context } from '@deepseek-ai/cordis'\n// Type-only merges: ctx.settingsScope (settings), ctx.locale (locale), and the\n// `settings.section` slot declaration (ui-settings).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport type { Config } from '../index.ts'\nimport { MODEL_VISIBILITY_NS } from '../namespace.ts'\nimport {\n ModelVisibilityCardController,\n type ModelCatalogReader,\n type ModelCatalogRemote,\n} from './controller.ts'\nimport { ModelVisibilityCard } from './ModelVisibilityCard.tsx'\nimport { LOCALE_NS, en, zh } from './locales.ts'\n\nexport const name = 'dsh-model-visibility'\n// `remote.session` carries the Host model catalog; `settingsScope` is the\n// durable hidden-list scope; `slots`/`locale` are the contribution surfaces.\nexport const inject = ['slots', 'locale', 'settingsScope', 'remote', 'remote.session'] as const\n\nexport function apply(ctx: Context): void {\n ctx.locale.register(LOCALE_NS, { zh, en })\n\n const scope = ctx.settingsScope.bind<Config>({ namespace: MODEL_VISIBILITY_NS })\n // The assembled Remote service is typed through the api-remotes package; the\n // 0.1.5 build mangles its catalog interface name, so narrow to the wire shape\n // this plugin consumes instead of importing that declaration chain.\n const remote = (ctx as unknown as { remote: ModelCatalogRemote }).remote\n const catalog: ModelCatalogReader = {\n async read() {\n const response = await remote.session.modelCatalog()\n if (!response.ok) {\n throw new Error(`${response.error.code}: ${response.error.message}`)\n }\n return response.value.groups\n },\n }\n const controller = new ModelVisibilityCardController(scope, catalog)\n // The section card consumes the locale seat through the inject face\n // (mirroring the official Models page, whose face carries `t`).\n const t = ctx.locale.bind(LOCALE_NS)\n\r\n // Register as a top-level settings SECTION (nav entry 「模型可见性」, ordered\r\n // right after the official 模型 page at order=10) instead of a tab inside\r\n // the Plugins section — the card is a whole-page surface (search + batch\r\n // controls read better with room to breathe). The label thunk re-binds the\r\n // locale each call so a host language switch shows fresh text.\r\n //\r\n // Face contract for `settings.section` contributions (ui-slots): every\r\n // field of the injected object spreads onto the component as props, and\r\n // each `hooks` entry becomes a `use<Name>` snapshot-selector hook. The\r\n // component destructures { controller, useSnapshot, t } exactly like the\r\n // official ModelsSection does.\r\n const injected = () => ({\r\n controller,\r\n t,\r\n hooks: { snapshot: controller.store },\r\n })\r\n ctx.slots.inject('settings.section', () => ctx.slots.register({\r\n name: 'settings.section',\r\n id: 'model-visibility',\r\n order: 11,\r\n label: () => t('nav'),\r\n inject: injected,\r\n } as never, ModelVisibilityCard as never))\r\n}\r\n"],"mappings":"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"}
|
package/lib/index.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
2
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
+
//#region src/namespace.ts
|
|
3
|
+
/**
|
|
4
|
+
* Shared settings namespace of the model-visibility plugin. Lives in its own
|
|
5
|
+
* module so the browser half can name the namespace without value-importing
|
|
6
|
+
* the Host half (which would drag the settings schema and its Node-side deps
|
|
7
|
+
* into the client bundle).
|
|
8
|
+
*/
|
|
9
|
+
const MODEL_VISIBILITY_NS = "model-visibility";
|
|
10
|
+
//#endregion
|
|
3
11
|
//#region src/index.ts
|
|
4
12
|
const name = "dsh-model-visibility";
|
|
5
|
-
const inject = ["llm"];
|
|
6
|
-
/** The settings namespace both halves address (the card keys on it too). */
|
|
7
|
-
const MODEL_VISIBILITY_NS = settingsNamespace("model-visibility");
|
|
13
|
+
const inject = ["llm", "settings"];
|
|
8
14
|
const Config = z.object({ hidden: z.array(z.object({
|
|
9
15
|
provider: z.string().required(),
|
|
10
16
|
model: z.string().required(),
|
|
11
17
|
name: z.string()
|
|
12
18
|
})).default([]) });
|
|
13
19
|
function apply(ctx, config) {
|
|
14
|
-
|
|
15
|
-
installSettingsSection(ctx, MODEL_VISIBILITY_NS, Config, config, {
|
|
16
|
-
setSource: (current) => {
|
|
17
|
-
source = current;
|
|
18
|
-
},
|
|
19
|
-
onChange: () => {}
|
|
20
|
-
});
|
|
20
|
+
const scope = ctx.settings.register(MODEL_VISIBILITY_NS, Config, { base: config });
|
|
21
21
|
const llm = ctx.llm;
|
|
22
22
|
const original = llm.listModels.bind(llm);
|
|
23
23
|
ctx.effect(() => {
|
|
24
24
|
Object.defineProperty(llm, "listModels", {
|
|
25
25
|
value: async (provider) => {
|
|
26
26
|
const models = await original(provider);
|
|
27
|
-
const hidden = new Set(
|
|
27
|
+
const hidden = new Set(scope.get().hidden.filter((entry) => entry.provider === provider).map((entry) => entry.model));
|
|
28
28
|
if (hidden.size === 0) return models;
|
|
29
29
|
return models.filter((model) => !hidden.has(model.id));
|
|
30
30
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-
|
|
1
|
+
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-store';
|
|
2
2
|
import type { ModelVisibilityCardController, ModelVisibilityState } from './controller.ts';
|
|
3
3
|
/** Composed props: spread face fields + the `useSnapshot` hook from hooks.snapshot. */
|
|
4
4
|
export interface ModelVisibilityCardProps {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
2
|
+
import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
3
3
|
import type { Config } from '../index.ts';
|
|
4
4
|
/** One toggle row. */
|
|
5
5
|
export interface ModelRow {
|
|
@@ -24,6 +24,42 @@ export interface ModelVisibilityState {
|
|
|
24
24
|
total: number;
|
|
25
25
|
visible: number;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* One catalog group as the Host's `remote.session.modelCatalog()` advertises
|
|
29
|
+
* it. The 0.1.5 published Remote types mangle the catalog interface name away,
|
|
30
|
+
* so this plugin restates the wire shape it actually consumes.
|
|
31
|
+
*/
|
|
32
|
+
export interface CatalogModelGroup {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
models: readonly {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
}[];
|
|
39
|
+
}
|
|
40
|
+
/** Host model catalog outcome carried by the Remote result envelope. */
|
|
41
|
+
export type ModelCatalogOutcome = {
|
|
42
|
+
ok: true;
|
|
43
|
+
value: {
|
|
44
|
+
groups: readonly CatalogModelGroup[];
|
|
45
|
+
};
|
|
46
|
+
} | {
|
|
47
|
+
ok: false;
|
|
48
|
+
error: {
|
|
49
|
+
code: string;
|
|
50
|
+
message: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
/** The assembled Client Remote face this plugin consumes. */
|
|
54
|
+
export interface ModelCatalogRemote {
|
|
55
|
+
session: {
|
|
56
|
+
modelCatalog(): Promise<ModelCatalogOutcome>;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Injected catalog reader: decouples the controller from the Remote wire shape. */
|
|
60
|
+
export interface ModelCatalogReader {
|
|
61
|
+
read(): Promise<readonly CatalogModelGroup[]>;
|
|
62
|
+
}
|
|
27
63
|
/** The face the slot registration injects into the card component. */
|
|
28
64
|
export interface ModelVisibilityFace {
|
|
29
65
|
hooks: {
|
|
@@ -41,13 +77,13 @@ export interface ModelVisibilityFace {
|
|
|
41
77
|
}
|
|
42
78
|
export declare class ModelVisibilityCardController {
|
|
43
79
|
private readonly scope;
|
|
44
|
-
private readonly
|
|
80
|
+
private readonly catalogReader;
|
|
45
81
|
/** Exposed: the section contribution passes the store through its face as `hooks.snapshot`. */
|
|
46
82
|
readonly store: SnapshotStore<ModelVisibilityState>;
|
|
47
83
|
private catalog;
|
|
48
84
|
private loadError;
|
|
49
85
|
private generation;
|
|
50
|
-
constructor(scope: SettingsScope<Config>,
|
|
86
|
+
constructor(scope: SettingsScope<Config>, catalogReader: ModelCatalogReader);
|
|
51
87
|
/** The registration-side inject face. */
|
|
52
88
|
face(): ModelVisibilityFace;
|
|
53
89
|
/** Re-read the catalog; an older response never overwrites a newer one. */
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-model-visibility — browser half.
|
|
3
3
|
*
|
|
4
|
-
* Registers the model-visibility card
|
|
5
|
-
* (`settings.
|
|
4
|
+
* Registers the model-visibility card as a top-level settings section
|
|
5
|
+
* (`settings.section`) and its locale dictionaries. Cross-plugin
|
|
6
6
|
* collaboration goes through cordis services only: the slot key's
|
|
7
7
|
* declaration, the settings scope, and the wire handle arrive as type-only
|
|
8
8
|
* merges, never value imports (the loader bundle-purity rule).
|
|
9
9
|
*/
|
|
10
10
|
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
-
import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client';
|
|
12
|
-
declare module '@deepseek-ai/cordis' {
|
|
13
|
-
interface Context {
|
|
14
|
-
connection: ConnectionHandle;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
11
|
export declare const name = "dsh-model-visibility";
|
|
18
|
-
export declare const inject: readonly ["slots", "locale", "
|
|
12
|
+
export declare const inject: readonly ["slots", "locale", "settingsScope", "remote", "remote.session"];
|
|
19
13
|
export declare function apply(ctx: Context): void;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Owns the `model-visibility` settings namespace (the hidden-model list) and
|
|
5
5
|
* filters the model catalog at its source: `ctx.llm.listModels` is wrapped
|
|
6
|
-
* so
|
|
7
|
-
* (the settings page)
|
|
6
|
+
* so every catalog read (the conversation selector's
|
|
7
|
+
* `remote.session.modelCatalog()` and the settings page) stops advertising
|
|
8
|
+
* hidden models. Catalog membership
|
|
8
9
|
* is advisory in the harness, so hiding a model a session already uses
|
|
9
10
|
* never breaks that session's dispatch.
|
|
10
11
|
*/
|
|
11
12
|
import type { Context } from '@deepseek-ai/cordis';
|
|
12
13
|
import z from '@deepseek-ai/schemastery';
|
|
14
|
+
import { MODEL_VISIBILITY_NS } from './namespace.ts';
|
|
13
15
|
export declare const name = "dsh-model-visibility";
|
|
14
|
-
export declare const inject: readonly ["llm"];
|
|
15
|
-
|
|
16
|
-
export declare const MODEL_VISIBILITY_NS: import("@deepseek-ai/dsh-settings").SettingsNamespace;
|
|
16
|
+
export declare const inject: readonly ["llm", "settings"];
|
|
17
|
+
export { MODEL_VISIBILITY_NS };
|
|
17
18
|
/** One hidden model: the provider/model route pair plus a display-name snapshot. */
|
|
18
19
|
export interface HiddenModel {
|
|
19
20
|
provider: string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared settings namespace of the model-visibility plugin. Lives in its own
|
|
3
|
+
* module so the browser half can name the namespace without value-importing
|
|
4
|
+
* the Host half (which would drag the settings schema and its Node-side deps
|
|
5
|
+
* into the client bundle).
|
|
6
|
+
*/
|
|
7
|
+
export declare const MODEL_VISIBILITY_NS = "model-visibility";
|
package/package.json
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-model-visibility",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "DeepSeek Harness plugin: control which models appear in the model selector — per-model switches, provider-level batch toggles, and search. Hiding a model never touches your provider config or running sessions.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "lib/index.js",
|
|
7
|
-
"types": "lib/types/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./lib/types/index.d.ts",
|
|
11
|
-
"default": "./lib/index.js"
|
|
12
|
-
},
|
|
13
|
-
"./client": {
|
|
14
|
-
"types": "./lib/types/client/index.d.ts",
|
|
15
|
-
"default": "./lib/client.js"
|
|
16
|
-
},
|
|
17
|
-
"./package.json": "./package.json"
|
|
18
|
-
},
|
|
19
|
-
"files": [
|
|
20
|
-
"lib/index.js",
|
|
21
|
-
"lib/client.js",
|
|
22
|
-
"lib/client.js.map",
|
|
23
|
-
"lib/types/**/*.d.ts",
|
|
24
|
-
"cordis.patch.yml",
|
|
25
|
-
"LICENSE",
|
|
26
|
-
"README.md",
|
|
27
|
-
"README.en.md"
|
|
28
|
-
],
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-model-visibility",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "DeepSeek Harness plugin: control which models appear in the model selector — per-model switches, provider-level batch toggles, and search. Hiding a model never touches your provider config or running sessions.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./client": {
|
|
14
|
+
"types": "./lib/types/client/index.d.ts",
|
|
15
|
+
"default": "./lib/client.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib/index.js",
|
|
21
|
+
"lib/client.js",
|
|
22
|
+
"lib/client.js.map",
|
|
23
|
+
"lib/types/**/*.d.ts",
|
|
24
|
+
"cordis.patch.yml",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"README.md",
|
|
27
|
+
"README.en.md"
|
|
28
|
+
],
|
|
29
29
|
"dsh": {
|
|
30
30
|
"bundle": {
|
|
31
31
|
"patch": "./cordis.patch.yml"
|
|
@@ -33,51 +33,53 @@
|
|
|
33
33
|
"client": {
|
|
34
34
|
"platform": "web",
|
|
35
35
|
"inject": [
|
|
36
|
-
"@deepseek-ai/dsh-
|
|
36
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
37
|
+
"@deepseek-ai/dsh-client-locale",
|
|
38
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
37
39
|
]
|
|
38
40
|
}
|
|
39
41
|
},
|
|
40
|
-
"scripts": {
|
|
41
|
-
"build": "tsc -p tsconfig.json && tsdown",
|
|
42
|
-
"prepare": "tsc -p tsconfig.json && tsdown",
|
|
43
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
44
|
-
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -p tsconfig.json && tsdown",
|
|
44
|
+
"prepare": "tsc -p tsconfig.json && tsdown",
|
|
45
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
46
|
+
},
|
|
45
47
|
"peerDependencies": {
|
|
46
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
48
|
+
"@deepseek-ai/cordis": "^4.0.2"
|
|
47
49
|
},
|
|
48
50
|
"dependencies": {
|
|
49
|
-
"@deepseek-ai/
|
|
50
|
-
"@deepseek-ai/schemastery": "^3.18.1"
|
|
51
|
+
"@deepseek-ai/schemastery": "^3.18.2"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
54
|
-
"@deepseek-ai/dsh-
|
|
55
|
-
"@deepseek-ai/dsh-client-locale": "0.
|
|
56
|
-
"@deepseek-ai/dsh-client-
|
|
57
|
-
"@deepseek-ai/dsh-client-ui-primitives": "
|
|
58
|
-
"@deepseek-ai/dsh-client-ui-
|
|
59
|
-
"@deepseek-ai/dsh-client-ui-settings
|
|
60
|
-
"@deepseek-ai/dsh-client-ui-slots": "
|
|
61
|
-
"@deepseek-ai/dsh-llm": "0.
|
|
54
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
55
|
+
"@deepseek-ai/dsh-api-remotes": "0.1.5-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.2",
|
|
57
|
+
"@deepseek-ai/dsh-client-store": "0.1.5-rc.2",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.2",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2",
|
|
60
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2",
|
|
61
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2",
|
|
62
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.2",
|
|
63
|
+
"@deepseek-ai/dsh-settings": "0.1.5-rc.2",
|
|
62
64
|
"@types/react": "^18.3.0",
|
|
63
65
|
"react": "^18.3.0",
|
|
64
66
|
"tsdown": "^0.12.0",
|
|
65
|
-
"typescript": "^5.7.0"
|
|
66
|
-
},
|
|
67
|
-
"publishConfig": {
|
|
68
|
-
"access": "public"
|
|
69
|
-
},
|
|
70
|
-
"repository": {
|
|
71
|
-
"type": "git",
|
|
72
|
-
"url": "git+https://github.com/Lzh3070/dsh-model-visibility.git"
|
|
73
|
-
},
|
|
74
|
-
"keywords": [
|
|
75
|
-
"dsh",
|
|
76
|
-
"dsh-plugin",
|
|
77
|
-
"deepseek-harness",
|
|
78
|
-
"model",
|
|
79
|
-
"visibility",
|
|
80
|
-
"selector"
|
|
81
|
-
],
|
|
82
|
-
"license": "MIT"
|
|
83
|
-
}
|
|
67
|
+
"typescript": "^5.7.0"
|
|
68
|
+
},
|
|
69
|
+
"publishConfig": {
|
|
70
|
+
"access": "public"
|
|
71
|
+
},
|
|
72
|
+
"repository": {
|
|
73
|
+
"type": "git",
|
|
74
|
+
"url": "git+https://github.com/Lzh3070/dsh-model-visibility.git"
|
|
75
|
+
},
|
|
76
|
+
"keywords": [
|
|
77
|
+
"dsh",
|
|
78
|
+
"dsh-plugin",
|
|
79
|
+
"deepseek-harness",
|
|
80
|
+
"model",
|
|
81
|
+
"visibility",
|
|
82
|
+
"selector"
|
|
83
|
+
],
|
|
84
|
+
"license": "MIT"
|
|
85
|
+
}
|