@klarkxy/dsh-session-manager 0.3.3 → 0.4.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 +16 -13
- package/README.zh-CN.md +16 -13
- package/lib/client.inner.cjs +1474 -253
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +1474 -253
- package/lib/index.d.ts +8 -0
- package/lib/index.js +3364 -1977
- package/lib/index.js.map +1 -1
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +4 -4
package/lib/client.inner.cjs
CHANGED
|
@@ -66,6 +66,9 @@ const text = {
|
|
|
66
66
|
creatingContinuation: "正在创建新会话",
|
|
67
67
|
continueNewHint: "在来源会话的工作区新建会话,将摘要和原会话 ID 填入草稿,不自动发送。",
|
|
68
68
|
compactNothing: "没有值得压缩的内容。",
|
|
69
|
+
settingsTitle: "会话管理",
|
|
70
|
+
settingsGroupTitle: "会话行为",
|
|
71
|
+
settingsGroupHint: "继续灰影、自动继续、交接与会话摘要",
|
|
69
72
|
settingsMode: "继续灰影",
|
|
70
73
|
modeFixed: "固定文本",
|
|
71
74
|
modeAuto: "模型生成",
|
|
@@ -74,7 +77,12 @@ const text = {
|
|
|
74
77
|
continueTextLabel: "固定文本内容",
|
|
75
78
|
continueTextPlaceholder: "留空使用默认「继续」",
|
|
76
79
|
ghostModelLabel: "生成模型",
|
|
77
|
-
ghostModelHint: "
|
|
80
|
+
ghostModelHint: "每轮结束后生成一次并缓存;不选择时使用默认辅助模型。",
|
|
81
|
+
judgeModelLabel: "判定模型",
|
|
82
|
+
judgeModelHint: "判断回复是否提前结束;不选择时使用默认辅助模型。",
|
|
83
|
+
handoffGroup: "交接摘要",
|
|
84
|
+
handoffModelLabel: "交接模型",
|
|
85
|
+
handoffModelHint: "工具请求会话交接摘要时使用;不选择时使用默认辅助模型。",
|
|
78
86
|
welcomeBackGroup: "回到会话时",
|
|
79
87
|
recapEnabledLabel: "显示会话摘要",
|
|
80
88
|
recapEnabledHint: "在对话中回顾上次进展。",
|
|
@@ -82,10 +90,24 @@ const text = {
|
|
|
82
90
|
compactEnabledLabel: "建议压缩上下文",
|
|
83
91
|
compactEnabledHint: "长时间离开后,建议先压缩上下文。",
|
|
84
92
|
compactMinutesLabel: "离开多久后显示建议",
|
|
93
|
+
autoContinue: "自动继续",
|
|
94
|
+
autoContinueOff: "关闭",
|
|
95
|
+
autoContinueCautious: "谨慎",
|
|
96
|
+
autoContinueBold: "奔放",
|
|
97
|
+
autoContinueTooltip: "回复提前结束时自动发送继续。谨慎:有明确依据才继续;奔放:没完成就继续,低风险的选择按推荐方案走。会话开始后锁定。",
|
|
98
|
+
autoContinueLocked: "请在会话开始前选择自动继续",
|
|
99
|
+
autoContinueStale: "状态已在别处更改,已刷新",
|
|
100
|
+
autoContinueFailed: "切换失败",
|
|
101
|
+
autoContinueGroup: "自动继续",
|
|
102
|
+
autoContinueDefaultLabel: "新会话默认形态",
|
|
103
|
+
autoContinueDefaultHint: "只影响之后新建的会话;发送第一条消息前可在输入框上方单独选择,会话开始后保持锁定。",
|
|
104
|
+
autoContinueMaxLabel: "续跑次数上限",
|
|
105
|
+
autoContinueMaxHint: "你每发一条消息之后,最多自动继续的次数(1–10)。",
|
|
106
|
+
autoContinueMaxInvalid: "请填写 1–10 的整数。",
|
|
85
107
|
minutesUnit: "分钟",
|
|
86
108
|
modelLabel: "摘要模型",
|
|
87
|
-
modelFollowOption: "
|
|
88
|
-
modelGroupHint: "
|
|
109
|
+
modelFollowOption: "默认辅助模型",
|
|
110
|
+
modelGroupHint: "只用于欢迎回来摘要;留空使用默认辅助模型。",
|
|
89
111
|
saveSettings: "保存",
|
|
90
112
|
settingsSaving: "正在保存",
|
|
91
113
|
settingsSaved: "已保存",
|
|
@@ -128,6 +150,9 @@ const text = {
|
|
|
128
150
|
creatingContinuation: "Creating new session",
|
|
129
151
|
continueNewHint: "Create a session in the source session's workspace and fill its draft with the recap and source session ID, without sending.",
|
|
130
152
|
compactNothing: "There is nothing useful to compact.",
|
|
153
|
+
settingsTitle: "Session Management",
|
|
154
|
+
settingsGroupTitle: "Session behavior",
|
|
155
|
+
settingsGroupHint: "Continue ghost, auto-continue, handoff and session recap",
|
|
131
156
|
settingsMode: "Continue ghost",
|
|
132
157
|
modeFixed: "Fixed text",
|
|
133
158
|
modeAuto: "Model-generated",
|
|
@@ -136,7 +161,12 @@ const text = {
|
|
|
136
161
|
continueTextLabel: "Fixed text",
|
|
137
162
|
continueTextPlaceholder: "Empty uses the default \"Continue\"",
|
|
138
163
|
ghostModelLabel: "Generation model",
|
|
139
|
-
ghostModelHint: "Generated once after each turn and cached. The default follows the
|
|
164
|
+
ghostModelHint: "Generated once after each turn and cached. The default follows the shared auxiliary model.",
|
|
165
|
+
judgeModelLabel: "Judge model",
|
|
166
|
+
judgeModelHint: "Decides whether a reply stopped early. The default follows the shared auxiliary model.",
|
|
167
|
+
handoffGroup: "Session handoff",
|
|
168
|
+
handoffModelLabel: "Handoff model",
|
|
169
|
+
handoffModelHint: "Used when an agent asks for a session handoff summary. The default follows the shared auxiliary model.",
|
|
140
170
|
welcomeBackGroup: "When you return",
|
|
141
171
|
recapEnabledLabel: "Show session recap",
|
|
142
172
|
recapEnabledHint: "Review your previous progress in the conversation.",
|
|
@@ -144,10 +174,24 @@ const text = {
|
|
|
144
174
|
compactEnabledLabel: "Suggest context compaction",
|
|
145
175
|
compactEnabledHint: "Suggest compacting the context after a long time away.",
|
|
146
176
|
compactMinutesLabel: "Show suggestion after",
|
|
177
|
+
autoContinue: "Auto-continue",
|
|
178
|
+
autoContinueOff: "Off",
|
|
179
|
+
autoContinueCautious: "Cautious",
|
|
180
|
+
autoContinueBold: "Bold",
|
|
181
|
+
autoContinueTooltip: "Sends a follow-up when a reply stops early. Cautious: continues only on clear evidence. Bold: continues unless done, taking the recommended option on low-risk choices. Locked once the session starts.",
|
|
182
|
+
autoContinueLocked: "Choose Auto-continue before the session starts",
|
|
183
|
+
autoContinueStale: "State changed elsewhere; refreshed",
|
|
184
|
+
autoContinueFailed: "Could not change Auto-continue",
|
|
185
|
+
autoContinueGroup: "Auto-continue",
|
|
186
|
+
autoContinueDefaultLabel: "Default for new sessions",
|
|
187
|
+
autoContinueDefaultHint: "Only affects sessions created later. Choose above the composer before sending the first message; the choice stays locked once the session starts.",
|
|
188
|
+
autoContinueMaxLabel: "Maximum continuations",
|
|
189
|
+
autoContinueMaxHint: "How many times it may continue automatically after each message you send (1–10).",
|
|
190
|
+
autoContinueMaxInvalid: "Enter a whole number from 1 to 10.",
|
|
147
191
|
minutesUnit: "minutes",
|
|
148
192
|
modelLabel: "Recap model",
|
|
149
|
-
modelFollowOption: "
|
|
150
|
-
modelGroupHint: "Used only for the welcome-back recap; empty follows the
|
|
193
|
+
modelFollowOption: "Default auxiliary model",
|
|
194
|
+
modelGroupHint: "Used only for the welcome-back recap; empty follows the default auxiliary model.",
|
|
151
195
|
saveSettings: "Save",
|
|
152
196
|
settingsSaving: "Saving",
|
|
153
197
|
settingsSaved: "Saved",
|
|
@@ -205,6 +249,10 @@ const EDIT_PREPARE_ENDPOINT = "edit.prepare";
|
|
|
205
249
|
const EDIT_FILE_ENDPOINT = "edit.file";
|
|
206
250
|
const CONFIG_READ_ENDPOINT = "config.read";
|
|
207
251
|
const CONFIG_WRITE_ENDPOINT = "config.write";
|
|
252
|
+
const AUTO_CONTINUE_STATUS_ENDPOINT = "autocontinue.status";
|
|
253
|
+
const AUTO_CONTINUE_UPDATE_ENDPOINT = "autocontinue.update";
|
|
254
|
+
/** This plugin's own row inside the shared Session Management settings list. */
|
|
255
|
+
const SESSION_SETTINGS_ITEM_ID = "session-manager";
|
|
208
256
|
/** Card entry id in the chat flow; the recap renders at the latest completed turn's tail. */
|
|
209
257
|
const WELCOME_BACK_CARD_ID = "@klarkxy/dsh-session-manager.welcome-back";
|
|
210
258
|
const TURN_TAIL_SLOT = "conversation.chat.turnTail";
|
|
@@ -794,6 +842,15 @@ function parseModelMenuChoices(value, bound) {
|
|
|
794
842
|
});
|
|
795
843
|
return choices;
|
|
796
844
|
}
|
|
845
|
+
/** Reasoning-effort options for one choice; the saved value stays selectable. */
|
|
846
|
+
function modelMenuEffortOptions(choice, current) {
|
|
847
|
+
const listed = (choice?.efforts ?? []).map((item) => ({ ...item }));
|
|
848
|
+
if (current && !listed.some((item) => item.id === current)) listed.push({
|
|
849
|
+
id: current,
|
|
850
|
+
name: current
|
|
851
|
+
});
|
|
852
|
+
return listed;
|
|
853
|
+
}
|
|
797
854
|
//#endregion
|
|
798
855
|
//#region ../dsh-model-route/src/catalog-ui.tsx
|
|
799
856
|
/**
|
|
@@ -1785,8 +1842,48 @@ ${modelMenuCss}
|
|
|
1785
1842
|
font-size: 13px;
|
|
1786
1843
|
line-height: 18px;
|
|
1787
1844
|
}
|
|
1788
|
-
/*
|
|
1789
|
-
*
|
|
1845
|
+
/* General-section item: a named details group mirroring the host's native
|
|
1846
|
+
* item rows (16px vertical padding, a 0.5px l2 rule the shell drops on the
|
|
1847
|
+
* last item, 24px label-control gutter). The body mounts on first expand and
|
|
1848
|
+
* stays mounted while collapsed, so a fold never drops an unsaved draft. */
|
|
1849
|
+
.${FREE_CHAT_ROOT}.dsm-item {
|
|
1850
|
+
padding: 0;
|
|
1851
|
+
border-bottom: 0.5px solid var(--dsw-alias-border-l2);
|
|
1852
|
+
}
|
|
1853
|
+
.${FREE_CHAT_ROOT} .dsm-item-summary {
|
|
1854
|
+
display: flex;
|
|
1855
|
+
align-items: center;
|
|
1856
|
+
justify-content: space-between;
|
|
1857
|
+
gap: 24px;
|
|
1858
|
+
padding: 16px 0;
|
|
1859
|
+
list-style: none;
|
|
1860
|
+
cursor: pointer;
|
|
1861
|
+
}
|
|
1862
|
+
.${FREE_CHAT_ROOT} .dsm-item-summary::-webkit-details-marker { display: none; }
|
|
1863
|
+
.${FREE_CHAT_ROOT} .dsm-item-summary > span:first-child { min-width: 0; }
|
|
1864
|
+
.${FREE_CHAT_ROOT} .dsm-item-title {
|
|
1865
|
+
display: block;
|
|
1866
|
+
font-size: 14px;
|
|
1867
|
+
line-height: 20px;
|
|
1868
|
+
color: var(--dsw-alias-label-primary);
|
|
1869
|
+
}
|
|
1870
|
+
.${FREE_CHAT_ROOT} .dsm-item-desc {
|
|
1871
|
+
display: block;
|
|
1872
|
+
margin-top: 4px;
|
|
1873
|
+
font-size: 12px;
|
|
1874
|
+
line-height: 18px;
|
|
1875
|
+
color: var(--dsw-alias-label-secondary);
|
|
1876
|
+
}
|
|
1877
|
+
.${FREE_CHAT_ROOT} .dsm-item-chevron {
|
|
1878
|
+
width: 14px;
|
|
1879
|
+
height: 14px;
|
|
1880
|
+
color: var(--dsw-alias-label-secondary);
|
|
1881
|
+
transition: transform 0.15s ease;
|
|
1882
|
+
}
|
|
1883
|
+
.${FREE_CHAT_ROOT}.dsm-item[open] > .dsm-item-summary .dsm-item-chevron { transform: rotate(180deg); }
|
|
1884
|
+
.${FREE_CHAT_ROOT} .dsm-item-body { padding: 0 0 16px; }
|
|
1885
|
+
/* Settings group body: one quiet reading surface. Rules, not nested wells,
|
|
1886
|
+
* separate sections; dependent controls align with the feature they belong to. */
|
|
1790
1887
|
.${FREE_CHAT_ROOT} .dsm-settings {
|
|
1791
1888
|
display: flex;
|
|
1792
1889
|
flex-direction: column;
|
|
@@ -1925,6 +2022,50 @@ ${modelMenuCss}
|
|
|
1925
2022
|
}
|
|
1926
2023
|
.${FREE_CHAT_ROOT} .dsh-welcome-done { margin: 6px 0 0; }
|
|
1927
2024
|
|
|
2025
|
+
/* Hero-row Auto-continue chip: icon + mode label, tinted while a mode is on. */
|
|
2026
|
+
.${FREE_CHAT_ROOT}.smac-option {
|
|
2027
|
+
display: inline-flex;
|
|
2028
|
+
align-items: center;
|
|
2029
|
+
gap: 6px;
|
|
2030
|
+
}
|
|
2031
|
+
.${FREE_CHAT_ROOT} .smac-chip {
|
|
2032
|
+
display: inline-flex;
|
|
2033
|
+
align-items: center;
|
|
2034
|
+
gap: 4px;
|
|
2035
|
+
height: 28px;
|
|
2036
|
+
padding: 0 10px;
|
|
2037
|
+
border-radius: 999px;
|
|
2038
|
+
font-size: 13px;
|
|
2039
|
+
line-height: 20px;
|
|
2040
|
+
border: 1px solid var(--dsw-alias-border-l3);
|
|
2041
|
+
background: transparent;
|
|
2042
|
+
color: var(--dsw-alias-label-secondary);
|
|
2043
|
+
cursor: pointer;
|
|
2044
|
+
}
|
|
2045
|
+
.${FREE_CHAT_ROOT} .smac-chip svg { width: 16px; height: 16px; }
|
|
2046
|
+
.${FREE_CHAT_ROOT} .smac-chip .smac-chevron { width: 12px; height: 12px; }
|
|
2047
|
+
.${FREE_CHAT_ROOT} .smac-chip:hover:not(:disabled) {
|
|
2048
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
2049
|
+
}
|
|
2050
|
+
.${FREE_CHAT_ROOT} .smac-chip.smac-chip-on {
|
|
2051
|
+
background: var(--dsw-alias-button-primary-fill);
|
|
2052
|
+
border-color: var(--dsw-alias-button-primary-fill);
|
|
2053
|
+
color: var(--dsw-alias-label-primary-foreground);
|
|
2054
|
+
}
|
|
2055
|
+
.${FREE_CHAT_ROOT} .smac-chip.smac-chip-on:hover:not(:disabled) {
|
|
2056
|
+
background: var(--dsw-alias-button-primary-hover);
|
|
2057
|
+
border-color: var(--dsw-alias-button-primary-hover);
|
|
2058
|
+
}
|
|
2059
|
+
.${FREE_CHAT_ROOT} .smac-chip:disabled {
|
|
2060
|
+
opacity: 0.5;
|
|
2061
|
+
cursor: default;
|
|
2062
|
+
}
|
|
2063
|
+
.${FREE_CHAT_ROOT} .smac-note {
|
|
2064
|
+
font-size: 12px;
|
|
2065
|
+
line-height: 18px;
|
|
2066
|
+
color: var(--dsw-alias-label-tertiary);
|
|
2067
|
+
}
|
|
2068
|
+
|
|
1928
2069
|
.${FREE_CHAT_ROOT} .dsm-user-original { display: contents; }
|
|
1929
2070
|
.${FREE_CHAT_ROOT} .dsm-user-original[hidden] { display: none; }
|
|
1930
2071
|
.${FREE_CHAT_ROOT} .dsm-message-edit { width: min(100%, 720px); align-self: flex-end; }
|
|
@@ -3109,7 +3250,7 @@ function createPresenceConfigModel(host, delay = (ms) => new Promise((resolve) =
|
|
|
3109
3250
|
function same(view, patch) {
|
|
3110
3251
|
if (view === void 0) return false;
|
|
3111
3252
|
return Object.entries(patch).every(([key, value]) => {
|
|
3112
|
-
if (key === "model" || key === "ghostModel") return sameRoute(view[key], value);
|
|
3253
|
+
if (key === "model" || key === "ghostModel" || key === "judgeModel" || key === "handoffModel") return sameRoute(view[key], value);
|
|
3113
3254
|
return view[key] === value;
|
|
3114
3255
|
});
|
|
3115
3256
|
}
|
|
@@ -3335,6 +3476,175 @@ function createWelcomeBackModel(host, ids = () => crypto.randomUUID(), minInterv
|
|
|
3335
3476
|
};
|
|
3336
3477
|
}
|
|
3337
3478
|
//#endregion
|
|
3479
|
+
//#region src/auto-continue-option.tsx
|
|
3480
|
+
/**
|
|
3481
|
+
* The hero-row Auto-continue option: a chip that opens a three-mode menu on a
|
|
3482
|
+
* blank session and locks once the session starts. Mirrors the delegation
|
|
3483
|
+
* chip's revision discipline: stale reads never roll the chip back, a 'stale'
|
|
3484
|
+
* rejection refetches, and 'session-locked' explains itself in the tooltip.
|
|
3485
|
+
*/
|
|
3486
|
+
var RpcError = class extends Error {
|
|
3487
|
+
code;
|
|
3488
|
+
constructor(code, message) {
|
|
3489
|
+
super(message);
|
|
3490
|
+
this.name = "RpcError";
|
|
3491
|
+
this.code = code;
|
|
3492
|
+
}
|
|
3493
|
+
};
|
|
3494
|
+
async function rpc(call, endpoint, payload) {
|
|
3495
|
+
const result = await call(endpoint, payload);
|
|
3496
|
+
if (!result || result.ok !== true) {
|
|
3497
|
+
const failed = result && "error" in result ? result.error : void 0;
|
|
3498
|
+
throw new RpcError(failed?.code ?? "unknown", failed?.message ?? "");
|
|
3499
|
+
}
|
|
3500
|
+
return result.value;
|
|
3501
|
+
}
|
|
3502
|
+
const MODES = [
|
|
3503
|
+
"off",
|
|
3504
|
+
"cautious",
|
|
3505
|
+
"bold"
|
|
3506
|
+
];
|
|
3507
|
+
function AutoContinueOption({ sessionId, call, subscribeGeneration, useSession, locale }) {
|
|
3508
|
+
const [status, setStatus] = (0, react.useState)();
|
|
3509
|
+
const [saving, setSaving] = (0, react.useState)(false);
|
|
3510
|
+
const [notice, setNotice] = (0, react.useState)("");
|
|
3511
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
3512
|
+
const generationRef = (0, react.useRef)(0);
|
|
3513
|
+
const readSeqRef = (0, react.useRef)(0);
|
|
3514
|
+
const openState = useSession?.((snapshot) => snapshot.openState);
|
|
3515
|
+
const sessionRunning = useSession?.((snapshot) => snapshot.running === true);
|
|
3516
|
+
const sessionBlank = useSession?.((snapshot) => snapshot.blank);
|
|
3517
|
+
const submitting = useSession?.((snapshot) => snapshot.awaitingFirstTurn === true || (snapshot.pendingSubmissions?.length ?? 0) > 0);
|
|
3518
|
+
const load = (0, react.useCallback)(async () => {
|
|
3519
|
+
const generation = generationRef.current;
|
|
3520
|
+
const seq = ++readSeqRef.current;
|
|
3521
|
+
try {
|
|
3522
|
+
const next = await rpc(call, AUTO_CONTINUE_STATUS_ENDPOINT, { sessionId });
|
|
3523
|
+
if (generationRef.current !== generation || seq !== readSeqRef.current) return;
|
|
3524
|
+
setStatus((prev) => prev && prev.sessionId === next.sessionId && next.revision < prev.revision ? prev : next);
|
|
3525
|
+
} catch {}
|
|
3526
|
+
}, [call, sessionId]);
|
|
3527
|
+
(0, react.useEffect)(() => {
|
|
3528
|
+
generationRef.current += 1;
|
|
3529
|
+
readSeqRef.current += 1;
|
|
3530
|
+
setStatus(void 0);
|
|
3531
|
+
setSaving(false);
|
|
3532
|
+
setNotice("");
|
|
3533
|
+
setOpen(false);
|
|
3534
|
+
}, [sessionId]);
|
|
3535
|
+
(0, react.useEffect)(() => {
|
|
3536
|
+
load();
|
|
3537
|
+
}, [
|
|
3538
|
+
load,
|
|
3539
|
+
openState,
|
|
3540
|
+
sessionRunning,
|
|
3541
|
+
sessionBlank
|
|
3542
|
+
]);
|
|
3543
|
+
(0, react.useEffect)(() => {
|
|
3544
|
+
const off = subscribeGeneration?.(() => {
|
|
3545
|
+
load();
|
|
3546
|
+
});
|
|
3547
|
+
const win = typeof window === "undefined" ? void 0 : window;
|
|
3548
|
+
const onFocus = () => {
|
|
3549
|
+
load();
|
|
3550
|
+
};
|
|
3551
|
+
win?.addEventListener("focus", onFocus);
|
|
3552
|
+
return () => {
|
|
3553
|
+
off?.();
|
|
3554
|
+
win?.removeEventListener("focus", onFocus);
|
|
3555
|
+
};
|
|
3556
|
+
}, [load, subscribeGeneration]);
|
|
3557
|
+
(0, react.useEffect)(() => {
|
|
3558
|
+
if (!notice) return;
|
|
3559
|
+
const timer = setTimeout(() => setNotice(""), 4e3);
|
|
3560
|
+
return () => clearTimeout(timer);
|
|
3561
|
+
}, [notice]);
|
|
3562
|
+
const choose = (0, react.useCallback)(async (mode) => {
|
|
3563
|
+
if (!status || saving || status.locked || sessionBlank === false || sessionRunning || submitting || mode === status.mode) return;
|
|
3564
|
+
const generation = generationRef.current;
|
|
3565
|
+
readSeqRef.current += 1;
|
|
3566
|
+
setSaving(true);
|
|
3567
|
+
setNotice("");
|
|
3568
|
+
try {
|
|
3569
|
+
const next = await rpc(call, AUTO_CONTINUE_UPDATE_ENDPOINT, {
|
|
3570
|
+
sessionId,
|
|
3571
|
+
mode,
|
|
3572
|
+
expectedRevision: status.revision
|
|
3573
|
+
});
|
|
3574
|
+
if (generationRef.current !== generation) return;
|
|
3575
|
+
setStatus(next);
|
|
3576
|
+
} catch (reason) {
|
|
3577
|
+
if (generationRef.current !== generation) return;
|
|
3578
|
+
if (reason instanceof RpcError && (reason.code === "stale" || reason.code === "session-locked")) {
|
|
3579
|
+
setNotice(reason.code === "stale" ? "autoContinueStale" : "autoContinueLocked");
|
|
3580
|
+
load();
|
|
3581
|
+
} else setNotice("autoContinueFailed");
|
|
3582
|
+
} finally {
|
|
3583
|
+
if (generationRef.current === generation) setSaving(false);
|
|
3584
|
+
}
|
|
3585
|
+
}, [
|
|
3586
|
+
call,
|
|
3587
|
+
load,
|
|
3588
|
+
saving,
|
|
3589
|
+
sessionId,
|
|
3590
|
+
status,
|
|
3591
|
+
sessionBlank,
|
|
3592
|
+
sessionRunning,
|
|
3593
|
+
submitting
|
|
3594
|
+
]);
|
|
3595
|
+
if (!status || status.role === "executor" || sessionBlank === false) return null;
|
|
3596
|
+
const mode = status.mode;
|
|
3597
|
+
const locked = status.locked || sessionRunning === true || submitting === true;
|
|
3598
|
+
const tooltip = locked ? uiText(locale, "autoContinueLocked") : notice ? uiText(locale, notice) : uiText(locale, "autoContinueTooltip");
|
|
3599
|
+
const modeKey = mode === "cautious" ? "autoContinueCautious" : mode === "bold" ? "autoContinueBold" : "autoContinueOff";
|
|
3600
|
+
const label = `${uiText(locale, "autoContinue")} · ${uiText(locale, modeKey)}`;
|
|
3601
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3602
|
+
className: `${FREE_CHAT_ROOT} smac-option`,
|
|
3603
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
|
|
3604
|
+
open,
|
|
3605
|
+
align: "end",
|
|
3606
|
+
portal: true,
|
|
3607
|
+
listClassName: FREE_CHAT_ROOT,
|
|
3608
|
+
selectedId: mode,
|
|
3609
|
+
items: MODES.map((value) => ({
|
|
3610
|
+
id: value,
|
|
3611
|
+
label: uiText(locale, value === "off" ? "autoContinueOff" : value === "cautious" ? "autoContinueCautious" : "autoContinueBold")
|
|
3612
|
+
})),
|
|
3613
|
+
onSelect: (id) => {
|
|
3614
|
+
setOpen(false);
|
|
3615
|
+
choose(id);
|
|
3616
|
+
},
|
|
3617
|
+
onClose: () => setOpen(false),
|
|
3618
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
3619
|
+
label: tooltip,
|
|
3620
|
+
side: "bottom",
|
|
3621
|
+
gap: 4,
|
|
3622
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
|
|
3623
|
+
className: `smac-chip${mode !== "off" ? " smac-chip-on" : ""}`,
|
|
3624
|
+
active: mode !== "off",
|
|
3625
|
+
"aria-haspopup": "menu",
|
|
3626
|
+
"aria-expanded": open,
|
|
3627
|
+
"aria-label": label,
|
|
3628
|
+
disabled: locked || saving,
|
|
3629
|
+
onClick: () => setOpen((current) => !current),
|
|
3630
|
+
children: [
|
|
3631
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, {}),
|
|
3632
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3633
|
+
className: "smac-chip-label",
|
|
3634
|
+
children: label
|
|
3635
|
+
}),
|
|
3636
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: "smac-chevron" })
|
|
3637
|
+
]
|
|
3638
|
+
})
|
|
3639
|
+
})
|
|
3640
|
+
}), notice ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3641
|
+
role: "status",
|
|
3642
|
+
className: "smac-note",
|
|
3643
|
+
children: uiText(locale, notice)
|
|
3644
|
+
}) : null]
|
|
3645
|
+
});
|
|
3646
|
+
}
|
|
3647
|
+
//#endregion
|
|
3338
3648
|
//#region ../dsh-plugin-kit/src/contracts.ts
|
|
3339
3649
|
/** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */
|
|
3340
3650
|
function modelSettingsChangesIdentity(packageName) {
|
|
@@ -3348,6 +3658,153 @@ function modelSettingsChangesIdentity(packageName) {
|
|
|
3348
3658
|
namespace: segment
|
|
3349
3659
|
};
|
|
3350
3660
|
}
|
|
3661
|
+
/**
|
|
3662
|
+
* Shared "Session Management" settings section id. The section is provided by
|
|
3663
|
+
* shared infrastructure (see `registerSessionSettingsItem` in `client-utils`),
|
|
3664
|
+
* not by any one business plugin: every participant registers only a row into
|
|
3665
|
+
* {@link SESSION_SETTINGS_ITEM_SLOT}, so any enabled subset yields the same page.
|
|
3666
|
+
*/
|
|
3667
|
+
const SESSION_SETTINGS_SECTION_ID = "session-manager";
|
|
3668
|
+
/**
|
|
3669
|
+
* Child list slot declared by the shared Session Management page. Neutral
|
|
3670
|
+
* naming on purpose: participants never import each other, they only
|
|
3671
|
+
* contribute `{id, order}` rows into this slot while the page is alive.
|
|
3672
|
+
*/
|
|
3673
|
+
const SESSION_SETTINGS_ITEM_SLOT = "dsh.session-settings.item";
|
|
3674
|
+
/**
|
|
3675
|
+
* Host-owned nav icon seat: a root-scoped list where each entry's `id` must
|
|
3676
|
+
* equal the `settings.section` id it decorates. Owner props are
|
|
3677
|
+
* `{size, active}`; icons stay `aria-hidden` so the section label remains the
|
|
3678
|
+
* accessible name. Contributors into a shared section may register the same
|
|
3679
|
+
* icon `id` with different priorities — the ledger keeps every entry while the
|
|
3680
|
+
* host renders one icon per section.
|
|
3681
|
+
*/
|
|
3682
|
+
const SETTINGS_SECTION_ICON_SLOT = "settings.section.icon";
|
|
3683
|
+
/** Authenticated host-RPC channel for get/set of the shared default. */
|
|
3684
|
+
const AUXILIARY_MODEL_CHANNEL = "/dsh-auxiliary-model";
|
|
3685
|
+
/** Model-settings namespace under which the shared default is published. */
|
|
3686
|
+
const AUXILIARY_MODEL_NS = "@klarkxy/dsh-plugin-kit/auxiliary-model";
|
|
3687
|
+
/**
|
|
3688
|
+
* Typert identity of the shared change stream. Mounted on the root context
|
|
3689
|
+
* with ownerNs filtering, so frames only carry {@link AUXILIARY_MODEL_NS}.
|
|
3690
|
+
*/
|
|
3691
|
+
const AUXILIARY_MODEL_CHANGES_IDENTITY = modelSettingsChangesIdentity("@klarkxy/dsh-plugin-kit");
|
|
3692
|
+
//#endregion
|
|
3693
|
+
//#region ../dsh-plugin-kit/src/client-utils.ts
|
|
3694
|
+
/** One `<svg>` with the shared icon chrome; children are plain elements. */
|
|
3695
|
+
function lineIcon(children) {
|
|
3696
|
+
return function SettingsSectionIcon(props) {
|
|
3697
|
+
return (0, react.createElement)("svg", {
|
|
3698
|
+
"aria-hidden": "true",
|
|
3699
|
+
width: props.size ?? 16,
|
|
3700
|
+
height: props.size ?? 16,
|
|
3701
|
+
viewBox: "0 0 16 16",
|
|
3702
|
+
fill: "none",
|
|
3703
|
+
stroke: "currentColor",
|
|
3704
|
+
strokeWidth: 1,
|
|
3705
|
+
strokeLinecap: "round",
|
|
3706
|
+
strokeLinejoin: "round"
|
|
3707
|
+
}, ...children.map(([tag, attrs], index) => (0, react.createElement)(tag, {
|
|
3708
|
+
key: index,
|
|
3709
|
+
...attrs
|
|
3710
|
+
})));
|
|
3711
|
+
};
|
|
3712
|
+
}
|
|
3713
|
+
/** Chat bubble for the shared Session Management section. */
|
|
3714
|
+
const IconSessionSettings = lineIcon([["path", { d: "M8.5 13a5.5 5.5 0 0 0 5.5-5.5A5.5 5.5 0 0 0 8.5 2 5.5 5.5 0 0 0 3 7.5a5.48 5.48 0 0 0 .9 3L2.8 12.6a.5.5 0 0 0 .62.62l2.9-.98a5.48 5.48 0 0 0 2.18.76Z" }]]);
|
|
3715
|
+
/** Private Cordis rendezvous; every bundled copy of this helper resolves the same root service. */
|
|
3716
|
+
const SESSION_SETTINGS_PAGE_SERVICE = "dshSessionSettingsPage";
|
|
3717
|
+
/** The page component renders only its declared child list: items paint their own titles and styles. */
|
|
3718
|
+
function SessionSettingsPageView(props) {
|
|
3719
|
+
return props.renderSlot(SESSION_SETTINGS_ITEM_SLOT, {});
|
|
3720
|
+
}
|
|
3721
|
+
/**
|
|
3722
|
+
* Acquire the root-owned page service, creating and providing it on first use.
|
|
3723
|
+
* One counter covers participants, the section wait and the provider: the last
|
|
3724
|
+
* release removes the wait and then the provider synchronously (Cordis deletes
|
|
3725
|
+
* the impl before its async disposer awaits), so a later acquire sees a clean
|
|
3726
|
+
* get() miss and can re-establish the page.
|
|
3727
|
+
*/
|
|
3728
|
+
function acquireSessionSettingsPage(root) {
|
|
3729
|
+
const existing = root.get(SESSION_SETTINGS_PAGE_SERVICE, false);
|
|
3730
|
+
if (existing) return existing;
|
|
3731
|
+
const slots = root.slots;
|
|
3732
|
+
let users = 0;
|
|
3733
|
+
let stopPage;
|
|
3734
|
+
let unprovide;
|
|
3735
|
+
const page = {
|
|
3736
|
+
retain() {
|
|
3737
|
+
users += 1;
|
|
3738
|
+
if (!stopPage) stopPage = slots.inject("settings.section", () => slots.register({
|
|
3739
|
+
name: "settings.section",
|
|
3740
|
+
id: SESSION_SETTINGS_SECTION_ID,
|
|
3741
|
+
order: 30,
|
|
3742
|
+
label: () => {
|
|
3743
|
+
const locale = root.get("locale", false);
|
|
3744
|
+
return String(locale?.getSnapshot?.().active ?? "").startsWith("zh") ? "会话管理" : "Session Management";
|
|
3745
|
+
},
|
|
3746
|
+
children: { [SESSION_SETTINGS_ITEM_SLOT]: {
|
|
3747
|
+
kind: "list",
|
|
3748
|
+
scope: "root"
|
|
3749
|
+
} }
|
|
3750
|
+
}, SessionSettingsPageView));
|
|
3751
|
+
},
|
|
3752
|
+
release() {
|
|
3753
|
+
if (users <= 0) return;
|
|
3754
|
+
users -= 1;
|
|
3755
|
+
if (users > 0) return;
|
|
3756
|
+
stopPage?.();
|
|
3757
|
+
stopPage = void 0;
|
|
3758
|
+
unprovide?.();
|
|
3759
|
+
}
|
|
3760
|
+
};
|
|
3761
|
+
unprovide = root.provide(SESSION_SETTINGS_PAGE_SERVICE, page);
|
|
3762
|
+
return page;
|
|
3763
|
+
}
|
|
3764
|
+
/**
|
|
3765
|
+
* Contribute one row to the shared Session Management settings page.
|
|
3766
|
+
*
|
|
3767
|
+
* `ctx` is the plugin's own context: the row's slot wait unloads with the
|
|
3768
|
+
* plugin, while the page itself is retained on the shared root context for as
|
|
3769
|
+
* long as any contributor lives. There is no fallback seat — when every
|
|
3770
|
+
* contributor is gone the section disappears with the page. Returns a disposer.
|
|
3771
|
+
* Shared root-owned infrastructure (the auxiliary-model card) calls this on
|
|
3772
|
+
* `ctx.root` so its row, icon and page share are owned by the root lifetime
|
|
3773
|
+
* and released by the returned disposer, not by any plugin fiber.
|
|
3774
|
+
*/
|
|
3775
|
+
function registerSessionSettingsItem(ctx, item, component) {
|
|
3776
|
+
const slots = ctx.slots;
|
|
3777
|
+
const root = ctx.root;
|
|
3778
|
+
const page = acquireSessionSettingsPage(root);
|
|
3779
|
+
let stopItem;
|
|
3780
|
+
let stopIcon;
|
|
3781
|
+
try {
|
|
3782
|
+
page.retain();
|
|
3783
|
+
stopItem = slots.inject(SESSION_SETTINGS_ITEM_SLOT, () => slots.register({
|
|
3784
|
+
name: SESSION_SETTINGS_ITEM_SLOT,
|
|
3785
|
+
...item
|
|
3786
|
+
}, component));
|
|
3787
|
+
stopIcon = slots.inject(SETTINGS_SECTION_ICON_SLOT, () => slots.register({
|
|
3788
|
+
name: SETTINGS_SECTION_ICON_SLOT,
|
|
3789
|
+
id: SESSION_SETTINGS_SECTION_ID,
|
|
3790
|
+
order: item.order,
|
|
3791
|
+
priority: item.order
|
|
3792
|
+
}, IconSessionSettings));
|
|
3793
|
+
} catch (error) {
|
|
3794
|
+
stopIcon?.();
|
|
3795
|
+
stopItem?.();
|
|
3796
|
+
page.release();
|
|
3797
|
+
throw error;
|
|
3798
|
+
}
|
|
3799
|
+
let disposed = false;
|
|
3800
|
+
return () => {
|
|
3801
|
+
if (disposed) return;
|
|
3802
|
+
disposed = true;
|
|
3803
|
+
stopIcon?.();
|
|
3804
|
+
stopItem?.();
|
|
3805
|
+
page.release();
|
|
3806
|
+
};
|
|
3807
|
+
}
|
|
3351
3808
|
//#endregion
|
|
3352
3809
|
//#region ../dsh-plugin-kit/src/model-settings-client.ts
|
|
3353
3810
|
/** This stream carries invalidations only; ready is never a settings snapshot. */
|
|
@@ -3517,6 +3974,488 @@ function createModelSettingsSubscriber(ctx, packageName, identity = modelSetting
|
|
|
3517
3974
|
} });
|
|
3518
3975
|
}
|
|
3519
3976
|
//#endregion
|
|
3977
|
+
//#region ../dsh-plugin-kit/src/auxiliary-model-client.tsx
|
|
3978
|
+
const AUXILIARY_MODEL_PACKAGE = "@klarkxy/dsh-plugin-kit";
|
|
3979
|
+
const AUXILIARY_MODEL_SEAT_ID = "auxiliary-model";
|
|
3980
|
+
/** Private Cordis rendezvous; every bundled copy resolves the same root service. */
|
|
3981
|
+
const AUXILIARY_MODEL_CARD_SERVICE = "dshAuxiliaryModelSettingsCard";
|
|
3982
|
+
/** Theme-token-only stylesheet: officialUiCss scope plus the shared ModelMenu recipe. */
|
|
3983
|
+
const auxiliaryModelCss = `${officialUiCss("ams-root")}${modelMenuCss}
|
|
3984
|
+
.ams-root .ams-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
|
|
3985
|
+
`;
|
|
3986
|
+
const auxiliaryCopy = {
|
|
3987
|
+
zh: {
|
|
3988
|
+
title: "默认辅助模型",
|
|
3989
|
+
help: "各辅助功能(如标题生成、摘要)没有单独指定模型时使用的模型;仅影响声明使用它的功能。",
|
|
3990
|
+
model: "辅助模型",
|
|
3991
|
+
modelDefault: "跟随会话或宿主默认",
|
|
3992
|
+
modelHint: "不选择模型时,各功能按自己的规则决定对话模型。",
|
|
3993
|
+
effortDefault: "跟随模型默认",
|
|
3994
|
+
loading: "正在读取设置…",
|
|
3995
|
+
failed: "无法读取模型设置。",
|
|
3996
|
+
stale: "设置已被其他窗口更新,请刷新后重试。",
|
|
3997
|
+
saveFailed: "保存失败,原设置未更改。请重试。",
|
|
3998
|
+
saved: "已保存。",
|
|
3999
|
+
retry: "重试",
|
|
4000
|
+
refresh: "刷新设置",
|
|
4001
|
+
syncUnavailable: "自动刷新不可用,请手动刷新设置。"
|
|
4002
|
+
},
|
|
4003
|
+
en: {
|
|
4004
|
+
title: "Default Auxiliary Model",
|
|
4005
|
+
help: "The model used by auxiliary features (such as title generation or summarizing) that do not name their own model; only features that opted in are affected.",
|
|
4006
|
+
model: "Auxiliary model",
|
|
4007
|
+
modelDefault: "Follow session or host default",
|
|
4008
|
+
modelHint: "With no model selected, each feature follows its own rules for the conversation model.",
|
|
4009
|
+
effortDefault: "Follow model default",
|
|
4010
|
+
loading: "Loading settings…",
|
|
4011
|
+
failed: "Could not load model settings.",
|
|
4012
|
+
stale: "Settings were changed elsewhere; refresh and retry.",
|
|
4013
|
+
saveFailed: "Save failed; the previous setting was not changed. Try again.",
|
|
4014
|
+
saved: "Saved.",
|
|
4015
|
+
retry: "Retry",
|
|
4016
|
+
refresh: "Refresh settings",
|
|
4017
|
+
syncUnavailable: "Automatic refresh unavailable; refresh settings manually."
|
|
4018
|
+
}
|
|
4019
|
+
};
|
|
4020
|
+
/** A failed RPC keeps its code so the card can translate known rejections. */
|
|
4021
|
+
var AuxiliaryRpcError = class extends Error {
|
|
4022
|
+
code;
|
|
4023
|
+
constructor(code, message) {
|
|
4024
|
+
super(message);
|
|
4025
|
+
this.code = code;
|
|
4026
|
+
this.name = "AuxiliaryRpcError";
|
|
4027
|
+
}
|
|
4028
|
+
};
|
|
4029
|
+
/** Host UI language, following live switches with the host's English fallback. */
|
|
4030
|
+
function useAuxiliaryLocale(client) {
|
|
4031
|
+
const subscribe = (0, react.useCallback)((listener) => client.locale?.subscribe(listener) ?? (() => {}), [client]);
|
|
4032
|
+
const snapshot = (0, react.useCallback)(() => client.locale?.getSnapshot().active ?? "en", [client]);
|
|
4033
|
+
return String((0, react.useSyncExternalStore)(subscribe, snapshot, snapshot)).startsWith("zh") ? "zh" : "en";
|
|
4034
|
+
}
|
|
4035
|
+
function isAbort(error) {
|
|
4036
|
+
return error instanceof Error && (error.name === "AbortError" || /abort|cancel/i.test(error.message));
|
|
4037
|
+
}
|
|
4038
|
+
function isStaleRejection(error) {
|
|
4039
|
+
if (!(error instanceof AuxiliaryRpcError)) return false;
|
|
4040
|
+
return error.code === "stale" || error.code === "auxiliary-model/conflict" || /changed|revision|reload|stale|conflict/i.test(error.message);
|
|
4041
|
+
}
|
|
4042
|
+
async function callAuxiliaryRpc(client, endpoint, payload = {}) {
|
|
4043
|
+
const result = await client.connection.rpc.call(AUXILIARY_MODEL_CHANNEL, endpoint, payload);
|
|
4044
|
+
if (!result || result.ok !== true) {
|
|
4045
|
+
const error = result && "error" in result ? result.error : void 0;
|
|
4046
|
+
throw new AuxiliaryRpcError(error?.code ?? "unknown", error?.message ?? `RPC ${endpoint} failed`);
|
|
4047
|
+
}
|
|
4048
|
+
return result.value;
|
|
4049
|
+
}
|
|
4050
|
+
/**
|
|
4051
|
+
* The shared card. All state comes from the shared host service over the
|
|
4052
|
+
* plugin-kit RPC channel; the card only renders the stored row.
|
|
4053
|
+
*/
|
|
4054
|
+
function AuxiliaryModelCard({ client, subscribe }) {
|
|
4055
|
+
const language = useAuxiliaryLocale(client);
|
|
4056
|
+
const text = auxiliaryCopy[language];
|
|
4057
|
+
const [settings, setSettings] = (0, react.useState)();
|
|
4058
|
+
const catalogState = useModelCatalog((0, react.useCallback)(() => readModelCatalog(client), [client]), { subscribe: (0, react.useCallback)((change) => client.connection.generation?.subscribe(change), [client]) });
|
|
4059
|
+
const { refresh: refreshCatalog } = catalogState;
|
|
4060
|
+
const choices = parseModelMenuChoices(catalogState.catalog, settings?.model ?? void 0);
|
|
4061
|
+
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
4062
|
+
const [error, setError] = (0, react.useState)("");
|
|
4063
|
+
const [note, setNote] = (0, react.useState)(false);
|
|
4064
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
4065
|
+
const [syncError, setSyncError] = (0, react.useState)("");
|
|
4066
|
+
const operation = (0, react.useRef)({
|
|
4067
|
+
epoch: 0,
|
|
4068
|
+
active: false,
|
|
4069
|
+
reading: false,
|
|
4070
|
+
writing: false,
|
|
4071
|
+
pending: false
|
|
4072
|
+
});
|
|
4073
|
+
const refreshRef = (0, react.useRef)(() => {});
|
|
4074
|
+
const refresh = () => {
|
|
4075
|
+
const state = operation.current;
|
|
4076
|
+
if (!state.active) return;
|
|
4077
|
+
if (state.reading || state.writing) {
|
|
4078
|
+
state.pending = true;
|
|
4079
|
+
return;
|
|
4080
|
+
}
|
|
4081
|
+
state.pending = false;
|
|
4082
|
+
state.reading = true;
|
|
4083
|
+
setBusy(true);
|
|
4084
|
+
const epoch = state.epoch;
|
|
4085
|
+
callAuxiliaryRpc(client, "defaults.get").then((next) => {
|
|
4086
|
+
if (state.active && state.epoch === epoch && !state.pending) {
|
|
4087
|
+
setSettings(next);
|
|
4088
|
+
setError("");
|
|
4089
|
+
}
|
|
4090
|
+
}).catch((reason) => {
|
|
4091
|
+
if (state.active && state.epoch === epoch && !state.pending && !isAbort(reason)) setError("failed");
|
|
4092
|
+
}).finally(() => {
|
|
4093
|
+
if (!state.active || state.epoch !== epoch) return;
|
|
4094
|
+
state.reading = false;
|
|
4095
|
+
setBusy(false);
|
|
4096
|
+
if (state.pending) refreshRef.current();
|
|
4097
|
+
});
|
|
4098
|
+
};
|
|
4099
|
+
refreshRef.current = refresh;
|
|
4100
|
+
(0, react.useEffect)(() => {
|
|
4101
|
+
const state = operation.current;
|
|
4102
|
+
state.epoch++;
|
|
4103
|
+
state.active = true;
|
|
4104
|
+
state.reading = false;
|
|
4105
|
+
state.writing = false;
|
|
4106
|
+
state.pending = false;
|
|
4107
|
+
setBusy(false);
|
|
4108
|
+
setSettings(void 0);
|
|
4109
|
+
setError("");
|
|
4110
|
+
setNote(false);
|
|
4111
|
+
refreshRef.current();
|
|
4112
|
+
return () => {
|
|
4113
|
+
state.active = false;
|
|
4114
|
+
state.epoch++;
|
|
4115
|
+
};
|
|
4116
|
+
}, [client]);
|
|
4117
|
+
(0, react.useEffect)(() => {
|
|
4118
|
+
setSyncError("");
|
|
4119
|
+
let live = true;
|
|
4120
|
+
let stop;
|
|
4121
|
+
try {
|
|
4122
|
+
stop = subscribe?.(() => {
|
|
4123
|
+
if (live) {
|
|
4124
|
+
setSyncError("");
|
|
4125
|
+
refreshCatalog();
|
|
4126
|
+
refreshRef.current();
|
|
4127
|
+
}
|
|
4128
|
+
}, (message) => {
|
|
4129
|
+
if (live) setSyncError(message);
|
|
4130
|
+
});
|
|
4131
|
+
} catch {
|
|
4132
|
+
if (live) setSyncError("Model settings sync service unavailable");
|
|
4133
|
+
}
|
|
4134
|
+
return () => {
|
|
4135
|
+
live = false;
|
|
4136
|
+
try {
|
|
4137
|
+
stop?.();
|
|
4138
|
+
} catch {}
|
|
4139
|
+
};
|
|
4140
|
+
}, [subscribe, refreshCatalog]);
|
|
4141
|
+
const save = (0, react.useCallback)((model) => {
|
|
4142
|
+
const state = operation.current;
|
|
4143
|
+
if (!settings || !state.active || state.writing || state.reading) return;
|
|
4144
|
+
state.writing = true;
|
|
4145
|
+
const epoch = state.epoch;
|
|
4146
|
+
setBusy(true);
|
|
4147
|
+
setNote(false);
|
|
4148
|
+
setError("");
|
|
4149
|
+
callAuxiliaryRpc(client, "defaults.set", {
|
|
4150
|
+
model: model ?? {
|
|
4151
|
+
provider: "",
|
|
4152
|
+
model: ""
|
|
4153
|
+
},
|
|
4154
|
+
expectedRevision: settings.revision
|
|
4155
|
+
}).then((next) => {
|
|
4156
|
+
if (state.active && state.epoch === epoch) {
|
|
4157
|
+
setSettings(next);
|
|
4158
|
+
setNote(true);
|
|
4159
|
+
refreshCatalog();
|
|
4160
|
+
}
|
|
4161
|
+
}).catch((reason) => {
|
|
4162
|
+
if (state.active && state.epoch === epoch) setError(isStaleRejection(reason) ? "stale" : "saveFailed");
|
|
4163
|
+
}).finally(() => {
|
|
4164
|
+
if (!state.active || state.epoch !== epoch) return;
|
|
4165
|
+
state.writing = false;
|
|
4166
|
+
setBusy(false);
|
|
4167
|
+
if (state.pending) refreshRef.current();
|
|
4168
|
+
});
|
|
4169
|
+
}, [
|
|
4170
|
+
client,
|
|
4171
|
+
settings,
|
|
4172
|
+
refreshCatalog
|
|
4173
|
+
]);
|
|
4174
|
+
const locked = busy;
|
|
4175
|
+
if (!settings) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4176
|
+
className: "ams-root dsh-ui-card",
|
|
4177
|
+
"data-testid": "auxiliary-model-settings",
|
|
4178
|
+
children: [
|
|
4179
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4180
|
+
className: "dsh-ui-heading",
|
|
4181
|
+
children: text.title
|
|
4182
|
+
}),
|
|
4183
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4184
|
+
className: "dsh-ui-help",
|
|
4185
|
+
children: text.help
|
|
4186
|
+
}),
|
|
4187
|
+
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4188
|
+
role: "alert",
|
|
4189
|
+
className: "dsh-ui-error",
|
|
4190
|
+
children: text[error]
|
|
4191
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4192
|
+
role: "status",
|
|
4193
|
+
className: "dsh-ui-hint",
|
|
4194
|
+
children: text.loading
|
|
4195
|
+
}),
|
|
4196
|
+
syncError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4197
|
+
role: "status",
|
|
4198
|
+
className: "dsh-ui-hint",
|
|
4199
|
+
children: text.syncUnavailable
|
|
4200
|
+
}),
|
|
4201
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4202
|
+
className: "dsh-ui-actions",
|
|
4203
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4204
|
+
variant: "ghost",
|
|
4205
|
+
size: "sm",
|
|
4206
|
+
disabled: locked,
|
|
4207
|
+
onClick: () => refreshRef.current(),
|
|
4208
|
+
children: text.retry
|
|
4209
|
+
})
|
|
4210
|
+
})
|
|
4211
|
+
]
|
|
4212
|
+
});
|
|
4213
|
+
const current = settings.model ?? {
|
|
4214
|
+
provider: "",
|
|
4215
|
+
model: ""
|
|
4216
|
+
};
|
|
4217
|
+
const follow = !current.provider || !current.model;
|
|
4218
|
+
const selected = follow ? void 0 : choices.find((item) => item.provider === current.provider && item.model === current.model);
|
|
4219
|
+
const efforts = follow ? [] : modelMenuEffortOptions(selected, current.reasoningEffort);
|
|
4220
|
+
const currentLabel = follow ? text.modelDefault : selected?.label ?? `${current.provider} / ${current.model}`;
|
|
4221
|
+
const selectedEffort = efforts.find((item) => item.id === (current.reasoningEffort || ""));
|
|
4222
|
+
const effortName = selectedEffort ? modelMenuEffortLabel(selectedEffort, language) : void 0;
|
|
4223
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4224
|
+
className: "ams-root dsh-ui-card",
|
|
4225
|
+
"data-testid": "auxiliary-model-settings",
|
|
4226
|
+
children: [
|
|
4227
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4228
|
+
className: "dsh-ui-heading",
|
|
4229
|
+
children: text.title
|
|
4230
|
+
}),
|
|
4231
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4232
|
+
className: "dsh-ui-help",
|
|
4233
|
+
children: text.help
|
|
4234
|
+
}),
|
|
4235
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4236
|
+
className: "dsh-ui-field",
|
|
4237
|
+
children: [
|
|
4238
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4239
|
+
className: "dsh-ui-label",
|
|
4240
|
+
children: text.model
|
|
4241
|
+
}),
|
|
4242
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
4243
|
+
state: catalogState,
|
|
4244
|
+
locale: language
|
|
4245
|
+
}),
|
|
4246
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
4247
|
+
locale: language,
|
|
4248
|
+
open: menuOpen && !locked,
|
|
4249
|
+
onOpenChange: (next) => setMenuOpen(next && !locked),
|
|
4250
|
+
listClassName: "ams-root ams-menu-portal",
|
|
4251
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4252
|
+
type: "button",
|
|
4253
|
+
className: "dsh-model-menu-trigger",
|
|
4254
|
+
disabled: locked,
|
|
4255
|
+
"aria-label": `${text.model}: ${currentLabel}`,
|
|
4256
|
+
children: [
|
|
4257
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4258
|
+
className: "dsh-model-menu-triggerLabel",
|
|
4259
|
+
children: currentLabel
|
|
4260
|
+
}),
|
|
4261
|
+
!follow && efforts.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4262
|
+
className: "dsh-model-menu-triggerEffort",
|
|
4263
|
+
children: effortName ?? text.effortDefault
|
|
4264
|
+
}) : null,
|
|
4265
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen && !locked ? " dsh-model-menu-chevronOpen" : ""}` })
|
|
4266
|
+
]
|
|
4267
|
+
}),
|
|
4268
|
+
choices,
|
|
4269
|
+
selected: follow ? void 0 : {
|
|
4270
|
+
provider: current.provider,
|
|
4271
|
+
model: current.model
|
|
4272
|
+
},
|
|
4273
|
+
onPick: (picked) => save({
|
|
4274
|
+
provider: picked.provider,
|
|
4275
|
+
model: picked.model
|
|
4276
|
+
}),
|
|
4277
|
+
leading: [{
|
|
4278
|
+
id: "default",
|
|
4279
|
+
label: text.modelDefault,
|
|
4280
|
+
selected: follow
|
|
4281
|
+
}],
|
|
4282
|
+
onPickLeading: () => save(null),
|
|
4283
|
+
efforts,
|
|
4284
|
+
selectedEffort: current.reasoningEffort || "",
|
|
4285
|
+
onPickEffort: (effort) => save(follow ? null : {
|
|
4286
|
+
provider: current.provider,
|
|
4287
|
+
model: current.model,
|
|
4288
|
+
...effort ? { reasoningEffort: effort } : {}
|
|
4289
|
+
}),
|
|
4290
|
+
modelLabel: text.model,
|
|
4291
|
+
modelValue: currentLabel,
|
|
4292
|
+
defaultEffortLabel: text.effortDefault
|
|
4293
|
+
}),
|
|
4294
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4295
|
+
className: "dsh-ui-hint",
|
|
4296
|
+
children: text.modelHint
|
|
4297
|
+
})
|
|
4298
|
+
]
|
|
4299
|
+
}),
|
|
4300
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4301
|
+
className: "ams-row",
|
|
4302
|
+
children: [
|
|
4303
|
+
syncError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4304
|
+
role: "status",
|
|
4305
|
+
className: "dsh-ui-hint",
|
|
4306
|
+
children: text.syncUnavailable
|
|
4307
|
+
}),
|
|
4308
|
+
error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4309
|
+
role: "alert",
|
|
4310
|
+
className: "dsh-ui-error",
|
|
4311
|
+
children: text[error]
|
|
4312
|
+
}),
|
|
4313
|
+
note && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4314
|
+
role: "status",
|
|
4315
|
+
className: "dsh-ui-notice",
|
|
4316
|
+
children: text.saved
|
|
4317
|
+
}),
|
|
4318
|
+
error || syncError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4319
|
+
variant: "ghost",
|
|
4320
|
+
size: "sm",
|
|
4321
|
+
disabled: locked,
|
|
4322
|
+
onClick: () => refreshRef.current(),
|
|
4323
|
+
children: text.refresh
|
|
4324
|
+
}) : null
|
|
4325
|
+
]
|
|
4326
|
+
})
|
|
4327
|
+
]
|
|
4328
|
+
});
|
|
4329
|
+
}
|
|
4330
|
+
const AUXILIARY_MODEL_WATCH_COORDINATOR = "dshAuxiliaryModelWatchCoordinator";
|
|
4331
|
+
function acquireWatchCoordinator(root) {
|
|
4332
|
+
const existing = root.get(AUXILIARY_MODEL_WATCH_COORDINATOR, false);
|
|
4333
|
+
if (existing) return existing;
|
|
4334
|
+
let unprovide;
|
|
4335
|
+
const coordinator = {
|
|
4336
|
+
tail: Promise.resolve(),
|
|
4337
|
+
active: 0,
|
|
4338
|
+
mount() {
|
|
4339
|
+
coordinator.active += 1;
|
|
4340
|
+
const previousTail = coordinator.tail;
|
|
4341
|
+
const lease = {
|
|
4342
|
+
closed: false,
|
|
4343
|
+
done() {
|
|
4344
|
+
if (lease.closed) return;
|
|
4345
|
+
lease.closed = true;
|
|
4346
|
+
coordinator.active -= 1;
|
|
4347
|
+
const owned = lease.fiber;
|
|
4348
|
+
lease.fiber = void 0;
|
|
4349
|
+
coordinator.tail = coordinator.tail.then(() => Promise.resolve(owned?.dispose()).catch(() => {}));
|
|
4350
|
+
coordinator.tail.then(() => {
|
|
4351
|
+
if (coordinator.active > 0) return;
|
|
4352
|
+
try {
|
|
4353
|
+
if (root.get(AUXILIARY_MODEL_WATCH_COORDINATOR, false) === coordinator) Promise.resolve(unprovide?.()).catch(() => {});
|
|
4354
|
+
} catch {}
|
|
4355
|
+
});
|
|
4356
|
+
},
|
|
4357
|
+
fiber: void 0
|
|
4358
|
+
};
|
|
4359
|
+
previousTail.then(() => {
|
|
4360
|
+
if (lease.closed) return;
|
|
4361
|
+
try {
|
|
4362
|
+
lease.fiber = root.inject(["remote"], (injected) => {
|
|
4363
|
+
const scope = injected;
|
|
4364
|
+
const mounting = scope.remote.$mount(modelSettingsChangesRemote(AUXILIARY_MODEL_PACKAGE, AUXILIARY_MODEL_CHANGES_IDENTITY));
|
|
4365
|
+
mounting.catch(() => {});
|
|
4366
|
+
scope.effect(() => async () => {
|
|
4367
|
+
await (await mounting.catch(() => void 0))?.();
|
|
4368
|
+
});
|
|
4369
|
+
});
|
|
4370
|
+
} catch {}
|
|
4371
|
+
});
|
|
4372
|
+
return lease;
|
|
4373
|
+
}
|
|
4374
|
+
};
|
|
4375
|
+
unprovide = root.provide(AUXILIARY_MODEL_WATCH_COORDINATOR, coordinator);
|
|
4376
|
+
return coordinator;
|
|
4377
|
+
}
|
|
4378
|
+
/**
|
|
4379
|
+
* Acquire the root-owned card service, creating it on first use. One counter
|
|
4380
|
+
* covers participants, the seat wait and the change-stream mount: the last
|
|
4381
|
+
* release removes the wait, then the watch contribution, then the provider
|
|
4382
|
+
* itself synchronously (Cordis deletes the impl before its async disposer
|
|
4383
|
+
* awaits), so a later acquire sees a clean get() miss and can re-establish.
|
|
4384
|
+
*/
|
|
4385
|
+
function acquireAuxiliaryModelCard(root, ctx) {
|
|
4386
|
+
const existing = root.get(AUXILIARY_MODEL_CARD_SERVICE, false);
|
|
4387
|
+
if (existing) return existing;
|
|
4388
|
+
let users = 0;
|
|
4389
|
+
let styleElement;
|
|
4390
|
+
let stopItem;
|
|
4391
|
+
let unprovide;
|
|
4392
|
+
const watchLease = acquireWatchCoordinator(root).mount();
|
|
4393
|
+
const subscriber = createModelSettingsSubscriber(ctx.root, AUXILIARY_MODEL_PACKAGE, AUXILIARY_MODEL_CHANGES_IDENTITY, (ns) => ns === AUXILIARY_MODEL_NS);
|
|
4394
|
+
const render = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AuxiliaryModelCard, {
|
|
4395
|
+
client: root,
|
|
4396
|
+
subscribe: subscriber.subscribe
|
|
4397
|
+
});
|
|
4398
|
+
const service = {
|
|
4399
|
+
retain() {
|
|
4400
|
+
users += 1;
|
|
4401
|
+
if (styleElement === void 0 && typeof document !== "undefined") {
|
|
4402
|
+
styleElement = document.createElement("style");
|
|
4403
|
+
styleElement.setAttribute("data-plugin", "dsh-auxiliary-model-shared");
|
|
4404
|
+
styleElement.textContent = auxiliaryModelCss;
|
|
4405
|
+
document.head.appendChild(styleElement);
|
|
4406
|
+
}
|
|
4407
|
+
if (!stopItem) stopItem = registerSessionSettingsItem(ctx.root, {
|
|
4408
|
+
id: AUXILIARY_MODEL_SEAT_ID,
|
|
4409
|
+
order: 55
|
|
4410
|
+
}, render);
|
|
4411
|
+
},
|
|
4412
|
+
release() {
|
|
4413
|
+
if (users <= 0) return;
|
|
4414
|
+
users -= 1;
|
|
4415
|
+
if (users > 0) return;
|
|
4416
|
+
stopItem?.();
|
|
4417
|
+
stopItem = void 0;
|
|
4418
|
+
styleElement?.remove();
|
|
4419
|
+
styleElement = void 0;
|
|
4420
|
+
watchLease.done();
|
|
4421
|
+
try {
|
|
4422
|
+
Promise.resolve(unprovide?.()).catch(() => {});
|
|
4423
|
+
} catch {}
|
|
4424
|
+
unprovide = void 0;
|
|
4425
|
+
}
|
|
4426
|
+
};
|
|
4427
|
+
unprovide = root.provide(AUXILIARY_MODEL_CARD_SERVICE, service);
|
|
4428
|
+
return service;
|
|
4429
|
+
}
|
|
4430
|
+
/**
|
|
4431
|
+
* Show the shared "default auxiliary model" card while the calling plugin is
|
|
4432
|
+
* enabled.
|
|
4433
|
+
*
|
|
4434
|
+
* `ctx` is the plugin's own client context: the card itself is retained on the
|
|
4435
|
+
* shared root context for as long as any participant lives, so an arbitrary
|
|
4436
|
+
* subset of enabled plugins yields the same single card. The mount's slot wait
|
|
4437
|
+
* and the change-stream contribution are owned by the root service — the first
|
|
4438
|
+
* participant's unload cannot take the card down. Returns a disposer; it may
|
|
4439
|
+
* be invoked by both ctx.effect and an explicit caller and releases at most
|
|
4440
|
+
* once, so a double call cannot unbalance the shared refcount.
|
|
4441
|
+
*/
|
|
4442
|
+
function registerAuxiliaryModelSettings(ctx) {
|
|
4443
|
+
const root = ctx.root;
|
|
4444
|
+
const service = acquireAuxiliaryModelCard(root, ctx);
|
|
4445
|
+
try {
|
|
4446
|
+
service.retain();
|
|
4447
|
+
} catch (error) {
|
|
4448
|
+
service.release();
|
|
4449
|
+
throw error;
|
|
4450
|
+
}
|
|
4451
|
+
let disposed = false;
|
|
4452
|
+
return () => {
|
|
4453
|
+
if (disposed) return;
|
|
4454
|
+
disposed = true;
|
|
4455
|
+
service.release();
|
|
4456
|
+
};
|
|
4457
|
+
}
|
|
4458
|
+
//#endregion
|
|
3520
4459
|
//#region src/presence-panel.tsx
|
|
3521
4460
|
/**
|
|
3522
4461
|
* Grey inline-completion for an empty composer, IDE style: the shipped
|
|
@@ -3761,8 +4700,12 @@ function toDraft(values) {
|
|
|
3761
4700
|
compactMinutes: String(compactEnabled ? values.compactMinutes : 60),
|
|
3762
4701
|
continueText: values.continueText,
|
|
3763
4702
|
continueMode: values.continueMode,
|
|
4703
|
+
autoContinueMode: values.autoContinueMode,
|
|
4704
|
+
autoContinueMax: String(values.autoContinueMax),
|
|
3764
4705
|
model: values.model === null ? "" : modelRouteKey(values.model.provider, values.model.model),
|
|
3765
|
-
ghostModel: values.ghostModel === null ? "" : modelRouteKey(values.ghostModel.provider, values.ghostModel.model)
|
|
4706
|
+
ghostModel: values.ghostModel === null ? "" : modelRouteKey(values.ghostModel.provider, values.ghostModel.model),
|
|
4707
|
+
judgeModel: values.judgeModel === null ? "" : modelRouteKey(values.judgeModel.provider, values.judgeModel.model),
|
|
4708
|
+
handoffModel: values.handoffModel === null ? "" : modelRouteKey(values.handoffModel.provider, values.handoffModel.model)
|
|
3766
4709
|
};
|
|
3767
4710
|
}
|
|
3768
4711
|
/** Enabled thresholds are whole minutes of at least 1. 0 stays the stored "off" value and is not a valid enabled draft. */
|
|
@@ -3771,8 +4714,14 @@ function minutesOf(text) {
|
|
|
3771
4714
|
const value = Number.parseInt(text.trim(), 10);
|
|
3772
4715
|
return Number.isSafeInteger(value) && value >= 1 ? value : void 0;
|
|
3773
4716
|
}
|
|
4717
|
+
/** Automatic continuations per human message: a whole number from 1 to 10. */
|
|
4718
|
+
function autoMaxOf(text) {
|
|
4719
|
+
if (!/^\d+$/.test(text.trim())) return void 0;
|
|
4720
|
+
const value = Number.parseInt(text.trim(), 10);
|
|
4721
|
+
return Number.isSafeInteger(value) && value >= 1 && value <= 10 ? value : void 0;
|
|
4722
|
+
}
|
|
3774
4723
|
function sameDraft(left, right) {
|
|
3775
|
-
return left.recapEnabled === right.recapEnabled && left.recapMinutes === right.recapMinutes && left.compactEnabled === right.compactEnabled && left.compactMinutes === right.compactMinutes && left.continueText === right.continueText && left.continueMode === right.continueMode && left.model === right.model && left.ghostModel === right.ghostModel;
|
|
4724
|
+
return left.recapEnabled === right.recapEnabled && left.recapMinutes === right.recapMinutes && left.compactEnabled === right.compactEnabled && left.compactMinutes === right.compactMinutes && left.continueText === right.continueText && left.continueMode === right.continueMode && left.autoContinueMode === right.autoContinueMode && left.autoContinueMax === right.autoContinueMax && left.model === right.model && left.ghostModel === right.ghostModel && left.judgeModel === right.judgeModel && left.handoffModel === right.handoffModel;
|
|
3776
4725
|
}
|
|
3777
4726
|
const missingCatalog = async () => {
|
|
3778
4727
|
throw new Error("Model catalog service unavailable");
|
|
@@ -3784,6 +4733,8 @@ function SessionManagerSettings(props) {
|
|
|
3784
4733
|
const catalog = useModelCatalog(props.modelCatalog ?? missingCatalog, { subscribe: props.subscribeCatalog });
|
|
3785
4734
|
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
3786
4735
|
const [ghostMenuOpen, setGhostMenuOpen] = (0, react.useState)(false);
|
|
4736
|
+
const [judgeMenuOpen, setJudgeMenuOpen] = (0, react.useState)(false);
|
|
4737
|
+
const [handoffMenuOpen, setHandoffMenuOpen] = (0, react.useState)(false);
|
|
3787
4738
|
const [externalChange, setExternalChange] = (0, react.useState)(false);
|
|
3788
4739
|
const dirty = (0, react.useRef)(false);
|
|
3789
4740
|
const baseline = (0, react.useRef)();
|
|
@@ -3826,325 +4777,539 @@ function SessionManagerSettings(props) {
|
|
|
3826
4777
|
});
|
|
3827
4778
|
const selectedRoute = parseModelRouteKey(draft.model);
|
|
3828
4779
|
const ghostRoute = parseModelRouteKey(draft.ghostModel);
|
|
3829
|
-
const
|
|
3830
|
-
const
|
|
3831
|
-
const
|
|
4780
|
+
const judgeRoute = parseModelRouteKey(draft.judgeModel);
|
|
4781
|
+
const handoffRoute = parseModelRouteKey(draft.handoffModel);
|
|
4782
|
+
const selectableChoices = parseModelMenuChoices(catalog.catalog);
|
|
4783
|
+
for (const bound of [
|
|
4784
|
+
selectedRoute,
|
|
4785
|
+
ghostRoute,
|
|
4786
|
+
judgeRoute,
|
|
4787
|
+
handoffRoute
|
|
4788
|
+
]) if (bound && !selectableChoices.some((choice) => choice.provider === bound.provider && choice.model === bound.model)) selectableChoices.push({
|
|
4789
|
+
provider: bound.provider,
|
|
4790
|
+
model: bound.model,
|
|
4791
|
+
label: `${bound.provider} / ${bound.model}`,
|
|
4792
|
+
efforts: []
|
|
4793
|
+
});
|
|
4794
|
+
const routeLabel = (key) => selectableChoices.find((choice) => modelRouteKey(choice.provider, choice.model) === key)?.label ?? copy("modelFollowOption");
|
|
4795
|
+
const selectedLabel = routeLabel(draft.model);
|
|
4796
|
+
const ghostLabel = routeLabel(draft.ghostModel);
|
|
4797
|
+
const judgeLabel = routeLabel(draft.judgeModel);
|
|
4798
|
+
const handoffLabel = routeLabel(draft.handoffModel);
|
|
3832
4799
|
const recapMinutes = draft.recapEnabled ? minutesOf(draft.recapMinutes) : 0;
|
|
3833
4800
|
const compactMinutes = draft.compactEnabled ? minutesOf(draft.compactMinutes) : 0;
|
|
4801
|
+
const autoContinueMax = autoMaxOf(draft.autoContinueMax);
|
|
3834
4802
|
const recapInvalid = recapMinutes === void 0;
|
|
3835
4803
|
const compactInvalid = compactMinutes === void 0;
|
|
3836
|
-
const
|
|
4804
|
+
const autoMaxInvalid = autoContinueMax === void 0;
|
|
4805
|
+
const invalid = recapInvalid || compactInvalid || autoMaxInvalid;
|
|
3837
4806
|
const fixedPanelId = `${tabsId}-fixed-panel`;
|
|
3838
4807
|
const autoPanelId = `${tabsId}-auto-panel`;
|
|
3839
4808
|
const save = () => {
|
|
3840
4809
|
if (invalid) return;
|
|
3841
|
-
const
|
|
3842
|
-
|
|
4810
|
+
const routePatch = (key) => {
|
|
4811
|
+
if (draft[key] === baseline.current?.[key]) return {};
|
|
4812
|
+
const parsed = parseModelRouteKey(draft[key]);
|
|
4813
|
+
return { [key]: parsed === void 0 ? null : {
|
|
4814
|
+
provider: parsed.provider,
|
|
4815
|
+
model: parsed.model
|
|
4816
|
+
} };
|
|
4817
|
+
};
|
|
3843
4818
|
setExternalChange(false);
|
|
3844
4819
|
props.model.save({
|
|
3845
4820
|
recapMinutes,
|
|
3846
4821
|
compactMinutes,
|
|
3847
4822
|
continueText: draft.continueText,
|
|
3848
4823
|
continueMode: draft.continueMode,
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
...
|
|
3854
|
-
|
|
3855
|
-
model: ghost.model
|
|
3856
|
-
} }
|
|
4824
|
+
autoContinueMode: draft.autoContinueMode,
|
|
4825
|
+
autoContinueMax,
|
|
4826
|
+
...routePatch("model"),
|
|
4827
|
+
...routePatch("ghostModel"),
|
|
4828
|
+
...routePatch("judgeModel"),
|
|
4829
|
+
...routePatch("handoffModel")
|
|
3857
4830
|
});
|
|
3858
4831
|
};
|
|
3859
4832
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3860
4833
|
className: `${FREE_CHAT_ROOT} dsh-ui-page`,
|
|
3861
4834
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3862
4835
|
className: "dsm-settings",
|
|
3863
|
-
children: [
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
4836
|
+
children: [
|
|
4837
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4838
|
+
className: "dsm-setting",
|
|
4839
|
+
"aria-labelledby": `${tabsId}-ghost-heading`,
|
|
4840
|
+
children: [
|
|
4841
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
4842
|
+
className: "dsm-setting-header",
|
|
4843
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
4844
|
+
id: `${tabsId}-ghost-heading`,
|
|
4845
|
+
className: "dsh-ui-title",
|
|
4846
|
+
children: copy("settingsMode")
|
|
4847
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4848
|
+
className: "dsh-ui-hint",
|
|
4849
|
+
children: copy("settingsModeHint")
|
|
4850
|
+
})]
|
|
4851
|
+
}),
|
|
4852
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4853
|
+
className: "dsm-ghost-controls",
|
|
4854
|
+
children: [
|
|
4855
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4856
|
+
className: "dsh-ui-field",
|
|
4857
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4858
|
+
className: "dsh-ui-label",
|
|
4859
|
+
children: copy("continueSourceLabel")
|
|
4860
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
|
|
4861
|
+
label: copy("continueSourceLabel"),
|
|
4862
|
+
items: [{
|
|
4863
|
+
id: `${tabsId}-fixed`,
|
|
4864
|
+
panelId: fixedPanelId,
|
|
4865
|
+
value: "fixed",
|
|
4866
|
+
label: copy("modeFixed")
|
|
4867
|
+
}, {
|
|
4868
|
+
id: `${tabsId}-auto`,
|
|
4869
|
+
panelId: autoPanelId,
|
|
4870
|
+
value: "auto",
|
|
4871
|
+
label: copy("modeAuto")
|
|
4872
|
+
}],
|
|
4873
|
+
value: draft.continueMode,
|
|
4874
|
+
onChange: (value) => edit({
|
|
4875
|
+
...draft,
|
|
4876
|
+
continueMode: value
|
|
4877
|
+
})
|
|
4878
|
+
})]
|
|
4879
|
+
}),
|
|
4880
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4881
|
+
role: "tabpanel",
|
|
4882
|
+
id: fixedPanelId,
|
|
4883
|
+
"aria-labelledby": `${tabsId}-fixed`,
|
|
4884
|
+
hidden: draft.continueMode !== "fixed",
|
|
4885
|
+
children: draft.continueMode === "fixed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4886
|
+
className: "dsh-ui-field",
|
|
4887
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4888
|
+
className: "dsh-ui-label",
|
|
4889
|
+
children: copy("continueTextLabel")
|
|
4890
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
4891
|
+
className: "dsm-ghost-input",
|
|
4892
|
+
value: draft.continueText,
|
|
4893
|
+
placeholder: copy("continueTextPlaceholder"),
|
|
4894
|
+
onChange: (event) => edit({
|
|
4895
|
+
...draft,
|
|
4896
|
+
continueText: event.target.value
|
|
4897
|
+
})
|
|
4898
|
+
})]
|
|
4899
|
+
}) : null
|
|
4900
|
+
}),
|
|
4901
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4902
|
+
role: "tabpanel",
|
|
4903
|
+
id: autoPanelId,
|
|
4904
|
+
"aria-labelledby": `${tabsId}-auto`,
|
|
4905
|
+
hidden: draft.continueMode !== "auto",
|
|
4906
|
+
children: draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4907
|
+
className: "dsh-ui-field dsm-model-field",
|
|
4908
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4909
|
+
className: "dsh-ui-label",
|
|
4910
|
+
children: copy("ghostModelLabel")
|
|
4911
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
4912
|
+
locale: props.locale,
|
|
4913
|
+
open: ghostMenuOpen,
|
|
4914
|
+
onOpenChange: setGhostMenuOpen,
|
|
4915
|
+
listClassName: FREE_CHAT_ROOT,
|
|
4916
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4917
|
+
type: "button",
|
|
4918
|
+
className: "dsh-model-menu-trigger",
|
|
4919
|
+
"aria-label": `${copy("ghostModelLabel")}: ${ghostLabel}`,
|
|
4920
|
+
"aria-haspopup": "menu",
|
|
4921
|
+
"aria-expanded": ghostMenuOpen,
|
|
4922
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4923
|
+
className: "dsh-model-menu-triggerLabel",
|
|
4924
|
+
children: ghostLabel
|
|
4925
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${ghostMenuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
4926
|
+
}),
|
|
4927
|
+
choices: selectableChoices,
|
|
4928
|
+
selected: ghostRoute,
|
|
4929
|
+
onPick: (route) => edit({
|
|
4930
|
+
...draft,
|
|
4931
|
+
ghostModel: modelRouteKey(route.provider, route.model)
|
|
4932
|
+
}),
|
|
4933
|
+
leading: [{
|
|
4934
|
+
id: "follow",
|
|
4935
|
+
label: copy("modelFollowOption"),
|
|
4936
|
+
selected: !ghostRoute
|
|
4937
|
+
}],
|
|
4938
|
+
onPickLeading: () => edit({
|
|
4939
|
+
...draft,
|
|
4940
|
+
ghostModel: ""
|
|
4941
|
+
}),
|
|
4942
|
+
modelValue: ghostLabel
|
|
4943
|
+
})]
|
|
4944
|
+
}) : null
|
|
4945
|
+
})
|
|
4946
|
+
]
|
|
4947
|
+
}),
|
|
4948
|
+
draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4949
|
+
className: "dsh-ui-hint",
|
|
4950
|
+
children: copy("ghostModelHint")
|
|
4951
|
+
}) : null
|
|
4952
|
+
]
|
|
4953
|
+
}),
|
|
4954
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4955
|
+
className: "dsm-setting",
|
|
4956
|
+
"aria-labelledby": `${tabsId}-auto-continue-heading`,
|
|
4957
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
3868
4958
|
className: "dsm-setting-header",
|
|
3869
|
-
children:
|
|
3870
|
-
id: `${tabsId}-
|
|
4959
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
4960
|
+
id: `${tabsId}-auto-continue-heading`,
|
|
3871
4961
|
className: "dsh-ui-title",
|
|
3872
|
-
children: copy("
|
|
3873
|
-
})
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
})]
|
|
3877
|
-
}),
|
|
3878
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3879
|
-
className: "dsm-ghost-controls",
|
|
4962
|
+
children: copy("autoContinueGroup")
|
|
4963
|
+
})
|
|
4964
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4965
|
+
className: "dsm-feature-options",
|
|
3880
4966
|
children: [
|
|
3881
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
4967
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3882
4968
|
className: "dsh-ui-field",
|
|
3883
|
-
children: [
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
4969
|
+
children: [
|
|
4970
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4971
|
+
className: "dsh-ui-label",
|
|
4972
|
+
children: copy("autoContinueDefaultLabel")
|
|
4973
|
+
}),
|
|
4974
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
4975
|
+
className: "dsh-ui-select",
|
|
4976
|
+
value: draft.autoContinueMode,
|
|
4977
|
+
onChange: (event) => edit({
|
|
4978
|
+
...draft,
|
|
4979
|
+
autoContinueMode: event.target.value
|
|
4980
|
+
}),
|
|
4981
|
+
children: [
|
|
4982
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4983
|
+
value: "off",
|
|
4984
|
+
children: copy("autoContinueOff")
|
|
4985
|
+
}),
|
|
4986
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4987
|
+
value: "cautious",
|
|
4988
|
+
children: copy("autoContinueCautious")
|
|
4989
|
+
}),
|
|
4990
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4991
|
+
value: "bold",
|
|
4992
|
+
children: copy("autoContinueBold")
|
|
4993
|
+
})
|
|
4994
|
+
]
|
|
4995
|
+
}),
|
|
4996
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4997
|
+
className: "dsh-ui-hint",
|
|
4998
|
+
children: copy("autoContinueDefaultHint")
|
|
3903
4999
|
})
|
|
3904
|
-
|
|
5000
|
+
]
|
|
3905
5001
|
}),
|
|
3906
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3907
|
-
|
|
3908
|
-
id: fixedPanelId,
|
|
3909
|
-
"aria-labelledby": `${tabsId}-fixed`,
|
|
3910
|
-
hidden: draft.continueMode !== "fixed",
|
|
3911
|
-
children: draft.continueMode === "fixed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5002
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
5003
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3912
5004
|
className: "dsh-ui-field",
|
|
3913
5005
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3914
5006
|
className: "dsh-ui-label",
|
|
3915
|
-
children: copy("
|
|
3916
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
3917
|
-
className: "dsm-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
5007
|
+
children: copy("autoContinueMaxLabel")
|
|
5008
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5009
|
+
className: "dsm-minutes-control",
|
|
5010
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
5011
|
+
className: "dsm-minutes",
|
|
5012
|
+
value: draft.autoContinueMax,
|
|
5013
|
+
inputMode: "numeric",
|
|
5014
|
+
"aria-invalid": autoMaxInvalid || void 0,
|
|
5015
|
+
"aria-describedby": autoMaxInvalid ? `${tabsId}-auto-max-error` : void 0,
|
|
5016
|
+
onChange: (event) => edit({
|
|
5017
|
+
...draft,
|
|
5018
|
+
autoContinueMax: event.target.value
|
|
5019
|
+
})
|
|
3923
5020
|
})
|
|
3924
5021
|
})]
|
|
5022
|
+
}),
|
|
5023
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5024
|
+
className: "dsh-ui-hint",
|
|
5025
|
+
children: copy("autoContinueMaxHint")
|
|
5026
|
+
}),
|
|
5027
|
+
autoMaxInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5028
|
+
id: `${tabsId}-auto-max-error`,
|
|
5029
|
+
className: "dsh-ui-error",
|
|
5030
|
+
role: "alert",
|
|
5031
|
+
children: copy("autoContinueMaxInvalid")
|
|
3925
5032
|
}) : null
|
|
3926
|
-
}),
|
|
3927
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
hidden: draft.continueMode !== "auto",
|
|
3932
|
-
children: draft.continueMode === "auto" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3933
|
-
className: "dsh-ui-field dsm-model-field",
|
|
3934
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5033
|
+
] }),
|
|
5034
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5035
|
+
className: "dsh-ui-field dsm-model-field",
|
|
5036
|
+
children: [
|
|
5037
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3935
5038
|
className: "dsh-ui-label",
|
|
3936
|
-
children: copy("
|
|
3937
|
-
}),
|
|
5039
|
+
children: copy("judgeModelLabel")
|
|
5040
|
+
}),
|
|
5041
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
3938
5042
|
locale: props.locale,
|
|
3939
|
-
open:
|
|
3940
|
-
onOpenChange:
|
|
5043
|
+
open: judgeMenuOpen,
|
|
5044
|
+
onOpenChange: setJudgeMenuOpen,
|
|
3941
5045
|
listClassName: FREE_CHAT_ROOT,
|
|
3942
5046
|
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3943
5047
|
type: "button",
|
|
3944
5048
|
className: "dsh-model-menu-trigger",
|
|
3945
|
-
"aria-label": `${copy("
|
|
5049
|
+
"aria-label": `${copy("judgeModelLabel")}: ${judgeLabel}`,
|
|
3946
5050
|
"aria-haspopup": "menu",
|
|
3947
|
-
"aria-expanded":
|
|
5051
|
+
"aria-expanded": judgeMenuOpen,
|
|
3948
5052
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3949
5053
|
className: "dsh-model-menu-triggerLabel",
|
|
3950
|
-
children:
|
|
3951
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${
|
|
5054
|
+
children: judgeLabel
|
|
5055
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${judgeMenuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
3952
5056
|
}),
|
|
3953
5057
|
choices: selectableChoices,
|
|
3954
|
-
selected:
|
|
5058
|
+
selected: judgeRoute,
|
|
3955
5059
|
onPick: (route) => edit({
|
|
3956
5060
|
...draft,
|
|
3957
|
-
|
|
5061
|
+
judgeModel: modelRouteKey(route.provider, route.model)
|
|
3958
5062
|
}),
|
|
3959
5063
|
leading: [{
|
|
3960
5064
|
id: "follow",
|
|
3961
5065
|
label: copy("modelFollowOption"),
|
|
3962
|
-
selected: !
|
|
5066
|
+
selected: !judgeRoute
|
|
3963
5067
|
}],
|
|
3964
5068
|
onPickLeading: () => edit({
|
|
3965
5069
|
...draft,
|
|
3966
|
-
|
|
5070
|
+
judgeModel: ""
|
|
3967
5071
|
}),
|
|
3968
|
-
modelValue:
|
|
3969
|
-
})
|
|
3970
|
-
|
|
5072
|
+
modelValue: judgeLabel
|
|
5073
|
+
}),
|
|
5074
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5075
|
+
className: "dsh-ui-hint",
|
|
5076
|
+
children: copy("judgeModelHint")
|
|
5077
|
+
})
|
|
5078
|
+
]
|
|
3971
5079
|
})
|
|
3972
5080
|
]
|
|
3973
|
-
})
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3980
|
-
className: "dsm-setting",
|
|
3981
|
-
"aria-labelledby": `${tabsId}-return-heading`,
|
|
3982
|
-
children: [
|
|
3983
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
5081
|
+
})]
|
|
5082
|
+
}),
|
|
5083
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5084
|
+
className: "dsm-setting",
|
|
5085
|
+
"aria-labelledby": `${tabsId}-handoff-heading`,
|
|
5086
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
3984
5087
|
className: "dsm-setting-header",
|
|
3985
5088
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3986
|
-
id: `${tabsId}-
|
|
5089
|
+
id: `${tabsId}-handoff-heading`,
|
|
3987
5090
|
className: "dsh-ui-title",
|
|
3988
|
-
children: copy("
|
|
5091
|
+
children: copy("handoffGroup")
|
|
3989
5092
|
})
|
|
3990
|
-
}),
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
5093
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5094
|
+
className: "dsm-feature-options",
|
|
5095
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5096
|
+
className: "dsh-ui-field dsm-model-field",
|
|
5097
|
+
children: [
|
|
5098
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5099
|
+
className: "dsh-ui-label",
|
|
5100
|
+
children: copy("handoffModelLabel")
|
|
5101
|
+
}),
|
|
5102
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
5103
|
+
locale: props.locale,
|
|
5104
|
+
open: handoffMenuOpen,
|
|
5105
|
+
onOpenChange: setHandoffMenuOpen,
|
|
5106
|
+
listClassName: FREE_CHAT_ROOT,
|
|
5107
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5108
|
+
type: "button",
|
|
5109
|
+
className: "dsh-model-menu-trigger",
|
|
5110
|
+
"aria-label": `${copy("handoffModelLabel")}: ${handoffLabel}`,
|
|
5111
|
+
"aria-haspopup": "menu",
|
|
5112
|
+
"aria-expanded": handoffMenuOpen,
|
|
5113
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5114
|
+
className: "dsh-model-menu-triggerLabel",
|
|
5115
|
+
children: handoffLabel
|
|
5116
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${handoffMenuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
5117
|
+
}),
|
|
5118
|
+
choices: selectableChoices,
|
|
5119
|
+
selected: handoffRoute,
|
|
5120
|
+
onPick: (route) => edit({
|
|
5121
|
+
...draft,
|
|
5122
|
+
handoffModel: modelRouteKey(route.provider, route.model)
|
|
5123
|
+
}),
|
|
5124
|
+
leading: [{
|
|
5125
|
+
id: "follow",
|
|
5126
|
+
label: copy("modelFollowOption"),
|
|
5127
|
+
selected: !handoffRoute
|
|
5128
|
+
}],
|
|
5129
|
+
onPickLeading: () => edit({
|
|
5130
|
+
...draft,
|
|
5131
|
+
handoffModel: ""
|
|
5132
|
+
}),
|
|
5133
|
+
modelValue: handoffLabel
|
|
5134
|
+
}),
|
|
5135
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4001
5136
|
className: "dsh-ui-hint",
|
|
4002
|
-
children: copy("
|
|
4003
|
-
})]
|
|
4004
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
4005
|
-
label: copy("recapEnabledLabel"),
|
|
4006
|
-
checked: draft.recapEnabled,
|
|
4007
|
-
onChange: (next) => edit({
|
|
4008
|
-
...draft,
|
|
4009
|
-
recapEnabled: next
|
|
5137
|
+
children: copy("handoffModelHint")
|
|
4010
5138
|
})
|
|
5139
|
+
]
|
|
5140
|
+
})
|
|
5141
|
+
})]
|
|
5142
|
+
}),
|
|
5143
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5144
|
+
className: "dsm-setting",
|
|
5145
|
+
"aria-labelledby": `${tabsId}-return-heading`,
|
|
5146
|
+
children: [
|
|
5147
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
|
|
5148
|
+
className: "dsm-setting-header",
|
|
5149
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
5150
|
+
id: `${tabsId}-return-heading`,
|
|
5151
|
+
className: "dsh-ui-title",
|
|
5152
|
+
children: copy("welcomeBackGroup")
|
|
5153
|
+
})
|
|
5154
|
+
}),
|
|
5155
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5156
|
+
className: "dsm-feature",
|
|
5157
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5158
|
+
className: "dsh-ui-toggle-row",
|
|
5159
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5160
|
+
className: "dsh-ui-toggle-text",
|
|
5161
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5162
|
+
className: "dsh-ui-toggle-label",
|
|
5163
|
+
children: copy("recapEnabledLabel")
|
|
5164
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5165
|
+
className: "dsh-ui-hint",
|
|
5166
|
+
children: copy("recapEnabledHint")
|
|
5167
|
+
})]
|
|
5168
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
5169
|
+
label: copy("recapEnabledLabel"),
|
|
5170
|
+
checked: draft.recapEnabled,
|
|
5171
|
+
onChange: (next) => edit({
|
|
5172
|
+
...draft,
|
|
5173
|
+
recapEnabled: next
|
|
5174
|
+
})
|
|
5175
|
+
})]
|
|
5176
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5177
|
+
className: "dsm-feature-options",
|
|
5178
|
+
"data-disabled": draft.recapEnabled ? void 0 : "true",
|
|
5179
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5180
|
+
className: "dsh-ui-fields-grid",
|
|
5181
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
5182
|
+
className: "dsh-ui-field",
|
|
5183
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5184
|
+
className: "dsh-ui-label",
|
|
5185
|
+
children: copy("recapMinutesLabel")
|
|
5186
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5187
|
+
className: "dsm-minutes-control",
|
|
5188
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
5189
|
+
className: "dsm-minutes",
|
|
5190
|
+
value: draft.recapMinutes,
|
|
5191
|
+
inputMode: "numeric",
|
|
5192
|
+
disabled: !draft.recapEnabled,
|
|
5193
|
+
"aria-invalid": recapInvalid || void 0,
|
|
5194
|
+
"aria-describedby": recapInvalid ? `${tabsId}-recap-error` : void 0,
|
|
5195
|
+
onChange: (event) => edit({
|
|
5196
|
+
...draft,
|
|
5197
|
+
recapMinutes: event.target.value
|
|
5198
|
+
})
|
|
5199
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5200
|
+
className: "dsh-ui-meta",
|
|
5201
|
+
children: copy("minutesUnit")
|
|
5202
|
+
})]
|
|
5203
|
+
})]
|
|
5204
|
+
}), recapInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5205
|
+
id: `${tabsId}-recap-error`,
|
|
5206
|
+
className: "dsh-ui-error",
|
|
5207
|
+
role: "alert",
|
|
5208
|
+
children: copy("minutesInvalid")
|
|
5209
|
+
}) : null] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5210
|
+
className: "dsh-ui-field dsm-model-field",
|
|
5211
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5212
|
+
className: "dsh-ui-label",
|
|
5213
|
+
children: copy("modelLabel")
|
|
5214
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
5215
|
+
locale: props.locale,
|
|
5216
|
+
open: menuOpen,
|
|
5217
|
+
onOpenChange: setMenuOpen,
|
|
5218
|
+
listClassName: FREE_CHAT_ROOT,
|
|
5219
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5220
|
+
type: "button",
|
|
5221
|
+
className: "dsh-model-menu-trigger",
|
|
5222
|
+
disabled: !draft.recapEnabled,
|
|
5223
|
+
"aria-label": `${copy("modelLabel")}: ${selectedLabel}`,
|
|
5224
|
+
"aria-haspopup": "menu",
|
|
5225
|
+
"aria-expanded": menuOpen,
|
|
5226
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5227
|
+
className: "dsh-model-menu-triggerLabel",
|
|
5228
|
+
children: selectedLabel
|
|
5229
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
5230
|
+
}),
|
|
5231
|
+
choices: selectableChoices,
|
|
5232
|
+
selected: selectedRoute,
|
|
5233
|
+
onPick: (route) => edit({
|
|
5234
|
+
...draft,
|
|
5235
|
+
model: modelRouteKey(route.provider, route.model)
|
|
5236
|
+
}),
|
|
5237
|
+
leading: [{
|
|
5238
|
+
id: "follow",
|
|
5239
|
+
label: copy("modelFollowOption"),
|
|
5240
|
+
selected: !selectedRoute
|
|
5241
|
+
}],
|
|
5242
|
+
onPickLeading: () => edit({
|
|
5243
|
+
...draft,
|
|
5244
|
+
model: ""
|
|
5245
|
+
}),
|
|
5246
|
+
modelValue: selectedLabel
|
|
5247
|
+
})]
|
|
5248
|
+
})]
|
|
5249
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
5250
|
+
state: catalog,
|
|
5251
|
+
locale: props.locale
|
|
5252
|
+
})]
|
|
4011
5253
|
})]
|
|
4012
|
-
}),
|
|
4013
|
-
|
|
4014
|
-
|
|
5254
|
+
}),
|
|
5255
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5256
|
+
className: "dsm-feature",
|
|
4015
5257
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4016
|
-
className: "dsh-ui-
|
|
4017
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
5258
|
+
className: "dsh-ui-toggle-row",
|
|
5259
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5260
|
+
className: "dsh-ui-toggle-text",
|
|
5261
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5262
|
+
className: "dsh-ui-toggle-label",
|
|
5263
|
+
children: copy("compactEnabledLabel")
|
|
5264
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5265
|
+
className: "dsh-ui-hint",
|
|
5266
|
+
children: copy("compactEnabledHint")
|
|
5267
|
+
})]
|
|
5268
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
5269
|
+
label: copy("compactEnabledLabel"),
|
|
5270
|
+
checked: draft.compactEnabled,
|
|
5271
|
+
onChange: (next) => edit({
|
|
5272
|
+
...draft,
|
|
5273
|
+
compactEnabled: next
|
|
5274
|
+
})
|
|
5275
|
+
})]
|
|
5276
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5277
|
+
className: "dsm-feature-options dsm-compact-options",
|
|
5278
|
+
"data-disabled": draft.compactEnabled ? void 0 : "true",
|
|
5279
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4018
5280
|
className: "dsh-ui-field",
|
|
4019
5281
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4020
5282
|
className: "dsh-ui-label",
|
|
4021
|
-
children: copy("
|
|
5283
|
+
children: copy("compactMinutesLabel")
|
|
4022
5284
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4023
5285
|
className: "dsm-minutes-control",
|
|
4024
5286
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
4025
5287
|
className: "dsm-minutes",
|
|
4026
|
-
value: draft.
|
|
5288
|
+
value: draft.compactMinutes,
|
|
4027
5289
|
inputMode: "numeric",
|
|
4028
|
-
disabled: !draft.
|
|
4029
|
-
"aria-invalid":
|
|
4030
|
-
"aria-describedby":
|
|
5290
|
+
disabled: !draft.compactEnabled,
|
|
5291
|
+
"aria-invalid": compactInvalid || void 0,
|
|
5292
|
+
"aria-describedby": compactInvalid ? `${tabsId}-compact-error` : void 0,
|
|
4031
5293
|
onChange: (event) => edit({
|
|
4032
5294
|
...draft,
|
|
4033
|
-
|
|
5295
|
+
compactMinutes: event.target.value
|
|
4034
5296
|
})
|
|
4035
5297
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4036
5298
|
className: "dsh-ui-meta",
|
|
4037
5299
|
children: copy("minutesUnit")
|
|
4038
5300
|
})]
|
|
4039
5301
|
})]
|
|
4040
|
-
}),
|
|
4041
|
-
id: `${tabsId}-
|
|
5302
|
+
}), compactInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5303
|
+
id: `${tabsId}-compact-error`,
|
|
4042
5304
|
className: "dsh-ui-error",
|
|
4043
5305
|
role: "alert",
|
|
4044
5306
|
children: copy("minutesInvalid")
|
|
4045
|
-
}) : null] })
|
|
4046
|
-
className: "dsh-ui-field dsm-model-field",
|
|
4047
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4048
|
-
className: "dsh-ui-label",
|
|
4049
|
-
children: copy("modelLabel")
|
|
4050
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
4051
|
-
locale: props.locale,
|
|
4052
|
-
open: menuOpen,
|
|
4053
|
-
onOpenChange: setMenuOpen,
|
|
4054
|
-
listClassName: FREE_CHAT_ROOT,
|
|
4055
|
-
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4056
|
-
type: "button",
|
|
4057
|
-
className: "dsh-model-menu-trigger",
|
|
4058
|
-
disabled: !draft.recapEnabled,
|
|
4059
|
-
"aria-label": `${copy("modelLabel")}: ${selectedLabel}`,
|
|
4060
|
-
"aria-haspopup": "menu",
|
|
4061
|
-
"aria-expanded": menuOpen,
|
|
4062
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4063
|
-
className: "dsh-model-menu-triggerLabel",
|
|
4064
|
-
children: selectedLabel
|
|
4065
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen ? " dsh-model-menu-chevronOpen" : ""}` })]
|
|
4066
|
-
}),
|
|
4067
|
-
choices: selectableChoices,
|
|
4068
|
-
selected: selectedRoute,
|
|
4069
|
-
onPick: (route) => edit({
|
|
4070
|
-
...draft,
|
|
4071
|
-
model: modelRouteKey(route.provider, route.model)
|
|
4072
|
-
}),
|
|
4073
|
-
leading: [{
|
|
4074
|
-
id: "follow",
|
|
4075
|
-
label: copy("modelFollowOption"),
|
|
4076
|
-
selected: !selectedRoute
|
|
4077
|
-
}],
|
|
4078
|
-
onPickLeading: () => edit({
|
|
4079
|
-
...draft,
|
|
4080
|
-
model: ""
|
|
4081
|
-
}),
|
|
4082
|
-
modelValue: selectedLabel
|
|
4083
|
-
})]
|
|
4084
|
-
})]
|
|
4085
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
4086
|
-
state: catalog,
|
|
4087
|
-
locale: props.locale
|
|
4088
|
-
})]
|
|
4089
|
-
})]
|
|
4090
|
-
}),
|
|
4091
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4092
|
-
className: "dsm-feature",
|
|
4093
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4094
|
-
className: "dsh-ui-toggle-row",
|
|
4095
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4096
|
-
className: "dsh-ui-toggle-text",
|
|
4097
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4098
|
-
className: "dsh-ui-toggle-label",
|
|
4099
|
-
children: copy("compactEnabledLabel")
|
|
4100
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4101
|
-
className: "dsh-ui-hint",
|
|
4102
|
-
children: copy("compactEnabledHint")
|
|
4103
|
-
})]
|
|
4104
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
4105
|
-
label: copy("compactEnabledLabel"),
|
|
4106
|
-
checked: draft.compactEnabled,
|
|
4107
|
-
onChange: (next) => edit({
|
|
4108
|
-
...draft,
|
|
4109
|
-
compactEnabled: next
|
|
4110
|
-
})
|
|
5307
|
+
}) : null] })
|
|
4111
5308
|
})]
|
|
4112
|
-
})
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
className: "dsh-ui-field",
|
|
4117
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4118
|
-
className: "dsh-ui-label",
|
|
4119
|
-
children: copy("compactMinutesLabel")
|
|
4120
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4121
|
-
className: "dsm-minutes-control",
|
|
4122
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
4123
|
-
className: "dsm-minutes",
|
|
4124
|
-
value: draft.compactMinutes,
|
|
4125
|
-
inputMode: "numeric",
|
|
4126
|
-
disabled: !draft.compactEnabled,
|
|
4127
|
-
"aria-invalid": compactInvalid || void 0,
|
|
4128
|
-
"aria-describedby": compactInvalid ? `${tabsId}-compact-error` : void 0,
|
|
4129
|
-
onChange: (event) => edit({
|
|
4130
|
-
...draft,
|
|
4131
|
-
compactMinutes: event.target.value
|
|
4132
|
-
})
|
|
4133
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4134
|
-
className: "dsh-ui-meta",
|
|
4135
|
-
children: copy("minutesUnit")
|
|
4136
|
-
})]
|
|
4137
|
-
})]
|
|
4138
|
-
}), compactInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4139
|
-
id: `${tabsId}-compact-error`,
|
|
4140
|
-
className: "dsh-ui-error",
|
|
4141
|
-
role: "alert",
|
|
4142
|
-
children: copy("minutesInvalid")
|
|
4143
|
-
}) : null] })
|
|
4144
|
-
})]
|
|
4145
|
-
})
|
|
4146
|
-
]
|
|
4147
|
-
})]
|
|
5309
|
+
})
|
|
5310
|
+
]
|
|
5311
|
+
})
|
|
5312
|
+
]
|
|
4148
5313
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4149
5314
|
className: "dsh-ui-form-footer dsm-settings-footer",
|
|
4150
5315
|
children: [
|
|
@@ -5044,6 +6209,21 @@ function apply(ctx) {
|
|
|
5044
6209
|
...props
|
|
5045
6210
|
}))));
|
|
5046
6211
|
stops.push(installTemporaryWorkspacePicker(client.slots, temporary, temporaryHost.createWorkspace, client.locale));
|
|
6212
|
+
stops.push(client.slots.inject("conversation.hero.options", () => client.slots.register({
|
|
6213
|
+
name: "conversation.hero.options",
|
|
6214
|
+
id: "auto-continue",
|
|
6215
|
+
order: 30,
|
|
6216
|
+
inject: () => ({
|
|
6217
|
+
call: (endpoint, payload) => client.connection?.rpc?.call(TEMP_WORKSPACE_CHANNEL, endpoint, payload),
|
|
6218
|
+
subscribeGeneration: (listener) => client.connection?.generation?.subscribe(listener)
|
|
6219
|
+
})
|
|
6220
|
+
}, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoContinueOption, {
|
|
6221
|
+
sessionId: props.sessionId,
|
|
6222
|
+
call: props.call,
|
|
6223
|
+
subscribeGeneration: props.subscribeGeneration,
|
|
6224
|
+
useSession: props.useSession,
|
|
6225
|
+
locale: useLiveLocale(client.locale)
|
|
6226
|
+
}))));
|
|
5047
6227
|
stops.push(client.slots.inject(USER_ACTIONS_SLOT, () => client.slots.register({
|
|
5048
6228
|
name: USER_ACTIONS_SLOT,
|
|
5049
6229
|
id: USER_ACTIONS_ID,
|
|
@@ -5095,15 +6275,16 @@ function apply(ctx) {
|
|
|
5095
6275
|
locale: useLiveLocale(client.locale),
|
|
5096
6276
|
model: welcomeBack
|
|
5097
6277
|
}))));
|
|
5098
|
-
stops.push(
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
6278
|
+
stops.push(registerSessionSettingsItem(ctx, {
|
|
6279
|
+
id: SESSION_SETTINGS_ITEM_ID,
|
|
6280
|
+
order: 65
|
|
6281
|
+
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionManagerSettingsItem, {
|
|
5102
6282
|
model: presenceConfig,
|
|
5103
6283
|
locale: useLiveLocale(client.locale),
|
|
5104
6284
|
modelCatalog,
|
|
5105
|
-
subscribeCatalog
|
|
5106
|
-
|
|
6285
|
+
subscribeCatalog,
|
|
6286
|
+
subscribeSettings
|
|
6287
|
+
})));
|
|
5107
6288
|
stops.push(client.slots.inject("shell.overlay", () => client.slots.register({
|
|
5108
6289
|
name: "shell.overlay",
|
|
5109
6290
|
id: DELETE_DIALOG_ID,
|
|
@@ -5126,6 +6307,7 @@ function apply(ctx) {
|
|
|
5126
6307
|
})
|
|
5127
6308
|
] });
|
|
5128
6309
|
})));
|
|
6310
|
+
stops.push(registerAuxiliaryModelSettings(ctx));
|
|
5129
6311
|
const findCurrent = () => Object.values(client.sessions?.list.getSnapshot().byId ?? {}).find((row) => (row?.retainedBy?.mainView ?? 0) > 0)?.id ?? "";
|
|
5130
6312
|
let current = findCurrent();
|
|
5131
6313
|
const onReturn = () => {
|
|
@@ -5220,10 +6402,49 @@ function DeleteMenuItem(props) {
|
|
|
5220
6402
|
children: uiText(locale, "deleteChat")
|
|
5221
6403
|
});
|
|
5222
6404
|
}
|
|
6405
|
+
/**
|
|
6406
|
+
* Session Management item: a named native details group. The body mounts on
|
|
6407
|
+
* the first expand only — the settings page must not fire every plugin's
|
|
6408
|
+
* config and model reads — and stays mounted afterwards so collapsing never
|
|
6409
|
+
* drops an unsaved draft or the settings-change subscription.
|
|
6410
|
+
*/
|
|
6411
|
+
function SessionManagerSettingsItem(props) {
|
|
6412
|
+
const [visited, setVisited] = (0, react.useState)(false);
|
|
6413
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
6414
|
+
className: `${FREE_CHAT_ROOT} dsm-item`,
|
|
6415
|
+
"data-testid": "session-manager-settings-item",
|
|
6416
|
+
onToggle: (event) => {
|
|
6417
|
+
if (event.currentTarget.open) setVisited(true);
|
|
6418
|
+
},
|
|
6419
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", {
|
|
6420
|
+
className: "dsm-item-summary",
|
|
6421
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6422
|
+
className: "dsm-item-title",
|
|
6423
|
+
children: uiText(props.locale, "settingsGroupTitle")
|
|
6424
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6425
|
+
className: "dsm-item-desc",
|
|
6426
|
+
children: uiText(props.locale, "settingsGroupHint")
|
|
6427
|
+
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
|
|
6428
|
+
className: "dsm-item-chevron",
|
|
6429
|
+
"aria-hidden": "true"
|
|
6430
|
+
})]
|
|
6431
|
+
}), visited ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6432
|
+
className: "dsm-item-body",
|
|
6433
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SessionManagerSettings, {
|
|
6434
|
+
model: props.model,
|
|
6435
|
+
locale: props.locale,
|
|
6436
|
+
modelCatalog: props.modelCatalog,
|
|
6437
|
+
subscribeCatalog: props.subscribeCatalog,
|
|
6438
|
+
subscribeSettings: props.subscribeSettings
|
|
6439
|
+
})
|
|
6440
|
+
}) : null]
|
|
6441
|
+
});
|
|
6442
|
+
}
|
|
5223
6443
|
function useLiveLocale(locale) {
|
|
5224
6444
|
return copyLocale((0, react.useSyncExternalStore)((listener) => locale?.subscribe(listener) ?? (() => {}), () => locale?.getSnapshot().active ?? "en", () => locale?.getSnapshot().active ?? "en"));
|
|
5225
6445
|
}
|
|
5226
6446
|
//#endregion
|
|
6447
|
+
exports.SessionManagerSettingsItem = SessionManagerSettingsItem;
|
|
5227
6448
|
exports.apply = apply;
|
|
5228
6449
|
exports.inject = inject;
|
|
5229
6450
|
exports.name = name;
|