dsh-edge 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +183 -0
- package/README.zh.md +183 -0
- package/THIRD_PARTY_NOTICES.md +199 -0
- package/dist/_headers +3 -0
- package/dist/assets/fonts/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/assets/fonts/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/assets/fonts/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/assets/fonts/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/assets/fonts/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/assets/fonts/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/assets/fonts/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/assets/fonts/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/assets/fonts/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/assets/fonts/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/assets/index-C-1AiF3k.js +112 -0
- package/dist/assets/index-CSGf6Qzd.css +1 -0
- package/dist/assets/langs/c-BIGW1oBm.js +2 -0
- package/dist/assets/langs/cpp-DIPi6g--.js +2 -0
- package/dist/assets/langs/csharp-DSvCPggb.js +2 -0
- package/dist/assets/langs/css-CLj8gQPS.js +2 -0
- package/dist/assets/langs/go-C27-OAKa.js +2 -0
- package/dist/assets/langs/html-CfGypltT.js +2 -0
- package/dist/assets/langs/ini-BEwlwnbL.js +2 -0
- package/dist/assets/langs/java-CylS5w8V.js +2 -0
- package/dist/assets/langs/kotlin-BdnUsdx6.js +2 -0
- package/dist/assets/langs/less-B1dDrJ26.js +2 -0
- package/dist/assets/langs/lua-BaeVxFsk.js +2 -0
- package/dist/assets/langs/markdown-Cvjx9yec.js +2 -0
- package/dist/assets/langs/mdx-Cmh6b_Ma.js +2 -0
- package/dist/assets/langs/php-D-uCvoST.js +2 -0
- package/dist/assets/langs/python-B6aJPvgy.js +2 -0
- package/dist/assets/langs/ruby-DEItuUFs.js +2 -0
- package/dist/assets/langs/rust-B1yitclQ.js +2 -0
- package/dist/assets/langs/scss-D5BDwBP9.js +2 -0
- package/dist/assets/langs/sql-CRqJ_cUM.js +2 -0
- package/dist/assets/langs/swift-D82vCrfD.js +2 -0
- package/dist/assets/langs/toml-vGWfd6FD.js +2 -0
- package/dist/assets/langs/xml-sdJ4AIDG.js +2 -0
- package/dist/assets/langs/yaml-Buea-lGh.js +2 -0
- package/dist/assets/vendor-Cjbwl5VI.js +411 -0
- package/dist/assets/vendor-CjyC-hUb.css +1 -0
- package/dist/favicon.svg +8 -0
- package/dist/index.html +35 -0
- package/dist/manifest.webmanifest +16 -0
- package/dist/plugins/@deepseek-ai/dsh-api-gateway/client.js +420 -0
- package/dist/plugins/@deepseek-ai/dsh-api-remotes/client.js +5937 -0
- package/dist/plugins/@deepseek-ai/dsh-client-connection/client.js +10207 -0
- package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +1231 -0
- package/dist/plugins/@deepseek-ai/dsh-client-modules/client.js +259 -0
- package/dist/plugins/@deepseek-ai/dsh-client-runtime/client.js +10539 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +1725 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +1116 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +9832 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +373 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-edge/client.js +549 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +859 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +280 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +456 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +804 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +461 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings/client.js +254 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +609 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +292 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +327 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +710 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +1312 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +1630 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +7370 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +700 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +474 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +2426 -0
- package/dist/plugins/@deepseek-ai/dsh-typert-registry/client.js +1372 -0
- package/package.json +97 -0
- package/scripts/assemble-web.mjs +230 -0
- package/scripts/bundle-size.d.mts +2 -0
- package/scripts/bundle-size.mjs +26 -0
- package/scripts/bundle.mjs +53 -0
- package/scripts/cli.d.mts +39 -0
- package/scripts/cli.mjs +496 -0
- package/scripts/install.d.mts +149 -0
- package/scripts/install.mjs +989 -0
- package/scripts/legal-files.mjs +24 -0
- package/scripts/verify-packed.mjs +32 -0
- package/scripts/wrangler-config.d.mts +18 -0
- package/scripts/wrangler-config.mjs +84 -0
- package/src/agent.ts +119 -0
- package/src/auth.ts +352 -0
- package/src/deepseek.ts +231 -0
- package/src/deployment.ts +147 -0
- package/src/direct-shell-core-empty.ts +10 -0
- package/src/direct-shell-protocol.ts +5 -0
- package/src/direct-shell.ts +341 -0
- package/src/do-session-persistence.ts +1064 -0
- package/src/edge-api.ts +796 -0
- package/src/edge-credentials.ts +56 -0
- package/src/edge-remotes.ts +41 -0
- package/src/edge-workspace-store.ts +339 -0
- package/src/http.ts +212 -0
- package/src/index.ts +196 -0
- package/src/instance.ts +1007 -0
- package/src/isolated-direct-shell-unavailable.ts +11 -0
- package/src/protocol.ts +39 -0
- package/src/release.ts +9 -0
- package/src/session-store.ts +1110 -0
- package/src/sse.ts +86 -0
- package/src/web-search.ts +38 -0
- package/src/workspace.ts +270 -0
- package/wrangler.jsonc +56 -0
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@deepseek-ai/dsh-client-ui-model-selection",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
|
|
8
|
+
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
9
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
let react = require("react");
|
|
11
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
12
|
+
//#region lib/types/client/directory.js
|
|
13
|
+
/** One session's shared directory controller; disposed with the session scope. */
|
|
14
|
+
var ModelDirectory = class {
|
|
15
|
+
sessions;
|
|
16
|
+
sessionId;
|
|
17
|
+
available;
|
|
18
|
+
/** The shared snapshot both entries render from (uSES-safe store). */
|
|
19
|
+
store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)({
|
|
20
|
+
current: null,
|
|
21
|
+
routable: null,
|
|
22
|
+
groups: [],
|
|
23
|
+
failures: [],
|
|
24
|
+
status: "idle",
|
|
25
|
+
error: null
|
|
26
|
+
});
|
|
27
|
+
/** Latest operation wins; an older response never overwrites a newer one. */
|
|
28
|
+
generation = 0;
|
|
29
|
+
disposed = false;
|
|
30
|
+
/**
|
|
31
|
+
* @param sessions - the session wire face (captured from the plugin's root connection).
|
|
32
|
+
* @param sessionId - the owning session.
|
|
33
|
+
* @param available - whether this session may use Agent-bound model RPCs.
|
|
34
|
+
*/
|
|
35
|
+
constructor(sessions, sessionId, available) {
|
|
36
|
+
this.sessions = sessions;
|
|
37
|
+
this.sessionId = sessionId;
|
|
38
|
+
this.available = available;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Refresh the advisory directory (both entries call this on open).
|
|
42
|
+
* Failure preserves the last good groups and current selection.
|
|
43
|
+
* @returns the fresh directory value.
|
|
44
|
+
*/
|
|
45
|
+
async load() {
|
|
46
|
+
this.assertAvailable();
|
|
47
|
+
const generation = ++this.generation;
|
|
48
|
+
this.store.update((s) => {
|
|
49
|
+
s.status = "loading";
|
|
50
|
+
s.error = null;
|
|
51
|
+
});
|
|
52
|
+
const { result } = await this.sessions.models({ sessionId: this.sessionId });
|
|
53
|
+
if (this.disposed || generation !== this.generation) {
|
|
54
|
+
if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
|
|
55
|
+
return result.value;
|
|
56
|
+
}
|
|
57
|
+
if (!result.ok) {
|
|
58
|
+
this.store.update((s) => {
|
|
59
|
+
s.status = "error";
|
|
60
|
+
s.error = `${result.error.code}: ${result.error.message}`;
|
|
61
|
+
});
|
|
62
|
+
throw new Error(`session.models failed: ${result.error.code}: ${result.error.message}`);
|
|
63
|
+
}
|
|
64
|
+
const { current, routable, groups, failures } = result.value;
|
|
65
|
+
this.store.update((s) => {
|
|
66
|
+
s.current = current;
|
|
67
|
+
s.routable = routable;
|
|
68
|
+
s.groups = groups;
|
|
69
|
+
s.failures = failures;
|
|
70
|
+
s.status = "ready";
|
|
71
|
+
s.error = null;
|
|
72
|
+
});
|
|
73
|
+
return result.value;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Select the complete provider/model/reasoning selection (both entries submit through here). Success
|
|
77
|
+
* updates the shared current; failure surfaces on the store and throws so
|
|
78
|
+
* each entry's own retry surface engages.
|
|
79
|
+
* @param selection - provider, provider-owned model id, and optional adapter-owned effort.
|
|
80
|
+
*/
|
|
81
|
+
async select(selection) {
|
|
82
|
+
this.assertAvailable();
|
|
83
|
+
const generation = ++this.generation;
|
|
84
|
+
this.store.update((s) => {
|
|
85
|
+
s.status = "selecting";
|
|
86
|
+
s.error = null;
|
|
87
|
+
});
|
|
88
|
+
const { result } = await this.sessions.selectModel({
|
|
89
|
+
sessionId: this.sessionId,
|
|
90
|
+
provider: selection.provider,
|
|
91
|
+
model: selection.model,
|
|
92
|
+
...selection.reasoningEffort === void 0 ? {} : { reasoningEffort: selection.reasoningEffort }
|
|
93
|
+
});
|
|
94
|
+
if (this.disposed || generation !== this.generation) {
|
|
95
|
+
if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (!result.ok) {
|
|
99
|
+
this.store.update((s) => {
|
|
100
|
+
s.status = "error";
|
|
101
|
+
s.error = `${result.error.code}: ${result.error.message}`;
|
|
102
|
+
});
|
|
103
|
+
throw new Error(`session.selectModel failed: ${result.error.code}: ${result.error.message}`);
|
|
104
|
+
}
|
|
105
|
+
this.store.update((s) => {
|
|
106
|
+
s.current = result.value.selected;
|
|
107
|
+
s.routable = true;
|
|
108
|
+
s.status = "ready";
|
|
109
|
+
s.error = null;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Drop the previous Host generation's projection and repull it. Clearing
|
|
114
|
+
* first prevents an unconsumed process-local selection from being displayed
|
|
115
|
+
* while the restarted Host has restored the last logged model selection.
|
|
116
|
+
*/
|
|
117
|
+
resetConnected() {
|
|
118
|
+
if (this.disposed) return;
|
|
119
|
+
++this.generation;
|
|
120
|
+
this.store.update((s) => {
|
|
121
|
+
s.current = null;
|
|
122
|
+
s.routable = null;
|
|
123
|
+
s.groups = [];
|
|
124
|
+
s.failures = [];
|
|
125
|
+
s.status = "idle";
|
|
126
|
+
s.error = null;
|
|
127
|
+
});
|
|
128
|
+
if (!this.available()) return;
|
|
129
|
+
this.load().catch(() => {});
|
|
130
|
+
}
|
|
131
|
+
/** Scope teardown: late settlements lose write access to the store. */
|
|
132
|
+
dispose() {
|
|
133
|
+
this.disposed = true;
|
|
134
|
+
}
|
|
135
|
+
assertAvailable() {
|
|
136
|
+
if (!this.available()) throw new Error("model selection is unavailable for addressed subagent sessions");
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region lib/types/client/service.js
|
|
141
|
+
/**
|
|
142
|
+
* ModelDirectoryResolver (`ctx.modelDirectories`): the root owner of per-session
|
|
143
|
+
* {@link ModelDirectory} instances. Both selection entries (the /model popup
|
|
144
|
+
* and the composer model seat) resolve their session's directory through
|
|
145
|
+
* this service, which is what makes the dual entry one shared state.
|
|
146
|
+
*
|
|
147
|
+
* Per-session storage follows the client service pattern (InputTriggerService /
|
|
148
|
+
* CommandUiRuntime): a lazy service-internal map whose entry is deleted by the
|
|
149
|
+
* owning scope's disposer. The host `dsh-scope` ScopedLayers registry does
|
|
150
|
+
* does not belong here: it derives scope from the host carrier mechanism
|
|
151
|
+
* (object-keyed), while client scopes tag contexts with branded SessionId
|
|
152
|
+
* strings, and it models global+shadow named registries — this is a
|
|
153
|
+
* per-session singleton with no global layer to merge.
|
|
154
|
+
*/
|
|
155
|
+
/** The `ctx.modelDirectories` session model-selection service. */
|
|
156
|
+
var ModelDirectoryResolver = class extends _deepseek_ai_cordis.Service {
|
|
157
|
+
static inject = [
|
|
158
|
+
"connection",
|
|
159
|
+
"sessions",
|
|
160
|
+
"remote"
|
|
161
|
+
];
|
|
162
|
+
live = { directories: /* @__PURE__ */ new Map() };
|
|
163
|
+
/** Localized composer-block copy; this plugin owns the string it raises. */
|
|
164
|
+
blockReason;
|
|
165
|
+
/**
|
|
166
|
+
* @param ctx - owning root context (the service registers itself as `models`).
|
|
167
|
+
* @param config - the bound translator for this plugin's own dictionary.
|
|
168
|
+
*/
|
|
169
|
+
constructor(ctx, config) {
|
|
170
|
+
super(ctx, "modelDirectories");
|
|
171
|
+
this.blockReason = config.blockReason;
|
|
172
|
+
ctx.on("connection/reset", () => {
|
|
173
|
+
for (const directory of this.live.directories.values()) directory.resetConnected();
|
|
174
|
+
});
|
|
175
|
+
const refresh = () => {
|
|
176
|
+
for (const directory of this.live.directories.values()) directory.load().catch(() => void 0);
|
|
177
|
+
};
|
|
178
|
+
ctx.remote.$on("llm/adapters-updated", refresh);
|
|
179
|
+
ctx.remote.$on("settings/document-updated", refresh);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Resolve the per-session shared directory (lazy; the scope disposer
|
|
183
|
+
* removes and disposes it). Unknown sessions fail loud.
|
|
184
|
+
* @param sessionId - the owning session.
|
|
185
|
+
* @returns the resident directory both entries share.
|
|
186
|
+
*/
|
|
187
|
+
directoryFor(sessionId) {
|
|
188
|
+
const { live } = this;
|
|
189
|
+
const existing = live.directories.get(sessionId);
|
|
190
|
+
if (existing !== void 0) return existing;
|
|
191
|
+
const sessions = this.ctx.get("sessions");
|
|
192
|
+
const actx = sessions.scope(sessionId);
|
|
193
|
+
if (actx === void 0) throw new Error(`ui-model-selection: session "${String(sessionId)}" resolved no scope`);
|
|
194
|
+
const directory = new ModelDirectory(this.ctx.get("connection").api.sessions, sessionId, () => sessions.subagentAddress(sessionId) === void 0);
|
|
195
|
+
live.directories.set(sessionId, directory);
|
|
196
|
+
const conversation = this.ctx.get("conversation");
|
|
197
|
+
if (conversation !== void 0) {
|
|
198
|
+
const publish = () => {
|
|
199
|
+
conversation.blocks.set(sessionId, directory.store.getSnapshot().routable === false ? { reason: this.blockReason() } : void 0);
|
|
200
|
+
};
|
|
201
|
+
publish();
|
|
202
|
+
actx.effect(() => {
|
|
203
|
+
const stop = directory.store.subscribe(publish);
|
|
204
|
+
return () => {
|
|
205
|
+
stop();
|
|
206
|
+
conversation.blocks.set(sessionId, void 0);
|
|
207
|
+
};
|
|
208
|
+
}, "ui-model-selection: composer block");
|
|
209
|
+
}
|
|
210
|
+
actx.effect(() => () => {
|
|
211
|
+
directory.dispose();
|
|
212
|
+
live.directories.delete(sessionId);
|
|
213
|
+
}, "ui-model-selection: session directory");
|
|
214
|
+
return directory;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
219
|
+
function r(e) {
|
|
220
|
+
var t, f, n = "";
|
|
221
|
+
if ("string" == typeof e || "number" == typeof e) n += e;
|
|
222
|
+
else if ("object" == typeof e) if (Array.isArray(e)) {
|
|
223
|
+
var o = e.length;
|
|
224
|
+
for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
|
|
225
|
+
} else for (f in e) e[f] && (n && (n += " "), n += f);
|
|
226
|
+
return n;
|
|
227
|
+
}
|
|
228
|
+
function clsx() {
|
|
229
|
+
for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
|
|
230
|
+
return n;
|
|
231
|
+
}
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region \0dsh-css:/private/var/folders/nm/n5smty9962gf5rhjc5855kt80000gn/T/dsh-edge-release-worktree.XXXXXX.RcJjogaGt4/repo/packages/client/ui-model-selection/src/client/ModelSelect.module.css.mjs
|
|
234
|
+
const css = "._6ruuW_root{min-width:0;position:relative}._6ruuW_trigger{min-width:0;max-width:220px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:24px;outline:none;align-items:center;gap:4px;padding:0 4px 0 8px;font-size:13px;font-weight:500;line-height:20px;display:flex}._6ruuW_trigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}._6ruuW_trigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}._6ruuW_trigger:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}._6ruuW_triggerLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}._6ruuW_triggerEffort{color:var(--dsw-alias-label-caption);flex:none}._6ruuW_chevron{color:var(--dsw-alias-label-caption);flex:none;transition:transform .12s}._6ruuW_chevronOpen{transform:rotate(180deg)}._6ruuW_menu{z-index:20;border:1px solid var(--dsw-alias-border-inverted);background:var(--dsw-specific-menu);width:min(240px,100vw - 32px);max-height:min(360px,100vh - 96px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;flex-direction:column;padding:4px;display:flex;position:absolute;bottom:calc(100% + 8px);right:0;overflow:hidden}._6ruuW_status,._6ruuW_empty{color:var(--dsw-alias-label-tertiary);padding:10px;font-size:13px;line-height:20px}._6ruuW_error,._6ruuW_warning{background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);border-radius:8px;justify-content:space-between;align-items:flex-start;gap:8px;margin-bottom:4px;padding:7px 8px;font-size:12px;line-height:18px;display:flex}._6ruuW_warning{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-state-warn-label)}._6ruuW_retry{color:inherit;font:inherit;cursor:pointer;background:0 0;border:none;flex:none;padding:0;font-weight:600}._6ruuW_groups{min-height:0;overflow-y:auto}._6ruuW_group+._6ruuW_group{margin-top:4px}._6ruuW_groupTitle{z-index:1;background:var(--dsw-specific-menu);color:var(--dsw-alias-label-tertiary);padding:5px 8px 3px;font-size:12px;font-weight:500;line-height:18px;position:sticky;top:0}._6ruuW_option{width:100%;min-height:38px;color:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:10px;outline:none;align-items:center;gap:8px;padding:6px 8px;display:flex}._6ruuW_option:hover:not(:disabled),._6ruuW_option:focus-visible{background:var(--dsw-alias-interactive-bg-hover)}._6ruuW_selected{background:0 0}._6ruuW_option:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}._6ruuW_optionCopy{flex-direction:column;flex:1;min-width:0;display:flex}._6ruuW_modelName{color:inherit;text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:20px;overflow:hidden}._6ruuW_description{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;overflow:hidden}._6ruuW_check{color:var(--dsw-alias-label-primary);flex:0 0 18px;place-items:center;display:grid}._6ruuW_cell{width:100%;height:40px;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;background:0 0;border:none;border-radius:10px;align-items:center;gap:8px;padding:0 10px;font-size:14px;line-height:22px;display:flex}._6ruuW_cell:hover{background:var(--dsw-alias-interactive-bg-hover)}._6ruuW_cellLabel{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}._6ruuW_cellValue{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-tertiary);flex:0 auto;overflow:hidden}._6ruuW_cellChevron{color:var(--dsw-alias-label-tertiary);flex:none}";
|
|
235
|
+
const tagId = "@deepseek-ai/dsh-client-ui-model-selection/ModelSelect.module.css";
|
|
236
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
237
|
+
const tag = document.createElement("style");
|
|
238
|
+
tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-model-selection";
|
|
239
|
+
tag.dataset.pluginCss = tagId;
|
|
240
|
+
tag.textContent = css;
|
|
241
|
+
document.head.appendChild(tag);
|
|
242
|
+
}
|
|
243
|
+
var ModelSelect_module_css_default = {
|
|
244
|
+
"trigger": "_6ruuW_trigger",
|
|
245
|
+
"groupTitle": "_6ruuW_groupTitle",
|
|
246
|
+
"chevron": "_6ruuW_chevron",
|
|
247
|
+
"optionCopy": "_6ruuW_optionCopy",
|
|
248
|
+
"groups": "_6ruuW_groups",
|
|
249
|
+
"check": "_6ruuW_check",
|
|
250
|
+
"error": "_6ruuW_error",
|
|
251
|
+
"cell": "_6ruuW_cell",
|
|
252
|
+
"root": "_6ruuW_root",
|
|
253
|
+
"group": "_6ruuW_group",
|
|
254
|
+
"option": "_6ruuW_option",
|
|
255
|
+
"selected": "_6ruuW_selected",
|
|
256
|
+
"modelName": "_6ruuW_modelName",
|
|
257
|
+
"empty": "_6ruuW_empty",
|
|
258
|
+
"triggerLabel": "_6ruuW_triggerLabel",
|
|
259
|
+
"chevronOpen": "_6ruuW_chevronOpen",
|
|
260
|
+
"retry": "_6ruuW_retry",
|
|
261
|
+
"description": "_6ruuW_description",
|
|
262
|
+
"cellLabel": "_6ruuW_cellLabel",
|
|
263
|
+
"cellValue": "_6ruuW_cellValue",
|
|
264
|
+
"cellChevron": "_6ruuW_cellChevron",
|
|
265
|
+
"status": "_6ruuW_status",
|
|
266
|
+
"warning": "_6ruuW_warning",
|
|
267
|
+
"menu": "_6ruuW_menu",
|
|
268
|
+
"triggerEffort": "_6ruuW_triggerEffort"
|
|
269
|
+
};
|
|
270
|
+
//#endregion
|
|
271
|
+
//#region lib/types/client/ModelSelect.js
|
|
272
|
+
/**
|
|
273
|
+
* ModelSelect: the composer's named model seat (`conversation.input.model`).
|
|
274
|
+
* Two-level selection per figma 496:26454's MenuDropdown: the root menu is
|
|
275
|
+
* the Model / Effort row pair (label + current value + a right chevron),
|
|
276
|
+
* each drilling into its own list — the provider-grouped model list over
|
|
277
|
+
* the shared directory, and the effort levels. The trigger (313:14108's
|
|
278
|
+
* ToggleButton) shows both: model name + effort in the caption tone.
|
|
279
|
+
* Data and submission ride the SAME per-session ModelDirectory as the
|
|
280
|
+
* /model popup; exact-model reasoning metadata and the selected effort come
|
|
281
|
+
* from the Host rather than a client-owned vocabulary. A rejected selection
|
|
282
|
+
* announces through the shared transient Toast anchored to the composer
|
|
283
|
+
* card; the in-menu strip with Retry remains the catalog-load surface.
|
|
284
|
+
*/
|
|
285
|
+
/**
|
|
286
|
+
* Render the composer model seat.
|
|
287
|
+
* @param props - owner share (locked) + injected face (shared directory
|
|
288
|
+
* store/verbs) + the standard locale seat.
|
|
289
|
+
* @returns the trigger and, while open, the two-level menu.
|
|
290
|
+
*/
|
|
291
|
+
function ModelSelect({ locked, available, directory, load, select, t }) {
|
|
292
|
+
const state = (0, react.useSyncExternalStore)((fn) => directory.subscribe(fn), () => directory.getSnapshot());
|
|
293
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
294
|
+
const [pane, setPane] = (0, react.useState)("root");
|
|
295
|
+
const lastActionRef = (0, react.useRef)("load");
|
|
296
|
+
const [toast, setToast] = (0, react.useState)(null);
|
|
297
|
+
const toastSeq = (0, react.useRef)(0);
|
|
298
|
+
const rootRef = (0, react.useRef)(null);
|
|
299
|
+
const triggerRef = (0, react.useRef)(null);
|
|
300
|
+
const itemRefs = (0, react.useRef)([]);
|
|
301
|
+
const id = (0, react.useId)();
|
|
302
|
+
const choices = (0, react.useMemo)(() => state.groups.flatMap((group) => group.models.map((model) => ({
|
|
303
|
+
group,
|
|
304
|
+
model,
|
|
305
|
+
selection: {
|
|
306
|
+
provider: group.id,
|
|
307
|
+
model: model.id,
|
|
308
|
+
...model.reasoning?.defaultEffort === void 0 ? {} : { reasoningEffort: model.reasoning.defaultEffort }
|
|
309
|
+
}
|
|
310
|
+
}))), [state.groups]);
|
|
311
|
+
const currentChoice = choices[state.current === null ? -1 : choices.findIndex((c) => c.selection.provider === state.current?.provider && c.selection.model === state.current.model)];
|
|
312
|
+
const reasoning = currentChoice?.model.reasoning;
|
|
313
|
+
const effectiveEffort = state.current?.reasoningEffort ?? reasoning?.defaultEffort;
|
|
314
|
+
const effortLabel = reasoning === void 0 ? void 0 : effectiveEffort === void 0 ? t("effort.providerDefault") : reasoning.efforts.find((level) => level.id === effectiveEffort)?.name ?? effectiveEffort;
|
|
315
|
+
const effortChoices = (0, react.useMemo)(() => reasoning === void 0 ? [] : [...reasoning.defaultEffort === void 0 ? [{
|
|
316
|
+
key: "provider-default",
|
|
317
|
+
effort: void 0,
|
|
318
|
+
label: t("effort.providerDefault")
|
|
319
|
+
}] : [], ...reasoning.efforts.map((effort) => ({
|
|
320
|
+
key: `effort:${effort.id}`,
|
|
321
|
+
effort: effort.id,
|
|
322
|
+
label: effort.name,
|
|
323
|
+
...effort.description === void 0 ? {} : { description: effort.description }
|
|
324
|
+
}))], [reasoning, t]);
|
|
325
|
+
const busy = state.status === "selecting";
|
|
326
|
+
const reload = () => {
|
|
327
|
+
lastActionRef.current = "load";
|
|
328
|
+
load();
|
|
329
|
+
};
|
|
330
|
+
(0, react.useEffect)(() => {
|
|
331
|
+
if (available) {
|
|
332
|
+
lastActionRef.current = "load";
|
|
333
|
+
load();
|
|
334
|
+
}
|
|
335
|
+
}, [available, load]);
|
|
336
|
+
(0, react.useEffect)(() => {
|
|
337
|
+
if (!open) return;
|
|
338
|
+
const closeOutside = (event) => {
|
|
339
|
+
if (!rootRef.current?.contains(event.target)) setOpen(false);
|
|
340
|
+
};
|
|
341
|
+
document.addEventListener("mousedown", closeOutside);
|
|
342
|
+
return () => {
|
|
343
|
+
document.removeEventListener("mousedown", closeOutside);
|
|
344
|
+
};
|
|
345
|
+
}, [open]);
|
|
346
|
+
if (!available) return null;
|
|
347
|
+
const show = () => {
|
|
348
|
+
setPane("root");
|
|
349
|
+
setOpen(true);
|
|
350
|
+
reload();
|
|
351
|
+
};
|
|
352
|
+
const close = (restoreFocus = false) => {
|
|
353
|
+
setOpen(false);
|
|
354
|
+
setPane("root");
|
|
355
|
+
if (restoreFocus) queueMicrotask(() => {
|
|
356
|
+
triggerRef.current?.focus();
|
|
357
|
+
});
|
|
358
|
+
};
|
|
359
|
+
const moveFocus = (offset) => {
|
|
360
|
+
const items = itemRefs.current.filter((item) => item !== null);
|
|
361
|
+
if (items.length === 0) return;
|
|
362
|
+
const active = items.findIndex((item) => item === document.activeElement);
|
|
363
|
+
items[(Math.max(active, 0) + offset + items.length) % items.length]?.focus();
|
|
364
|
+
};
|
|
365
|
+
const onRootKeyDown = (event) => {
|
|
366
|
+
if (event.key === "Escape" && open) {
|
|
367
|
+
event.preventDefault();
|
|
368
|
+
if (pane !== "root") setPane("root");
|
|
369
|
+
else close(true);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (!open) return;
|
|
373
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
374
|
+
event.preventDefault();
|
|
375
|
+
moveFocus(event.key === "ArrowDown" ? 1 : -1);
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
const onBlur = (event) => {
|
|
379
|
+
if (event.relatedTarget instanceof Node && rootRef.current?.contains(event.relatedTarget)) return;
|
|
380
|
+
close();
|
|
381
|
+
};
|
|
382
|
+
const settleSelection = (accepted) => {
|
|
383
|
+
if (accepted) {
|
|
384
|
+
if (rootRef.current !== null) close(true);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const message = directory.getSnapshot().error;
|
|
388
|
+
if (message !== null) {
|
|
389
|
+
toastSeq.current += 1;
|
|
390
|
+
setToast({
|
|
391
|
+
seq: toastSeq.current,
|
|
392
|
+
text: t("error.action", { message })
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
const choose = (selection) => {
|
|
397
|
+
if (state.current?.provider === selection.provider && state.current.model === selection.model) {
|
|
398
|
+
close(true);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
lastActionRef.current = "select";
|
|
402
|
+
select(selection).then(settleSelection);
|
|
403
|
+
};
|
|
404
|
+
const chooseEffort = (effort) => {
|
|
405
|
+
if (state.current === null) return;
|
|
406
|
+
if (effectiveEffort === effort) {
|
|
407
|
+
close(true);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
const selection = {
|
|
411
|
+
provider: state.current.provider,
|
|
412
|
+
model: state.current.model,
|
|
413
|
+
...effort === void 0 ? {} : { reasoningEffort: effort }
|
|
414
|
+
};
|
|
415
|
+
lastActionRef.current = "select";
|
|
416
|
+
select(selection).then(settleSelection);
|
|
417
|
+
};
|
|
418
|
+
const modelLabel = currentChoice?.model.name ?? t("trigger.fallback");
|
|
419
|
+
const triggerLabel = effortLabel === void 0 ? modelLabel : `${modelLabel} · ${effortLabel}`;
|
|
420
|
+
const triggerAria = currentChoice === void 0 ? t("trigger.selectAria") : effortLabel === void 0 ? t("trigger.aria", { model: modelLabel }) : t("trigger.ariaEffort", {
|
|
421
|
+
model: modelLabel,
|
|
422
|
+
effort: effortLabel
|
|
423
|
+
});
|
|
424
|
+
itemRefs.current = [];
|
|
425
|
+
let itemIndex = 0;
|
|
426
|
+
const itemRef = () => {
|
|
427
|
+
const at = itemIndex++;
|
|
428
|
+
return (node) => {
|
|
429
|
+
itemRefs.current[at] = node;
|
|
430
|
+
};
|
|
431
|
+
};
|
|
432
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
433
|
+
ref: rootRef,
|
|
434
|
+
className: ModelSelect_module_css_default.root,
|
|
435
|
+
onKeyDown: onRootKeyDown,
|
|
436
|
+
onBlur,
|
|
437
|
+
children: [
|
|
438
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
439
|
+
ref: triggerRef,
|
|
440
|
+
type: "button",
|
|
441
|
+
className: ModelSelect_module_css_default.trigger,
|
|
442
|
+
"aria-label": triggerAria,
|
|
443
|
+
"aria-haspopup": "menu",
|
|
444
|
+
"aria-expanded": open,
|
|
445
|
+
"aria-controls": open ? `${id}-menu` : void 0,
|
|
446
|
+
title: triggerLabel,
|
|
447
|
+
disabled: locked,
|
|
448
|
+
onClick: () => {
|
|
449
|
+
if (open) close();
|
|
450
|
+
else show();
|
|
451
|
+
},
|
|
452
|
+
children: [
|
|
453
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
454
|
+
className: ModelSelect_module_css_default.triggerLabel,
|
|
455
|
+
children: modelLabel
|
|
456
|
+
}),
|
|
457
|
+
effortLabel !== void 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
458
|
+
className: ModelSelect_module_css_default.triggerEffort,
|
|
459
|
+
children: effortLabel
|
|
460
|
+
}),
|
|
461
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: clsx(ModelSelect_module_css_default.chevron, open && ModelSelect_module_css_default.chevronOpen) })
|
|
462
|
+
]
|
|
463
|
+
}),
|
|
464
|
+
open && (0, react_jsx_runtime.jsxs)("div", {
|
|
465
|
+
id: `${id}-menu`,
|
|
466
|
+
className: ModelSelect_module_css_default.menu,
|
|
467
|
+
role: "menu",
|
|
468
|
+
"aria-label": t("menu.aria"),
|
|
469
|
+
"aria-busy": state.status === "loading" || busy,
|
|
470
|
+
children: [
|
|
471
|
+
pane === "root" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
472
|
+
ref: itemRef(),
|
|
473
|
+
type: "button",
|
|
474
|
+
role: "menuitem",
|
|
475
|
+
className: ModelSelect_module_css_default.cell,
|
|
476
|
+
onClick: () => {
|
|
477
|
+
setPane("model");
|
|
478
|
+
},
|
|
479
|
+
children: [
|
|
480
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
481
|
+
className: ModelSelect_module_css_default.cellLabel,
|
|
482
|
+
children: t("menu.model")
|
|
483
|
+
}),
|
|
484
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
485
|
+
className: ModelSelect_module_css_default.cellValue,
|
|
486
|
+
children: modelLabel
|
|
487
|
+
}),
|
|
488
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { className: ModelSelect_module_css_default.cellChevron })
|
|
489
|
+
]
|
|
490
|
+
}), reasoning !== void 0 && (0, react_jsx_runtime.jsxs)("button", {
|
|
491
|
+
ref: itemRef(),
|
|
492
|
+
type: "button",
|
|
493
|
+
role: "menuitem",
|
|
494
|
+
className: ModelSelect_module_css_default.cell,
|
|
495
|
+
onClick: () => {
|
|
496
|
+
setPane("effort");
|
|
497
|
+
},
|
|
498
|
+
children: [
|
|
499
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
500
|
+
className: ModelSelect_module_css_default.cellLabel,
|
|
501
|
+
children: t("menu.effort")
|
|
502
|
+
}),
|
|
503
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
504
|
+
className: ModelSelect_module_css_default.cellValue,
|
|
505
|
+
children: effortLabel
|
|
506
|
+
}),
|
|
507
|
+
(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { className: ModelSelect_module_css_default.cellChevron })
|
|
508
|
+
]
|
|
509
|
+
})] }),
|
|
510
|
+
pane === "model" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
511
|
+
state.status === "loading" && (0, react_jsx_runtime.jsx)("div", {
|
|
512
|
+
className: ModelSelect_module_css_default.status,
|
|
513
|
+
children: t("status.loading")
|
|
514
|
+
}),
|
|
515
|
+
state.error !== null && lastActionRef.current === "load" && (0, react_jsx_runtime.jsxs)("div", {
|
|
516
|
+
className: ModelSelect_module_css_default.error,
|
|
517
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: t("error.action", { message: state.error }) }), (0, react_jsx_runtime.jsx)("button", {
|
|
518
|
+
type: "button",
|
|
519
|
+
className: ModelSelect_module_css_default.retry,
|
|
520
|
+
onClick: reload,
|
|
521
|
+
children: t("retry")
|
|
522
|
+
})]
|
|
523
|
+
}),
|
|
524
|
+
state.failures.map((failure) => (0, react_jsx_runtime.jsxs)("div", {
|
|
525
|
+
className: ModelSelect_module_css_default.warning,
|
|
526
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: t("warning.groupLoad", {
|
|
527
|
+
name: failure.name,
|
|
528
|
+
message: failure.message
|
|
529
|
+
}) }), (0, react_jsx_runtime.jsx)("button", {
|
|
530
|
+
type: "button",
|
|
531
|
+
className: ModelSelect_module_css_default.retry,
|
|
532
|
+
onClick: reload,
|
|
533
|
+
children: t("retry")
|
|
534
|
+
})]
|
|
535
|
+
}, failure.id)),
|
|
536
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
537
|
+
className: clsx(ModelSelect_module_css_default.groups, "scrollable"),
|
|
538
|
+
children: state.groups.map((group) => {
|
|
539
|
+
const headingId = `${id}-${group.id}`;
|
|
540
|
+
return (0, react_jsx_runtime.jsxs)("section", {
|
|
541
|
+
role: "group",
|
|
542
|
+
"aria-labelledby": headingId,
|
|
543
|
+
className: ModelSelect_module_css_default.group,
|
|
544
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
545
|
+
className: ModelSelect_module_css_default.groupTitle,
|
|
546
|
+
id: headingId,
|
|
547
|
+
children: group.name
|
|
548
|
+
}), group.models.map((model) => {
|
|
549
|
+
const selected = state.current?.provider === group.id && state.current.model === model.id;
|
|
550
|
+
return (0, react_jsx_runtime.jsxs)("button", {
|
|
551
|
+
ref: itemRef(),
|
|
552
|
+
type: "button",
|
|
553
|
+
role: "menuitemradio",
|
|
554
|
+
"aria-checked": selected,
|
|
555
|
+
className: clsx(ModelSelect_module_css_default.option, selected && ModelSelect_module_css_default.selected),
|
|
556
|
+
title: model.name,
|
|
557
|
+
disabled: busy,
|
|
558
|
+
onClick: () => {
|
|
559
|
+
choose({
|
|
560
|
+
provider: group.id,
|
|
561
|
+
model: model.id
|
|
562
|
+
});
|
|
563
|
+
},
|
|
564
|
+
children: [(0, react_jsx_runtime.jsxs)("span", {
|
|
565
|
+
className: ModelSelect_module_css_default.optionCopy,
|
|
566
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
567
|
+
className: ModelSelect_module_css_default.modelName,
|
|
568
|
+
children: model.name
|
|
569
|
+
}), model.description !== void 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
570
|
+
className: ModelSelect_module_css_default.description,
|
|
571
|
+
children: model.description
|
|
572
|
+
})]
|
|
573
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
574
|
+
className: ModelSelect_module_css_default.check,
|
|
575
|
+
children: selected ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, {}) : null
|
|
576
|
+
})]
|
|
577
|
+
}, model.id);
|
|
578
|
+
})]
|
|
579
|
+
}, group.id);
|
|
580
|
+
})
|
|
581
|
+
}),
|
|
582
|
+
state.status === "ready" && choices.length === 0 && (0, react_jsx_runtime.jsx)("div", {
|
|
583
|
+
className: ModelSelect_module_css_default.empty,
|
|
584
|
+
children: t("empty.models")
|
|
585
|
+
})
|
|
586
|
+
] }),
|
|
587
|
+
pane === "effort" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [state.error !== null && lastActionRef.current === "load" && (0, react_jsx_runtime.jsxs)("div", {
|
|
588
|
+
className: ModelSelect_module_css_default.error,
|
|
589
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: t("error.action", { message: state.error }) }), (0, react_jsx_runtime.jsx)("button", {
|
|
590
|
+
type: "button",
|
|
591
|
+
className: ModelSelect_module_css_default.retry,
|
|
592
|
+
onClick: reload,
|
|
593
|
+
children: t("action.reload")
|
|
594
|
+
})]
|
|
595
|
+
}), effortChoices.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
|
|
596
|
+
className: ModelSelect_module_css_default.empty,
|
|
597
|
+
children: t("empty.efforts")
|
|
598
|
+
}) : effortChoices.map((level) => (0, react_jsx_runtime.jsxs)("button", {
|
|
599
|
+
ref: itemRef(),
|
|
600
|
+
type: "button",
|
|
601
|
+
role: "menuitemradio",
|
|
602
|
+
"aria-checked": effectiveEffort === level.effort,
|
|
603
|
+
className: clsx(ModelSelect_module_css_default.option, effectiveEffort === level.effort && ModelSelect_module_css_default.selected),
|
|
604
|
+
disabled: busy,
|
|
605
|
+
onClick: () => {
|
|
606
|
+
chooseEffort(level.effort);
|
|
607
|
+
},
|
|
608
|
+
children: [(0, react_jsx_runtime.jsxs)("span", {
|
|
609
|
+
className: ModelSelect_module_css_default.optionCopy,
|
|
610
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
611
|
+
className: ModelSelect_module_css_default.modelName,
|
|
612
|
+
children: level.label
|
|
613
|
+
}), level.description !== void 0 && (0, react_jsx_runtime.jsx)("span", {
|
|
614
|
+
className: ModelSelect_module_css_default.description,
|
|
615
|
+
children: level.description
|
|
616
|
+
})]
|
|
617
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
618
|
+
className: ModelSelect_module_css_default.check,
|
|
619
|
+
children: effectiveEffort === level.effort ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, {}) : null
|
|
620
|
+
})]
|
|
621
|
+
}, level.key))] })
|
|
622
|
+
]
|
|
623
|
+
}),
|
|
624
|
+
toast !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
625
|
+
text: toast.text,
|
|
626
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {}),
|
|
627
|
+
anchor: rootRef.current?.closest("[data-composer-card]") ?? null,
|
|
628
|
+
onDone: () => {
|
|
629
|
+
setToast(null);
|
|
630
|
+
}
|
|
631
|
+
}, toast.seq)
|
|
632
|
+
]
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
//#endregion
|
|
636
|
+
//#region lib/types/client/locales.js
|
|
637
|
+
/**
|
|
638
|
+
* `model` namespace dictionaries.
|
|
639
|
+
*
|
|
640
|
+
* `trigger.selectAria` reads identically to `trigger.fallback` today and is
|
|
641
|
+
* still a separate key: the visible fallback label and the accessible name of
|
|
642
|
+
* an unset trigger are free to diverge per locale, and folding it into
|
|
643
|
+
* `trigger.aria` would announce the degenerate "Select model, current Select
|
|
644
|
+
* model".
|
|
645
|
+
*/
|
|
646
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
647
|
+
const zh = {
|
|
648
|
+
"command.description": "选择本会话使用的模型",
|
|
649
|
+
"option.loadError": "目录加载失败:{message}",
|
|
650
|
+
"trigger.fallback": "选择模型",
|
|
651
|
+
"trigger.selectAria": "选择模型",
|
|
652
|
+
"trigger.aria": "选择模型,当前 {model}",
|
|
653
|
+
"trigger.ariaEffort": "选择模型,当前 {model},推理等级 {effort}",
|
|
654
|
+
"menu.aria": "模型与推理等级",
|
|
655
|
+
"menu.model": "模型",
|
|
656
|
+
"menu.effort": "推理等级",
|
|
657
|
+
"effort.providerDefault": "Default",
|
|
658
|
+
"status.loading": "正在刷新模型列表…",
|
|
659
|
+
"error.action": "模型操作失败:{message}",
|
|
660
|
+
"action.reload": "重新加载",
|
|
661
|
+
"warning.groupLoad": "{name} 加载失败:{message}",
|
|
662
|
+
"empty.models": "没有可用的模型。",
|
|
663
|
+
"blocked.composer": "当前模型不可用,请先选择模型",
|
|
664
|
+
"empty.efforts": "当前模型未提供推理等级。"
|
|
665
|
+
};
|
|
666
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
667
|
+
const en = {
|
|
668
|
+
"command.description": "Select the model for this conversation",
|
|
669
|
+
"option.loadError": "Catalog failed to load: {message}",
|
|
670
|
+
"trigger.fallback": "Select model",
|
|
671
|
+
"trigger.selectAria": "Select model",
|
|
672
|
+
"trigger.aria": "Select model, current {model}",
|
|
673
|
+
"trigger.ariaEffort": "Select model, current {model}, reasoning effort {effort}",
|
|
674
|
+
"menu.aria": "Model and reasoning effort",
|
|
675
|
+
"menu.model": "Model",
|
|
676
|
+
"menu.effort": "Effort",
|
|
677
|
+
"effort.providerDefault": "Default",
|
|
678
|
+
"status.loading": "Refreshing model list…",
|
|
679
|
+
"error.action": "Model operation failed: {message}",
|
|
680
|
+
"action.reload": "Reload",
|
|
681
|
+
"warning.groupLoad": "{name} failed to load: {message}",
|
|
682
|
+
"empty.models": "No models available.",
|
|
683
|
+
"blocked.composer": "This model is unavailable — select one to continue",
|
|
684
|
+
"empty.efforts": "This model provides no reasoning effort levels."
|
|
685
|
+
};
|
|
686
|
+
//#endregion
|
|
687
|
+
//#region lib/types/client/index.js
|
|
688
|
+
/** One selectable row's id: an opaque row key (resolved by lookup, never parsed). */
|
|
689
|
+
function rowId(providerId, modelId) {
|
|
690
|
+
return `${providerId}/${modelId}`;
|
|
691
|
+
}
|
|
692
|
+
/** Flatten the directory into popup rows; failure rows are listed for visibility but never selectable. */
|
|
693
|
+
function optionsOf(directory, t) {
|
|
694
|
+
const rows = [];
|
|
695
|
+
for (const group of directory.groups) for (const model of group.models) rows.push({
|
|
696
|
+
id: rowId(group.id, model.id),
|
|
697
|
+
label: model.name,
|
|
698
|
+
detail: model.description !== void 0 ? `${group.name} · ${model.description}` : group.name,
|
|
699
|
+
...directory.current.provider === group.id && directory.current.model === model.id ? { active: true } : {}
|
|
700
|
+
});
|
|
701
|
+
for (const failure of directory.failures) rows.push({
|
|
702
|
+
id: `failure/${failure.id}`,
|
|
703
|
+
label: failure.name,
|
|
704
|
+
detail: t("option.loadError", { message: failure.message })
|
|
705
|
+
});
|
|
706
|
+
return rows;
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Resolve a picked row back to its model selection by matching against the loaded
|
|
710
|
+
* groups (the same data the rows were built from — ids stay opaque).
|
|
711
|
+
* @param state - the session's directory snapshot.
|
|
712
|
+
* @param id - the picked row id.
|
|
713
|
+
* @returns the row's model selection, or undefined for failure rows / stale ids.
|
|
714
|
+
*/
|
|
715
|
+
function selectionOf(state, id) {
|
|
716
|
+
for (const group of state.groups) for (const model of group.models) {
|
|
717
|
+
if (rowId(group.id, model.id) !== id) continue;
|
|
718
|
+
const reasoningEffort = state.current?.provider === group.id && state.current.model === model.id ? state.current?.reasoningEffort ?? model.reasoning?.defaultEffort : model.reasoning?.defaultEffort;
|
|
719
|
+
return {
|
|
720
|
+
provider: group.id,
|
|
721
|
+
model: model.id,
|
|
722
|
+
...reasoningEffort === void 0 ? {} : { reasoningEffort }
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
/** Dictionary namespace owned by this plugin. */
|
|
727
|
+
const NS = "model";
|
|
728
|
+
/** Required services: the contribution registry, the seat's slot registry, locale, and the service's own faces. */
|
|
729
|
+
const inject = [
|
|
730
|
+
"commandUi",
|
|
731
|
+
"connection",
|
|
732
|
+
"locale",
|
|
733
|
+
"sessions",
|
|
734
|
+
"slots",
|
|
735
|
+
"remote"
|
|
736
|
+
];
|
|
737
|
+
/**
|
|
738
|
+
* Client plugin body: mount ModelDirectoryResolver, register the `model` dictionaries,
|
|
739
|
+
* then register the /model popup contribution and the composer model seat
|
|
740
|
+
* over the service.
|
|
741
|
+
* @param ctx - client root context.
|
|
742
|
+
*/
|
|
743
|
+
function apply(ctx) {
|
|
744
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
745
|
+
zh,
|
|
746
|
+
en
|
|
747
|
+
}), "ui-model-selection: dictionaries");
|
|
748
|
+
const t = ctx.locale.bind(NS);
|
|
749
|
+
ctx.plugin(ModelDirectoryResolver, { blockReason: () => t("blocked.composer") });
|
|
750
|
+
ctx.inject(["commandUi", "modelDirectories"], (scope) => {
|
|
751
|
+
const command = scope.get("commandUi");
|
|
752
|
+
const models = scope.modelDirectories;
|
|
753
|
+
const sessions = scope.sessions;
|
|
754
|
+
scope.effect(() => command.register({
|
|
755
|
+
name: "model",
|
|
756
|
+
description: t("command.description"),
|
|
757
|
+
available: (session) => sessions.subagentAddress(session.sessionId) === void 0,
|
|
758
|
+
ui: {
|
|
759
|
+
kind: "popupSelect",
|
|
760
|
+
options: async (session) => {
|
|
761
|
+
if (sessions.subagentAddress(session.sessionId) !== void 0) throw new Error("model selection is unavailable for addressed subagent sessions");
|
|
762
|
+
return optionsOf(await models.directoryFor(session.sessionId).load(), t);
|
|
763
|
+
},
|
|
764
|
+
onSelect: async (option, session) => {
|
|
765
|
+
if (sessions.subagentAddress(session.sessionId) !== void 0) throw new Error("model selection is unavailable for addressed subagent sessions");
|
|
766
|
+
const directory = models.directoryFor(session.sessionId);
|
|
767
|
+
const selection = selectionOf(directory.store.getSnapshot(), option.id);
|
|
768
|
+
if (selection === void 0) throw new Error("this provider's catalog failed to load — pick a model from a loaded group");
|
|
769
|
+
await directory.select(selection);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}), "ui-model-selection: /model contribution");
|
|
773
|
+
});
|
|
774
|
+
ctx.inject(["slots", "modelDirectories"], (scope) => {
|
|
775
|
+
const models = scope.modelDirectories;
|
|
776
|
+
const sessions = scope.sessions;
|
|
777
|
+
scope.slots.inject("conversation.input.model", () => scope.slots.register({
|
|
778
|
+
name: "conversation.input.model",
|
|
779
|
+
locale: NS,
|
|
780
|
+
inject: (sessionId) => {
|
|
781
|
+
const directory = models.directoryFor(sessionId);
|
|
782
|
+
const available = sessions.subagentAddress(sessionId) === void 0;
|
|
783
|
+
return {
|
|
784
|
+
available,
|
|
785
|
+
directory: directory.store,
|
|
786
|
+
load: () => {
|
|
787
|
+
if (available) directory.load().catch(() => {});
|
|
788
|
+
},
|
|
789
|
+
select: (selection) => available ? directory.select(selection).then(() => true, () => false) : Promise.resolve(false)
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
}, ModelSelect));
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
//#endregion
|
|
796
|
+
exports.ModelDirectory = ModelDirectory;
|
|
797
|
+
exports.ModelDirectoryResolver = ModelDirectoryResolver;
|
|
798
|
+
exports.apply = apply;
|
|
799
|
+
exports.inject = inject;
|
|
800
|
+
return module.exports;
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
//# sourceMappingURL=client.js.map
|