@morlay/dsh-session-mode 0.0.1-alpha.4 → 0.0.1
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 +80 -67
- package/dist/client.cjs +165 -802
- package/dist/client.d.cts +22 -187
- package/dist/client.d.mts +22 -187
- package/dist/index.d.cts +183 -9
- package/dist/index.d.mts +183 -9
- package/dist/index.mjs +183 -31
- package/dist/rows.d.cts +38 -0
- package/dist/rows.d.mts +38 -0
- package/dist/rows.mjs +503 -0
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +46 -53
- package/src/client/SessionModeSeat.tsx +38 -19
- package/src/client/index.ts +143 -92
- package/src/client/locales.ts +18 -72
- package/src/index.ts +145 -18
- package/{tool/modes.ts → src/mode-sources.ts} +32 -14
- package/src/modes.ts +197 -36
- package/src/rows.ts +57 -0
- package/cordis.patch.yml +0 -69
- package/dist/shared-FNWe-_rS.d.cts +0 -106
- package/dist/shared-FNWe-_rS.d.mts +0 -106
- package/src/client/ModelDefaultsCard.styles.ts +0 -134
- package/src/client/ModelDefaultsCard.tsx +0 -233
- package/src/client/SessionModeLabel.module.css +0 -24
- package/src/client/SessionModeLabel.tsx +0 -44
- package/src/client/model-defaults-card-controller.ts +0 -408
- package/tool/patch.ts +0 -91
package/dist/client.cjs
CHANGED
|
@@ -5,694 +5,8 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
|
-
let _morlay_dsh_client_ui_primitives_client = require("@morlay/dsh-client-ui-primitives/client");
|
|
9
|
-
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
-
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
11
8
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
12
|
-
|
|
13
|
-
const zh = {
|
|
14
|
-
seatHint: "选择这个会话的模式",
|
|
15
|
-
headerHint: "这个会话运行的模式",
|
|
16
|
-
noDescription: "(无说明)",
|
|
17
|
-
switchRefused: "切换失败:{reason}",
|
|
18
|
-
loadFailed: "读不到模式清单",
|
|
19
|
-
cardTitle: "会话模式",
|
|
20
|
-
cardDescription: "给每个会话模式指定默认模型;不指定就跟全局默认模型。",
|
|
21
|
-
defaultsTitle: "各模式的默认模型",
|
|
22
|
-
defaultsHint: "只在会话还没有模型事实(没选过模型、也还没跑过请求)时生效;留空就是跟全局默认模型。改完按下面的保存写进设置。",
|
|
23
|
-
providerLabel: "服务商",
|
|
24
|
-
modelLabel: "模型",
|
|
25
|
-
effortLabel: "思考档位",
|
|
26
|
-
inheritOption: "跟全局默认",
|
|
27
|
-
chooseModel: "选择模型…",
|
|
28
|
-
effortDefaultOption: "跟模型默认",
|
|
29
|
-
staleOption: "{value}(不在目录里)",
|
|
30
|
-
overridden: "已覆盖",
|
|
31
|
-
reset: "恢复默认",
|
|
32
|
-
modelRequired: "还没选模型",
|
|
33
|
-
readOnly: "本部署的设置为只读。",
|
|
34
|
-
unavailable: "本插件当前未加载,暂时无法配置。",
|
|
35
|
-
save: "保存",
|
|
36
|
-
saving: "保存中…",
|
|
37
|
-
saveFailed: "本部署没有接受这些值,已保留供你修改。",
|
|
38
|
-
rosterFailed: "读不到会话模式清单,暂时列不出模式。",
|
|
39
|
-
directoryFailed: "读不到模型目录,暂时只能看当前值或恢复默认。",
|
|
40
|
-
directoryPartial: "这些服务商的目录没读到:{names}"
|
|
41
|
-
};
|
|
42
|
-
const en = {
|
|
43
|
-
seatHint: "Pick the mode for this session",
|
|
44
|
-
headerHint: "The mode this session runs",
|
|
45
|
-
noDescription: "(no description)",
|
|
46
|
-
switchRefused: "Switch refused: {reason}",
|
|
47
|
-
loadFailed: "Could not load the mode roster",
|
|
48
|
-
cardTitle: "Session mode",
|
|
49
|
-
cardDescription: "Set the default model of each session mode; unset follows the global default.",
|
|
50
|
-
defaultsTitle: "Default model per mode",
|
|
51
|
-
defaultsHint: "Applies only while a session has no model fact yet (no model picked, no request made); unset follows the global default model. Save writes it into the settings document.",
|
|
52
|
-
providerLabel: "Provider",
|
|
53
|
-
modelLabel: "Model",
|
|
54
|
-
effortLabel: "Reasoning effort",
|
|
55
|
-
inheritOption: "Same as global default",
|
|
56
|
-
chooseModel: "Choose a model…",
|
|
57
|
-
effortDefaultOption: "Model default",
|
|
58
|
-
staleOption: "{value} (not in the directory)",
|
|
59
|
-
overridden: "Overridden",
|
|
60
|
-
reset: "Reset to default",
|
|
61
|
-
modelRequired: "No model picked yet",
|
|
62
|
-
readOnly: "This deployment stores settings read-only.",
|
|
63
|
-
unavailable: "This plugin is not loaded, so it cannot be configured right now.",
|
|
64
|
-
save: "Save",
|
|
65
|
-
saving: "Saving…",
|
|
66
|
-
saveFailed: "The deployment did not accept these values; they were left for you to correct.",
|
|
67
|
-
rosterFailed: "Could not load the session mode roster, so no mode can be listed.",
|
|
68
|
-
directoryFailed: "Could not read the model directory, so only the current value or a reset is available.",
|
|
69
|
-
directoryPartial: "These providers could not be read: {names}"
|
|
70
|
-
};
|
|
71
|
-
/**
|
|
72
|
-
* 共享设置表单外框的文案,从本页字典里读。
|
|
73
|
-
* @param t - 本页的字典读取器。
|
|
74
|
-
* @returns `SettingsForm` 渲染用的标签。
|
|
75
|
-
*/
|
|
76
|
-
function formLabels(t) {
|
|
77
|
-
return {
|
|
78
|
-
unavailable: t("unavailable"),
|
|
79
|
-
readOnly: t("readOnly"),
|
|
80
|
-
saveFailed: t("saveFailed"),
|
|
81
|
-
save: t("save"),
|
|
82
|
-
saving: t("saving")
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
//#endregion
|
|
86
|
-
//#region src/client/ModelDefaultsCard.styles.ts
|
|
87
|
-
const styles = {
|
|
88
|
-
section: {
|
|
89
|
-
display: "flex",
|
|
90
|
-
flexDirection: "column",
|
|
91
|
-
gap: "12px"
|
|
92
|
-
},
|
|
93
|
-
heading: {
|
|
94
|
-
margin: "0",
|
|
95
|
-
fontSize: "14px",
|
|
96
|
-
lineHeight: "20px",
|
|
97
|
-
fontWeight: "600",
|
|
98
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.label.primary
|
|
99
|
-
},
|
|
100
|
-
hint: {
|
|
101
|
-
margin: "0",
|
|
102
|
-
fontSize: "12px",
|
|
103
|
-
lineHeight: "1.5",
|
|
104
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.label.tertiary
|
|
105
|
-
},
|
|
106
|
-
notice: {
|
|
107
|
-
margin: "0",
|
|
108
|
-
fontSize: "12px",
|
|
109
|
-
lineHeight: "1.5",
|
|
110
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.state.warn.label
|
|
111
|
-
},
|
|
112
|
-
rows: {
|
|
113
|
-
display: "flex",
|
|
114
|
-
flexDirection: "column",
|
|
115
|
-
gap: "12px"
|
|
116
|
-
},
|
|
117
|
-
row: {
|
|
118
|
-
display: "flex",
|
|
119
|
-
flexDirection: "column",
|
|
120
|
-
gap: "8px",
|
|
121
|
-
padding: "10px 12px",
|
|
122
|
-
border: `0.5px solid ${String(_morlay_dsh_client_ui_primitives_client.dsw.alias.border.l2)}`,
|
|
123
|
-
borderRadius: "8px"
|
|
124
|
-
},
|
|
125
|
-
rowHead: {
|
|
126
|
-
display: "flex",
|
|
127
|
-
alignItems: "center",
|
|
128
|
-
gap: "8px"
|
|
129
|
-
},
|
|
130
|
-
rowName: {
|
|
131
|
-
fontSize: "13px",
|
|
132
|
-
lineHeight: "1.5",
|
|
133
|
-
fontWeight: "500",
|
|
134
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.label.primary
|
|
135
|
-
},
|
|
136
|
-
badge: {
|
|
137
|
-
display: "inline-flex",
|
|
138
|
-
alignItems: "center",
|
|
139
|
-
borderRadius: "999px",
|
|
140
|
-
padding: "1px 8px",
|
|
141
|
-
fontSize: "11px",
|
|
142
|
-
lineHeight: "17px",
|
|
143
|
-
background: _morlay_dsh_client_ui_primitives_client.dsw.alias.bg.module.platform,
|
|
144
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.label.secondary
|
|
145
|
-
},
|
|
146
|
-
reset: {
|
|
147
|
-
marginLeft: "auto",
|
|
148
|
-
border: "none",
|
|
149
|
-
background: "none",
|
|
150
|
-
padding: 0,
|
|
151
|
-
font: "inherit",
|
|
152
|
-
fontSize: "12px",
|
|
153
|
-
lineHeight: "1.5",
|
|
154
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.label.secondary,
|
|
155
|
-
cursor: "pointer",
|
|
156
|
-
"&:hover:not(:disabled)": { color: _morlay_dsh_client_ui_primitives_client.dsw.alias.label.primary },
|
|
157
|
-
"&:disabled": { cursor: "default" }
|
|
158
|
-
},
|
|
159
|
-
rowHint: {
|
|
160
|
-
margin: "0",
|
|
161
|
-
fontSize: "12px",
|
|
162
|
-
lineHeight: "1.5",
|
|
163
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.label.tertiary
|
|
164
|
-
},
|
|
165
|
-
fields: {
|
|
166
|
-
display: "grid",
|
|
167
|
-
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
|
|
168
|
-
gap: "12px"
|
|
169
|
-
},
|
|
170
|
-
field: {
|
|
171
|
-
display: "flex",
|
|
172
|
-
flexDirection: "column",
|
|
173
|
-
gap: "4px",
|
|
174
|
-
minWidth: 0
|
|
175
|
-
},
|
|
176
|
-
fieldLabel: {
|
|
177
|
-
fontSize: "12px",
|
|
178
|
-
lineHeight: "1.5",
|
|
179
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.label.secondary
|
|
180
|
-
},
|
|
181
|
-
select: {
|
|
182
|
-
height: "30px",
|
|
183
|
-
padding: "0 8px",
|
|
184
|
-
border: `0.5px solid ${String(_morlay_dsh_client_ui_primitives_client.dsw.alias.border.l4)}`,
|
|
185
|
-
borderRadius: "8px",
|
|
186
|
-
background: _morlay_dsh_client_ui_primitives_client.dsw.alias.bg.layer["3"],
|
|
187
|
-
font: "inherit",
|
|
188
|
-
fontSize: "13px",
|
|
189
|
-
lineHeight: "1.5",
|
|
190
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.label.primary,
|
|
191
|
-
"&:focus-visible": {
|
|
192
|
-
outline: "none",
|
|
193
|
-
borderColor: _morlay_dsh_client_ui_primitives_client.dsw.alias.brand.primary
|
|
194
|
-
},
|
|
195
|
-
"&:disabled": {
|
|
196
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.label.tertiary,
|
|
197
|
-
cursor: "default"
|
|
198
|
-
}
|
|
199
|
-
},
|
|
200
|
-
invalid: {
|
|
201
|
-
fontSize: "12px",
|
|
202
|
-
lineHeight: "1.5",
|
|
203
|
-
color: _morlay_dsh_client_ui_primitives_client.dsw.alias.state.error.primary
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
//#endregion
|
|
207
|
-
//#region src/client/ModelDefaultsCard.tsx
|
|
208
|
-
/**
|
|
209
|
-
* 设置页「会话模式」那张卡片:每个模式一行「默认模型」,一段一起保存。
|
|
210
|
-
*
|
|
211
|
-
* 页面(Plugins 列表)画标题与一句话说明:`view: 'summary'` 给那句话,`view: 'page'` 给表单。编辑是 staged
|
|
212
|
-
* 的——选择器只改草稿,按保存才写一次 `mutate`;"恢复默认"(或把 provider 选回「跟全局默认」)把这一行的
|
|
213
|
-
* 用户层清掉,回到装配层那份。控件是本卡自研的:这里的编辑面是一张**动态映射**(模式 → provider / model /
|
|
214
|
-
* 档位),原语的标量字段控件套不进去——只借它的壳(保存 / 丢弃 / 只读 / 失败与 `SettingsFormShell`)。
|
|
215
|
-
*/
|
|
216
|
-
/**
|
|
217
|
-
* 选择器的选项:目录里的那些 + 当前值那条(不在目录里时补上)。
|
|
218
|
-
*
|
|
219
|
-
* 补当前值是必须的:`<select>` 的 value 不在选项里时浏览器会显示成别的选项,用户按保存就会把它写成
|
|
220
|
-
* 那个值——配置被悄悄改掉。(目录读不到、或适配器卸了的时候正是这个局面。)
|
|
221
|
-
* @param entries - 目录里的选项(provider 或 model)。
|
|
222
|
-
* @param current - 当前生效值的那一项 id。
|
|
223
|
-
* @param t - 本页字典。
|
|
224
|
-
* @returns 选项列表。
|
|
225
|
-
*/
|
|
226
|
-
function options(entries, current, t) {
|
|
227
|
-
if (current === void 0 || entries.some((entry) => entry.id === current)) return [...entries];
|
|
228
|
-
return [...entries, {
|
|
229
|
-
id: current,
|
|
230
|
-
name: t("staleOption", { value: current })
|
|
231
|
-
}];
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* 画一个模式的默认模型那一行。
|
|
235
|
-
* @param props - 这一行的值、可选项、字典与编辑动作。
|
|
236
|
-
* @returns 一行选择器(provider / model / 档位)与"恢复默认"。
|
|
237
|
-
*/
|
|
238
|
-
function ModeRow({ row, groups, disabled, t, onProvider, onModel, onEffort, onReset }) {
|
|
239
|
-
const models = groups.find((group) => group.id === row.value?.provider)?.models ?? [];
|
|
240
|
-
const efforts = models.find((entry) => entry.id === row.value?.model)?.reasoning?.efforts ?? [];
|
|
241
|
-
const withEffort = efforts.length > 0 || row.value?.reasoningEffort !== void 0;
|
|
242
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
243
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.row),
|
|
244
|
-
children: [
|
|
245
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
246
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.rowHead),
|
|
247
|
-
children: [
|
|
248
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
249
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.rowName),
|
|
250
|
-
children: row.name
|
|
251
|
-
}),
|
|
252
|
-
row.overridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
253
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.badge),
|
|
254
|
-
children: t("overridden")
|
|
255
|
-
}) : null,
|
|
256
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
257
|
-
type: "button",
|
|
258
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.reset),
|
|
259
|
-
disabled: disabled || !row.overridden,
|
|
260
|
-
onClick: onReset,
|
|
261
|
-
children: t("reset")
|
|
262
|
-
})
|
|
263
|
-
]
|
|
264
|
-
}),
|
|
265
|
-
row.description === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
266
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.rowHint),
|
|
267
|
-
children: row.description
|
|
268
|
-
}),
|
|
269
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
270
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.fields),
|
|
271
|
-
children: [
|
|
272
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
273
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.field),
|
|
274
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
275
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.fieldLabel),
|
|
276
|
-
children: t("providerLabel")
|
|
277
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
278
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.select),
|
|
279
|
-
value: row.value?.provider ?? "",
|
|
280
|
-
disabled,
|
|
281
|
-
onChange: (event) => {
|
|
282
|
-
onProvider(event.target.value);
|
|
283
|
-
},
|
|
284
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
285
|
-
value: "",
|
|
286
|
-
children: t("inheritOption")
|
|
287
|
-
}), options(groups.map((group) => ({
|
|
288
|
-
id: group.id,
|
|
289
|
-
name: group.name
|
|
290
|
-
})), row.value?.provider, t).map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
291
|
-
value: option.id,
|
|
292
|
-
children: option.name
|
|
293
|
-
}, option.id))]
|
|
294
|
-
})]
|
|
295
|
-
}),
|
|
296
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
297
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.field),
|
|
298
|
-
children: [
|
|
299
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
300
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.fieldLabel),
|
|
301
|
-
children: t("modelLabel")
|
|
302
|
-
}),
|
|
303
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
304
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.select),
|
|
305
|
-
value: row.value?.model ?? "",
|
|
306
|
-
disabled: disabled || row.value === void 0,
|
|
307
|
-
onChange: (event) => {
|
|
308
|
-
onModel(event.target.value);
|
|
309
|
-
},
|
|
310
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
311
|
-
value: "",
|
|
312
|
-
children: t("chooseModel")
|
|
313
|
-
}), options(models.map((entry) => ({
|
|
314
|
-
id: entry.id,
|
|
315
|
-
name: entry.name
|
|
316
|
-
})), row.value?.model, t).map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
317
|
-
value: option.id,
|
|
318
|
-
children: option.name
|
|
319
|
-
}, option.id))]
|
|
320
|
-
}),
|
|
321
|
-
row.invalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
322
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.invalid),
|
|
323
|
-
role: "status",
|
|
324
|
-
children: t("modelRequired")
|
|
325
|
-
}) : null
|
|
326
|
-
]
|
|
327
|
-
}),
|
|
328
|
-
withEffort ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
329
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.field),
|
|
330
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
331
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.fieldLabel),
|
|
332
|
-
children: t("effortLabel")
|
|
333
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
334
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.select),
|
|
335
|
-
value: row.value?.reasoningEffort ?? "",
|
|
336
|
-
disabled: disabled || row.value?.model === void 0 || row.value.model === "",
|
|
337
|
-
onChange: (event) => {
|
|
338
|
-
onEffort(event.target.value);
|
|
339
|
-
},
|
|
340
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
341
|
-
value: "",
|
|
342
|
-
children: t("effortDefaultOption")
|
|
343
|
-
}), options(efforts.map((effort) => ({
|
|
344
|
-
id: effort.id,
|
|
345
|
-
name: effort.name
|
|
346
|
-
})), row.value?.reasoningEffort, t).map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
347
|
-
value: option.id,
|
|
348
|
-
children: option.name
|
|
349
|
-
}, option.id))]
|
|
350
|
-
})]
|
|
351
|
-
}) : null
|
|
352
|
-
]
|
|
353
|
-
})
|
|
354
|
-
]
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* 渲染这张卡片(或它的一句话说明)。
|
|
359
|
-
* @param props - 字典、快照,以及编辑与保存 / 丢弃动作。
|
|
360
|
-
* @returns 摘要文本,或每个模式一行的默认模型表单。
|
|
361
|
-
*/
|
|
362
|
-
function ModelDefaultsCard(props) {
|
|
363
|
-
const { t } = props;
|
|
364
|
-
const state = props.useModelDefaultsCard((snapshot) => snapshot);
|
|
365
|
-
const headingId = (0, react.useId)();
|
|
366
|
-
if (props.view === "summary") return t("cardDescription");
|
|
367
|
-
const disabled = !state.writable || state.saving;
|
|
368
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_morlay_dsh_client_ui_primitives_client.SettingsForm, {
|
|
369
|
-
labels: formLabels(t),
|
|
370
|
-
state,
|
|
371
|
-
onSave: props.save,
|
|
372
|
-
onDiscard: props.discard,
|
|
373
|
-
children: state.available ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
374
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.section),
|
|
375
|
-
"aria-labelledby": headingId,
|
|
376
|
-
children: [
|
|
377
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
378
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.heading),
|
|
379
|
-
id: headingId,
|
|
380
|
-
children: t("defaultsTitle")
|
|
381
|
-
}),
|
|
382
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
383
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.hint),
|
|
384
|
-
children: t("defaultsHint")
|
|
385
|
-
}),
|
|
386
|
-
state.rosterStatus === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
387
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.notice),
|
|
388
|
-
role: "status",
|
|
389
|
-
children: t("rosterFailed")
|
|
390
|
-
}) : null,
|
|
391
|
-
state.directoryStatus === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
392
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.notice),
|
|
393
|
-
role: "status",
|
|
394
|
-
children: t("directoryFailed")
|
|
395
|
-
}) : null,
|
|
396
|
-
state.directoryFailures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
397
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.notice),
|
|
398
|
-
role: "status",
|
|
399
|
-
children: t("directoryPartial", { names: state.directoryFailures.join("、") })
|
|
400
|
-
}) : null,
|
|
401
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
402
|
-
..._morlay_dsh_client_ui_primitives_client.styling.props(styles.rows),
|
|
403
|
-
children: state.rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModeRow, {
|
|
404
|
-
row,
|
|
405
|
-
groups: state.directory,
|
|
406
|
-
disabled,
|
|
407
|
-
t,
|
|
408
|
-
onProvider: (provider) => {
|
|
409
|
-
props.setProvider(row.id, provider);
|
|
410
|
-
},
|
|
411
|
-
onModel: (model) => {
|
|
412
|
-
props.setModel(row.id, model);
|
|
413
|
-
},
|
|
414
|
-
onEffort: (effort) => {
|
|
415
|
-
props.setEffort(row.id, effort);
|
|
416
|
-
},
|
|
417
|
-
onReset: () => {
|
|
418
|
-
props.resetMode(row.id);
|
|
419
|
-
}
|
|
420
|
-
}, row.id))
|
|
421
|
-
})
|
|
422
|
-
]
|
|
423
|
-
}) : null
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
//#endregion
|
|
427
|
-
//#region src/client/model-defaults-card-controller.ts
|
|
428
|
-
/** 本包 host 行 id:settings 的命名空间名与 `config` 的 volatile 字段都在它下面。 */
|
|
429
|
-
const SESSION_MODE_NS = "session-mode";
|
|
430
|
-
/** 从 settings 的一层里取 `models`(层是原始 JSON 形状,取不到就是空)。 */
|
|
431
|
-
function layerModels(layer) {
|
|
432
|
-
const models = layer?.models;
|
|
433
|
-
return typeof models === "object" && models !== null ? models : {};
|
|
434
|
-
}
|
|
435
|
-
/** 两份模型是不是同一份(三个字段逐个比;缺省字段与 `undefined` 同义)。 */
|
|
436
|
-
function sameModel(left, right) {
|
|
437
|
-
return right !== void 0 && left.provider === right.provider && left.model === right.model && left.reasoningEffort === right.reasoningEffort;
|
|
438
|
-
}
|
|
439
|
-
/** 草稿是不是半成品(选了 provider 还没选 model)——半成品不许保存。 */
|
|
440
|
-
function incomplete(draft) {
|
|
441
|
-
return draft !== void 0 && draft !== null && draft.model === "";
|
|
442
|
-
}
|
|
443
|
-
/** 把 staged 的三元组写成 config 里的那个值(空档位不写键,别把"跟模型默认"物化成一个值)。 */
|
|
444
|
-
function modelValue(draft) {
|
|
445
|
-
return {
|
|
446
|
-
provider: draft.provider,
|
|
447
|
-
model: draft.model,
|
|
448
|
-
...draft.reasoningEffort === void 0 ? {} : { reasoningEffort: draft.reasoningEffort }
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
/** 把一张动态映射编辑成一次设置的写。 */
|
|
452
|
-
var ModelDefaultsCardController = class {
|
|
453
|
-
scope;
|
|
454
|
-
sources;
|
|
455
|
-
store;
|
|
456
|
-
unsubscribe;
|
|
457
|
-
drafts = /* @__PURE__ */ new Map();
|
|
458
|
-
roster;
|
|
459
|
-
rosterStatus = "loading";
|
|
460
|
-
directory = [];
|
|
461
|
-
directoryFailures = [];
|
|
462
|
-
directoryStatus = "loading";
|
|
463
|
-
saving = false;
|
|
464
|
-
failed = false;
|
|
465
|
-
disposed = false;
|
|
466
|
-
saveGeneration = 0;
|
|
467
|
-
/**
|
|
468
|
-
* @param scope - 本包 host 行(`session-mode`)的共享配置表单。
|
|
469
|
-
* @param sources - 模式清单与模型目录的两条读取。
|
|
470
|
-
*/
|
|
471
|
-
constructor(scope, sources) {
|
|
472
|
-
this.scope = scope;
|
|
473
|
-
this.sources = sources;
|
|
474
|
-
this.store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(this.projection());
|
|
475
|
-
this.unsubscribe = scope.subscribe(() => {
|
|
476
|
-
this.publish();
|
|
477
|
-
});
|
|
478
|
-
this.loadRoster();
|
|
479
|
-
this.loadDirectory();
|
|
480
|
-
}
|
|
481
|
-
/** 停止观察设置,并让晚到的写入结果不再落到状态上。 */
|
|
482
|
-
dispose() {
|
|
483
|
-
this.disposed = true;
|
|
484
|
-
this.saveGeneration += 1;
|
|
485
|
-
this.unsubscribe();
|
|
486
|
-
}
|
|
487
|
-
/**
|
|
488
|
-
* 把状态与动作绑到槽位渲染器上。
|
|
489
|
-
* @returns 快照与编辑、保存动作。
|
|
490
|
-
*/
|
|
491
|
-
inject() {
|
|
492
|
-
return {
|
|
493
|
-
hooks: { modelDefaultsCard: this.store },
|
|
494
|
-
setProvider: (modeId, provider) => {
|
|
495
|
-
this.setProvider(modeId, provider);
|
|
496
|
-
},
|
|
497
|
-
setModel: (modeId, model) => {
|
|
498
|
-
this.setModel(modeId, model);
|
|
499
|
-
},
|
|
500
|
-
setEffort: (modeId, effort) => {
|
|
501
|
-
this.setEffort(modeId, effort);
|
|
502
|
-
},
|
|
503
|
-
resetMode: (modeId) => {
|
|
504
|
-
this.stage(modeId, null);
|
|
505
|
-
},
|
|
506
|
-
save: () => {
|
|
507
|
-
this.save();
|
|
508
|
-
},
|
|
509
|
-
discard: () => {
|
|
510
|
-
this.discard();
|
|
511
|
-
}
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
/** 用户层有没有这一条。 */
|
|
515
|
-
overridden(modeId) {
|
|
516
|
-
return Object.hasOwn(layerModels(this.scope.getSnapshot().user), modeId);
|
|
517
|
-
}
|
|
518
|
-
/** 生效值(用户层叠在装配层上)。 */
|
|
519
|
-
current(modeId) {
|
|
520
|
-
return this.scope.getSnapshot().value?.models?.[modeId];
|
|
521
|
-
}
|
|
522
|
-
/** 装配层那份(用户层清掉之后剩下的)。 */
|
|
523
|
-
base(modeId) {
|
|
524
|
-
return layerModels(this.scope.getSnapshot().base)[modeId];
|
|
525
|
-
}
|
|
526
|
-
/**
|
|
527
|
-
* 这一行现在的值:草稿优先,否则是生效值;草稿是"回到默认"时看装配层那份(它清掉的就是用户层)。
|
|
528
|
-
* 同一个值既是选择器显示的东西,也是下一次编辑的起点。
|
|
529
|
-
* @param modeId - 模式 id。
|
|
530
|
-
* @returns 这一行呈现的模型;`undefined` = 跟全局默认。
|
|
531
|
-
*/
|
|
532
|
-
shown(modeId) {
|
|
533
|
-
const draft = this.drafts.get(modeId);
|
|
534
|
-
if (draft === null) return this.base(modeId);
|
|
535
|
-
return draft ?? this.current(modeId);
|
|
536
|
-
}
|
|
537
|
-
/**
|
|
538
|
-
* 这一行的草稿算不算一次真的编辑。
|
|
539
|
-
* @param modeId - 模式 id。
|
|
540
|
-
* @returns 保存会不会改变什么。
|
|
541
|
-
*/
|
|
542
|
-
dirty(modeId) {
|
|
543
|
-
const draft = this.drafts.get(modeId);
|
|
544
|
-
if (draft === void 0) return false;
|
|
545
|
-
if (draft === null) return this.overridden(modeId);
|
|
546
|
-
return !sameModel(draft, this.current(modeId));
|
|
547
|
-
}
|
|
548
|
-
/** 保存之后这一行会不会留下用户层那一条("已覆盖"与"恢复默认"都看它)。 */
|
|
549
|
-
leavesOverride(modeId) {
|
|
550
|
-
return this.overridden(modeId) || this.drafts.get(modeId) !== void 0;
|
|
551
|
-
}
|
|
552
|
-
setProvider(modeId, provider) {
|
|
553
|
-
if (!this.writable() || provider === this.shown(modeId)?.provider) return;
|
|
554
|
-
if (provider === "") {
|
|
555
|
-
this.stage(modeId, null);
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
this.stage(modeId, {
|
|
559
|
-
provider,
|
|
560
|
-
model: ""
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
setModel(modeId, model) {
|
|
564
|
-
const current = this.shown(modeId);
|
|
565
|
-
if (!this.writable() || current === void 0) return;
|
|
566
|
-
const keep = this.efforts(current.provider, model).some((effort) => effort.id === current.reasoningEffort);
|
|
567
|
-
this.stage(modeId, {
|
|
568
|
-
provider: current.provider,
|
|
569
|
-
model,
|
|
570
|
-
...keep && current.reasoningEffort !== void 0 ? { reasoningEffort: current.reasoningEffort } : {}
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
setEffort(modeId, effort) {
|
|
574
|
-
const current = this.shown(modeId);
|
|
575
|
-
if (!this.writable() || current === void 0) return;
|
|
576
|
-
this.stage(modeId, {
|
|
577
|
-
provider: current.provider,
|
|
578
|
-
model: current.model,
|
|
579
|
-
...effort === "" ? {} : { reasoningEffort: effort }
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
stage(modeId, draft) {
|
|
583
|
-
if (!this.writable()) return;
|
|
584
|
-
this.drafts.set(modeId, draft);
|
|
585
|
-
this.failed = false;
|
|
586
|
-
this.publish();
|
|
587
|
-
}
|
|
588
|
-
discard() {
|
|
589
|
-
if (this.saving || this.drafts.size === 0) return;
|
|
590
|
-
this.drafts.clear();
|
|
591
|
-
this.failed = false;
|
|
592
|
-
this.publish();
|
|
593
|
-
}
|
|
594
|
-
/** 可以编辑:这一行被服务到(`status: ready`)且文档接受写入。 */
|
|
595
|
-
writable() {
|
|
596
|
-
const snapshot = this.scope.getSnapshot();
|
|
597
|
-
return !this.disposed && !this.saving && snapshot.status === "ready" && snapshot.writable;
|
|
598
|
-
}
|
|
599
|
-
/** 这一次保存要写的 path op(按编辑顺序;没变的行不写)。 */
|
|
600
|
-
plannedOps() {
|
|
601
|
-
const ops = [];
|
|
602
|
-
for (const [modeId, draft] of this.drafts) {
|
|
603
|
-
if (!this.dirty(modeId)) continue;
|
|
604
|
-
ops.push(draft === null ? {
|
|
605
|
-
op: "unset",
|
|
606
|
-
path: ["models", modeId]
|
|
607
|
-
} : {
|
|
608
|
-
op: "set",
|
|
609
|
-
path: ["models", modeId],
|
|
610
|
-
value: modelValue(draft)
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
return ops;
|
|
614
|
-
}
|
|
615
|
-
async save() {
|
|
616
|
-
const snapshot = this.scope.getSnapshot();
|
|
617
|
-
const ops = this.plannedOps();
|
|
618
|
-
if (this.disposed || this.saving || snapshot.status !== "ready" || !snapshot.writable || ops.length === 0 || [...this.drafts.values()].some(incomplete)) return;
|
|
619
|
-
const generation = this.saveGeneration;
|
|
620
|
-
this.saving = true;
|
|
621
|
-
this.failed = false;
|
|
622
|
-
this.publish();
|
|
623
|
-
let landed = false;
|
|
624
|
-
try {
|
|
625
|
-
landed = await this.scope.mutate(ops, snapshot.revision);
|
|
626
|
-
} catch {
|
|
627
|
-
landed = false;
|
|
628
|
-
}
|
|
629
|
-
if (generation !== this.saveGeneration || this.disposed) return;
|
|
630
|
-
this.saving = false;
|
|
631
|
-
this.failed = !landed;
|
|
632
|
-
if (landed) this.drafts.clear();
|
|
633
|
-
this.publish();
|
|
634
|
-
}
|
|
635
|
-
async loadRoster() {
|
|
636
|
-
try {
|
|
637
|
-
this.roster = await this.sources.roster();
|
|
638
|
-
this.rosterStatus = "ready";
|
|
639
|
-
} catch {
|
|
640
|
-
this.rosterStatus = "error";
|
|
641
|
-
}
|
|
642
|
-
if (!this.disposed) this.publish();
|
|
643
|
-
}
|
|
644
|
-
async loadDirectory() {
|
|
645
|
-
try {
|
|
646
|
-
const directory = await this.sources.directory();
|
|
647
|
-
this.directory = directory.groups;
|
|
648
|
-
this.directoryFailures = directory.failures;
|
|
649
|
-
this.directoryStatus = "ready";
|
|
650
|
-
} catch {
|
|
651
|
-
this.directoryStatus = "error";
|
|
652
|
-
}
|
|
653
|
-
if (!this.disposed) this.publish();
|
|
654
|
-
}
|
|
655
|
-
/** 某个 provider 在目录里的模型。 */
|
|
656
|
-
modelsOf(provider) {
|
|
657
|
-
return this.directory.find((group) => group.id === provider)?.models ?? [];
|
|
658
|
-
}
|
|
659
|
-
/** 某个 provider + model 声明的档位(模型自带;没声明就是没有)。 */
|
|
660
|
-
efforts(provider, model) {
|
|
661
|
-
return this.modelsOf(provider).find((entry) => entry.id === model)?.reasoning?.efforts ?? [];
|
|
662
|
-
}
|
|
663
|
-
projection() {
|
|
664
|
-
const snapshot = this.scope.getSnapshot();
|
|
665
|
-
const invalid = [...this.drafts.values()].some(incomplete);
|
|
666
|
-
return {
|
|
667
|
-
available: snapshot.status === "ready",
|
|
668
|
-
writable: snapshot.writable,
|
|
669
|
-
dirty: [...this.drafts.keys()].some((modeId) => this.dirty(modeId)),
|
|
670
|
-
invalid,
|
|
671
|
-
saving: this.saving,
|
|
672
|
-
failed: this.failed,
|
|
673
|
-
rows: (this.roster?.modes ?? []).map((mode) => {
|
|
674
|
-
const value = this.shown(mode.id);
|
|
675
|
-
return {
|
|
676
|
-
id: mode.id,
|
|
677
|
-
name: mode.name,
|
|
678
|
-
...mode.description === void 0 ? {} : { description: mode.description },
|
|
679
|
-
...value === void 0 ? {} : { value },
|
|
680
|
-
overridden: this.leavesOverride(mode.id),
|
|
681
|
-
dirty: this.dirty(mode.id),
|
|
682
|
-
invalid: incomplete(this.drafts.get(mode.id))
|
|
683
|
-
};
|
|
684
|
-
}),
|
|
685
|
-
directory: this.directory,
|
|
686
|
-
rosterStatus: this.rosterStatus,
|
|
687
|
-
directoryStatus: this.directoryStatus,
|
|
688
|
-
directoryFailures: this.directoryFailures
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
publish() {
|
|
692
|
-
this.store.set(this.projection());
|
|
693
|
-
}
|
|
694
|
-
};
|
|
695
|
-
//#endregion
|
|
9
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
696
10
|
//#region src/shared.ts
|
|
697
11
|
/**
|
|
698
12
|
* host 半与 client 半共用的接口面:一条 HTTP 路径、模式行的对外形状、请求与响应体。
|
|
@@ -773,53 +87,6 @@ window.__ModuleLoader__.load({
|
|
|
773
87
|
return roster;
|
|
774
88
|
}
|
|
775
89
|
//#endregion
|
|
776
|
-
//#region \0dsh-css-modules:/Users/morlay/src/github.com/morlay/dsh-plugin/packages/profile/dsh-session-mode/src/client/SessionModeLabel.module.css.mjs
|
|
777
|
-
const css$1 = ".HBEdcq_label{background:var(--dsw-alias-bg-layer-2);max-width:180px;height:22px;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;border-radius:6px;align-items:center;gap:4px;padding:0 6px;font-size:12px;line-height:22px;display:inline-flex;overflow:hidden}.HBEdcq_icon{opacity:.7;flex:none}";
|
|
778
|
-
const tagId$1 = "@morlay/dsh-session-mode/SessionModeLabel.module.css";
|
|
779
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"@morlay/dsh-session-mode/SessionModeLabel.module.css\"]") === null) {
|
|
780
|
-
const tag = document.createElement("style");
|
|
781
|
-
tag.dataset.plugin = "@morlay/dsh-session-mode";
|
|
782
|
-
tag.dataset.pluginCss = tagId$1;
|
|
783
|
-
tag.textContent = css$1;
|
|
784
|
-
document.head.appendChild(tag);
|
|
785
|
-
}
|
|
786
|
-
var SessionModeLabel_module_css_default = {
|
|
787
|
-
"label": "HBEdcq_label",
|
|
788
|
-
"icon": "HBEdcq_icon"
|
|
789
|
-
};
|
|
790
|
-
//#endregion
|
|
791
|
-
//#region src/client/SessionModeLabel.tsx
|
|
792
|
-
/**
|
|
793
|
-
* 会话头部的模式标签:**只读**。
|
|
794
|
-
*
|
|
795
|
-
* 一个会话的装配在它开始第一轮时就定下了,头部又是"跑起来之后"才值得看的东西——在这里放一个能切模式的
|
|
796
|
-
* 控件,等于承诺一次 host 会拒绝的切换。说出这个会话运行的是什么,才是这里诚实的表述;选择本身在
|
|
797
|
-
* 新会话屏幕的 chip 上。
|
|
798
|
-
*/
|
|
799
|
-
/**
|
|
800
|
-
* 渲染这个会话的模式名。
|
|
801
|
-
* @param props - 槽位合成的 props。
|
|
802
|
-
* @returns 标签;清单没读到时返回 null(此时名字无从得知)。
|
|
803
|
-
*/
|
|
804
|
-
function SessionModeLabel({ sessionId, useSessions, t }) {
|
|
805
|
-
const roster = useRoster();
|
|
806
|
-
const mode = useSessions((state) => {
|
|
807
|
-
const value = state.byId[sessionId]?.projectionValues?.sessionMode;
|
|
808
|
-
return typeof value === "string" ? value : void 0;
|
|
809
|
-
});
|
|
810
|
-
if (roster === void 0) return null;
|
|
811
|
-
const id = mode ?? roster.default;
|
|
812
|
-
const chosen = roster.modes.find((entry) => entry.id === id);
|
|
813
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
814
|
-
className: SessionModeLabel_module_css_default.label,
|
|
815
|
-
title: chosen?.description ?? t("headerHint"),
|
|
816
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconAgentPresetOutlineRegular, {
|
|
817
|
-
size: 14,
|
|
818
|
-
className: SessionModeLabel_module_css_default.icon
|
|
819
|
-
}), chosen?.name ?? id]
|
|
820
|
-
});
|
|
821
|
-
}
|
|
822
|
-
//#endregion
|
|
823
90
|
//#region \0dsh-css-modules:/Users/morlay/src/github.com/morlay/dsh-plugin/packages/profile/dsh-session-mode/src/client/SessionModeSeat.module.css.mjs
|
|
824
91
|
const css = ".dDMF8q_menuAnchor{min-width:54px;max-width:100%}.dDMF8q_seat{min-width:0;max-width:min(100%,240px);min-height:28px;color:var(--dsw-alias-label-primary);white-space:nowrap;cursor:pointer;background:0 0;border:none;border-radius:16px;align-items:center;gap:4px;padding:0 8px;font-size:13px;font-weight:500;line-height:20px;display:inline-flex}.dDMF8q_seat:not(:disabled):hover,.dDMF8q_seat[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover)}.dDMF8q_seat:disabled{cursor:default;color:var(--dsw-alias-label-quaternary)}.dDMF8q_seatIcon{color:var(--dsw-alias-label-primary);flex:none}.dDMF8q_seatLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.dDMF8q_chevron{color:var(--dsw-alias-label-caption);flex:none}.dDMF8q_item{flex-direction:column;gap:2px;max-width:280px;display:flex}.dDMF8q_itemName{color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}.dDMF8q_itemDesc{color:var(--dsw-alias-label-caption);white-space:normal;font-size:12px;line-height:16px}";
|
|
825
92
|
const tagId = "@morlay/dsh-session-mode/SessionModeSeat.module.css";
|
|
@@ -831,42 +98,64 @@ window.__ModuleLoader__.load({
|
|
|
831
98
|
document.head.appendChild(tag);
|
|
832
99
|
}
|
|
833
100
|
var SessionModeSeat_module_css_default = {
|
|
834
|
-
"
|
|
101
|
+
"itemName": "dDMF8q_itemName",
|
|
835
102
|
"seatLabel": "dDMF8q_seatLabel",
|
|
836
|
-
"menuAnchor": "dDMF8q_menuAnchor",
|
|
837
103
|
"chevron": "dDMF8q_chevron",
|
|
838
|
-
"item": "dDMF8q_item",
|
|
839
104
|
"seatIcon": "dDMF8q_seatIcon",
|
|
840
|
-
"
|
|
841
|
-
"
|
|
105
|
+
"itemDesc": "dDMF8q_itemDesc",
|
|
106
|
+
"item": "dDMF8q_item",
|
|
107
|
+
"menuAnchor": "dDMF8q_menuAnchor",
|
|
108
|
+
"seat": "dDMF8q_seat"
|
|
842
109
|
};
|
|
843
110
|
//#endregion
|
|
844
111
|
//#region src/client/SessionModeSeat.tsx
|
|
845
112
|
/**
|
|
846
|
-
*
|
|
113
|
+
* composer 里的模式 chip:**本包在会话里唯一的面**(点开是 coding / chat 两项)。
|
|
114
|
+
*
|
|
115
|
+
* 选择只在会话开始**之前**有效——一旦跑过 turn,那段历史是在某个模式的工具与提示词下产生的。所以 chip 在
|
|
116
|
+
* 会话里有两个形态:空白期是选择器,开过 turn 之后**只读**(只写当前模式,点不动)。判据是 host 的投影
|
|
117
|
+
* `sessionModeEditable`(`turn/start` 一落库就为 `false`),与服务端拒绝切换读的是同一份事实。
|
|
847
118
|
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
119
|
+
* 它挂在 composer 上:新会话屏也是一个 blank session 的 composer,选择正好发生在那里;会话头部则没有它的
|
|
120
|
+
* 位置——头部只在"跑起来之后"值得看,而那时的 chip 已经把当前模式写在脸上,再挂一个只读标签就是同一句话的
|
|
121
|
+
* 复读。
|
|
851
122
|
*/
|
|
852
123
|
/**
|
|
853
|
-
*
|
|
124
|
+
* 渲染 composer 里的模式切换 chip。
|
|
125
|
+
*
|
|
126
|
+
* 为什么在 composer(`conversation.input.left`):**新会话屏也是一个 blank session 的 composer**,选择正好
|
|
127
|
+
* 发生在那里;会话头部槽位在新会话屏根本不存在,而会话跑起来之后头部也不该重复说一遍 chip 已经写着的事。
|
|
128
|
+
* 官方 roster 那个座位(`conversation.hero.agentPreset`)是单注册槽位、已被官方占了,所以用自己的 list 槽位
|
|
129
|
+
* 并存。
|
|
854
130
|
* @param props - 槽位合成的 props。
|
|
855
|
-
* @returns chip
|
|
131
|
+
* @returns chip;清单没读到、或会话未知时返回 null。
|
|
856
132
|
*/
|
|
857
|
-
function SessionModeSeat({ sessionId, useSessions,
|
|
133
|
+
function SessionModeSeat({ sessionId, useSessions, t }) {
|
|
858
134
|
const roster = useRoster();
|
|
859
135
|
const selected = useSessions((state) => {
|
|
860
136
|
const value = sessionId === void 0 ? void 0 : state.byId[sessionId]?.projectionValues?.sessionMode;
|
|
861
137
|
return typeof value === "string" ? value : void 0;
|
|
862
138
|
});
|
|
863
|
-
const
|
|
139
|
+
const editable = useSessions((state) => {
|
|
140
|
+
const value = sessionId === void 0 ? void 0 : state.byId[sessionId]?.projectionValues?.sessionModeEditable;
|
|
141
|
+
return typeof value === "boolean" ? value : void 0;
|
|
142
|
+
});
|
|
864
143
|
const [open, setOpen] = (0, react.useState)(false);
|
|
865
144
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
866
145
|
const [error, setError] = (0, react.useState)(null);
|
|
867
|
-
if (
|
|
146
|
+
if (roster === void 0) return null;
|
|
868
147
|
const current = selected ?? roster.default;
|
|
869
148
|
const label = roster.modes.find((mode) => mode.id === current)?.name ?? current;
|
|
149
|
+
if (editable === false) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
150
|
+
type: "button",
|
|
151
|
+
className: SessionModeSeat_module_css_default.seat,
|
|
152
|
+
disabled: true,
|
|
153
|
+
title: t("lockedHint"),
|
|
154
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconAgentPresetOutlineRegular, { className: SessionModeSeat_module_css_default.seatIcon }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
155
|
+
className: SessionModeSeat_module_css_default.seatLabel,
|
|
156
|
+
children: label
|
|
157
|
+
})]
|
|
158
|
+
});
|
|
870
159
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
871
160
|
open,
|
|
872
161
|
onClose: () => {
|
|
@@ -922,85 +211,159 @@ window.__ModuleLoader__.load({
|
|
|
922
211
|
});
|
|
923
212
|
}
|
|
924
213
|
//#endregion
|
|
925
|
-
//#region src/client/
|
|
926
|
-
/** 浏览器半插件的字典命名空间。 */
|
|
927
|
-
const NS = "session-mode";
|
|
214
|
+
//#region src/client/locales.ts
|
|
928
215
|
/**
|
|
929
|
-
*
|
|
216
|
+
* client 半的文案:命名空间 `session-mode`(与 locale 注册时用的 key 一致)。
|
|
930
217
|
*
|
|
931
|
-
*
|
|
932
|
-
*
|
|
218
|
+
* 只有会话里那个模式 chip 用这份字典:模式的名字与说明由 host 的清单给(那是数据,不是文案)。本行的配置页
|
|
219
|
+
* (各模式的默认模型)由通用 schema 表单渲染,文案在它自己的字典里。
|
|
933
220
|
*/
|
|
934
|
-
const
|
|
221
|
+
const zh = {
|
|
222
|
+
seatHint: "选择这个会话的模式",
|
|
223
|
+
lockedHint: "这个会话已经开始:换模式请新开一个会话",
|
|
224
|
+
noDescription: "(无说明)",
|
|
225
|
+
provider: "服务商",
|
|
226
|
+
providerHint: "服务商 id:部署里注册的任意路由——不论哪个适配器插件注册的(官方的、我们自己的都算)。",
|
|
227
|
+
model: "模型",
|
|
228
|
+
modelHint: "模型 id。",
|
|
229
|
+
reasoningEffort: "思考档位",
|
|
230
|
+
reasoningEffortHint: "省略就跟服务商自己的默认。"
|
|
231
|
+
};
|
|
232
|
+
const en = {
|
|
233
|
+
seatHint: "Pick the mode for this session",
|
|
234
|
+
lockedHint: "This session has started: start a new one to pick a mode",
|
|
235
|
+
noDescription: "(no description)",
|
|
236
|
+
provider: "Provider",
|
|
237
|
+
providerHint: "Provider id: any route registered in this deployment, whichever adapter plugin supplies it.",
|
|
238
|
+
model: "Model",
|
|
239
|
+
modelHint: "Model id.",
|
|
240
|
+
reasoningEffort: "Reasoning effort",
|
|
241
|
+
reasoningEffortHint: "Unset keeps the provider's own default."
|
|
242
|
+
};
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/client/index.ts
|
|
245
|
+
/** 浏览器半插件的字典命名空间。 */
|
|
246
|
+
const NS = "session-mode";
|
|
247
|
+
/** 这一行里需要中文文案的字段(都在 `modes.<模式>.defaultModel` 里,所以按模板路径注册一次)。 */
|
|
248
|
+
const FIELDS = [
|
|
249
|
+
"provider",
|
|
250
|
+
"model",
|
|
251
|
+
"reasoningEffort"
|
|
252
|
+
];
|
|
253
|
+
/** 动态键的占位段(与通用表单的字段树同一约定)。 */
|
|
254
|
+
const DYNAMIC = "*";
|
|
935
255
|
/** 需要的服务:槽位与字典(会话列表经槽位的标准 props 到达组件,不必自己 inject)。 */
|
|
936
256
|
const inject = ["slots", "locale"];
|
|
257
|
+
/** 本包 host 行 id:行配置页读的就是这个命名空间。 */
|
|
258
|
+
const SESSION_MODE_NS = "session-mode";
|
|
259
|
+
/** 按路径读一段配置里的值(本包只读 provider 档案里的模型清单)。 */
|
|
260
|
+
function readAt(root, path) {
|
|
261
|
+
return path.reduce((node, segment) => {
|
|
262
|
+
if (Array.isArray(node)) return node[Number(segment)];
|
|
263
|
+
if (typeof node !== "object" || node === null) return void 0;
|
|
264
|
+
return Reflect.get(node, segment);
|
|
265
|
+
}, root);
|
|
266
|
+
}
|
|
937
267
|
/**
|
|
938
|
-
*
|
|
268
|
+
* 装上会话里的两个面,以及本行配置页的字段文案。
|
|
269
|
+
*
|
|
939
270
|
* @param ctx - 浏览器插件上下文。
|
|
940
271
|
*/
|
|
941
272
|
function apply(ctx) {
|
|
273
|
+
const t = ctx.locale.bind(NS);
|
|
942
274
|
ctx.effect(() => ctx.locale.register(NS, {
|
|
943
275
|
zh,
|
|
944
276
|
en
|
|
945
277
|
}), "session-mode: dictionaries");
|
|
278
|
+
ctx.inject(["schemaFormHints"], (scope) => scope.effect(() => {
|
|
279
|
+
const hints = scope.schemaFormHints;
|
|
280
|
+
const offs = FIELDS.map((key) => hints.describe(SESSION_MODE_NS, [
|
|
281
|
+
"modes",
|
|
282
|
+
DYNAMIC,
|
|
283
|
+
"defaultModel",
|
|
284
|
+
key
|
|
285
|
+
], () => ({
|
|
286
|
+
label: t(key),
|
|
287
|
+
hint: t(`${key}Hint`)
|
|
288
|
+
})));
|
|
289
|
+
return () => {
|
|
290
|
+
for (const off of offs) off();
|
|
291
|
+
};
|
|
292
|
+
}, "session-mode: field wording"));
|
|
293
|
+
ctx.inject(["schemaFormHints"], (scope) => scope.effect(() => {
|
|
294
|
+
const hints = scope.schemaFormHints;
|
|
295
|
+
const remote = scope.get("remote");
|
|
296
|
+
const forms = scope.get("configForms");
|
|
297
|
+
if (remote === void 0 || forms === void 0) return () => {};
|
|
298
|
+
let providers = [];
|
|
299
|
+
const load = async () => {
|
|
300
|
+
const [routes, directory] = await Promise.all([remote.llm.listProviders(), remote.llm.listConfigurableProviders()]);
|
|
301
|
+
if (!routes.ok || !directory.ok) return;
|
|
302
|
+
const merged = /* @__PURE__ */ new Map();
|
|
303
|
+
for (const entry of directory.value) merged.set(entry.provider, {
|
|
304
|
+
value: entry.provider,
|
|
305
|
+
label: entry.displayName,
|
|
306
|
+
settingsNs: entry.settingsNs,
|
|
307
|
+
settingsPath: [...entry.settingsPath]
|
|
308
|
+
});
|
|
309
|
+
for (const route of routes.value) if (!merged.has(route.id)) merged.set(route.id, {
|
|
310
|
+
value: route.id,
|
|
311
|
+
label: route.name,
|
|
312
|
+
settingsNs: "",
|
|
313
|
+
settingsPath: []
|
|
314
|
+
});
|
|
315
|
+
providers = [...merged.values()];
|
|
316
|
+
hints.refresh();
|
|
317
|
+
};
|
|
318
|
+
const modelsOf = (provider) => {
|
|
319
|
+
if (typeof provider !== "string") return [];
|
|
320
|
+
const entry = providers.find((candidate) => candidate.value === provider);
|
|
321
|
+
if (entry === void 0 || entry.settingsNs === "") return [];
|
|
322
|
+
const profile = readAt(forms.get(entry.settingsNs).getSnapshot().value, entry.settingsPath);
|
|
323
|
+
const models = typeof profile === "object" && profile !== null ? Reflect.get(profile, "models") : void 0;
|
|
324
|
+
if (!Array.isArray(models)) return [];
|
|
325
|
+
return models.flatMap((model) => {
|
|
326
|
+
const id = typeof model === "object" && model !== null ? Reflect.get(model, "id") : void 0;
|
|
327
|
+
return typeof id === "string" ? [{ value: id }] : [];
|
|
328
|
+
});
|
|
329
|
+
};
|
|
330
|
+
const offs = [
|
|
331
|
+
hints.source("llm-providers", { options: () => providers.map((entry) => ({
|
|
332
|
+
value: entry.value,
|
|
333
|
+
label: entry.label
|
|
334
|
+
})) }),
|
|
335
|
+
hints.source("llm-models", {
|
|
336
|
+
dependsOn: [["provider"]],
|
|
337
|
+
options: (read) => modelsOf(read(["provider"]))
|
|
338
|
+
}),
|
|
339
|
+
remote.$on("llm/adapters-updated", () => {
|
|
340
|
+
load();
|
|
341
|
+
}),
|
|
342
|
+
remote.$on("settings/document-updated", () => {
|
|
343
|
+
hints.refresh();
|
|
344
|
+
})
|
|
345
|
+
];
|
|
346
|
+
load();
|
|
347
|
+
return () => {
|
|
348
|
+
for (const off of offs) off();
|
|
349
|
+
};
|
|
350
|
+
}, "session-mode: model candidates"));
|
|
946
351
|
ctx.inject(["slots", "conversation"], (scope) => {
|
|
947
352
|
scope.effect(() => {
|
|
948
353
|
const seat = scope.slots.register({
|
|
949
|
-
name: "conversation.
|
|
950
|
-
locale: NS
|
|
951
|
-
}, SessionModeSeat);
|
|
952
|
-
const label = scope.slots.register({
|
|
953
|
-
name: "conversation.session.header.actions",
|
|
354
|
+
name: "conversation.input.left",
|
|
954
355
|
id: "session-mode",
|
|
955
|
-
order:
|
|
356
|
+
order: 0,
|
|
956
357
|
locale: NS
|
|
957
|
-
},
|
|
358
|
+
}, SessionModeSeat);
|
|
958
359
|
return () => {
|
|
959
360
|
seat();
|
|
960
|
-
label();
|
|
961
|
-
};
|
|
962
|
-
}, "session-mode: hero chip and header label");
|
|
963
|
-
});
|
|
964
|
-
ctx.inject([
|
|
965
|
-
"slots",
|
|
966
|
-
"locale",
|
|
967
|
-
"configForms",
|
|
968
|
-
"remote",
|
|
969
|
-
"remote.session"
|
|
970
|
-
], (forms) => {
|
|
971
|
-
const t = forms.locale.bind(NS);
|
|
972
|
-
forms.effect(() => {
|
|
973
|
-
const controller = new ModelDefaultsCardController(forms.configForms.get(SESSION_MODE_NS), {
|
|
974
|
-
roster: fetchRoster,
|
|
975
|
-
directory: async () => {
|
|
976
|
-
const response = await forms.remote.session.modelCatalog();
|
|
977
|
-
if (!response.ok) throw new Error("model catalog is unavailable");
|
|
978
|
-
return {
|
|
979
|
-
groups: response.value.groups,
|
|
980
|
-
failures: response.value.failures.map((failure) => failure.name)
|
|
981
|
-
};
|
|
982
|
-
}
|
|
983
|
-
});
|
|
984
|
-
const face = controller.inject();
|
|
985
|
-
const page = forms.configForms.whileServed([SESSION_MODE_NS], () => forms.slots.inject("plugins.row.config", () => forms.slots.register({
|
|
986
|
-
name: "plugins.row.config",
|
|
987
|
-
key: SESSION_MODE_ROW_CONFIG_KEY,
|
|
988
|
-
inject: () => ({
|
|
989
|
-
...face,
|
|
990
|
-
t
|
|
991
|
-
})
|
|
992
|
-
}, ModelDefaultsCard)));
|
|
993
|
-
return () => {
|
|
994
|
-
page();
|
|
995
|
-
controller.dispose();
|
|
996
361
|
};
|
|
997
|
-
}, "session-mode:
|
|
362
|
+
}, "session-mode: composer chip");
|
|
998
363
|
});
|
|
999
364
|
}
|
|
1000
365
|
//#endregion
|
|
1001
|
-
exports.ModelDefaultsCardController = ModelDefaultsCardController;
|
|
1002
366
|
exports.SESSION_MODE_NS = SESSION_MODE_NS;
|
|
1003
|
-
exports.SESSION_MODE_ROW_CONFIG_KEY = SESSION_MODE_ROW_CONFIG_KEY;
|
|
1004
367
|
exports.apply = apply;
|
|
1005
368
|
exports.inject = inject;
|
|
1006
369
|
return module.exports;
|