dsh-effort-menu 1.0.0 → 1.1.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/README.md +11 -7
- package/README.zh-CN.md +11 -7
- package/client.js +124 -5
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A replacement model menu for the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) composer: pick a model and set its reasoning effort in the same popup.
|
|
4
4
|
|
|
5
5
|
[](./LICENSE)
|
|
6
|
-
[](#compatibility)
|
|
7
7
|
|
|
8
8
|
English | [简体中文](./README.zh-CN.md)
|
|
9
9
|
|
|
@@ -35,23 +35,27 @@ Hovering the provider row opens a flyout with every provider's models.
|
|
|
35
35
|
|
|
36
36
|
## Install
|
|
37
37
|
|
|
38
|
-
Requires
|
|
38
|
+
Requires DSH `0.2.2-rc.1` or newer, with a build that ships the `dsh-client-ui-conversation`, `dsh-client-ui-model-selection` and `dsh-client-locale` client packages (see [Compatibility](#compatibility)).
|
|
39
39
|
|
|
40
40
|
### Web
|
|
41
41
|
|
|
42
42
|
With a CLI-managed profile, one command installs it into `web`.
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
|
-
dsh plugin --profile web add
|
|
45
|
+
dsh plugin --profile web add dsh-effort-menu
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
There is no build step. The browser half is the committed `client.js`. Restart `dsh web`, or reload the page.
|
|
49
49
|
|
|
50
50
|
### Desktop
|
|
51
51
|
|
|
52
|
-
The `desktop` profile is managed by the app
|
|
52
|
+
The `desktop` profile is managed by the app. Fully quit the app before using the CLI, or keep it running and install the checkout directory through **Plugin Manager**.
|
|
53
53
|
|
|
54
54
|
```sh
|
|
55
|
+
# CLI install, with the desktop app fully quit
|
|
56
|
+
dsh plugin --profile desktop add dsh-effort-menu
|
|
57
|
+
|
|
58
|
+
# Or clone it and point Plugin Manager at the directory
|
|
55
59
|
git clone https://github.com/xuanfenglajiao/dsh-effort-menu.git
|
|
56
60
|
```
|
|
57
61
|
|
|
@@ -65,7 +69,7 @@ If you would rather not use the CLI, edit the profile's own `package.json`.
|
|
|
65
69
|
|
|
66
70
|
```json
|
|
67
71
|
"dependencies": {
|
|
68
|
-
"dsh-effort-menu": "
|
|
72
|
+
"dsh-effort-menu": "^1.0.0"
|
|
69
73
|
}
|
|
70
74
|
```
|
|
71
75
|
|
|
@@ -81,7 +85,7 @@ Run `pnpm install` in that directory and restart `dsh web`.
|
|
|
81
85
|
|
|
82
86
|
```json
|
|
83
87
|
"dependencies": {
|
|
84
|
-
"dsh-effort-menu": "
|
|
88
|
+
"dsh-effort-menu": "^1.0.0"
|
|
85
89
|
}
|
|
86
90
|
```
|
|
87
91
|
|
|
@@ -91,7 +95,7 @@ Run `pnpm install` and restart the app.
|
|
|
91
95
|
|
|
92
96
|
## Compatibility
|
|
93
97
|
|
|
94
|
-
- Developed and tested on DeepSeek Harness Desktop 44.7.0 (DSH `0.2.2-rc.1`) on Windows 11
|
|
98
|
+
- Supports DSH `0.2.2-rc.1` and newer. Developed and tested on DeepSeek Harness Desktop 44.7.0 (DSH `0.2.2-rc.1`) on Windows 11; earlier DSH versions are not supported.
|
|
95
99
|
- The current model must expose `reasoning.efforts` for the slider to have anything to show. Otherwise the menu says the model provides no levels.
|
|
96
100
|
- Renders nothing in subagent sessions.
|
|
97
101
|
|
package/README.zh-CN.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 输入框换一个模型菜单,选模型和调思考强度在同一个弹层里完成。
|
|
4
4
|
|
|
5
5
|
[](./LICENSE)
|
|
6
|
-
[](#兼容性)
|
|
7
7
|
|
|
8
8
|
[English](./README.md) | 简体中文
|
|
9
9
|
|
|
@@ -35,23 +35,27 @@
|
|
|
35
35
|
|
|
36
36
|
## 安装
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
需要 DSH `0.2.2-rc.1` 或更新版本,并自带 `dsh-client-ui-conversation`、`dsh-client-ui-model-selection` 和 `dsh-client-locale` 三个客户端包(见[兼容性](#兼容性))。
|
|
39
39
|
|
|
40
40
|
### Web 端
|
|
41
41
|
|
|
42
42
|
用 CLI 管理 profile 时,一条命令装进 `web`。
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
|
-
dsh plugin --profile web add
|
|
45
|
+
dsh plugin --profile web add dsh-effort-menu
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
本插件没有构建步骤,浏览器端就是仓库里提交的 `client.js`。装完重启 `dsh web`,或者刷新页面。
|
|
49
49
|
|
|
50
50
|
### 桌面端
|
|
51
51
|
|
|
52
|
-
`desktop` profile
|
|
52
|
+
`desktop` profile 由应用管理。先完全退出桌面应用,再用 CLI 安装;或者不退出,打开应用内的 **Plugin Manager** 选择安装这个目录。
|
|
53
53
|
|
|
54
54
|
```sh
|
|
55
|
+
# CLI 安装,需要先完全退出桌面应用
|
|
56
|
+
dsh plugin --profile desktop add dsh-effort-menu
|
|
57
|
+
|
|
58
|
+
# 或者克隆下来,在 Plugin Manager 里选择这个目录
|
|
55
59
|
git clone https://github.com/xuanfenglajiao/dsh-effort-menu.git
|
|
56
60
|
```
|
|
57
61
|
|
|
@@ -65,7 +69,7 @@ git clone https://github.com/xuanfenglajiao/dsh-effort-menu.git
|
|
|
65
69
|
|
|
66
70
|
```json
|
|
67
71
|
"dependencies": {
|
|
68
|
-
"dsh-effort-menu": "
|
|
72
|
+
"dsh-effort-menu": "^1.0.0"
|
|
69
73
|
}
|
|
70
74
|
```
|
|
71
75
|
|
|
@@ -81,7 +85,7 @@ git clone https://github.com/xuanfenglajiao/dsh-effort-menu.git
|
|
|
81
85
|
|
|
82
86
|
```json
|
|
83
87
|
"dependencies": {
|
|
84
|
-
"dsh-effort-menu": "
|
|
88
|
+
"dsh-effort-menu": "^1.0.0"
|
|
85
89
|
}
|
|
86
90
|
```
|
|
87
91
|
|
|
@@ -91,7 +95,7 @@ git clone https://github.com/xuanfenglajiao/dsh-effort-menu.git
|
|
|
91
95
|
|
|
92
96
|
## 兼容性
|
|
93
97
|
|
|
94
|
-
-
|
|
98
|
+
- 支持 DSH `0.2.2-rc.1` 及更新版本。开发和测试用的是 DeepSeek Harness Desktop 44.7.0(DSH `0.2.2-rc.1`),Windows 11;更早的 DSH 版本不受支持。
|
|
95
99
|
- 当前模型必须公布 `reasoning.efforts`,滑杆才有内容可显示。否则菜单会提示该模型没有可选档位。
|
|
96
100
|
- 在子代理会话中不渲染任何内容。
|
|
97
101
|
|
package/client.js
CHANGED
|
@@ -22,6 +22,7 @@ window.__ModuleLoader__.load({
|
|
|
22
22
|
'menu.recent': '最近使用',
|
|
23
23
|
'menu.allModels': '全部模型',
|
|
24
24
|
'menu.effort': '思考强度',
|
|
25
|
+
'menu.manageModels': '管理模型',
|
|
25
26
|
'provider.account': 'DeepSeek 账号',
|
|
26
27
|
'trigger.fallback': '请选择模型',
|
|
27
28
|
'trigger.selectAria': '请选择模型',
|
|
@@ -44,6 +45,7 @@ window.__ModuleLoader__.load({
|
|
|
44
45
|
'menu.recent': 'Recently used',
|
|
45
46
|
'menu.allModels': 'All models',
|
|
46
47
|
'menu.effort': 'Thinking intensity',
|
|
48
|
+
'menu.manageModels': 'Manage models',
|
|
47
49
|
'provider.account': 'DeepSeek Account',
|
|
48
50
|
'trigger.fallback': 'Select model',
|
|
49
51
|
'trigger.selectAria': 'Select model',
|
|
@@ -87,7 +89,7 @@ window.__ModuleLoader__.load({
|
|
|
87
89
|
.es-providerChevron{width:14px;height:14px;flex:none;color:var(--dsw-alias-label-caption,var(--dsw-alias-label-secondary));transition:transform .18s ease}
|
|
88
90
|
.es-flyout{box-sizing:border-box;position:fixed;z-index:1101;width:280px;max-width:calc(100vw - 24px);max-height:min(480px,calc(100vh - 24px));display:flex;flex-direction:column;padding:6px;border-radius:12px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-specific-input-major,var(--dsw-alias-bg-overlay));box-shadow:var(--dsw-elevation-prominent,0 18px 48px rgba(0,0,0,.45));color:var(--dsw-alias-label-primary);font-size:var(--dsh-content-font-size,13px);line-height:calc(20px + var(--dsh-content-font-delta,0px));overflow:hidden}
|
|
89
91
|
.es-flyout:focus{outline:none}
|
|
90
|
-
.es-flyoutList{display:flex;flex-direction:column;min-height:0;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}
|
|
92
|
+
.es-flyoutList{display:flex;flex-direction:column;min-height:0;flex:1;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}
|
|
91
93
|
.es-sectionLabel{flex:none;padding:7px 8px 3px;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-size:calc(11px + var(--dsh-content-font-delta-secondary,0px));line-height:16px;font-weight:500}
|
|
92
94
|
.es-modelGroup{display:flex;flex-direction:column}
|
|
93
95
|
.es-groupHeading{flex:none;padding:6px 8px 2px;color:var(--dsw-alias-label-tertiary,var(--dsw-alias-label-secondary));font-size:calc(11px + var(--dsh-content-font-delta-secondary,0px));line-height:16px;font-weight:500}
|
|
@@ -102,6 +104,13 @@ window.__ModuleLoader__.load({
|
|
|
102
104
|
.es-check{flex:0 0 14px;display:grid;place-items:center;color:var(--dsw-alias-label-primary)}
|
|
103
105
|
.es-effortDivider{border-top:1px solid var(--dsw-alias-border-l1);margin:8px 0 0;flex:none}
|
|
104
106
|
.es-effortBlock{padding:8px 2px 2px;border-radius:10px;flex:none}
|
|
107
|
+
.es-footerDivider{border-top:1px solid var(--dsw-alias-border-l1);margin:8px 0 0;flex:none}
|
|
108
|
+
.es-footer{padding:4px 0 0;flex:none}
|
|
109
|
+
.es-manageRow{box-sizing:border-box;width:100%;height:calc(36px + var(--dsh-content-font-delta,0px));display:flex;align-items:center;gap:8px;padding:0 8px;margin:0;border:0;border-radius:8px;background:0 0;color:inherit;font:inherit;text-align:left;cursor:pointer;outline:none;transition:background-color .16s ease}
|
|
110
|
+
@media (hover:hover){.es-manageRow:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}}
|
|
111
|
+
.es-manageRow:focus-visible{background:var(--dsw-alias-interactive-bg-hover);box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l2)}
|
|
112
|
+
.es-manageRow:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}
|
|
113
|
+
.es-manageLabel{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:var(--dsh-content-font-size,13px);line-height:calc(20px + var(--dsh-content-font-delta,0px))}
|
|
105
114
|
.es-effortHeader{display:flex;align-items:center;gap:8px;padding:2px 6px 0}
|
|
106
115
|
.es-effortLabel{color:var(--dsw-alias-label-primary);flex:none;font-size:var(--dsh-content-font-size,13px);line-height:calc(20px + var(--dsh-content-font-delta,0px))}
|
|
107
116
|
.es-effortValue{color:var(--dsw-alias-label-primary);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:var(--dsh-content-font-size,13px);line-height:calc(20px + var(--dsh-content-font-delta,0px))}
|
|
@@ -212,6 +221,98 @@ window.__ModuleLoader__.load({
|
|
|
212
221
|
);
|
|
213
222
|
}
|
|
214
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Click the Models row in an open Settings dialog. The row index comes from the
|
|
226
|
+
* live `settings.section` ledger (the shell sorts its nav by `order`), with a
|
|
227
|
+
* localized label fallback for shells whose registry is not exposed here.
|
|
228
|
+
*/
|
|
229
|
+
function selectModelsSection(scope, panel) {
|
|
230
|
+
let index = -1;
|
|
231
|
+
try {
|
|
232
|
+
const sections = scope.slots
|
|
233
|
+
.entries('settings.section')
|
|
234
|
+
.map((entry) => ({
|
|
235
|
+
id: entry.options === undefined || entry.options.id === undefined ? '' : entry.options.id,
|
|
236
|
+
order: entry.options === undefined || entry.options.order === undefined ? 0 : entry.options.order,
|
|
237
|
+
}))
|
|
238
|
+
.sort((left, right) => left.order - right.order);
|
|
239
|
+
index = sections.findIndex((section) => section.id === 'models');
|
|
240
|
+
} catch (_error) {
|
|
241
|
+
index = -1;
|
|
242
|
+
}
|
|
243
|
+
const nav = panel.querySelector('nav');
|
|
244
|
+
const buttons = nav === null ? [] : Array.from(nav.querySelectorAll('button'));
|
|
245
|
+
let target = index >= 0 && index < buttons.length ? buttons[index] : null;
|
|
246
|
+
if (target === null) {
|
|
247
|
+
const label = /^(模型|Models?|モデル|모델|Модели|Modèles|Modelle|Modelos|Modelli)$/i;
|
|
248
|
+
target = buttons.find((button) => label.test((button.textContent || '').trim())) || null;
|
|
249
|
+
}
|
|
250
|
+
if (target !== null) target.click();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Open Settings on its Models section. The settings shell keeps its open
|
|
255
|
+
* state in the store registered on `sidebar.settings`, so the exact live
|
|
256
|
+
* instance is reachable through the slot ledger. If that route is absent,
|
|
257
|
+
* invoke the shipped `settings.open` command and click the Models row in the
|
|
258
|
+
* rendered dialog instead.
|
|
259
|
+
*/
|
|
260
|
+
function openModelsSettings(scope) {
|
|
261
|
+
const existing = document.querySelector('[data-shortcut-modal="settings"]');
|
|
262
|
+
if (existing !== null) {
|
|
263
|
+
selectModelsSection(scope, existing);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
let opened = false;
|
|
267
|
+
try {
|
|
268
|
+
const entries =
|
|
269
|
+
typeof scope.slots.entriesOfSlot === 'function'
|
|
270
|
+
? scope.slots.entriesOfSlot('sidebar.settings')
|
|
271
|
+
: scope.slots.entries('sidebar.settings');
|
|
272
|
+
const entry = entries.find(
|
|
273
|
+
(candidate) =>
|
|
274
|
+
candidate !== undefined &&
|
|
275
|
+
candidate !== null &&
|
|
276
|
+
candidate.store !== undefined &&
|
|
277
|
+
typeof candidate.store.create === 'function',
|
|
278
|
+
);
|
|
279
|
+
const instance = entry === undefined ? undefined : entry.store.create();
|
|
280
|
+
if (
|
|
281
|
+
instance !== undefined &&
|
|
282
|
+
instance !== null &&
|
|
283
|
+
instance.actions !== undefined &&
|
|
284
|
+
typeof instance.actions.openSection === 'function'
|
|
285
|
+
) {
|
|
286
|
+
instance.actions.openSection('models');
|
|
287
|
+
opened = true;
|
|
288
|
+
}
|
|
289
|
+
} catch (_error) {
|
|
290
|
+
opened = false;
|
|
291
|
+
}
|
|
292
|
+
if (!opened) {
|
|
293
|
+
const shortcuts = scope.shortcuts;
|
|
294
|
+
const registry = shortcuts === undefined || shortcuts === null ? undefined : shortcuts.registry;
|
|
295
|
+
if (registry !== undefined && registry !== null && typeof registry.invoke === 'function') {
|
|
296
|
+
registry.invoke('settings.open', { region: 'page', modal: null, target: null });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// The dialog is portaled on the next React commit; retry briefly when the
|
|
300
|
+
// shell store route missed and the command opened the dialog asynchronously.
|
|
301
|
+
let attempts = 0;
|
|
302
|
+
const settle = () => {
|
|
303
|
+
const panel = document.querySelector('[data-shortcut-modal="settings"]');
|
|
304
|
+
if (panel === null) {
|
|
305
|
+
if (attempts < 40) {
|
|
306
|
+
attempts += 1;
|
|
307
|
+
window.setTimeout(settle, 50);
|
|
308
|
+
}
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
selectModelsSection(scope, panel);
|
|
312
|
+
};
|
|
313
|
+
window.setTimeout(settle, 30);
|
|
314
|
+
}
|
|
315
|
+
|
|
215
316
|
const RECENT_KEY = 'dsh-effort-menu/recent-models';
|
|
216
317
|
/** The menu lists at most this many recent models, the current one included. */
|
|
217
318
|
const RECENT_VISIBLE = 3;
|
|
@@ -576,7 +677,7 @@ window.__ModuleLoader__.load({
|
|
|
576
677
|
* @param props - owner share (`locked`), the injected directory face, and locale `t`.
|
|
577
678
|
* @returns the trigger button plus, while open, a portaled menu.
|
|
578
679
|
*/
|
|
579
|
-
function EffortSeat({ locked, available, directory, load, select, resolveText, t }) {
|
|
680
|
+
function EffortSeat({ locked, available, directory, load, select, resolveText, t, manageModels }) {
|
|
580
681
|
const state = React.useSyncExternalStore(
|
|
581
682
|
(callback) => directory.subscribe(callback),
|
|
582
683
|
() => directory.getSnapshot(),
|
|
@@ -1159,7 +1260,7 @@ window.__ModuleLoader__.load({
|
|
|
1159
1260
|
return;
|
|
1160
1261
|
}
|
|
1161
1262
|
if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') return;
|
|
1162
|
-
const rows = Array.from(event.currentTarget.querySelectorAll('.es-modelRow')).filter((row) => !row.disabled);
|
|
1263
|
+
const rows = Array.from(event.currentTarget.querySelectorAll('.es-modelRow, .es-manageRow')).filter((row) => !row.disabled);
|
|
1163
1264
|
if (rows.length === 0) return;
|
|
1164
1265
|
event.preventDefault();
|
|
1165
1266
|
const at = rows.indexOf(document.activeElement);
|
|
@@ -1169,6 +1270,23 @@ window.__ModuleLoader__.load({
|
|
|
1169
1270
|
},
|
|
1170
1271
|
},
|
|
1171
1272
|
h('div', { className: 'es-flyoutList' }, allGroups),
|
|
1273
|
+
h('div', { className: 'es-footerDivider' }),
|
|
1274
|
+
h(
|
|
1275
|
+
'div',
|
|
1276
|
+
{ className: 'es-footer' },
|
|
1277
|
+
h(
|
|
1278
|
+
'button',
|
|
1279
|
+
{
|
|
1280
|
+
type: 'button',
|
|
1281
|
+
className: 'es-manageRow',
|
|
1282
|
+
onClick: () => {
|
|
1283
|
+
close(false);
|
|
1284
|
+
if (typeof manageModels === 'function') manageModels();
|
|
1285
|
+
},
|
|
1286
|
+
},
|
|
1287
|
+
h('span', { className: 'es-manageLabel' }, t('menu.manageModels')),
|
|
1288
|
+
),
|
|
1289
|
+
),
|
|
1172
1290
|
);
|
|
1173
1291
|
|
|
1174
1292
|
return h(
|
|
@@ -1203,14 +1321,14 @@ window.__ModuleLoader__.load({
|
|
|
1203
1321
|
return currentModel === null && state.current !== null && state.current !== undefined;
|
|
1204
1322
|
}
|
|
1205
1323
|
|
|
1206
|
-
const inject = ['locale', 'slots', 'modelDirectories', 'sessions'];
|
|
1324
|
+
const inject = ['locale', 'slots', 'modelDirectories', 'sessions', 'shortcuts'];
|
|
1207
1325
|
|
|
1208
1326
|
/**
|
|
1209
1327
|
* Mount the replacement seat over the shipped per-session model directory.
|
|
1210
1328
|
* @param ctx - client root context.
|
|
1211
1329
|
*/
|
|
1212
1330
|
function apply(ctx) {
|
|
1213
|
-
ctx.inject(['locale', 'slots', 'modelDirectories', 'sessions'], (scope) => {
|
|
1331
|
+
ctx.inject(['locale', 'slots', 'modelDirectories', 'sessions', 'shortcuts'], (scope) => {
|
|
1214
1332
|
scope.effect(() => scope.locale.register(NS, { zh, en }), 'effort-menu: dictionaries');
|
|
1215
1333
|
const models = scope.modelDirectories;
|
|
1216
1334
|
const sessions = scope.sessions;
|
|
@@ -1231,6 +1349,7 @@ window.__ModuleLoader__.load({
|
|
|
1231
1349
|
},
|
|
1232
1350
|
resolveText: (text) => scope.locale.resolveText(text),
|
|
1233
1351
|
select: (selection) => (available ? directory.select(selection) : Promise.resolve(undefined)),
|
|
1352
|
+
manageModels: () => openModelsSettings(scope),
|
|
1234
1353
|
};
|
|
1235
1354
|
},
|
|
1236
1355
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-effort-menu",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Replaces the composer's model menu with a recent-models list, an all-providers flyout, and a draggable reasoning-effort slider.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh-plugin",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"immediately": true,
|
|
57
57
|
"inject": [
|
|
58
58
|
"@deepseek-ai/dsh-client-locale",
|
|
59
|
+
"@deepseek-ai/dsh-client-shortcuts",
|
|
59
60
|
"@deepseek-ai/dsh-client-ui-conversation",
|
|
60
61
|
"@deepseek-ai/dsh-client-ui-model-selection"
|
|
61
62
|
]
|