dsh-effort-menu 1.0.1 → 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/client.js +124 -5
- package/package.json +2 -1
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.0
|
|
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
|
]
|