dsh-thinkbar 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 +22 -0
- package/README.md +112 -0
- package/README.zh.md +112 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.cjs +679 -0
- package/lib/client.cjs.map +1 -0
- package/lib/index.js +11 -0
- package/lib/index.js.map +1 -0
- package/lib/types/client/ModelTriggerBridge.d.ts +6 -0
- package/lib/types/client/ModelTriggerBridge.d.ts.map +1 -0
- package/lib/types/client/ReasoningWaitIndicator.d.ts +8 -0
- package/lib/types/client/ReasoningWaitIndicator.d.ts.map +1 -0
- package/lib/types/client/fill-face.d.ts +8 -0
- package/lib/types/client/fill-face.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +11 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/model-trigger-adapter.d.ts +10 -0
- package/lib/types/client/model-trigger-adapter.d.ts.map +1 -0
- package/lib/types/client/reasoning-wait-projection.d.ts +30 -0
- package/lib/types/client/reasoning-wait-projection.d.ts.map +1 -0
- package/lib/types/client/service.d.ts +18 -0
- package/lib/types/client/service.d.ts.map +1 -0
- package/lib/types/client/thermometer.d.ts +48 -0
- package/lib/types/client/thermometer.d.ts.map +1 -0
- package/lib/types/client/use-model-trigger.d.ts +4 -0
- package/lib/types/client/use-model-trigger.d.ts.map +1 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/index.d.ts.map +1 -0
- package/package.json +95 -0
package/lib/client.cjs
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-thinkbar",
|
|
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 react_dom = require("react-dom");
|
|
9
|
+
let react = require("react");
|
|
10
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
|
+
//#region src/client/thermometer.ts
|
|
12
|
+
const FULL_SCALE_MS = 2e4;
|
|
13
|
+
const MIN_FILL = .08;
|
|
14
|
+
const DRAIN_MS = 240;
|
|
15
|
+
const IRON_STOPS = [
|
|
16
|
+
{
|
|
17
|
+
t: 0,
|
|
18
|
+
hex: "var(--dsw-alias-button-info-fill)"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
t: 1 / 3,
|
|
22
|
+
hex: "#dc2626"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
t: 2 / 3,
|
|
26
|
+
hex: "#e07020"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
t: 1,
|
|
30
|
+
hex: "#ffcc00"
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
function reasoningWaitAppearance(projection, elapsed) {
|
|
34
|
+
if (projection?.active !== true) return { phase: "idle" };
|
|
35
|
+
const t = Math.min(1, Math.max(0, elapsed / FULL_SCALE_MS));
|
|
36
|
+
return {
|
|
37
|
+
phase: "thermometer",
|
|
38
|
+
t,
|
|
39
|
+
height: MIN_FILL + .92 * easeOutFill(t),
|
|
40
|
+
color: ironScale(t)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function extrapolateProjectionClock(projection, frameNow, anchor, identity) {
|
|
44
|
+
if (projection?.active !== true) return {
|
|
45
|
+
elapsed: 0,
|
|
46
|
+
anchor: null
|
|
47
|
+
};
|
|
48
|
+
const eventElapsed = Math.max(0, projection.streamTime - projection.waitOrigin);
|
|
49
|
+
const stamp = `${identity}:${projection.waitOrigin}:${projection.streamTime}`;
|
|
50
|
+
if (anchor?.stamp !== stamp) {
|
|
51
|
+
const elapsed = Math.max(anchor?.identity === identity ? anchor.renderedElapsed : 0, eventElapsed);
|
|
52
|
+
return {
|
|
53
|
+
elapsed,
|
|
54
|
+
anchor: {
|
|
55
|
+
stamp,
|
|
56
|
+
identity,
|
|
57
|
+
eventElapsed,
|
|
58
|
+
observedAt: frameNow,
|
|
59
|
+
renderedElapsed: elapsed
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const elapsed = Math.max(anchor.renderedElapsed, anchor.eventElapsed + frameNow - anchor.observedAt);
|
|
64
|
+
return {
|
|
65
|
+
elapsed,
|
|
66
|
+
anchor: {
|
|
67
|
+
...anchor,
|
|
68
|
+
renderedElapsed: elapsed
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function advanceReasoningWait(previous, input) {
|
|
73
|
+
const snapshot = reasoningWaitAppearance(input.projection, input.elapsed);
|
|
74
|
+
if (snapshot.phase === "thermometer") {
|
|
75
|
+
if (previous.phase === "thermometer" && previous.identity === input.identity && previous.t === snapshot.t && previous.height === snapshot.height && previous.color === snapshot.color) return previous;
|
|
76
|
+
return {
|
|
77
|
+
...snapshot,
|
|
78
|
+
identity: input.identity
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (input.reducedMotion) return previous.phase === "idle" ? previous : { phase: "idle" };
|
|
82
|
+
const sameIdentity = previous.phase !== "idle" && previous.identity === input.identity;
|
|
83
|
+
if (previous.phase === "thermometer" && sameIdentity) return {
|
|
84
|
+
phase: "drain",
|
|
85
|
+
identity: input.identity,
|
|
86
|
+
t: previous.t,
|
|
87
|
+
height: previous.height,
|
|
88
|
+
color: previous.color,
|
|
89
|
+
fromHeight: previous.height,
|
|
90
|
+
startedAt: input.frameNow
|
|
91
|
+
};
|
|
92
|
+
if (previous.phase === "drain" && sameIdentity) {
|
|
93
|
+
const elapsed = input.frameNow - previous.startedAt;
|
|
94
|
+
if (elapsed >= DRAIN_MS) return { phase: "idle" };
|
|
95
|
+
const height = previous.fromHeight * (1 - elapsed / DRAIN_MS);
|
|
96
|
+
if (height === previous.height) return previous;
|
|
97
|
+
return {
|
|
98
|
+
...previous,
|
|
99
|
+
height
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return previous.phase === "idle" ? previous : { phase: "idle" };
|
|
103
|
+
}
|
|
104
|
+
function nearestStop(t) {
|
|
105
|
+
for (const stop of IRON_STOPS) if (Math.abs(t - stop.t) <= 1 / FULL_SCALE_MS) return stop.hex;
|
|
106
|
+
}
|
|
107
|
+
function ironScale(t) {
|
|
108
|
+
const snapped = nearestStop(t);
|
|
109
|
+
if (snapped !== void 0) return snapped;
|
|
110
|
+
if (t < IRON_STOPS[1].t) return mixOklch(IRON_STOPS[0].hex, IRON_STOPS[1].hex, t / (1 / 3));
|
|
111
|
+
if (t < IRON_STOPS[2].t) return mixOklch(IRON_STOPS[1].hex, IRON_STOPS[2].hex, (t - 1 / 3) / (1 / 3));
|
|
112
|
+
return mixOklch(IRON_STOPS[2].hex, IRON_STOPS[3].hex, (t - 2 / 3) / (1 / 3));
|
|
113
|
+
}
|
|
114
|
+
function mixOklch(fromHex, toHex, amount) {
|
|
115
|
+
return `color-mix(in oklch, ${fromHex}, ${toHex} ${amount * 100}%)`;
|
|
116
|
+
}
|
|
117
|
+
function easeOutFill(linearT) {
|
|
118
|
+
if (linearT <= 0) return 0;
|
|
119
|
+
if (linearT >= 1) return 1;
|
|
120
|
+
const xAt = (u) => 3 * (1 - u) * u * u * .58 + u ** 3;
|
|
121
|
+
const yAt = (u) => 3 * (1 - u) * u * u + u ** 3;
|
|
122
|
+
let low = 0;
|
|
123
|
+
let high = 1;
|
|
124
|
+
for (let i = 0; i < 24; i++) {
|
|
125
|
+
const mid = (low + high) / 2;
|
|
126
|
+
if (xAt(mid) < linearT) low = mid;
|
|
127
|
+
else high = mid;
|
|
128
|
+
}
|
|
129
|
+
return yAt((low + high) / 2);
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/client/service.ts
|
|
133
|
+
/** Stateless reasoning-wait frame service. */
|
|
134
|
+
var ReasoningWaitService = class extends _deepseek_ai_cordis.Service {
|
|
135
|
+
static inject = [];
|
|
136
|
+
constructor(ctx) {
|
|
137
|
+
super(ctx, "reasoningWait");
|
|
138
|
+
}
|
|
139
|
+
clock(projection, frameNow, anchor, identity) {
|
|
140
|
+
return extrapolateProjectionClock(projection, frameNow, anchor, identity);
|
|
141
|
+
}
|
|
142
|
+
advance(previous, input) {
|
|
143
|
+
return advanceReasoningWait(previous, input);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region \0dsh-thinkbar-css:C:\Workspace\tower1229\dsh-thinkbar\src\client\ReasoningWaitIndicator.module.css.mjs
|
|
148
|
+
const css = ".lL-Mhq_anchor{display:none}[data-dsh-thinkbar-host]{isolation:isolate;position:relative;overflow:hidden}[data-dsh-thinkbar-host]>:not([data-dsh-thinkbar-layer]){z-index:1;position:relative}.lL-Mhq_root,.lL-Mhq_fill{border-radius:inherit;pointer-events:none;position:absolute;inset:0}.lL-Mhq_root{z-index:0;overflow:hidden}.lL-Mhq_fill{inset:0 auto 0 0;overflow:hidden}.lL-Mhq_particle{background:var(--dsw-static-neutral-bluish-00);opacity:var(--dsh-particle-opacity,.8);animation:lL-Mhq_reasoning-wait-particle-drift var(--dsh-particle-dur,3s) ease-in-out var(--dsh-particle-delay,0s) infinite;border-radius:50%;position:absolute}@keyframes lL-Mhq_reasoning-wait-particle-drift{0%,to{opacity:var(--dsh-particle-opacity,.8);transform:translate(0)}50%{transform:translate(var(--dsh-particle-dx,0px), var(--dsh-particle-dy,-2px));opacity:calc(var(--dsh-particle-opacity,.8) * .5)}}@media (prefers-reduced-motion:reduce){.lL-Mhq_particle{animation:none}}";
|
|
149
|
+
const tagId = "dsh-thinkbar/ReasoningWaitIndicator.module.css";
|
|
150
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
151
|
+
const tag = document.createElement("style");
|
|
152
|
+
tag.dataset.plugin = "dsh-thinkbar";
|
|
153
|
+
tag.dataset.pluginCss = tagId;
|
|
154
|
+
tag.textContent = css;
|
|
155
|
+
document.head.appendChild(tag);
|
|
156
|
+
}
|
|
157
|
+
var ReasoningWaitIndicator_module_css_default = {
|
|
158
|
+
"anchor": "lL-Mhq_anchor",
|
|
159
|
+
"fill": "lL-Mhq_fill",
|
|
160
|
+
"particle": "lL-Mhq_particle",
|
|
161
|
+
"reasoning-wait-particle-drift": "lL-Mhq_reasoning-wait-particle-drift",
|
|
162
|
+
"root": "lL-Mhq_root"
|
|
163
|
+
};
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/client/ReasoningWaitIndicator.tsx
|
|
166
|
+
const FILL_PARTICLES = [
|
|
167
|
+
{
|
|
168
|
+
x: 10,
|
|
169
|
+
y: 15,
|
|
170
|
+
size: 2,
|
|
171
|
+
opacity: 1,
|
|
172
|
+
dur: 2.6,
|
|
173
|
+
delay: 0,
|
|
174
|
+
dx: 3,
|
|
175
|
+
dy: -2
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
x: 26,
|
|
179
|
+
y: 8,
|
|
180
|
+
size: 3,
|
|
181
|
+
opacity: .95,
|
|
182
|
+
dur: 3.4,
|
|
183
|
+
delay: -.8,
|
|
184
|
+
dx: -2,
|
|
185
|
+
dy: 3
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
x: 40,
|
|
189
|
+
y: 19,
|
|
190
|
+
size: 2,
|
|
191
|
+
opacity: 1,
|
|
192
|
+
dur: 2.2,
|
|
193
|
+
delay: -1.4,
|
|
194
|
+
dx: 3,
|
|
195
|
+
dy: -3
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
x: 56,
|
|
199
|
+
y: 12,
|
|
200
|
+
size: 2,
|
|
201
|
+
opacity: .9,
|
|
202
|
+
dur: 4.1,
|
|
203
|
+
delay: -.4,
|
|
204
|
+
dx: -3,
|
|
205
|
+
dy: 2
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
x: 72,
|
|
209
|
+
y: 21,
|
|
210
|
+
size: 3,
|
|
211
|
+
opacity: 1,
|
|
212
|
+
dur: 3,
|
|
213
|
+
delay: -2.1,
|
|
214
|
+
dx: 2,
|
|
215
|
+
dy: -2
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
x: 90,
|
|
219
|
+
y: 7,
|
|
220
|
+
size: 2,
|
|
221
|
+
opacity: .95,
|
|
222
|
+
dur: 2.8,
|
|
223
|
+
delay: -1.1,
|
|
224
|
+
dx: -2,
|
|
225
|
+
dy: 3
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
x: 106,
|
|
229
|
+
y: 16,
|
|
230
|
+
size: 2,
|
|
231
|
+
opacity: 1,
|
|
232
|
+
dur: 3.6,
|
|
233
|
+
delay: -.6,
|
|
234
|
+
dx: 3,
|
|
235
|
+
dy: -2
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
x: 124,
|
|
239
|
+
y: 10,
|
|
240
|
+
size: 3,
|
|
241
|
+
opacity: .9,
|
|
242
|
+
dur: 4.4,
|
|
243
|
+
delay: -2.8,
|
|
244
|
+
dx: -3,
|
|
245
|
+
dy: 2
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
x: 142,
|
|
249
|
+
y: 19,
|
|
250
|
+
size: 2,
|
|
251
|
+
opacity: 1,
|
|
252
|
+
dur: 2.4,
|
|
253
|
+
delay: -1.7,
|
|
254
|
+
dx: 2,
|
|
255
|
+
dy: -3
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
x: 160,
|
|
259
|
+
y: 13,
|
|
260
|
+
size: 2,
|
|
261
|
+
opacity: .95,
|
|
262
|
+
dur: 3.2,
|
|
263
|
+
delay: -.9,
|
|
264
|
+
dx: -2,
|
|
265
|
+
dy: 3
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
x: 180,
|
|
269
|
+
y: 8,
|
|
270
|
+
size: 3,
|
|
271
|
+
opacity: .95,
|
|
272
|
+
dur: 3.8,
|
|
273
|
+
delay: -2.4,
|
|
274
|
+
dx: 3,
|
|
275
|
+
dy: -2
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
x: 202,
|
|
279
|
+
y: 18,
|
|
280
|
+
size: 2,
|
|
281
|
+
opacity: 1,
|
|
282
|
+
dur: 2.7,
|
|
283
|
+
delay: -1.2,
|
|
284
|
+
dx: -3,
|
|
285
|
+
dy: 2
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
x: 226,
|
|
289
|
+
y: 12,
|
|
290
|
+
size: 2,
|
|
291
|
+
opacity: .9,
|
|
292
|
+
dur: 4.3,
|
|
293
|
+
delay: -.3,
|
|
294
|
+
dx: 2,
|
|
295
|
+
dy: -3
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
x: 250,
|
|
299
|
+
y: 17,
|
|
300
|
+
size: 3,
|
|
301
|
+
opacity: 1,
|
|
302
|
+
dur: 3.1,
|
|
303
|
+
delay: -1.9,
|
|
304
|
+
dx: -2,
|
|
305
|
+
dy: 2
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
x: 278,
|
|
309
|
+
y: 9,
|
|
310
|
+
size: 2,
|
|
311
|
+
opacity: .95,
|
|
312
|
+
dur: 2.9,
|
|
313
|
+
delay: -.7,
|
|
314
|
+
dx: 3,
|
|
315
|
+
dy: -2
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
x: 306,
|
|
319
|
+
y: 14,
|
|
320
|
+
size: 2,
|
|
321
|
+
opacity: .9,
|
|
322
|
+
dur: 3.7,
|
|
323
|
+
delay: -2.6,
|
|
324
|
+
dx: -2,
|
|
325
|
+
dy: 3
|
|
326
|
+
}
|
|
327
|
+
];
|
|
328
|
+
function particleStyle(particle) {
|
|
329
|
+
return {
|
|
330
|
+
left: `${particle.x}px`,
|
|
331
|
+
top: `${particle.y}px`,
|
|
332
|
+
width: `${particle.size}px`,
|
|
333
|
+
height: `${particle.size}px`,
|
|
334
|
+
"--dsh-particle-opacity": String(particle.opacity),
|
|
335
|
+
"--dsh-particle-dx": `${particle.dx}px`,
|
|
336
|
+
"--dsh-particle-dy": `${particle.dy}px`,
|
|
337
|
+
"--dsh-particle-dur": `${particle.dur}s`,
|
|
338
|
+
"--dsh-particle-delay": `${particle.delay}s`
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function ReasoningWaitIndicator({ identity, projection, clock, advance }) {
|
|
342
|
+
const [waitState, setWaitState] = (0, react.useState)({ phase: "idle" });
|
|
343
|
+
const clockAnchorRef = (0, react.useRef)(null);
|
|
344
|
+
const reducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches ?? false;
|
|
345
|
+
const frameNow = useFrameNow(projection?.active === true || waitState.phase !== "idle");
|
|
346
|
+
const clockRead = clock(projection, frameNow, clockAnchorRef.current, identity);
|
|
347
|
+
clockAnchorRef.current = clockRead.anchor;
|
|
348
|
+
const input = {
|
|
349
|
+
projection,
|
|
350
|
+
elapsed: clockRead.elapsed,
|
|
351
|
+
frameNow,
|
|
352
|
+
reducedMotion,
|
|
353
|
+
identity
|
|
354
|
+
};
|
|
355
|
+
const view = advance(waitState, input);
|
|
356
|
+
(0, react.useLayoutEffect)(() => {
|
|
357
|
+
setWaitState((previous) => advance(previous, input));
|
|
358
|
+
}, [
|
|
359
|
+
projection,
|
|
360
|
+
clockRead.elapsed,
|
|
361
|
+
frameNow,
|
|
362
|
+
reducedMotion,
|
|
363
|
+
identity,
|
|
364
|
+
advance
|
|
365
|
+
]);
|
|
366
|
+
if (view.phase === "idle") return null;
|
|
367
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
368
|
+
className: ReasoningWaitIndicator_module_css_default.root,
|
|
369
|
+
"data-reasoning-wait": view.phase,
|
|
370
|
+
"aria-hidden": "true",
|
|
371
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
372
|
+
className: ReasoningWaitIndicator_module_css_default.fill,
|
|
373
|
+
"data-reasoning-wait-fill": "",
|
|
374
|
+
style: {
|
|
375
|
+
width: `${view.height * 100}%`,
|
|
376
|
+
backgroundColor: view.color
|
|
377
|
+
},
|
|
378
|
+
children: FILL_PARTICLES.map((particle, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
379
|
+
className: ReasoningWaitIndicator_module_css_default.particle,
|
|
380
|
+
"data-reasoning-wait-particle": "",
|
|
381
|
+
style: particleStyle(particle)
|
|
382
|
+
}, index))
|
|
383
|
+
})
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
function useFrameNow(active) {
|
|
387
|
+
const [, setTick] = (0, react.useState)(0);
|
|
388
|
+
(0, react.useEffect)(() => {
|
|
389
|
+
if (!active) return;
|
|
390
|
+
let frame = 0;
|
|
391
|
+
const tick = () => {
|
|
392
|
+
setTick((value) => value + 1);
|
|
393
|
+
frame = requestAnimationFrame(tick);
|
|
394
|
+
};
|
|
395
|
+
frame = requestAnimationFrame(tick);
|
|
396
|
+
return () => cancelAnimationFrame(frame);
|
|
397
|
+
}, [active]);
|
|
398
|
+
return performance.now();
|
|
399
|
+
}
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region src/client/model-trigger-adapter.ts
|
|
402
|
+
const COMPOSER_SELECTOR = "[data-composer-card]";
|
|
403
|
+
const MODEL_TRIGGER_SELECTOR = "button[aria-haspopup=\"menu\"]";
|
|
404
|
+
const HOST_ATTRIBUTE = "data-dsh-thinkbar-host";
|
|
405
|
+
const LAYER_ATTRIBUTE = "data-dsh-thinkbar-layer";
|
|
406
|
+
let compatibilityWarningShown = false;
|
|
407
|
+
/** Resolve the only semantic menu trigger following the public Slot anchor. */
|
|
408
|
+
function resolveModelTrigger(anchor) {
|
|
409
|
+
const composer = anchor.closest(COMPOSER_SELECTOR);
|
|
410
|
+
if (composer === null) return null;
|
|
411
|
+
const candidates = [...composer.querySelectorAll(MODEL_TRIGGER_SELECTOR)].filter((candidate) => Boolean(anchor.compareDocumentPosition(candidate) & Node.DOCUMENT_POSITION_FOLLOWING));
|
|
412
|
+
return candidates.length === 1 ? candidates[0] ?? null : null;
|
|
413
|
+
}
|
|
414
|
+
function warnCompatibility() {
|
|
415
|
+
if (compatibilityWarningShown) return;
|
|
416
|
+
compatibilityWarningShown = true;
|
|
417
|
+
console.warn("[dsh-thinkbar] Could not uniquely identify the DeepSeek Harness model selector; the indicator is disabled.");
|
|
418
|
+
}
|
|
419
|
+
function existingLayer(target) {
|
|
420
|
+
return [...target.children].find((child) => child instanceof HTMLSpanElement && child.hasAttribute(LAYER_ATTRIBUTE)) ?? null;
|
|
421
|
+
}
|
|
422
|
+
/** Attach one plugin-owned Portal layer without moving or rewriting host children. */
|
|
423
|
+
function mountModelTrigger(anchor) {
|
|
424
|
+
const target = resolveModelTrigger(anchor);
|
|
425
|
+
if (target === null) {
|
|
426
|
+
warnCompatibility();
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
const layer = existingLayer(target) ?? document.createElement("span");
|
|
430
|
+
layer.setAttribute(LAYER_ATTRIBUTE, "");
|
|
431
|
+
if (!layer.isConnected) target.prepend(layer);
|
|
432
|
+
target.setAttribute(HOST_ATTRIBUTE, "");
|
|
433
|
+
let disposed = false;
|
|
434
|
+
return {
|
|
435
|
+
layer,
|
|
436
|
+
dispose: () => {
|
|
437
|
+
if (disposed) return;
|
|
438
|
+
disposed = true;
|
|
439
|
+
layer.remove();
|
|
440
|
+
if (existingLayer(target) === null) target.removeAttribute(HOST_ATTRIBUTE);
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
//#endregion
|
|
445
|
+
//#region src/client/use-model-trigger.ts
|
|
446
|
+
/** Keep a Portal layer attached across ModelSelect and composer reconstruction. */
|
|
447
|
+
function useModelTrigger(anchorRef) {
|
|
448
|
+
const [layer, setLayer] = (0, react.useState)(null);
|
|
449
|
+
(0, react.useLayoutEffect)(() => {
|
|
450
|
+
const anchor = anchorRef.current;
|
|
451
|
+
if (anchor === null) return;
|
|
452
|
+
const composer = anchor.closest("[data-composer-card]");
|
|
453
|
+
if (composer === null) return;
|
|
454
|
+
let mount = null;
|
|
455
|
+
const refresh = () => {
|
|
456
|
+
const target = resolveModelTrigger(anchor);
|
|
457
|
+
if (mount?.layer.isConnected === true && mount.layer.parentElement === target) return;
|
|
458
|
+
mount?.dispose();
|
|
459
|
+
mount = mountModelTrigger(anchor);
|
|
460
|
+
setLayer(mount?.layer ?? null);
|
|
461
|
+
};
|
|
462
|
+
refresh();
|
|
463
|
+
const observer = new MutationObserver(refresh);
|
|
464
|
+
observer.observe(composer, {
|
|
465
|
+
childList: true,
|
|
466
|
+
subtree: true
|
|
467
|
+
});
|
|
468
|
+
return () => {
|
|
469
|
+
observer.disconnect();
|
|
470
|
+
mount?.dispose();
|
|
471
|
+
};
|
|
472
|
+
}, [anchorRef]);
|
|
473
|
+
return layer;
|
|
474
|
+
}
|
|
475
|
+
//#endregion
|
|
476
|
+
//#region src/client/ModelTriggerBridge.tsx
|
|
477
|
+
/** Public-Slot lifecycle bridge that portals the indicator into the model trigger. */
|
|
478
|
+
function ModelTriggerBridge({ useSession, sessionId, clock, advance }) {
|
|
479
|
+
const anchorRef = (0, react.useRef)(null);
|
|
480
|
+
const layer = useModelTrigger(anchorRef);
|
|
481
|
+
const projection = useSession((snapshot) => snapshot.views.get("dsh-thinkbar"));
|
|
482
|
+
const identity = projection === null || projection === void 0 ? String(sessionId) : `${String(sessionId)}:${projection.turn}:${projection.step}`;
|
|
483
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
484
|
+
ref: anchorRef,
|
|
485
|
+
className: ReasoningWaitIndicator_module_css_default.anchor,
|
|
486
|
+
"data-dsh-thinkbar-anchor": "",
|
|
487
|
+
"aria-hidden": "true",
|
|
488
|
+
children: layer === null ? null : (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningWaitIndicator, {
|
|
489
|
+
identity,
|
|
490
|
+
projection,
|
|
491
|
+
clock,
|
|
492
|
+
advance
|
|
493
|
+
}), layer)
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/client/reasoning-wait-projection.ts
|
|
498
|
+
function isCompactChunkEvent(event) {
|
|
499
|
+
const type = String(event.type);
|
|
500
|
+
return type === "chunkrow/text-chunks" || type === "chunkrow/reasoning-chunks" || type === "chunkrow/tool-call-chunks";
|
|
501
|
+
}
|
|
502
|
+
function projectionOf(state) {
|
|
503
|
+
const { retryPending: _retryPending, ...projection } = state;
|
|
504
|
+
return projection;
|
|
505
|
+
}
|
|
506
|
+
function eventIdentity(event) {
|
|
507
|
+
if (isCompactChunkEvent(event)) return `${event.data.turn}:${event.data.step}`;
|
|
508
|
+
if (event.type === "step/start" || event.type === "step/end" || event.type === "assistant/chunk" || event.type === "assistant/message" || event.type === "llm/retry" || event.type === "llm/retry-started") return `${event.data.turn}:${event.data.step}`;
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
function transition(state, time, tailKind, active, originTime = time) {
|
|
512
|
+
return {
|
|
513
|
+
...state,
|
|
514
|
+
waitOrigin: state.retryPending && tailKind === "reasoning" ? originTime : state.waitOrigin,
|
|
515
|
+
streamTime: time,
|
|
516
|
+
active,
|
|
517
|
+
tailKind,
|
|
518
|
+
retryPending: state.retryPending && tailKind === "empty"
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function chunkTransition(state, match) {
|
|
522
|
+
if (match.event.type !== "assistant/chunk") return state;
|
|
523
|
+
const { chunk } = match.event.data;
|
|
524
|
+
switch (chunk.type) {
|
|
525
|
+
case "block-start":
|
|
526
|
+
if (chunk.blockType === "reasoning") return transition(state, match.event.time, "reasoning", true);
|
|
527
|
+
if (chunk.blockType === "text") return transition(state, match.event.time, "text", false);
|
|
528
|
+
if (chunk.blockType === "tool-call") return transition(state, match.event.time, "tool", false);
|
|
529
|
+
return transition(state, match.event.time, "other", false);
|
|
530
|
+
case "reasoning-delta": return transition(state, match.event.time, "reasoning", true);
|
|
531
|
+
case "text-delta": return transition(state, match.event.time, "text", false);
|
|
532
|
+
case "tool-call-delta": return transition(state, match.event.time, "tool", false);
|
|
533
|
+
case "block-end":
|
|
534
|
+
if (chunk.block.type === "reasoning") return transition(state, match.event.time, "reasoning", true);
|
|
535
|
+
if (chunk.block.type === "text") return transition(state, match.event.time, "text", false);
|
|
536
|
+
if (chunk.block.type === "tool-call") return transition(state, match.event.time, "tool", false);
|
|
537
|
+
return transition(state, match.event.time, "other", false);
|
|
538
|
+
default: return state;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function compactEndTime(event) {
|
|
542
|
+
return event.data.dt.reduce((time, gap) => time + gap, event.time);
|
|
543
|
+
}
|
|
544
|
+
function compactFirstEvidenceTime(event) {
|
|
545
|
+
let time = event.time;
|
|
546
|
+
for (let index = 0; index < (event.data.texts?.length ?? 0); index++) {
|
|
547
|
+
if (event.data.texts?.[index] !== "") return time;
|
|
548
|
+
time += event.data.dt[index] ?? 0;
|
|
549
|
+
}
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
function compactTransition(state, event) {
|
|
553
|
+
const streamTime = compactEndTime(event);
|
|
554
|
+
if (event.type === "chunkrow/reasoning-chunks") {
|
|
555
|
+
const firstEvidenceTime = compactFirstEvidenceTime(event);
|
|
556
|
+
return firstEvidenceTime === null ? state : transition(state, streamTime, "reasoning", true, firstEvidenceTime);
|
|
557
|
+
}
|
|
558
|
+
if (event.type === "chunkrow/text-chunks") return transition(state, streamTime, "text", false);
|
|
559
|
+
return transition(state, streamTime, "tool", false);
|
|
560
|
+
}
|
|
561
|
+
function updateState(state, match) {
|
|
562
|
+
const event = match.event;
|
|
563
|
+
if (isCompactChunkEvent(event)) return compactTransition(state, event);
|
|
564
|
+
if (event.type === "assistant/chunk") return chunkTransition(state, match);
|
|
565
|
+
if (event.type === "llm/retry" || event.type === "llm/retry-started") return {
|
|
566
|
+
...state,
|
|
567
|
+
streamTime: event.time,
|
|
568
|
+
active: false,
|
|
569
|
+
tailKind: "empty",
|
|
570
|
+
retryPending: true
|
|
571
|
+
};
|
|
572
|
+
if (event.type === "assistant/message" || event.type === "step/end") return {
|
|
573
|
+
...state,
|
|
574
|
+
streamTime: event.time,
|
|
575
|
+
active: false,
|
|
576
|
+
retryPending: false
|
|
577
|
+
};
|
|
578
|
+
return state;
|
|
579
|
+
}
|
|
580
|
+
/** Per-Step event state machine for the reasoning indicator. */
|
|
581
|
+
const reasoningWaitDefinition = {
|
|
582
|
+
kind: "dsh-thinkbar/reasoning-wait",
|
|
583
|
+
target: "dsh-thinkbar",
|
|
584
|
+
match: (event) => {
|
|
585
|
+
const id = eventIdentity(event);
|
|
586
|
+
if (id === null) return null;
|
|
587
|
+
return {
|
|
588
|
+
id,
|
|
589
|
+
role: event.type === "step/start" ? "start" : "update"
|
|
590
|
+
};
|
|
591
|
+
},
|
|
592
|
+
start: (_context, match) => {
|
|
593
|
+
if (match.event.type !== "step/start") throw new Error("reasoning-wait start requires step/start");
|
|
594
|
+
return {
|
|
595
|
+
turn: match.event.data.turn,
|
|
596
|
+
step: match.event.data.step,
|
|
597
|
+
waitOrigin: match.event.time,
|
|
598
|
+
streamTime: match.event.time,
|
|
599
|
+
active: false,
|
|
600
|
+
tailKind: "empty",
|
|
601
|
+
retryPending: false
|
|
602
|
+
};
|
|
603
|
+
},
|
|
604
|
+
update: (context, match) => updateState(context.state, match),
|
|
605
|
+
publication: (match) => match.event.type === "assistant/chunk" || isCompactChunkEvent(match.event) ? "animation-frame" : "immediate",
|
|
606
|
+
buildViewNode: (context) => {
|
|
607
|
+
if (context.state === void 0) return null;
|
|
608
|
+
return {
|
|
609
|
+
key: context.key,
|
|
610
|
+
kind: context.kind,
|
|
611
|
+
id: context.id,
|
|
612
|
+
target: "dsh-thinkbar",
|
|
613
|
+
anchorSeq: context.matches.at(-1)?.event.seq ?? context.start?.event.seq ?? 0,
|
|
614
|
+
data: projectionOf(context.state)
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
function latestProjection(nodes) {
|
|
619
|
+
let latest;
|
|
620
|
+
for (const node of nodes) if (latest === void 0 || node.anchorSeq > latest.anchorSeq) latest = node;
|
|
621
|
+
return latest?.data ?? null;
|
|
622
|
+
}
|
|
623
|
+
/** Session-owned reducer selecting the latest Step projection. */
|
|
624
|
+
const reasoningWaitView = {
|
|
625
|
+
target: "dsh-thinkbar",
|
|
626
|
+
create: () => {
|
|
627
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
628
|
+
return {
|
|
629
|
+
empty: null,
|
|
630
|
+
replace: (input) => {
|
|
631
|
+
nodes.clear();
|
|
632
|
+
for (const node of input.nodes) nodes.set(node.key, node);
|
|
633
|
+
return latestProjection(nodes.values());
|
|
634
|
+
},
|
|
635
|
+
apply: (input) => {
|
|
636
|
+
for (const node of input.upserts) nodes.set(node.key, node);
|
|
637
|
+
return latestProjection(nodes.values());
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
//#endregion
|
|
643
|
+
//#region src/client/index.ts
|
|
644
|
+
/** Required public rc.2 registries. */
|
|
645
|
+
const inject = [
|
|
646
|
+
"slots",
|
|
647
|
+
"conversationEvents",
|
|
648
|
+
"conversationViews"
|
|
649
|
+
];
|
|
650
|
+
/** Mount the projection, frame service, and public-Slot DOM bridge. */
|
|
651
|
+
function apply(ctx) {
|
|
652
|
+
ctx.conversationEvents.register(reasoningWaitDefinition);
|
|
653
|
+
ctx.conversationViews.register(reasoningWaitView);
|
|
654
|
+
ctx.plugin(ReasoningWaitService);
|
|
655
|
+
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
656
|
+
name: "conversation.input.right",
|
|
657
|
+
id: "dsh-thinkbar",
|
|
658
|
+
order: 20,
|
|
659
|
+
inject: () => {
|
|
660
|
+
const reasoningWait = () => ctx.get("reasoningWait");
|
|
661
|
+
return {
|
|
662
|
+
clock: (projection, frameNow, anchor, identity) => reasoningWait()?.clock(projection, frameNow, anchor, identity) ?? {
|
|
663
|
+
elapsed: 0,
|
|
664
|
+
anchor: null
|
|
665
|
+
},
|
|
666
|
+
advance: (previous, input) => reasoningWait()?.advance(previous, input) ?? { phase: "idle" }
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
}, ModelTriggerBridge));
|
|
670
|
+
}
|
|
671
|
+
//#endregion
|
|
672
|
+
exports.ReasoningWaitService = ReasoningWaitService;
|
|
673
|
+
exports.apply = apply;
|
|
674
|
+
exports.inject = inject;
|
|
675
|
+
return module.exports;
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
//# sourceMappingURL=client.cjs.map
|