dsh-toolfold 0.1.7 → 0.1.9
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/CHANGELOG.md +41 -1
- package/README.en.md +130 -90
- package/README.md +118 -63
- package/lib/client.js +1954 -1836
- package/lib/index.js +265 -128
- package/package.json +17 -3
- package/lib/build-dynamic.cjs +0 -69
- package/lib/dynamic-body.js +0 -1732
package/lib/client.js
CHANGED
|
@@ -1,1838 +1,1956 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dsh-toolfold — CLIENT half (browser).
|
|
3
|
-
*
|
|
4
|
-
* Folding of consecutive tool calls and thinking in the DSH Web GUI chat
|
|
5
|
-
* flow, without replacing any built-in renderer:
|
|
6
|
-
*
|
|
7
|
-
* 1. Runs of consecutive `tool-call` rows fold into ONE compact bar: all
|
|
8
|
-
* cards of the run hide and the bar shows the LAST call's own one-line
|
|
9
|
-
* summary (cloned from the product card) plus "已折叠 N 个工具调用 · 点击展开".
|
|
10
|
-
* Clicking the bar toggles the run open/closed (state keyed by the run's
|
|
11
|
-
* first call). With "思考分隔" (splitThink, DEFAULT ON) ANY other row —
|
|
12
|
-
* settled thinking, in-progress thinking, AI text output, user
|
|
13
|
-
* messages — ends a run, so completed thinking SEPARATES runs:
|
|
14
|
-
* [read][thinking][write] folds into two independent bars ([read] and
|
|
15
|
-
* [write]) instead of one merged bar. With splitThink OFF the original
|
|
16
|
-
* behavior returns: a step that only settled Think rows occupy is
|
|
17
|
-
* transparent to the merge, so [read][thinking][write] folds the same
|
|
18
|
-
* way [read][write] does.
|
|
19
|
-
* 2. Settled Think rows (`[data-variant="think"][data-state="ok"]`, the
|
|
20
|
-
* product's own disclosure marker) are hidden entirely by default (the
|
|
21
|
-
* emptied row is removed from layout so no gap remains). With
|
|
22
|
-
* "保留思考" on they stay visible: between the folded bars in split
|
|
23
|
-
* mode, or folded with the run and re-inserted in their original order
|
|
24
|
-
* between the calls on expand in merge mode.
|
|
25
|
-
* 3. Streaming Think rows (`data-state="running"`) stay fully visible as
|
|
26
|
-
* their own independent row — in-progress thinking is never folded and
|
|
27
|
-
* keeps the calls around it apart until it completes.
|
|
28
|
-
* 4. Animations: expanding cascades the run's rows downward in a
|
|
29
|
-
* waterfall (staggered spring fall — rows land with a slight overshoot
|
|
30
|
-
* bounce); collapsing cascades them back up (staggered rise — each
|
|
31
|
-
* row fades and lifts on the SAME easing curve as its height shrink,
|
|
32
|
-
* so a tall card never looks "stuck" while its opacity is already
|
|
33
|
-
* gone) while each row's HEIGHT simultaneously shrinks to zero, so
|
|
34
|
-
* the content below the run follows upward continuously — no blank
|
|
35
|
-
* hole and no end snap. Cleanup runs exactly one step after the LAST
|
|
36
|
-
* row's animation finishes — no dead wait before the run disappears.
|
|
37
|
-
* When the reader is pinned near the bottom, expanding keeps the
|
|
38
|
-
* bar's viewport position fixed (the product's sticky-bottom follow
|
|
39
|
-
* would otherwise shove the bar out of the top).
|
|
40
|
-
*
|
|
41
|
-
* Performance (near-zero steady-state cost):
|
|
42
|
-
* - NO page-wide observer. The old body-subtree observer fired on every DOM
|
|
43
|
-
* mutation anywhere in the app; it is gone. Flow mounts/unmounts are
|
|
44
|
-
* caught by tiny childList-only observers on the flow parents (direct
|
|
45
|
-
* children only, no subtree) plus a visible-only safety rescan that
|
|
46
|
-
* backs off (3s → 10s) while the page is quiet.
|
|
47
|
-
* - Two-phase flow observers. A flow with no tool calls and no think
|
|
48
|
-
* blocks keeps a childList-only observer that fires only on row
|
|
49
|
-
* add/remove/reorder — while text streams inside its rows it costs
|
|
50
|
-
* nothing. Subtree + `data-state` attribute coverage engages only once
|
|
51
|
-
* the flow actually contains foldable content, and never downgrades.
|
|
52
|
-
* - Classified mutations: row adds/removes and think `data-state` flips
|
|
53
|
-
* run a full merge pass; content changes inside a tool-call row refresh
|
|
54
|
-
* ONLY the bar of the run whose LAST row was mutated, and only while
|
|
55
|
-
* that run is collapsed (throttled ~8/s). Expanded runs and non-last
|
|
56
|
-
* rows cost nothing. The single-appended-text-node case short-circuits
|
|
57
|
-
* with zero DOM walks, so assistant/think text streaming costs nothing.
|
|
58
|
-
* - Passes are rAF-batched, one per frame, dirty flows only; row
|
|
59
|
-
* assessments are cached per row (zero layout reads in steady state);
|
|
60
|
-
* bar parts are cached on the bar (no per-update selector lookups).
|
|
61
|
-
* - Visibility gating: while the tab is hidden the engine holds NO
|
|
62
|
-
* observers and NO timers — literally zero work. On return to
|
|
63
|
-
* visibility everything re-attaches and one refresh catches up.
|
|
64
|
-
*
|
|
65
|
-
* Settings (Settings → 插件 → 工具折叠, persisted through the DSH settings
|
|
66
|
-
* service into ~/.dsh/settings.yaml):
|
|
67
|
-
* - durMs: expand/collapse animation duration (0–2000, default 240);
|
|
68
|
-
* - keepThink: settled think stays visible instead of hidden (default off);
|
|
69
|
-
* - splitThink: settled think separates tool-call groups into independent
|
|
70
|
-
* bars (default on; off restores the old merge-across-thinking fold);
|
|
71
|
-
* - stats: live performance meter in the card (default off).
|
|
72
|
-
*
|
|
73
|
-
* Persistence is layered, in order of preference:
|
|
74
|
-
* 1. the official `settingsScope` transport (when the deployment exposes
|
|
75
|
-
* the `toolfold` namespace to the web client);
|
|
76
|
-
* 2. this plugin's host route `GET/POST /api/dsh-toolfold/settings`
|
|
77
|
-
* (registered by the host half, backed by the DSH settings service);
|
|
78
|
-
* 3. browser localStorage — degraded fallback for pages without the host
|
|
79
|
-
* half (remote browsers, dynamic-plugin dev mode).
|
|
80
|
-
*
|
|
81
|
-
* The engine works on the rendered DOM only (stable product attributes:
|
|
82
|
-
* `[data-chat-flow]`, `[data-chat-flow-kind]`, `[data-variant="think"]`,
|
|
83
|
-
* `data-state`, `[data-disclosure-row]`), so it degrades gracefully when the
|
|
84
|
-
* product markup changes: the worst case is that folding stops applying.
|
|
85
|
-
*
|
|
86
|
-
* Lifecycle: `installCollapseEngine(document, settings, timers)` returns a
|
|
87
|
-
* disposer; the plugin wires it through `ctx.effect` so stop/update removes
|
|
88
|
-
* the style tag, the observers, the timers, the bars and every injected
|
|
89
|
-
* class. Timers come from the Cordis `timer` service (ctx.timeout /
|
|
90
|
-
* ctx.interval disposers) because the dynamic client environment shadows the
|
|
91
|
-
* browser timer globals.
|
|
92
|
-
*/
|
|
93
1
|
window.__ModuleLoader__.load({
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
2
|
+
id: "dsh-toolfold",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var __dshSeed = null;
|
|
6
|
+
try {
|
|
7
|
+
if (typeof require === "function") __dshSeed = require("react");
|
|
8
|
+
} catch (err) {
|
|
9
|
+
__dshSeed = null;
|
|
10
|
+
}
|
|
11
|
+
if (__dshSeed === null && typeof React !== "undefined") __dshSeed = React;
|
|
12
|
+
if (typeof globalThis !== "undefined") globalThis.__dshToolfoldReact = __dshSeed;
|
|
13
|
+
//#region \0rolldown/runtime.js
|
|
14
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/client/settings.js
|
|
17
|
+
var require_settings = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
18
|
+
var SETTINGS_KEY = "dsh-toolfold.settings.v1";
|
|
19
|
+
/** Pre-rename key; migrated once so existing preferences survive. */
|
|
20
|
+
var LEGACY_SETTINGS_KEY = "dsh-codex-collapse.settings.v1";
|
|
21
|
+
var DEFAULT_SETTINGS = {
|
|
22
|
+
enabled: true,
|
|
23
|
+
durMs: 240,
|
|
24
|
+
thinkMode: "auto",
|
|
25
|
+
splitThink: true,
|
|
26
|
+
stats: false
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the canonical think mode for one raw section. An explicitly set,
|
|
30
|
+
* valid thinkMode wins; otherwise derive it from the deprecated thinkAuto /
|
|
31
|
+
* keepThink keys so pre-merge preferences survive the upgrade; default
|
|
32
|
+
* 'auto'. Total — never throws. (Host twin: resolveThinkMode in
|
|
33
|
+
* src/host/index.js.)
|
|
34
|
+
*/
|
|
35
|
+
function resolveThinkMode(section) {
|
|
36
|
+
var mode = section !== void 0 && section !== null ? section.thinkMode : void 0;
|
|
37
|
+
if (mode === "keep" || mode === "hide" || mode === "auto") return mode;
|
|
38
|
+
if (section !== void 0 && section !== null && section.thinkAuto === false) return section.keepThink === true ? "keep" : "hide";
|
|
39
|
+
return "auto";
|
|
40
|
+
}
|
|
41
|
+
/** Next canonical think mode for a patch: explicit valid value wins, else
|
|
42
|
+
* derive from legacy patch keys (old-host sections), else keep current so
|
|
43
|
+
* unrelated patches (e.g. {}) never reset it. */
|
|
44
|
+
function nextThinkMode(patch, current) {
|
|
45
|
+
if (patch.thinkMode === "keep" || patch.thinkMode === "hide" || patch.thinkMode === "auto") return patch.thinkMode;
|
|
46
|
+
if (patch.thinkAuto !== void 0 || patch.keepThink !== void 0) return resolveThinkMode({
|
|
47
|
+
thinkMode: void 0,
|
|
48
|
+
thinkAuto: patch.thinkAuto,
|
|
49
|
+
keepThink: patch.keepThink
|
|
50
|
+
});
|
|
51
|
+
return current;
|
|
52
|
+
}
|
|
53
|
+
function loadSettings() {
|
|
54
|
+
var base = {
|
|
55
|
+
enabled: true,
|
|
56
|
+
durMs: DEFAULT_SETTINGS.durMs,
|
|
57
|
+
thinkMode: DEFAULT_SETTINGS.thinkMode,
|
|
58
|
+
splitThink: DEFAULT_SETTINGS.splitThink,
|
|
59
|
+
stats: false
|
|
60
|
+
};
|
|
61
|
+
if (typeof localStorage === "undefined") return base;
|
|
62
|
+
var raw = null;
|
|
63
|
+
try {
|
|
64
|
+
raw = localStorage.getItem(SETTINGS_KEY);
|
|
65
|
+
if (raw === null) {
|
|
66
|
+
var legacy = localStorage.getItem(LEGACY_SETTINGS_KEY);
|
|
67
|
+
if (legacy !== null) {
|
|
68
|
+
raw = legacy;
|
|
69
|
+
localStorage.setItem(SETTINGS_KEY, legacy);
|
|
70
|
+
localStorage.removeItem(LEGACY_SETTINGS_KEY);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
return base;
|
|
75
|
+
}
|
|
76
|
+
if (raw === null) return base;
|
|
77
|
+
try {
|
|
78
|
+
var parsed = JSON.parse(raw);
|
|
79
|
+
var dur = Number(parsed.durMs);
|
|
80
|
+
return {
|
|
81
|
+
enabled: parsed.enabled !== false,
|
|
82
|
+
durMs: Number.isFinite(dur) ? Math.max(0, Math.min(2e3, Math.round(dur))) : DEFAULT_SETTINGS.durMs,
|
|
83
|
+
thinkMode: resolveThinkMode(parsed),
|
|
84
|
+
splitThink: parsed.splitThink !== false,
|
|
85
|
+
stats: parsed.stats === true
|
|
86
|
+
};
|
|
87
|
+
} catch (err) {
|
|
88
|
+
return base;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Minimal SnapshotStore: stable getSnapshot until update replaces it. */
|
|
92
|
+
function createSettingsStore() {
|
|
93
|
+
var snapshot = loadSettings();
|
|
94
|
+
var listeners = [];
|
|
95
|
+
return {
|
|
96
|
+
getSnapshot: function() {
|
|
97
|
+
return snapshot;
|
|
98
|
+
},
|
|
99
|
+
subscribe: function(listener) {
|
|
100
|
+
listeners.push(listener);
|
|
101
|
+
return function() {
|
|
102
|
+
var index = listeners.indexOf(listener);
|
|
103
|
+
if (index !== -1) listeners.splice(index, 1);
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
update: function(patch) {
|
|
107
|
+
snapshot = {
|
|
108
|
+
enabled: patch.enabled === void 0 ? snapshot.enabled : patch.enabled === true,
|
|
109
|
+
durMs: patch.durMs === void 0 ? snapshot.durMs : patch.durMs,
|
|
110
|
+
thinkMode: nextThinkMode(patch, snapshot.thinkMode),
|
|
111
|
+
splitThink: patch.splitThink === void 0 ? snapshot.splitThink : patch.splitThink === true,
|
|
112
|
+
stats: patch.stats === void 0 ? snapshot.stats : patch.stats
|
|
113
|
+
};
|
|
114
|
+
if (typeof localStorage !== "undefined") try {
|
|
115
|
+
localStorage.setItem(SETTINGS_KEY, JSON.stringify(snapshot));
|
|
116
|
+
} catch (err) {}
|
|
117
|
+
for (var i = 0; i < listeners.length; i++) listeners[i]();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
module.exports = {
|
|
122
|
+
createSettingsStore,
|
|
123
|
+
resolveThinkMode
|
|
124
|
+
};
|
|
125
|
+
}));
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/client/bridge.js
|
|
128
|
+
var require_bridge = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
129
|
+
var DSH_API = "/api/dsh-toolfold/settings";
|
|
130
|
+
function clampDur(value) {
|
|
131
|
+
var dur = Number(value);
|
|
132
|
+
return Number.isFinite(dur) ? Math.max(0, Math.min(2e3, Math.round(dur))) : void 0;
|
|
133
|
+
}
|
|
134
|
+
/** Merge one fully-resolved DSH section into the local store. */
|
|
135
|
+
function adoptSection(store, section) {
|
|
136
|
+
store.update({
|
|
137
|
+
enabled: section.enabled === void 0 ? void 0 : section.enabled === true,
|
|
138
|
+
durMs: clampDur(section.durMs),
|
|
139
|
+
thinkMode: section.thinkMode === void 0 ? void 0 : section.thinkMode,
|
|
140
|
+
keepThink: section.keepThink === void 0 ? void 0 : section.keepThink === true,
|
|
141
|
+
thinkAuto: section.thinkAuto === void 0 ? void 0 : section.thinkAuto === true,
|
|
142
|
+
splitThink: section.splitThink === void 0 ? void 0 : section.splitThink === true,
|
|
143
|
+
stats: section.stats === void 0 ? void 0 : section.stats === true
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function createSettingsBridge(ctx, store) {
|
|
147
|
+
var scope = null;
|
|
148
|
+
var scopeReady = false;
|
|
149
|
+
var routeOk = false;
|
|
150
|
+
var disposers = [];
|
|
151
|
+
var compatState = "unknown";
|
|
152
|
+
var compatVersion = null;
|
|
153
|
+
var compatWarned = false;
|
|
154
|
+
function setCompat(info) {
|
|
155
|
+
var state = info && (info.state === "ok" || info.state === "old" || info.state === "new") ? info.state : "unknown";
|
|
156
|
+
var version = info && typeof info.version === "string" ? info.version : null;
|
|
157
|
+
compatState = state;
|
|
158
|
+
compatVersion = version;
|
|
159
|
+
if (state !== "ok" && !compatWarned && typeof console !== "undefined" && typeof console.warn === "function") {
|
|
160
|
+
compatWarned = true;
|
|
161
|
+
console.warn("[dsh-toolfold] DSH 版本不匹配(当前 " + (version === null ? "未知" : version) + "):本插件支持 DSH >=0.1.2-rc.1 <0.1.3," + (state === "old" ? "当前版本过旧,请升级 DSH" : "当前版本过新,请等待插件更新") + "。");
|
|
162
|
+
}
|
|
163
|
+
store.update({});
|
|
164
|
+
}
|
|
165
|
+
var binder = null;
|
|
166
|
+
try {
|
|
167
|
+
if (typeof ctx.get === "function") binder = ctx.get("webUiSettings") || ctx.get("settingsScope") || null;
|
|
168
|
+
} catch (err) {
|
|
169
|
+
binder = null;
|
|
170
|
+
}
|
|
171
|
+
if (binder !== null && typeof binder.bind === "function") try {
|
|
172
|
+
scope = binder.bind({ namespace: "toolfold" });
|
|
173
|
+
} catch (err) {
|
|
174
|
+
scope = null;
|
|
175
|
+
}
|
|
176
|
+
if (scope !== null && typeof scope.subscribe === "function" && typeof scope.getSnapshot === "function") disposers.push(scope.subscribe(function() {
|
|
177
|
+
var snap = scope.getSnapshot();
|
|
178
|
+
if (snap === null || typeof snap !== "object") return;
|
|
179
|
+
if (snap.status === "ready" && snap.value !== null && typeof snap.value === "object") {
|
|
180
|
+
scopeReady = true;
|
|
181
|
+
routeOk = false;
|
|
182
|
+
adoptSection(store, snap.value);
|
|
183
|
+
} else if (snap.status === "unavailable") {
|
|
184
|
+
scopeReady = false;
|
|
185
|
+
routeLoad();
|
|
186
|
+
}
|
|
187
|
+
}));
|
|
188
|
+
function routeLoad() {
|
|
189
|
+
if (typeof fetch !== "function") return Promise.resolve(false);
|
|
190
|
+
return fetch(DSH_API, {
|
|
191
|
+
method: "GET",
|
|
192
|
+
cache: "no-store"
|
|
193
|
+
}).then(function(res) {
|
|
194
|
+
return res.ok ? res.json() : null;
|
|
195
|
+
}).then(function(body) {
|
|
196
|
+
if (body === null || body.ok !== true || body.value === null || typeof body.value !== "object") {
|
|
197
|
+
routeOk = false;
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
if (body.dsh !== null && typeof body.dsh === "object") setCompat(body.dsh);
|
|
201
|
+
if (scopeReady) return false;
|
|
202
|
+
routeOk = true;
|
|
203
|
+
adoptSection(store, body.value.value === void 0 ? body.value : body.value.value);
|
|
204
|
+
return true;
|
|
205
|
+
}).catch(function() {
|
|
206
|
+
routeOk = false;
|
|
207
|
+
return false;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function routeWrite(field, value, op) {
|
|
211
|
+
if (typeof fetch !== "function") return Promise.resolve(false);
|
|
212
|
+
return fetch(DSH_API, {
|
|
213
|
+
method: "POST",
|
|
214
|
+
cache: "no-store",
|
|
215
|
+
headers: { "content-type": "application/json" },
|
|
216
|
+
body: JSON.stringify(op === "unset" ? {
|
|
217
|
+
op: "unset",
|
|
218
|
+
field
|
|
219
|
+
} : {
|
|
220
|
+
op: "set",
|
|
221
|
+
field,
|
|
222
|
+
value
|
|
223
|
+
})
|
|
224
|
+
}).then(function(res) {
|
|
225
|
+
return res.ok ? res.json() : null;
|
|
226
|
+
}).then(function(body) {
|
|
227
|
+
if (body === null || body.ok !== true || body.value === null || typeof body.value !== "object") return false;
|
|
228
|
+
routeOk = true;
|
|
229
|
+
adoptSection(store, body.value.value === void 0 ? body.value : body.value.value);
|
|
230
|
+
return true;
|
|
231
|
+
}).catch(function() {
|
|
232
|
+
return false;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
/** Apply one field change: optimistic local update, then persist through the live tier. */
|
|
237
|
+
write: function(field, value) {
|
|
238
|
+
var patch = {};
|
|
239
|
+
patch[field] = value;
|
|
240
|
+
store.update(patch);
|
|
241
|
+
if (scopeReady && scope !== null && typeof scope.set === "function") scope.set(field, value).catch(function() {});
|
|
242
|
+
else routeWrite(field, value, "set");
|
|
243
|
+
},
|
|
244
|
+
unset: function(field) {
|
|
245
|
+
if (scopeReady && scope !== null && typeof scope.unset === "function") scope.unset(field).catch(function() {});
|
|
246
|
+
else routeWrite(field, void 0, "unset");
|
|
247
|
+
},
|
|
248
|
+
/** 'dsh' when a host-backed tier is live, 'local' otherwise. */
|
|
249
|
+
status: function() {
|
|
250
|
+
return scopeReady || routeOk ? "dsh" : "local";
|
|
251
|
+
},
|
|
252
|
+
/** Last host-reported DSH compatibility: { state, version }. */
|
|
253
|
+
compat: function() {
|
|
254
|
+
return {
|
|
255
|
+
state: compatState,
|
|
256
|
+
version: compatVersion
|
|
257
|
+
};
|
|
258
|
+
},
|
|
259
|
+
load: routeLoad,
|
|
260
|
+
dispose: function() {
|
|
261
|
+
for (var i = 0; i < disposers.length; i++) disposers[i]();
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
module.exports = { createSettingsBridge };
|
|
266
|
+
}));
|
|
267
|
+
//#endregion
|
|
268
|
+
//#region src/client/styles.js
|
|
269
|
+
var require_styles = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
270
|
+
module.exports = {
|
|
271
|
+
STYLE_ID: "dsh-toolfold/style",
|
|
272
|
+
CARD_STYLE_ID: "dsh-toolfold/card",
|
|
273
|
+
engineCss: [
|
|
274
|
+
"[data-chat-flow] [data-chat-flow-kind].ccxMerged{display:none}",
|
|
275
|
+
"[data-chat-flow] [data-chat-flow-kind=\"assistant-step\"].ccxEmpty{display:none}",
|
|
276
|
+
"[data-chat-flow]:not(.ccxKeepThink) [data-variant=\"think\"][data-state=\"ok\"]{display:none}",
|
|
277
|
+
"[data-chat-flow] .ccxWrapGone{display:none}",
|
|
278
|
+
"[data-chat-flow] .ccxBar{display:flex;align-items:center;gap:8px;box-sizing:border-box;width:100%;padding:0;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:13px;line-height:20px;cursor:pointer;text-align:left}",
|
|
279
|
+
"[data-chat-flow] .ccxBar:focus-visible{outline:2px solid var(--dsw-static-deepseek-500,#4d6bfe);outline-offset:1px}",
|
|
280
|
+
".ccxBarCall{flex:1 1 auto;min-width:0;display:flex;align-items:center;pointer-events:none;max-width:1400px;overflow:hidden;transition:max-width var(--ccx-dur,260ms) ease,opacity calc(var(--ccx-dur,260ms)*0.77) ease}",
|
|
281
|
+
"[data-chat-flow] .ccxBar.ccxExpanded .ccxBarCall{max-width:0;opacity:0}",
|
|
282
|
+
".ccxBarCall > *{flex:1 1 auto;min-width:0;width:100%}",
|
|
283
|
+
".ccxBarIcon{flex:none;font-size:10px;line-height:20px;opacity:.75;transition:transform var(--ccx-dur,240ms) ease}",
|
|
284
|
+
".ccxBarLabel{flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:transform var(--ccx-dur,240ms) ease}",
|
|
285
|
+
"@keyframes ccxFall{from{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translateY(0)}}",
|
|
286
|
+
"[data-chat-flow] [data-chat-flow-kind].ccxFalling{animation:ccxFall var(--ccx-dur,240ms) cubic-bezier(0.34,1.56,0.64,1) both;animation-delay:calc(var(--ccx-i,0)*var(--ccx-step,45ms))}",
|
|
287
|
+
"@keyframes ccxRise{from{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-20px)}}",
|
|
288
|
+
"[data-chat-flow] [data-chat-flow-kind].ccxCollapsing{animation:ccxRise var(--ccx-dur,240ms) cubic-bezier(0,0,0.58,1) both;animation-delay:calc(var(--ccx-i,0)*var(--ccx-step,45ms))}",
|
|
289
|
+
"@media (prefers-reduced-motion:reduce){[data-chat-flow] [data-chat-flow-kind].ccxFalling,[data-chat-flow] [data-chat-flow-kind].ccxCollapsing{animation:none}.ccxBarCall,.ccxBarIcon,.ccxBarLabel{transition:none}}"
|
|
290
|
+
].join(""),
|
|
291
|
+
cardCss: [
|
|
292
|
+
".ccxCard{list-style:none;border:1px solid var(--dsw-alias-border-l2,#e4e4e7);border-radius:12px;background:var(--dsw-alias-bg-layer-3,#fff);transition:border-color .16s,background .16s}",
|
|
293
|
+
".ccxCard:hover{border-color:var(--dsw-alias-label-dimmed,#a1a1aa)}",
|
|
294
|
+
".ccxCardOpen{background:var(--dsw-alias-bg-layer-2,#fafafa);border-color:var(--dsw-alias-label-dimmed,#a1a1aa)}",
|
|
295
|
+
".ccxHeader{width:100%;appearance:none;border:0;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;display:flex;align-items:center;gap:12px;padding:14px 16px;border-radius:12px}",
|
|
296
|
+
".ccxHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:-2px}",
|
|
297
|
+
".ccxHeadText{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}",
|
|
298
|
+
".ccxName{font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary,#222)}",
|
|
299
|
+
".ccxDescription{font-size:13px;line-height:1.5;color:var(--dsw-alias-label-tertiary,#888)}",
|
|
300
|
+
".ccxChevron{flex:none;font-size:14px;line-height:1;color:var(--dsw-alias-label-tertiary,#888);transition:transform .16s}",
|
|
301
|
+
".ccxChevronOpen{transform:rotate(180deg)}",
|
|
302
|
+
".ccxBody{border-top:1px solid var(--dsw-alias-border-l2,#e4e4e7);margin:0 16px;padding:4px 0 8px}",
|
|
303
|
+
".ccxField{display:flex;flex-direction:column;gap:6px;padding:12px 0}",
|
|
304
|
+
".ccxField + .ccxField{border-top:1px solid var(--dsw-alias-border-l2,#e4e4e7)}",
|
|
305
|
+
".ccxFieldHead{display:flex;align-items:center;gap:8px}",
|
|
306
|
+
".ccxFieldLabel{flex:1;min-width:0;font-size:13px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary,#222)}",
|
|
307
|
+
".ccxFieldHint{margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary,#888)}",
|
|
308
|
+
".ccxBadge{flex:none;border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px;white-space:nowrap;font-weight:500;background:var(--dsw-alias-bg-module-platform,#f0f0f2);color:var(--dsw-alias-label-secondary,#666)}",
|
|
309
|
+
".ccxRange{width:100%;height:34px;margin:0;accent-color:var(--dsw-alias-brand-primary,#4d6bfe);cursor:pointer}",
|
|
310
|
+
".ccxToggle{width:16px;height:16px;margin:0;flex:none;accent-color:var(--dsw-alias-brand-primary,#4d6bfe);cursor:pointer}",
|
|
311
|
+
".ccxSelect{flex:none;max-width:60%;font:inherit;font-size:13px;color:var(--dsw-alias-label-primary,#222);background:var(--dsw-alias-bg-layer-3,#fff);border:1px solid var(--dsw-alias-border-l2,#e4e4e7);border-radius:8px;padding:5px 8px;cursor:pointer}",
|
|
312
|
+
".ccxSelect:disabled{opacity:.5;cursor:default}",
|
|
313
|
+
".ccxSelect:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:1px}",
|
|
314
|
+
".ccxStatRow{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:6px 0;font-size:12px;line-height:1.5}",
|
|
315
|
+
".ccxStatValue{flex:none;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#666)}",
|
|
316
|
+
".ccxWarn{flex:none;position:relative;font-size:15px;line-height:1;cursor:help;color:#d97706}",
|
|
317
|
+
".ccxWarn::after{content:attr(data-tip);position:absolute;top:calc(100% + 8px);right:-6px;width:250px;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-bg-layer-3,#fff);border:1px solid var(--dsw-alias-border-l2,#e4e4e7);color:var(--dsw-alias-label-primary,#222);font-size:12px;font-weight:400;line-height:1.5;text-align:left;white-space:pre-line;box-shadow:0 4px 16px rgba(0,0,0,.14);opacity:0;visibility:hidden;pointer-events:none;z-index:60;transition:opacity .12s ease,visibility .12s ease}",
|
|
318
|
+
".ccxWarn:hover::after{opacity:1;visibility:visible}"
|
|
319
|
+
].join("")
|
|
320
|
+
};
|
|
321
|
+
}));
|
|
322
|
+
//#endregion
|
|
323
|
+
//#region src/client/engine.js
|
|
324
|
+
var require_engine = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
325
|
+
const { STYLE_ID, engineCss } = require_styles();
|
|
326
|
+
/**
|
|
327
|
+
* Install the folding engine on a document.
|
|
328
|
+
* @param doc - the browser document of the Web GUI page.
|
|
329
|
+
* @param settings - the shared settings store (getSnapshot/subscribe).
|
|
330
|
+
* @returns a disposer that removes every side effect.
|
|
331
|
+
*/
|
|
332
|
+
function installCollapseEngine(doc, settings, timers) {
|
|
333
|
+
var FLOW_SELECTOR = "[data-chat-flow]";
|
|
334
|
+
var BADGE_ATTR = "data-ccx-badge";
|
|
335
|
+
var MERGED_CLASS = "ccxMerged";
|
|
336
|
+
var EMPTY_CLASS = "ccxEmpty";
|
|
337
|
+
var FALLING_CLASS = "ccxFalling";
|
|
338
|
+
var COLLAPSING_CLASS = "ccxCollapsing";
|
|
339
|
+
var KEEP_CLASS = "ccxKeepThink";
|
|
340
|
+
/** Hidden settled-think WRAPPER: the product leaves the think's flex
|
|
341
|
+
* item in `.hWmORq_body` (a `gap:16px` column) even when the think root
|
|
342
|
+
* is display:none, so the wrapper must be hidden too or a 16px blank
|
|
343
|
+
* band survives under the folded think, above the row's answer text. */
|
|
344
|
+
var WRAP_GONE_CLASS = "ccxWrapGone";
|
|
345
|
+
var styleTag = null;
|
|
346
|
+
var adoptedStyle = false;
|
|
347
|
+
/** flow element -> observation mode ('light' | 'full') */
|
|
348
|
+
var flows = /* @__PURE__ */ new Map();
|
|
349
|
+
/** flow element -> its attached MutationObserver (absent while hidden) */
|
|
350
|
+
var flowObservers = /* @__PURE__ */ new Map();
|
|
351
|
+
/** parent element -> childList-only observer (flow mounts/unmounts) */
|
|
352
|
+
var parentObservers = /* @__PURE__ */ new Map();
|
|
353
|
+
/** flow element -> Set of expanded run node keys */
|
|
354
|
+
var expanded = /* @__PURE__ */ new Map();
|
|
355
|
+
/** flow element -> Map<run head key, bar element> */
|
|
356
|
+
var barsByFlow = /* @__PURE__ */ new Map();
|
|
357
|
+
/** flow element -> Set of run head keys mid collapse animation */
|
|
358
|
+
var collapsing = /* @__PURE__ */ new Map();
|
|
359
|
+
/** flows whose DOM changed since the last pass (pending re-pass) */
|
|
360
|
+
var dirtyFlows = /* @__PURE__ */ new Set();
|
|
361
|
+
/** per-flow reason for the pending pass: content | think | rows */
|
|
362
|
+
var flowReasons = /* @__PURE__ */ new Map();
|
|
363
|
+
/** flow element -> Set of tool rows touched by content mutations */
|
|
364
|
+
var contentRowsByFlow = /* @__PURE__ */ new Map();
|
|
365
|
+
/** per-flow counter for synthetic run keys (rows lacking a node key) */
|
|
366
|
+
var syntheticKeys = /* @__PURE__ */ new Map();
|
|
367
|
+
/** flows may have appeared or disappeared since the last pass */
|
|
368
|
+
var structureDirty = true;
|
|
369
|
+
/** observers live only while the tab is visible */
|
|
370
|
+
var active = true;
|
|
371
|
+
/** slow safety rescan while visible (catches remounted flows) */
|
|
372
|
+
var rescanTimer = null;
|
|
373
|
+
/** rescan delay, backing off while nothing changes (3s..10s) */
|
|
374
|
+
var rescanDelay = 3e3;
|
|
375
|
+
var rafId = 0;
|
|
376
|
+
var pending = false;
|
|
377
|
+
var disposed = false;
|
|
378
|
+
var offSettings = null;
|
|
379
|
+
/** deferred boot init (one frame after activation) */
|
|
380
|
+
var booted = false;
|
|
381
|
+
var bootRaf = 0;
|
|
382
|
+
/** cached MediaQueryList for prefers-reduced-motion */
|
|
383
|
+
var motionMql = null;
|
|
384
|
+
/** cache of confirmed tool-call rows (closestToolCall positive hits) */
|
|
385
|
+
var toolRowCache = /* @__PURE__ */ new WeakMap();
|
|
386
|
+
/**
|
|
387
|
+
* Assistant rows currently hidden as emptied think rows (carry
|
|
388
|
+
* EMPTY_CLASS). Kept so the single-text-node streaming fast path can
|
|
389
|
+
* notice answer text arriving inside one without walking the DOM on
|
|
390
|
+
* every ordinary streamed token.
|
|
391
|
+
*/
|
|
392
|
+
var hiddenRows = /* @__PURE__ */ new WeakSet();
|
|
393
|
+
/** Count of rows in `hiddenRows` (a WeakSet exposes no size). */
|
|
394
|
+
var hiddenCount = 0;
|
|
395
|
+
/** opt-in live cost stats (settings card); created at engine init */
|
|
396
|
+
var statsObj = null;
|
|
397
|
+
function statsBegin() {
|
|
398
|
+
if (statsObj === null || !statsObj.enabled) return null;
|
|
399
|
+
return performance.now();
|
|
400
|
+
}
|
|
401
|
+
function statsEnd(key, count, t0) {
|
|
402
|
+
if (t0 === null) return;
|
|
403
|
+
statsObj[key] += count;
|
|
404
|
+
statsObj[key + "Ms"] += performance.now() - t0;
|
|
405
|
+
}
|
|
406
|
+
function ensureStats() {
|
|
407
|
+
if (statsObj !== null) return statsObj;
|
|
408
|
+
statsObj = {
|
|
409
|
+
enabled: settings.getSnapshot().stats === true,
|
|
410
|
+
start: Date.now(),
|
|
411
|
+
obs: 0,
|
|
412
|
+
obsMs: 0,
|
|
413
|
+
refresh: 0,
|
|
414
|
+
refreshMs: 0,
|
|
415
|
+
pass: 0,
|
|
416
|
+
passMs: 0,
|
|
417
|
+
scan: 0,
|
|
418
|
+
scanMs: 0,
|
|
419
|
+
clone: 0,
|
|
420
|
+
cloneMs: 0,
|
|
421
|
+
skip: 0
|
|
422
|
+
};
|
|
423
|
+
return statsObj;
|
|
424
|
+
}
|
|
425
|
+
function reducedMotion() {
|
|
426
|
+
if (motionMql === null) motionMql = typeof matchMedia === "function" ? matchMedia("(prefers-reduced-motion: reduce)") : null;
|
|
427
|
+
return motionMql !== null && motionMql.matches;
|
|
428
|
+
}
|
|
429
|
+
function durMs() {
|
|
430
|
+
var value = settings.getSnapshot().durMs;
|
|
431
|
+
return reducedMotion() ? 0 : value;
|
|
432
|
+
}
|
|
433
|
+
function stepMs() {
|
|
434
|
+
var value = settings.getSnapshot().durMs;
|
|
435
|
+
return reducedMotion() ? 0 : Math.max(0, Math.min(60, Math.round(value * 45 / 240)));
|
|
436
|
+
}
|
|
437
|
+
/** Push the current durations into --ccx-dur / --ccx-step. */
|
|
438
|
+
function applySettings() {
|
|
439
|
+
var style = doc.documentElement.style;
|
|
440
|
+
style.setProperty("--ccx-dur", durMs() + "ms");
|
|
441
|
+
style.setProperty("--ccx-step", stepMs() + "ms");
|
|
442
|
+
}
|
|
443
|
+
function insertStyle() {
|
|
444
|
+
var existing = doc.querySelector("style[data-plugin-css=\"" + STYLE_ID + "\"]");
|
|
445
|
+
if (existing !== null) {
|
|
446
|
+
styleTag = existing;
|
|
447
|
+
adoptedStyle = true;
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
var tag = doc.createElement("style");
|
|
451
|
+
tag.setAttribute("data-plugin", "dsh-toolfold");
|
|
452
|
+
tag.setAttribute("data-plugin-css", STYLE_ID);
|
|
453
|
+
tag.textContent = engineCss;
|
|
454
|
+
doc.head.appendChild(tag);
|
|
455
|
+
styleTag = tag;
|
|
456
|
+
adoptedStyle = false;
|
|
457
|
+
}
|
|
458
|
+
var REASON_RANK = {
|
|
459
|
+
content: 1,
|
|
460
|
+
think: 2,
|
|
461
|
+
rows: 3
|
|
462
|
+
};
|
|
463
|
+
function markDirty(flow, reason) {
|
|
464
|
+
if (disposed) return;
|
|
465
|
+
var prev = flowReasons.get(flow);
|
|
466
|
+
if (prev === void 0 || REASON_RANK[prev] < REASON_RANK[reason]) flowReasons.set(flow, reason);
|
|
467
|
+
dirtyFlows.add(flow);
|
|
468
|
+
if (rescanDelay > 3e3) {
|
|
469
|
+
rescanDelay = 3e3;
|
|
470
|
+
if (rescanTimer !== null) {
|
|
471
|
+
timers.cancel(rescanTimer);
|
|
472
|
+
rescanTimer = null;
|
|
473
|
+
scheduleNextRescan();
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
schedule();
|
|
477
|
+
}
|
|
478
|
+
function schedule() {
|
|
479
|
+
if (pending || disposed) return;
|
|
480
|
+
pending = true;
|
|
481
|
+
if (typeof requestAnimationFrame === "function") rafId = requestAnimationFrame(function() {
|
|
482
|
+
pending = false;
|
|
483
|
+
refresh();
|
|
484
|
+
});
|
|
485
|
+
else {
|
|
486
|
+
pending = false;
|
|
487
|
+
refresh();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Whether an added/removed node can change folding outcomes: a row
|
|
492
|
+
* (`[data-chat-flow-kind]`) or a think block (`[data-variant="think"]`).
|
|
493
|
+
*/
|
|
494
|
+
function nodeHasMarker(node) {
|
|
495
|
+
if (node.nodeType !== 1) return false;
|
|
496
|
+
if (typeof node.matches === "function" && node.matches("[data-chat-flow-kind],[data-variant=\"think\"]")) return true;
|
|
497
|
+
if (typeof node.querySelector === "function" && node.querySelector("[data-chat-flow-kind],[data-variant=\"think\"]") !== null) return true;
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Nearest ancestor (or self) that is a tool-call row. Plain attribute
|
|
502
|
+
* walk — the MutationObserver hot path never invokes the selector
|
|
503
|
+
* engine. The RESULT is cached on the start element (both positive and
|
|
504
|
+
* negative), so streaming — which keeps mutating the same containers —
|
|
505
|
+
* amortizes the walk to O(1) per container after the first event.
|
|
506
|
+
* Staleness would require reparenting a container into a tool row,
|
|
507
|
+
* which the product never does; worst case one missed content refresh.
|
|
508
|
+
*/
|
|
509
|
+
function closestToolCall(el) {
|
|
510
|
+
var start = el.nodeType === 1 ? el : el.parentElement;
|
|
511
|
+
if (start === null) return null;
|
|
512
|
+
var hit = toolRowCache.get(start);
|
|
513
|
+
if (hit !== void 0) return hit;
|
|
514
|
+
var node = start;
|
|
515
|
+
while (node !== null && node.nodeType === 1) {
|
|
516
|
+
var cached = toolRowCache.get(node);
|
|
517
|
+
if (cached !== void 0) {
|
|
518
|
+
node = cached;
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
if (node.getAttribute("data-chat-flow-kind") === "tool-call") break;
|
|
522
|
+
node = node.parentElement;
|
|
523
|
+
}
|
|
524
|
+
toolRowCache.set(start, node);
|
|
525
|
+
return node;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Nearest flow row whose content just changed while the row itself is
|
|
529
|
+
* hidden (EMPTY_CLASS), or null. Mutations inside a think block never
|
|
530
|
+
* count: the product may keep writing reasoning tokens into a
|
|
531
|
+
* just-settled, display:none think, and those must not resurrect the
|
|
532
|
+
* row. Plain attribute walk — no selector engine on the hot path.
|
|
533
|
+
*/
|
|
534
|
+
function hiddenEmptyRow(el) {
|
|
535
|
+
var node = el.nodeType === 1 ? el : el.parentElement;
|
|
536
|
+
while (node !== null && node.nodeType === 1) {
|
|
537
|
+
if (node.getAttribute("data-variant") === "think") return null;
|
|
538
|
+
if (node.hasAttribute("data-chat-flow-kind")) return node.classList.contains(EMPTY_CLASS) ? node : null;
|
|
539
|
+
node = node.parentElement;
|
|
540
|
+
}
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Classify a mutation batch by what it can change:
|
|
545
|
+
* - 'rows': row-level add/remove/reorder, or added/removed nodes that
|
|
546
|
+
* are or contain rows / think blocks — the merge must be rebuilt;
|
|
547
|
+
* - 'think': a `data-state` flip on a think element (settled thinking
|
|
548
|
+
* starts hiding, empties its assistant row, and in merge mode stops
|
|
549
|
+
* separating runs) — the merge must be rebuilt;
|
|
550
|
+
* - 'content': a mutation inside a tool-call row (result streaming) —
|
|
551
|
+
* only that run's bar clone can be stale, no merge rebuild needed;
|
|
552
|
+
* - null: content streaming inside any other row (think text tokens,
|
|
553
|
+
* markdown spans) — cannot change any folding outcome.
|
|
554
|
+
* The single appended-text-node case short-circuits with zero DOM
|
|
555
|
+
* walks, so agent/think streaming costs nothing at all. Content
|
|
556
|
+
* mutations report the affected tool row(s) through `collect`, so the
|
|
557
|
+
* refresh touches only the bar of the run whose LAST row changed.
|
|
558
|
+
*/
|
|
559
|
+
function mutationReason(records, flow, collect) {
|
|
560
|
+
if (records.length === 1) {
|
|
561
|
+
var only = records[0];
|
|
562
|
+
if (only.type === "childList" && only.removedNodes.length === 0 && only.addedNodes.length === 1 && only.addedNodes[0].nodeType === 3) {
|
|
563
|
+
if (hiddenCount > 0) {
|
|
564
|
+
var resurrect = hiddenEmptyRow(only.addedNodes[0]);
|
|
565
|
+
if (resurrect !== null) {
|
|
566
|
+
rowInfo.delete(resurrect);
|
|
567
|
+
return "rows";
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
var best = 0;
|
|
574
|
+
for (var i = 0; i < records.length; i++) {
|
|
575
|
+
var record = records[i];
|
|
576
|
+
if (record.type === "attributes") {
|
|
577
|
+
if (record.attributeName === "hidden" || record.attributeName !== null && record.attributeName.indexOf("data-turn-process-") === 0) {
|
|
578
|
+
if (best < 3) best = 3;
|
|
579
|
+
} else if (record.attributeName === "data-state" && record.target !== null && record.target.nodeType === 1 && typeof record.target.matches === "function" && record.target.matches("[data-variant=\"think\"]")) {
|
|
580
|
+
if (best < 2) best = 2;
|
|
581
|
+
}
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
if (record.type === "characterData") {
|
|
585
|
+
var toolRow = closestToolCall(record.target);
|
|
586
|
+
if (toolRow !== null) {
|
|
587
|
+
if (collect !== void 0) collect(toolRow);
|
|
588
|
+
if (best < 1) best = 1;
|
|
589
|
+
} else {
|
|
590
|
+
var hiddenRow = hiddenEmptyRow(record.target);
|
|
591
|
+
if (hiddenRow !== null) {
|
|
592
|
+
rowInfo.delete(hiddenRow);
|
|
593
|
+
if (best < 3) best = 3;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
if (record.type !== "childList") continue;
|
|
599
|
+
var target = record.target;
|
|
600
|
+
if (target === flow) {
|
|
601
|
+
best = 3;
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
if (target !== null && target.nodeType === 1) {
|
|
605
|
+
var row = closestToolCall(target);
|
|
606
|
+
if (row !== null) {
|
|
607
|
+
if (collect !== void 0) collect(row);
|
|
608
|
+
if (best < 1) best = 1;
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
var hiddenRow = hiddenEmptyRow(target);
|
|
612
|
+
if (hiddenRow !== null) {
|
|
613
|
+
rowInfo.delete(hiddenRow);
|
|
614
|
+
if (best < 3) best = 3;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
var lists = [record.addedNodes, record.removedNodes];
|
|
618
|
+
for (var l = 0; l < lists.length && best < 3; l++) {
|
|
619
|
+
var nodes = lists[l];
|
|
620
|
+
for (var j = 0; j < nodes.length; j++) if (nodeHasMarker(nodes[j])) {
|
|
621
|
+
best = 3;
|
|
622
|
+
break;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
if (best === 0) return null;
|
|
627
|
+
if (best === 1) return "content";
|
|
628
|
+
if (best === 2) return "think";
|
|
629
|
+
return "rows";
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Attach (or replace) the per-flow observer for a mode. 'light' is a
|
|
633
|
+
* childList-only observer on the flow's direct children — the only
|
|
634
|
+
* events that can change folding are row adds/removes/reorders, so a
|
|
635
|
+
* pure-chat flow costs nothing while text streams inside its rows.
|
|
636
|
+
* 'full' adds subtree childList and `data-state` attribute coverage,
|
|
637
|
+
* which foldable content (tool cards, think blocks) actually needs.
|
|
638
|
+
*/
|
|
639
|
+
function attachFlowObserver(flow, mode) {
|
|
640
|
+
var existing = flowObservers.get(flow);
|
|
641
|
+
if (existing !== null && existing !== void 0) existing.disconnect();
|
|
642
|
+
if (typeof MutationObserver !== "function") {
|
|
643
|
+
flowObservers.set(flow, null);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
var observer;
|
|
647
|
+
if (mode === "light") {
|
|
648
|
+
observer = new MutationObserver(function() {
|
|
649
|
+
var t0 = statsBegin();
|
|
650
|
+
markDirty(flow, "rows");
|
|
651
|
+
statsEnd("obs", 1, t0);
|
|
652
|
+
});
|
|
653
|
+
observer.observe(flow, { childList: true });
|
|
654
|
+
} else {
|
|
655
|
+
observer = new MutationObserver(function(records) {
|
|
656
|
+
var t0 = statsBegin();
|
|
657
|
+
var contentSet = void 0;
|
|
658
|
+
var reason = mutationReason(records, flow, function(row) {
|
|
659
|
+
if (contentSet === void 0) {
|
|
660
|
+
contentSet = contentRowsByFlow.get(flow);
|
|
661
|
+
if (contentSet === void 0) {
|
|
662
|
+
contentSet = /* @__PURE__ */ new Set();
|
|
663
|
+
contentRowsByFlow.set(flow, contentSet);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
contentSet.add(row);
|
|
667
|
+
});
|
|
668
|
+
if (reason !== null) markDirty(flow, reason);
|
|
669
|
+
else if (statsObj !== null && statsObj.enabled) statsObj.skip += 1;
|
|
670
|
+
statsEnd("obs", 1, t0);
|
|
671
|
+
});
|
|
672
|
+
observer.observe(flow, {
|
|
673
|
+
childList: true,
|
|
674
|
+
subtree: true,
|
|
675
|
+
attributes: true,
|
|
676
|
+
attributeFilter: [
|
|
677
|
+
"data-state",
|
|
678
|
+
"hidden",
|
|
679
|
+
"data-turn-process-hidden",
|
|
680
|
+
"data-turn-process-member",
|
|
681
|
+
"data-turn-process-answer"
|
|
682
|
+
]
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
flowObservers.set(flow, observer);
|
|
686
|
+
}
|
|
687
|
+
/** Switch a flow's observation mode (attaches only while visible). */
|
|
688
|
+
function setFlowMode(flow, mode) {
|
|
689
|
+
if (flows.get(flow) === mode) return;
|
|
690
|
+
flows.set(flow, mode);
|
|
691
|
+
if (active) attachFlowObserver(flow, mode);
|
|
692
|
+
}
|
|
693
|
+
function discover() {
|
|
694
|
+
var list = doc.querySelectorAll(FLOW_SELECTOR);
|
|
695
|
+
for (var i = 0; i < list.length; i++) {
|
|
696
|
+
var flow = list[i];
|
|
697
|
+
if (flows.has(flow)) continue;
|
|
698
|
+
var needFull = typeof flow.querySelector === "function" && flow.querySelector("[data-chat-flow-kind=\"tool-call\"],[data-variant=\"think\"]") !== null;
|
|
699
|
+
flows.set(flow, needFull ? "full" : "light");
|
|
700
|
+
expanded.set(flow, /* @__PURE__ */ new Set());
|
|
701
|
+
barsByFlow.set(flow, /* @__PURE__ */ new Map());
|
|
702
|
+
collapsing.set(flow, /* @__PURE__ */ new Set());
|
|
703
|
+
if (active) attachFlowObserver(flow, needFull ? "full" : "light");
|
|
704
|
+
markDirty(flow, "rows");
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Flow mount/unmount detection WITHOUT a page-wide observer: each
|
|
709
|
+
* known flow's parent gets a childList-only observer (direct children
|
|
710
|
+
* only, no subtree), so it fires only when a sibling flow appears or
|
|
711
|
+
* disappears — never on chat churn. Parents that appeared while the
|
|
712
|
+
* engine was idle are covered by the slow visible-only rescan.
|
|
713
|
+
*/
|
|
714
|
+
function observeParents() {
|
|
715
|
+
if (!active || typeof MutationObserver !== "function") return;
|
|
716
|
+
flows.forEach(function(mode, flow) {
|
|
717
|
+
var parent = flow.parentElement;
|
|
718
|
+
if (parent === null || parentObservers.has(parent)) return;
|
|
719
|
+
var observer = new MutationObserver(function(records) {
|
|
720
|
+
var t0 = statsBegin();
|
|
721
|
+
var relevant = false;
|
|
722
|
+
for (var i = 0; i < records.length && !relevant; i++) {
|
|
723
|
+
var record = records[i];
|
|
724
|
+
if (record.type !== "childList") continue;
|
|
725
|
+
var lists = [record.addedNodes, record.removedNodes];
|
|
726
|
+
for (var l = 0; l < lists.length && !relevant; l++) {
|
|
727
|
+
var nodes = lists[l];
|
|
728
|
+
for (var j = 0; j < nodes.length; j++) {
|
|
729
|
+
var node = nodes[j];
|
|
730
|
+
if (node.nodeType !== 1 || typeof node.matches !== "function") continue;
|
|
731
|
+
if (node.matches(FLOW_SELECTOR) || typeof node.querySelector === "function" && node.querySelector(FLOW_SELECTOR) !== null) {
|
|
732
|
+
relevant = true;
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (relevant) {
|
|
739
|
+
structureDirty = true;
|
|
740
|
+
schedule();
|
|
741
|
+
}
|
|
742
|
+
statsEnd("obs", 1, t0);
|
|
743
|
+
});
|
|
744
|
+
observer.observe(parent, { childList: true });
|
|
745
|
+
parentObservers.set(parent, observer);
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
function rescanTick() {
|
|
749
|
+
rescanTimer = null;
|
|
750
|
+
if (disposed || !active || doc.hidden) return;
|
|
751
|
+
var t0 = statsBegin();
|
|
752
|
+
var before = flows.size;
|
|
753
|
+
discover();
|
|
754
|
+
pruneFlows();
|
|
755
|
+
observeParents();
|
|
756
|
+
if (flows.size === before) {
|
|
757
|
+
if (flows.size > 0) rescanDelay = Math.min(rescanDelay * 2, 1e4);
|
|
758
|
+
} else rescanDelay = 3e3;
|
|
759
|
+
statsEnd("scan", 1, t0);
|
|
760
|
+
scheduleNextRescan();
|
|
761
|
+
}
|
|
762
|
+
function scheduleNextRescan() {
|
|
763
|
+
if (rescanTimer !== null) return;
|
|
764
|
+
rescanTimer = timers.after(rescanTick, rescanDelay);
|
|
765
|
+
}
|
|
766
|
+
function startRescan() {
|
|
767
|
+
rescanDelay = 3e3;
|
|
768
|
+
scheduleNextRescan();
|
|
769
|
+
}
|
|
770
|
+
function stopRescan() {
|
|
771
|
+
if (rescanTimer !== null) {
|
|
772
|
+
timers.cancel(rescanTimer);
|
|
773
|
+
rescanTimer = null;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
function disconnectObservers() {
|
|
777
|
+
flowObservers.forEach(function(observer) {
|
|
778
|
+
if (observer !== null && observer !== void 0) observer.disconnect();
|
|
779
|
+
});
|
|
780
|
+
flowObservers.clear();
|
|
781
|
+
parentObservers.forEach(function(observer) {
|
|
782
|
+
observer.disconnect();
|
|
783
|
+
});
|
|
784
|
+
parentObservers.clear();
|
|
785
|
+
}
|
|
786
|
+
function onVisibility() {
|
|
787
|
+
setActive(!doc.hidden);
|
|
788
|
+
}
|
|
789
|
+
function setActive(on) {
|
|
790
|
+
if (on === active) return;
|
|
791
|
+
active = on;
|
|
792
|
+
if (on) {
|
|
793
|
+
if (!booted) {
|
|
794
|
+
bootInit();
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
flows.forEach(function(mode, flow) {
|
|
798
|
+
if (doc.contains(flow)) attachFlowObserver(flow, mode);
|
|
799
|
+
});
|
|
800
|
+
observeParents();
|
|
801
|
+
startRescan();
|
|
802
|
+
structureDirty = true;
|
|
803
|
+
schedule();
|
|
804
|
+
} else {
|
|
805
|
+
stopRescan();
|
|
806
|
+
disconnectObservers();
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
function rowList(flow) {
|
|
810
|
+
var rows = [];
|
|
811
|
+
var children = flow.children;
|
|
812
|
+
for (var i = 0; i < children.length; i++) {
|
|
813
|
+
var el = children[i];
|
|
814
|
+
if (el.nodeType === 1 && el.hasAttribute("data-chat-flow-kind") && !productHiddenRow(el)) rows.push(el);
|
|
815
|
+
}
|
|
816
|
+
return rows;
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Whether the product itself currently hides this flow row. DSH's
|
|
820
|
+
* completed-Turn transcript view "Compact" (对话信息折叠) keeps every row
|
|
821
|
+
* of a folded process window in the DOM but marks it `hidden` (the
|
|
822
|
+
* searchable-hidden attribute, so browser find can still reveal it).
|
|
823
|
+
* Rows the product hides must be invisible to folding — no runs, no
|
|
824
|
+
* bars, no empty-row work — so nothing of ours can survive as an
|
|
825
|
+
* orphaned element "outside" the product's own fold.
|
|
826
|
+
*/
|
|
827
|
+
function productHiddenRow(el) {
|
|
828
|
+
return el !== null && el.nodeType === 1 && el.hasAttribute("hidden");
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Whether the product's official completed-Turn fold (transcript view
|
|
832
|
+
* "Compact") is active for this flow: any row tagged as a process
|
|
833
|
+
* member or compacted answer, or a turn-process chip carrying text.
|
|
834
|
+
* The engine uses it for the "思考自动跟随官方折叠" rule: with the
|
|
835
|
+
* official fold active, the whole big block is already collapsed, so
|
|
836
|
+
* thinking content is preserved by default (it should not silently
|
|
837
|
+
* vanish under the fold); without the official fold, thinking takes
|
|
838
|
+
* space and the legacy default hides settled thinking.
|
|
839
|
+
*/
|
|
840
|
+
function flowHasOfficialFold(flow) {
|
|
841
|
+
if (flow === null || typeof flow.querySelector !== "function") return false;
|
|
842
|
+
if (flow.querySelector("[data-turn-process-member],[data-turn-process-answer]") !== null) return true;
|
|
843
|
+
var chips = flow.querySelectorAll("[data-chat-flow-kind=\"turn-process\"]");
|
|
844
|
+
for (var i = 0; i < chips.length; i++) if (chips[i].textContent.trim() !== "") return true;
|
|
845
|
+
return false;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Resolve the canonical think mode for a snapshot. An explicitly set,
|
|
849
|
+
* valid thinkMode wins; otherwise derive it from the deprecated
|
|
850
|
+
* thinkAuto / keepThink keys so pre-merge preferences survive; default
|
|
851
|
+
* 'auto'. Total — never throws. (Host twin: resolveThinkMode in
|
|
852
|
+
* src/host/index.js. Snapshots from the store are already normalized,
|
|
853
|
+
* so the legacy branch is belt-and-braces only.)
|
|
854
|
+
*/
|
|
855
|
+
function resolveThinkMode(snap) {
|
|
856
|
+
var mode = snap !== void 0 && snap !== null ? snap.thinkMode : void 0;
|
|
857
|
+
if (mode === "keep" || mode === "hide" || mode === "auto") return mode;
|
|
858
|
+
if (snap !== void 0 && snap !== null && snap.thinkAuto === false) return snap.keepThink === true ? "keep" : "hide";
|
|
859
|
+
return "auto";
|
|
860
|
+
}
|
|
861
|
+
/** Effective keep-think flag for one flow (see flowHasOfficialFold). */
|
|
862
|
+
function effectiveKeepThink(flow) {
|
|
863
|
+
var mode = resolveThinkMode(settings.getSnapshot());
|
|
864
|
+
if (mode === "keep") return true;
|
|
865
|
+
if (mode === "hide") return false;
|
|
866
|
+
return flowHasOfficialFold(flow);
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Per-row assessment of think-only content, cached by a signature of
|
|
870
|
+
* the row's think blocks (count + data-states + keepThink + splitThink
|
|
871
|
+
* flags). Reassessed only when the signature changes; the check is
|
|
872
|
+
* purely structural, so steady-state passes perform zero layout reads.
|
|
873
|
+
*
|
|
874
|
+
* In split mode (default) a row that only settled Think rows occupy
|
|
875
|
+
* ENDS the run it follows — completed thinking separates runs, it
|
|
876
|
+
* never merges across them. In merge mode it is transparent: it folds
|
|
877
|
+
* WITH the surrounding tool calls. Either way it is empty (hidden)
|
|
878
|
+
* when think is not preserved; with "保留思考" on it stays visible —
|
|
879
|
+
* between the bars in split mode, inside the run on expand in merge
|
|
880
|
+
* mode.
|
|
881
|
+
*/
|
|
882
|
+
var rowInfo = /* @__PURE__ */ new WeakMap();
|
|
883
|
+
var NEUTRAL_ASSESSMENT = {
|
|
884
|
+
sig: "",
|
|
885
|
+
thinks: [],
|
|
886
|
+
transparent: false,
|
|
887
|
+
empty: false
|
|
888
|
+
};
|
|
889
|
+
function thinkSig(thinks, keepThink, splitThink) {
|
|
890
|
+
var sig = (keepThink ? "1" : "0") + (splitThink ? "1" : "0");
|
|
891
|
+
for (var i = 0; i < thinks.length; i++) sig += ":" + (thinks[i].getAttribute("data-state") || "");
|
|
892
|
+
return sig;
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Whether an element contains nothing but think blocks and empty
|
|
896
|
+
* wrappers: every text leaf outside a think block is whitespace-only
|
|
897
|
+
* and every non-think element recurses. Structural — no layout reads,
|
|
898
|
+
* immune to wrapper padding/margins that a height check would mistake
|
|
899
|
+
* for content.
|
|
900
|
+
*/
|
|
901
|
+
function thinkOnlyContent(el) {
|
|
902
|
+
var nodes = el.childNodes;
|
|
903
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
904
|
+
var node = nodes[i];
|
|
905
|
+
if (node.nodeType === 3) {
|
|
906
|
+
if (node.textContent.trim() !== "") return false;
|
|
907
|
+
} else if (node.nodeType === 1) {
|
|
908
|
+
if (node.matches("[data-variant=\"think\"]")) continue;
|
|
909
|
+
if (!thinkOnlyContent(node)) return false;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
return true;
|
|
913
|
+
}
|
|
914
|
+
function assessRow(row, keepThink, splitThink) {
|
|
915
|
+
if (row.getAttribute("data-chat-flow-kind") !== "assistant-step") return NEUTRAL_ASSESSMENT;
|
|
916
|
+
var cached = rowInfo.get(row);
|
|
917
|
+
if (cached !== void 0 && cached.sig === thinkSig(cached.thinks, keepThink, splitThink)) return cached;
|
|
918
|
+
var thinks = row.querySelectorAll("[data-variant=\"think\"]");
|
|
919
|
+
var assessment = {
|
|
920
|
+
sig: thinkSig(thinks, keepThink, splitThink),
|
|
921
|
+
thinks,
|
|
922
|
+
transparent: false,
|
|
923
|
+
empty: false
|
|
924
|
+
};
|
|
925
|
+
if (thinks.length > 0 && thinkOnlyContent(row)) {
|
|
926
|
+
var allOk = true;
|
|
927
|
+
for (var i = 0; i < thinks.length; i++) if (thinks[i].getAttribute("data-state") !== "ok") {
|
|
928
|
+
allOk = false;
|
|
929
|
+
break;
|
|
930
|
+
}
|
|
931
|
+
if (allOk) {
|
|
932
|
+
assessment.transparent = !splitThink;
|
|
933
|
+
assessment.empty = !keepThink;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
rowInfo.set(row, assessment);
|
|
937
|
+
return assessment;
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Mirror the think-hiding CSS on the think's PARENT wrapper. The
|
|
941
|
+
* assistant body is a `gap:16px` flex column whose items are the think
|
|
942
|
+
* wrapper and the answer block; display:none on the think root alone
|
|
943
|
+
* leaves a 0-height wrapper item that still pins the answer 16px below
|
|
944
|
+
* the row top — the "blank band under the folded think". Hiding that
|
|
945
|
+
* wrapper (ccxWrapGone) restores the row to its true height. Guarded:
|
|
946
|
+
* only a wrapper whose sole element child is the think is hidden, so a
|
|
947
|
+
* layout that genuinely shares the container is never harmed. With
|
|
948
|
+
* 保留思考 on, or while the think is still streaming (state != ok),
|
|
949
|
+
* the wrapper must stay visible — remove any stale marker.
|
|
950
|
+
*/
|
|
951
|
+
function syncThinkWrappers(row, keepThink) {
|
|
952
|
+
var thinks = row.querySelectorAll("[data-variant=\"think\"]");
|
|
953
|
+
for (var i = 0; i < thinks.length; i++) {
|
|
954
|
+
var think = thinks[i];
|
|
955
|
+
var wrap = think.parentElement;
|
|
956
|
+
if (wrap === null || wrap === row) continue;
|
|
957
|
+
if (!keepThink && think.getAttribute("data-state") === "ok" && wrap.childElementCount === 1 && wrap.firstElementChild === think) {
|
|
958
|
+
if (!wrap.classList.contains(WRAP_GONE_CLASS)) wrap.classList.add(WRAP_GONE_CLASS);
|
|
959
|
+
} else if (wrap.classList.contains(WRAP_GONE_CLASS)) wrap.classList.remove(WRAP_GONE_CLASS);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
/** Strip interactivity from a card-row clone so it is display-only. */
|
|
963
|
+
function sanitizeClone(node) {
|
|
964
|
+
node.removeAttribute("role");
|
|
965
|
+
node.removeAttribute("tabindex");
|
|
966
|
+
node.removeAttribute("aria-expanded");
|
|
967
|
+
node.removeAttribute("data-expandable");
|
|
968
|
+
var buttons = node.querySelectorAll("button");
|
|
969
|
+
for (var i = 0; i < buttons.length; i++) {
|
|
970
|
+
var button = buttons[i];
|
|
971
|
+
var span = doc.createElement("span");
|
|
972
|
+
span.className = button.className;
|
|
973
|
+
span.textContent = button.textContent;
|
|
974
|
+
button.replaceWith(span);
|
|
975
|
+
}
|
|
976
|
+
return node;
|
|
977
|
+
}
|
|
978
|
+
function createBar(flow, run) {
|
|
979
|
+
var bar = doc.createElement("div");
|
|
980
|
+
bar.className = "ccxBar";
|
|
981
|
+
bar.setAttribute(BADGE_ATTR, "");
|
|
982
|
+
bar.setAttribute("role", "button");
|
|
983
|
+
bar.setAttribute("tabindex", "0");
|
|
984
|
+
var call = doc.createElement("span");
|
|
985
|
+
call.className = "ccxBarCall";
|
|
986
|
+
call.setAttribute("aria-hidden", "true");
|
|
987
|
+
var icon = doc.createElement("span");
|
|
988
|
+
icon.className = "ccxBarIcon";
|
|
989
|
+
var label = doc.createElement("span");
|
|
990
|
+
label.className = "ccxBarLabel";
|
|
991
|
+
bar.appendChild(call);
|
|
992
|
+
bar.appendChild(icon);
|
|
993
|
+
bar.appendChild(label);
|
|
994
|
+
bar._ccxIcon = icon;
|
|
995
|
+
bar._ccxLabel = label;
|
|
996
|
+
bar._ccxCall = call;
|
|
997
|
+
var toggle = function() {
|
|
998
|
+
var set = expanded.get(flow);
|
|
999
|
+
if (set === void 0 || disposed) return;
|
|
1000
|
+
var current = bar._ccxRun !== void 0 ? bar._ccxRun : run;
|
|
1001
|
+
if (set.has(current.headKey)) startCollapse(flow, current, bar);
|
|
1002
|
+
else {
|
|
1003
|
+
set.add(current.headKey);
|
|
1004
|
+
cancelCollapse(flow, current, bar);
|
|
1005
|
+
pass(flow);
|
|
1006
|
+
pinBar(bar, current);
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
bar.addEventListener("click", toggle);
|
|
1010
|
+
bar.addEventListener("keydown", function(event) {
|
|
1011
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
1012
|
+
event.preventDefault();
|
|
1013
|
+
toggle();
|
|
1014
|
+
});
|
|
1015
|
+
return bar;
|
|
1016
|
+
}
|
|
1017
|
+
/** Waterfall entrance: the run's rows cascade in top-down on expand. */
|
|
1018
|
+
function animateFall(bar, run) {
|
|
1019
|
+
var rows = run.rows;
|
|
1020
|
+
var dur = durMs();
|
|
1021
|
+
var step = stepMs();
|
|
1022
|
+
for (var i = 0; i < rows.length; i++) {
|
|
1023
|
+
rows[i].classList.remove(FALLING_CLASS, COLLAPSING_CLASS);
|
|
1024
|
+
rows[i].style.setProperty("--ccx-i", String(i));
|
|
1025
|
+
}
|
|
1026
|
+
for (var j = 0; j < rows.length; j++) rows[j].classList.add(FALLING_CLASS);
|
|
1027
|
+
if (bar._ccxFallTimer !== void 0) timers.cancel(bar._ccxFallTimer);
|
|
1028
|
+
bar._ccxFallTimer = timers.after(function() {
|
|
1029
|
+
bar._ccxFallTimer = void 0;
|
|
1030
|
+
if (disposed || !bar.isConnected) return;
|
|
1031
|
+
for (var k = 0; k < rows.length; k++) rows[k].classList.remove(FALLING_CLASS);
|
|
1032
|
+
}, Math.max(0, rows.length - 1) * step + dur + 60);
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Collapse each card's ROW in place: freeze the current height, then
|
|
1036
|
+
* transition height to 0 with the same stagger as the rise animation.
|
|
1037
|
+
* The measured trailing gap to the next visible row is cancelled via
|
|
1038
|
+
* margin-bottom, so everything below the run follows upward
|
|
1039
|
+
* continuously — no blank hole while the cards fade, no snap when the
|
|
1040
|
+
* merge finally applies. Styles are cleared on completion (and on
|
|
1041
|
+
* cancel/dispose) so rows return to auto layout.
|
|
1042
|
+
*/
|
|
1043
|
+
function animateRowShrink(rows, dur, step) {
|
|
1044
|
+
if (dur <= 0 || rows.length === 0) return;
|
|
1045
|
+
for (var i = 0; i < rows.length; i++) {
|
|
1046
|
+
var row = rows[i];
|
|
1047
|
+
var h = row.offsetHeight;
|
|
1048
|
+
if (h <= 0) continue;
|
|
1049
|
+
var gap = 0;
|
|
1050
|
+
var next = row.nextElementSibling;
|
|
1051
|
+
while (next !== null && next.offsetHeight === 0) next = next.nextElementSibling;
|
|
1052
|
+
if (next !== null) gap = Math.max(0, next.getBoundingClientRect().top - row.getBoundingClientRect().bottom);
|
|
1053
|
+
row.style.height = h + "px";
|
|
1054
|
+
row.style.overflow = "hidden";
|
|
1055
|
+
row.style.marginBottom = "0px";
|
|
1056
|
+
row.style.transition = "height " + dur + "ms cubic-bezier(0,0,0.58,1),margin-bottom " + dur + "ms cubic-bezier(0,0,0.58,1)";
|
|
1057
|
+
row.style.transitionDelay = i * step + "ms";
|
|
1058
|
+
row.offsetHeight;
|
|
1059
|
+
row.style.height = "0px";
|
|
1060
|
+
row.style.marginBottom = -gap + "px";
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
/** Remove the shrink animation's inline styles (restores auto layout). */
|
|
1064
|
+
function clearRowShrink(row) {
|
|
1065
|
+
row.style.removeProperty("height");
|
|
1066
|
+
row.style.removeProperty("overflow");
|
|
1067
|
+
row.style.removeProperty("margin-bottom");
|
|
1068
|
+
row.style.removeProperty("transition");
|
|
1069
|
+
row.style.removeProperty("transition-delay");
|
|
1070
|
+
}
|
|
1071
|
+
/** Rise-out exit: cards cascade back up, then the run collapses. */
|
|
1072
|
+
function startCollapse(flow, run, bar) {
|
|
1073
|
+
var set = expanded.get(flow);
|
|
1074
|
+
if (set === void 0 || !set.has(run.headKey)) return;
|
|
1075
|
+
set.delete(run.headKey);
|
|
1076
|
+
var per = collapsing.get(flow);
|
|
1077
|
+
if (per === void 0) {
|
|
1078
|
+
per = /* @__PURE__ */ new Set();
|
|
1079
|
+
collapsing.set(flow, per);
|
|
1080
|
+
}
|
|
1081
|
+
per.add(run.headKey);
|
|
1082
|
+
var rows = run.rows;
|
|
1083
|
+
var dur = durMs();
|
|
1084
|
+
var step = stepMs();
|
|
1085
|
+
for (var i = 0; i < rows.length; i++) {
|
|
1086
|
+
rows[i].classList.remove(FALLING_CLASS, COLLAPSING_CLASS);
|
|
1087
|
+
rows[i].style.setProperty("--ccx-i", String(i));
|
|
1088
|
+
}
|
|
1089
|
+
for (var j = 0; j < rows.length; j++) rows[j].classList.add(COLLAPSING_CLASS);
|
|
1090
|
+
animateRowShrink(rows, dur, step);
|
|
1091
|
+
bar._ccxCollapseRows = rows;
|
|
1092
|
+
pass(flow);
|
|
1093
|
+
armCollapseFinish(flow, run, bar);
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Arm (or re-arm) the collapse-finish timer. The delay covers one
|
|
1097
|
+
* full cascade — last row's stagger + rise + margin — measured from
|
|
1098
|
+
* the arm moment. `cascadeCount` defaults to the full covered row
|
|
1099
|
+
* set; extendCollapse passes only the NEW rows it just added, since
|
|
1100
|
+
* the already-covered rows have finished animating.
|
|
1101
|
+
*/
|
|
1102
|
+
function armCollapseFinish(flow, run, bar, cascadeCount) {
|
|
1103
|
+
if (bar._ccxCollapseTimer !== void 0) timers.cancel(bar._ccxCollapseTimer);
|
|
1104
|
+
var rows = bar._ccxCollapseRows;
|
|
1105
|
+
var dur = durMs();
|
|
1106
|
+
var step = stepMs();
|
|
1107
|
+
var n = cascadeCount !== void 0 ? cascadeCount : rows.length;
|
|
1108
|
+
var delay = Math.max(0, n - 1) * step + dur + 60;
|
|
1109
|
+
bar._ccxCollapseTimer = timers.after(function() {
|
|
1110
|
+
bar._ccxCollapseTimer = void 0;
|
|
1111
|
+
if (disposed || !flow.isConnected) return;
|
|
1112
|
+
var perNow = collapsing.get(flow);
|
|
1113
|
+
if (perNow !== void 0) {
|
|
1114
|
+
perNow.delete(run.headKey);
|
|
1115
|
+
if (perNow.size === 0) collapsing.delete(flow);
|
|
1116
|
+
}
|
|
1117
|
+
var all = bar._ccxCollapseRows;
|
|
1118
|
+
for (var k = 0; k < all.length; k++) {
|
|
1119
|
+
all[k].classList.remove(COLLAPSING_CLASS);
|
|
1120
|
+
clearRowShrink(all[k]);
|
|
1121
|
+
}
|
|
1122
|
+
bar._ccxCollapseRows = void 0;
|
|
1123
|
+
pass(flow);
|
|
1124
|
+
}, delay);
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* A run that is mid-collapse and still GROWING (the model streams the
|
|
1128
|
+
* newest tool calls into it) folds the newly added rows into the same
|
|
1129
|
+
* rise/shrink cascade — with their own relative stagger, starting
|
|
1130
|
+
* immediately — and re-arms the finish timer so the last new row's
|
|
1131
|
+
* animation completes before the merge applies. Without this the new
|
|
1132
|
+
* rows sit at full height and then snap shut with the merge.
|
|
1133
|
+
*/
|
|
1134
|
+
function extendCollapse(flow, run, bar) {
|
|
1135
|
+
var oldRows = bar._ccxCollapseRows;
|
|
1136
|
+
var rows = run.rows;
|
|
1137
|
+
if (oldRows === void 0 || rows.length <= oldRows.length) return;
|
|
1138
|
+
var dur = durMs();
|
|
1139
|
+
var step = stepMs();
|
|
1140
|
+
var added = [];
|
|
1141
|
+
for (var i = 0; i < rows.length; i++) if (oldRows.indexOf(rows[i]) === -1) added.push(rows[i]);
|
|
1142
|
+
if (added.length === 0) return;
|
|
1143
|
+
for (var j = 0; j < added.length; j++) {
|
|
1144
|
+
var row = added[j];
|
|
1145
|
+
row.classList.remove(FALLING_CLASS, COLLAPSING_CLASS);
|
|
1146
|
+
row.style.setProperty("--ccx-i", String(j));
|
|
1147
|
+
row.classList.add(COLLAPSING_CLASS);
|
|
1148
|
+
}
|
|
1149
|
+
animateRowShrink(added, dur, step);
|
|
1150
|
+
bar._ccxCollapseRows = rows;
|
|
1151
|
+
armCollapseFinish(flow, run, bar, added.length);
|
|
1152
|
+
}
|
|
1153
|
+
/** Abort a pending collapse (the user re-expanded mid-animation). */
|
|
1154
|
+
function cancelCollapse(flow, run, bar) {
|
|
1155
|
+
var per = collapsing.get(flow);
|
|
1156
|
+
if (per !== void 0) {
|
|
1157
|
+
per.delete(run.headKey);
|
|
1158
|
+
if (per.size === 0) collapsing.delete(flow);
|
|
1159
|
+
}
|
|
1160
|
+
if (bar !== void 0 && bar._ccxCollapseTimer !== void 0) {
|
|
1161
|
+
timers.cancel(bar._ccxCollapseTimer);
|
|
1162
|
+
bar._ccxCollapseTimer = void 0;
|
|
1163
|
+
}
|
|
1164
|
+
if (bar !== void 0) bar._ccxCollapseRows = void 0;
|
|
1165
|
+
var rows = run.rows;
|
|
1166
|
+
for (var i = 0; i < rows.length; i++) {
|
|
1167
|
+
rows[i].classList.remove(COLLAPSING_CLASS);
|
|
1168
|
+
clearRowShrink(rows[i]);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
function findScrollport(el) {
|
|
1172
|
+
var host = el.closest("[data-conversation-scroll]");
|
|
1173
|
+
if (host !== null) return host;
|
|
1174
|
+
var node = el.parentElement;
|
|
1175
|
+
while (node !== null) {
|
|
1176
|
+
var style = getComputedStyle(node);
|
|
1177
|
+
if ((style.overflowY === "auto" || style.overflowY === "scroll") && node.scrollHeight > node.clientHeight) return node;
|
|
1178
|
+
node = node.parentElement;
|
|
1179
|
+
}
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
function pinBar(bar, run) {
|
|
1183
|
+
if (typeof requestAnimationFrame !== "function") return;
|
|
1184
|
+
var sp = findScrollport(bar);
|
|
1185
|
+
if (sp === null) return;
|
|
1186
|
+
var savedTop = sp.scrollTop;
|
|
1187
|
+
var targetTop = bar.getBoundingClientRect().top - sp.getBoundingClientRect().top;
|
|
1188
|
+
sp.scrollTop = savedTop;
|
|
1189
|
+
var dur = durMs();
|
|
1190
|
+
var step = stepMs();
|
|
1191
|
+
var until = Date.now() + Math.max(0, run.rows.length - 1) * step + dur + 260;
|
|
1192
|
+
var stopped = false;
|
|
1193
|
+
var cancel = function() {
|
|
1194
|
+
stopped = true;
|
|
1195
|
+
sp.removeEventListener("wheel", cancel);
|
|
1196
|
+
sp.removeEventListener("touchstart", cancel);
|
|
1197
|
+
};
|
|
1198
|
+
sp.addEventListener("wheel", cancel, { passive: true });
|
|
1199
|
+
sp.addEventListener("touchstart", cancel, { passive: true });
|
|
1200
|
+
var frame = function() {
|
|
1201
|
+
if (stopped || disposed || !bar.isConnected) {
|
|
1202
|
+
cancel();
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
var delta = bar.getBoundingClientRect().top - sp.getBoundingClientRect().top - targetTop;
|
|
1206
|
+
if (Math.abs(delta) > .5) sp.scrollTop += delta;
|
|
1207
|
+
if (Date.now() < until) requestAnimationFrame(frame);
|
|
1208
|
+
else cancel();
|
|
1209
|
+
};
|
|
1210
|
+
requestAnimationFrame(frame);
|
|
1211
|
+
}
|
|
1212
|
+
/** Refresh the bar for one run: copy, aria, and the last-call clone. */
|
|
1213
|
+
function updateBar(bar, run) {
|
|
1214
|
+
bar._ccxRun = run;
|
|
1215
|
+
var total = run.toolRows.length;
|
|
1216
|
+
bar.setAttribute("aria-expanded", run.expanded ? "true" : "false");
|
|
1217
|
+
bar.setAttribute("aria-label", (run.expanded ? "收起" : "展开") + " " + total + " 个工具调用");
|
|
1218
|
+
bar.title = run.expanded ? "点击收起" : "点击展开";
|
|
1219
|
+
bar._ccxIcon.textContent = run.expanded ? "▾" : "▸";
|
|
1220
|
+
bar._ccxLabel.textContent = run.expanded ? "已展开 " + total + " 个工具调用 · 点击收起" : "已折叠 " + total + " 个工具调用 · 点击展开";
|
|
1221
|
+
var wasExpanded = bar._ccxPrevExpanded === true;
|
|
1222
|
+
bar.classList.toggle("ccxExpanded", run.expanded);
|
|
1223
|
+
if (run.expanded && !wasExpanded) animateFall(bar, run);
|
|
1224
|
+
bar._ccxPrevExpanded = run.expanded;
|
|
1225
|
+
if (!run.expanded) updateBarClone(bar);
|
|
1226
|
+
}
|
|
1227
|
+
/** Replace the bar's clone contents; counted as one clone op. */
|
|
1228
|
+
function replaceClone(call, node) {
|
|
1229
|
+
var t0 = statsBegin();
|
|
1230
|
+
while (call.firstChild !== null) call.removeChild(call.firstChild);
|
|
1231
|
+
call.appendChild(node);
|
|
1232
|
+
statsEnd("clone", 1, t0);
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Clone the last call's collapsed one-line row (product chrome) so
|
|
1236
|
+
* the bar itself displays the last tool call. Re-clone only when the
|
|
1237
|
+
* source row's markup changed (e.g. a tool result just arrived), and
|
|
1238
|
+
* at most every 120ms while a result streams in — the clone's
|
|
1239
|
+
* freshness is invisible beyond that, but serialization plus clone
|
|
1240
|
+
* DOM work would otherwise run on every frame of the stream.
|
|
1241
|
+
*/
|
|
1242
|
+
function updateBarClone(bar) {
|
|
1243
|
+
var run = bar._ccxRun;
|
|
1244
|
+
if (run === void 0) return;
|
|
1245
|
+
var call = bar._ccxCall;
|
|
1246
|
+
var source = run.lastRow.querySelector("[data-disclosure-row]");
|
|
1247
|
+
if (source !== null) {
|
|
1248
|
+
var sig = source.innerHTML;
|
|
1249
|
+
if (bar._ccxSig === sig) return;
|
|
1250
|
+
var now = Date.now();
|
|
1251
|
+
if (bar._ccxCloneAt !== void 0 && now - bar._ccxCloneAt < 120) return;
|
|
1252
|
+
bar._ccxCloneAt = now;
|
|
1253
|
+
bar._ccxSig = sig;
|
|
1254
|
+
replaceClone(call, sanitizeClone(source.cloneNode(true)));
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
var bashSource = run.lastRow.querySelector("[data-sample=\"bash\"]");
|
|
1258
|
+
if (bashSource !== null) {
|
|
1259
|
+
var bsig = bashSource.innerHTML;
|
|
1260
|
+
if (bar._ccxSig === bsig) return;
|
|
1261
|
+
var bnow = Date.now();
|
|
1262
|
+
if (bar._ccxCloneAt !== void 0 && bnow - bar._ccxCloneAt < 120) return;
|
|
1263
|
+
bar._ccxCloneAt = bnow;
|
|
1264
|
+
bar._ccxSig = bsig;
|
|
1265
|
+
replaceClone(call, sanitizeClone(bashSource.cloneNode(true)));
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
var card = run.lastRow.querySelector("[data-tool]");
|
|
1269
|
+
var head = card !== null && card.firstElementChild !== null ? card.firstElementChild : null;
|
|
1270
|
+
if (head !== null) {
|
|
1271
|
+
var hsig = head.innerHTML;
|
|
1272
|
+
if (bar._ccxSig !== hsig) {
|
|
1273
|
+
var now2 = Date.now();
|
|
1274
|
+
if (bar._ccxCloneAt === void 0 || now2 - bar._ccxCloneAt >= 120) {
|
|
1275
|
+
bar._ccxCloneAt = now2;
|
|
1276
|
+
bar._ccxSig = hsig;
|
|
1277
|
+
replaceClone(call, sanitizeClone(head.cloneNode(true)));
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
var fallback = run.lastRow.firstElementChild;
|
|
1283
|
+
if (fallback !== null) {
|
|
1284
|
+
var fsig = fallback.innerHTML;
|
|
1285
|
+
if (bar._ccxSig !== fsig) {
|
|
1286
|
+
var fnow = Date.now();
|
|
1287
|
+
if (bar._ccxCloneAt === void 0 || fnow - bar._ccxCloneAt >= 120) {
|
|
1288
|
+
bar._ccxCloneAt = fnow;
|
|
1289
|
+
bar._ccxSig = fsig;
|
|
1290
|
+
replaceClone(call, sanitizeClone(fallback.cloneNode(true)));
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
if (bar._ccxSig !== "") {
|
|
1296
|
+
bar._ccxSig = "";
|
|
1297
|
+
while (call.firstChild !== null) call.removeChild(call.firstChild);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
/** Create/update/remove the flow-level bars for the merged runs. */
|
|
1301
|
+
function syncBars(flow, runs) {
|
|
1302
|
+
var registry = barsByFlow.get(flow);
|
|
1303
|
+
if (registry === void 0) {
|
|
1304
|
+
registry = /* @__PURE__ */ new Map();
|
|
1305
|
+
barsByFlow.set(flow, registry);
|
|
1306
|
+
}
|
|
1307
|
+
var wanted = /* @__PURE__ */ new Set();
|
|
1308
|
+
for (var r = 0; r < runs.length; r++) {
|
|
1309
|
+
var run = runs[r];
|
|
1310
|
+
if (run.headKey === null) continue;
|
|
1311
|
+
wanted.add(run.headKey);
|
|
1312
|
+
var bar = registry.get(run.headKey);
|
|
1313
|
+
if (bar === void 0 || !flow.contains(bar)) {
|
|
1314
|
+
bar = createBar(flow, run);
|
|
1315
|
+
registry.set(run.headKey, bar);
|
|
1316
|
+
}
|
|
1317
|
+
updateBar(bar, run);
|
|
1318
|
+
if (bar._ccxCollapseRows !== void 0 && run.rows.length > bar._ccxCollapseRows.length) extendCollapse(flow, run, bar);
|
|
1319
|
+
var anchor = run.toolRows[0];
|
|
1320
|
+
if (bar.nextSibling !== anchor) flow.insertBefore(bar, anchor);
|
|
1321
|
+
}
|
|
1322
|
+
registry.forEach(function(bar, key) {
|
|
1323
|
+
if (!wanted.has(key)) {
|
|
1324
|
+
bar.remove();
|
|
1325
|
+
registry.delete(key);
|
|
1326
|
+
}
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Light pass for 'content'-dirty flows: the merge structure is
|
|
1331
|
+
* unchanged, so only bar clones can be stale. Touches ONLY the bars
|
|
1332
|
+
* of runs whose LAST row was mutated and which are collapsed — an
|
|
1333
|
+
* expanded run shows its own cards (no clone needed) and earlier
|
|
1334
|
+
* cards of a run are hidden (their content never reaches a bar).
|
|
1335
|
+
* No run rebuild, no class toggles, no layout.
|
|
1336
|
+
*/
|
|
1337
|
+
function barRefresh(flow, contentRows) {
|
|
1338
|
+
var registry = barsByFlow.get(flow);
|
|
1339
|
+
if (registry === void 0 || contentRows === void 0) return;
|
|
1340
|
+
registry.forEach(function(bar) {
|
|
1341
|
+
var run = bar._ccxRun;
|
|
1342
|
+
if (run === void 0 || run.expanded || !bar.isConnected) return;
|
|
1343
|
+
if (contentRows.has(run.lastRow)) updateBarClone(bar);
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
function pass(flow) {
|
|
1347
|
+
var t0 = statsBegin();
|
|
1348
|
+
var rows = rowList(flow);
|
|
1349
|
+
var set = expanded.get(flow);
|
|
1350
|
+
var per = collapsing.get(flow);
|
|
1351
|
+
var keepThink = effectiveKeepThink(flow);
|
|
1352
|
+
var splitThink = settings.getSnapshot().splitThink;
|
|
1353
|
+
if (set !== void 0 && set.size > 0) {
|
|
1354
|
+
var stale = [];
|
|
1355
|
+
set.forEach(function(key) {
|
|
1356
|
+
if (key.indexOf("ccx-run-") === 0) return;
|
|
1357
|
+
var found = false;
|
|
1358
|
+
for (var i = 0; i < rows.length; i++) if (rows[i].getAttribute("data-chat-flow-key") === key) {
|
|
1359
|
+
found = true;
|
|
1360
|
+
break;
|
|
1361
|
+
}
|
|
1362
|
+
if (!found) stale.push(key);
|
|
1363
|
+
});
|
|
1364
|
+
for (var s = 0; s < stale.length; s++) set.delete(stale[s]);
|
|
1365
|
+
}
|
|
1366
|
+
var runs = [];
|
|
1367
|
+
var current = [];
|
|
1368
|
+
var sawTool = false;
|
|
1369
|
+
var sawThink = false;
|
|
1370
|
+
var sawProcess = false;
|
|
1371
|
+
for (var i = 0; i < rows.length; i++) {
|
|
1372
|
+
var row = rows[i];
|
|
1373
|
+
var kind = row.getAttribute("data-chat-flow-kind");
|
|
1374
|
+
if (kind === "tool-call") {
|
|
1375
|
+
sawTool = true;
|
|
1376
|
+
current.push(row);
|
|
1377
|
+
} else if (kind === "assistant-step") {
|
|
1378
|
+
var assessment = assessRow(row, keepThink, splitThink);
|
|
1379
|
+
if (assessment.thinks.length > 0) sawThink = true;
|
|
1380
|
+
if (current.length > 0 && assessment.transparent) current.push(row);
|
|
1381
|
+
else {
|
|
1382
|
+
if (current.length > 0) runs.push(current);
|
|
1383
|
+
current = [];
|
|
1384
|
+
}
|
|
1385
|
+
} else {
|
|
1386
|
+
if (current.length > 0) runs.push(current);
|
|
1387
|
+
current = [];
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
if (current.length > 0) runs.push(current);
|
|
1391
|
+
if (!sawTool && !sawThink) {
|
|
1392
|
+
for (var pc = 0; pc < rows.length; pc++) if (rows[pc].getAttribute("data-chat-flow-kind") === "turn-process" && rows[pc].textContent.trim() !== "") {
|
|
1393
|
+
sawProcess = true;
|
|
1394
|
+
break;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
var mergedRows = /* @__PURE__ */ new Set();
|
|
1398
|
+
var mergedRuns = [];
|
|
1399
|
+
for (var r = 0; r < runs.length; r++) {
|
|
1400
|
+
var run = runs[r];
|
|
1401
|
+
var toolRows = [];
|
|
1402
|
+
var thinkRows = [];
|
|
1403
|
+
for (var t = 0; t < run.length; t++) if (run[t].getAttribute("data-chat-flow-kind") === "tool-call") toolRows.push(run[t]);
|
|
1404
|
+
else thinkRows.push(run[t]);
|
|
1405
|
+
if (toolRows.length < 2) continue;
|
|
1406
|
+
var headKey = toolRows[0].getAttribute("data-chat-flow-key");
|
|
1407
|
+
if (headKey === null || headKey === "") {
|
|
1408
|
+
var synth = syntheticKeys.get(flow);
|
|
1409
|
+
if (synth === void 0) synth = 0;
|
|
1410
|
+
syntheticKeys.set(flow, synth + 1);
|
|
1411
|
+
headKey = "ccx-run-" + synth;
|
|
1412
|
+
}
|
|
1413
|
+
var expandedRun = set !== void 0 && headKey !== null && set.has(headKey);
|
|
1414
|
+
var collapsingRun = per !== void 0 && headKey !== null && per.has(headKey);
|
|
1415
|
+
var hide = !expandedRun && !collapsingRun;
|
|
1416
|
+
for (var k = 0; k < toolRows.length; k++) {
|
|
1417
|
+
toolRows[k].classList.toggle(MERGED_CLASS, hide);
|
|
1418
|
+
if (hide) toolRows[k].classList.remove(FALLING_CLASS, COLLAPSING_CLASS);
|
|
1419
|
+
mergedRows.add(toolRows[k]);
|
|
1420
|
+
}
|
|
1421
|
+
for (var w = 0; w < thinkRows.length; w++) {
|
|
1422
|
+
thinkRows[w].classList.toggle(MERGED_CLASS, hide);
|
|
1423
|
+
if (hide) thinkRows[w].classList.remove(FALLING_CLASS, COLLAPSING_CLASS);
|
|
1424
|
+
mergedRows.add(thinkRows[w]);
|
|
1425
|
+
}
|
|
1426
|
+
mergedRuns.push({
|
|
1427
|
+
headKey,
|
|
1428
|
+
toolRows,
|
|
1429
|
+
thinkRows,
|
|
1430
|
+
rows: run,
|
|
1431
|
+
lastRow: toolRows[toolRows.length - 1],
|
|
1432
|
+
expanded: expandedRun
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
for (var m = 0; m < rows.length; m++) if (!mergedRows.has(rows[m])) rows[m].classList.remove(MERGED_CLASS, FALLING_CLASS, COLLAPSING_CLASS);
|
|
1436
|
+
if (per !== void 0 && per.size > 0) {
|
|
1437
|
+
var keys = [];
|
|
1438
|
+
per.forEach(function(key) {
|
|
1439
|
+
keys.push(key);
|
|
1440
|
+
});
|
|
1441
|
+
for (var p = 0; p < keys.length; p++) {
|
|
1442
|
+
var still = false;
|
|
1443
|
+
for (var q = 0; q < mergedRuns.length; q++) if (mergedRuns[q].headKey === keys[p]) {
|
|
1444
|
+
still = true;
|
|
1445
|
+
break;
|
|
1446
|
+
}
|
|
1447
|
+
if (!still) per.delete(keys[p]);
|
|
1448
|
+
}
|
|
1449
|
+
if (per.size === 0) collapsing.delete(flow);
|
|
1450
|
+
}
|
|
1451
|
+
syncBars(flow, mergedRuns);
|
|
1452
|
+
for (var x = 0; x < rows.length; x++) {
|
|
1453
|
+
var candidate = rows[x];
|
|
1454
|
+
if (candidate.getAttribute("data-chat-flow-kind") !== "assistant-step") continue;
|
|
1455
|
+
syncThinkWrappers(candidate, keepThink);
|
|
1456
|
+
if (keepThink) {
|
|
1457
|
+
if (candidate.classList.remove(EMPTY_CLASS)) {
|
|
1458
|
+
if (hiddenRows.delete(candidate)) hiddenCount -= 1;
|
|
1459
|
+
}
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
var assessment = assessRow(candidate, keepThink, splitThink);
|
|
1463
|
+
if (assessment.empty) {
|
|
1464
|
+
candidate.classList.add(EMPTY_CLASS);
|
|
1465
|
+
if (!hiddenRows.has(candidate)) {
|
|
1466
|
+
hiddenRows.add(candidate);
|
|
1467
|
+
hiddenCount += 1;
|
|
1468
|
+
}
|
|
1469
|
+
} else if (candidate.classList.remove(EMPTY_CLASS)) {
|
|
1470
|
+
if (hiddenRows.delete(candidate)) hiddenCount -= 1;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
statsEnd("pass", 1, t0);
|
|
1474
|
+
return {
|
|
1475
|
+
sawTool,
|
|
1476
|
+
sawThink,
|
|
1477
|
+
sawProcess
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
/** Drop observers/registries for flows no longer in the document. */
|
|
1481
|
+
function pruneFlows() {
|
|
1482
|
+
var flowsNow = [];
|
|
1483
|
+
flows.forEach(function(mode, flow) {
|
|
1484
|
+
flowsNow.push(flow);
|
|
1485
|
+
});
|
|
1486
|
+
for (var i = 0; i < flowsNow.length; i++) {
|
|
1487
|
+
var flow = flowsNow[i];
|
|
1488
|
+
if (doc.contains(flow)) continue;
|
|
1489
|
+
var observer = flowObservers.get(flow);
|
|
1490
|
+
if (observer !== null && observer !== void 0) observer.disconnect();
|
|
1491
|
+
flowObservers.delete(flow);
|
|
1492
|
+
flows.delete(flow);
|
|
1493
|
+
expanded.delete(flow);
|
|
1494
|
+
barsByFlow.delete(flow);
|
|
1495
|
+
collapsing.delete(flow);
|
|
1496
|
+
dirtyFlows.delete(flow);
|
|
1497
|
+
flowReasons.delete(flow);
|
|
1498
|
+
contentRowsByFlow.delete(flow);
|
|
1499
|
+
syntheticKeys.delete(flow);
|
|
1500
|
+
}
|
|
1501
|
+
parentObservers.forEach(function(observer, parent) {
|
|
1502
|
+
if (!doc.contains(parent)) {
|
|
1503
|
+
observer.disconnect();
|
|
1504
|
+
parentObservers.delete(parent);
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
var used = false;
|
|
1508
|
+
flows.forEach(function(mode, flow) {
|
|
1509
|
+
if (flow.parentElement === parent) used = true;
|
|
1510
|
+
});
|
|
1511
|
+
if (!used) {
|
|
1512
|
+
observer.disconnect();
|
|
1513
|
+
parentObservers.delete(parent);
|
|
1514
|
+
}
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
function refresh() {
|
|
1518
|
+
if (disposed || !active) return;
|
|
1519
|
+
var t0 = statsBegin();
|
|
1520
|
+
if (structureDirty) {
|
|
1521
|
+
structureDirty = false;
|
|
1522
|
+
discover();
|
|
1523
|
+
pruneFlows();
|
|
1524
|
+
observeParents();
|
|
1525
|
+
}
|
|
1526
|
+
var pending = [];
|
|
1527
|
+
dirtyFlows.forEach(function(flow) {
|
|
1528
|
+
pending.push(flow);
|
|
1529
|
+
});
|
|
1530
|
+
dirtyFlows.clear();
|
|
1531
|
+
for (var i = 0; i < pending.length; i++) {
|
|
1532
|
+
var flow = pending[i];
|
|
1533
|
+
if (!flows.has(flow) || !doc.contains(flow)) continue;
|
|
1534
|
+
var keep = effectiveKeepThink(flow);
|
|
1535
|
+
flow.classList.toggle(KEEP_CLASS, keep);
|
|
1536
|
+
var reason = flowReasons.get(flow);
|
|
1537
|
+
flowReasons.delete(flow);
|
|
1538
|
+
var contentRows = contentRowsByFlow.get(flow);
|
|
1539
|
+
if (contentRows !== void 0) contentRowsByFlow.delete(flow);
|
|
1540
|
+
if (reason === "content") {
|
|
1541
|
+
if (contentRows !== void 0) barRefresh(flow, contentRows);
|
|
1542
|
+
} else {
|
|
1543
|
+
var outcome = pass(flow);
|
|
1544
|
+
if (flows.get(flow) === "light" && (outcome.sawTool || outcome.sawThink || outcome.sawProcess)) setFlowMode(flow, "full");
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
statsEnd("refresh", 1, t0);
|
|
1548
|
+
}
|
|
1549
|
+
function dispose() {
|
|
1550
|
+
if (disposed) return;
|
|
1551
|
+
disposed = true;
|
|
1552
|
+
stopRescan();
|
|
1553
|
+
if (typeof doc.removeEventListener === "function") doc.removeEventListener("visibilitychange", onVisibility);
|
|
1554
|
+
if (offSettings !== null) offSettings();
|
|
1555
|
+
if (rafId !== 0 && typeof cancelAnimationFrame === "function") cancelAnimationFrame(rafId);
|
|
1556
|
+
if (bootRaf !== 0 && typeof cancelAnimationFrame === "function") cancelAnimationFrame(bootRaf);
|
|
1557
|
+
disconnectObservers();
|
|
1558
|
+
barsByFlow.forEach(function(registry) {
|
|
1559
|
+
registry.forEach(function(bar) {
|
|
1560
|
+
if (bar._ccxFallTimer !== void 0) timers.cancel(bar._ccxFallTimer);
|
|
1561
|
+
if (bar._ccxCollapseTimer !== void 0) timers.cancel(bar._ccxCollapseTimer);
|
|
1562
|
+
});
|
|
1563
|
+
});
|
|
1564
|
+
flows.clear();
|
|
1565
|
+
flowObservers.clear();
|
|
1566
|
+
parentObservers.clear();
|
|
1567
|
+
expanded.clear();
|
|
1568
|
+
barsByFlow.clear();
|
|
1569
|
+
collapsing.clear();
|
|
1570
|
+
dirtyFlows.clear();
|
|
1571
|
+
flowReasons.clear();
|
|
1572
|
+
contentRowsByFlow.clear();
|
|
1573
|
+
syntheticKeys.clear();
|
|
1574
|
+
var flowsNow = doc.querySelectorAll(FLOW_SELECTOR);
|
|
1575
|
+
for (var f = 0; f < flowsNow.length; f++) {
|
|
1576
|
+
flowsNow[f].classList.remove(KEEP_CLASS);
|
|
1577
|
+
var bars = flowsNow[f].querySelectorAll("[data-ccx-badge]");
|
|
1578
|
+
for (var b = 0; b < bars.length; b++) bars[b].remove();
|
|
1579
|
+
var tagged = flowsNow[f].querySelectorAll(".ccxMerged,.ccxEmpty,.ccxFalling,.ccxCollapsing,.ccxWrapGone");
|
|
1580
|
+
for (var t = 0; t < tagged.length; t++) tagged[t].classList.remove(MERGED_CLASS, EMPTY_CLASS, FALLING_CLASS, COLLAPSING_CLASS, WRAP_GONE_CLASS);
|
|
1581
|
+
}
|
|
1582
|
+
var allToolRows = doc.querySelectorAll("[data-chat-flow-kind=\"tool-call\"]");
|
|
1583
|
+
for (var tr = 0; tr < allToolRows.length; tr++) clearRowShrink(allToolRows[tr]);
|
|
1584
|
+
if (styleTag !== null && !adoptedStyle) styleTag.remove();
|
|
1585
|
+
styleTag = null;
|
|
1586
|
+
doc.documentElement.style.removeProperty("--ccx-dur");
|
|
1587
|
+
doc.documentElement.style.removeProperty("--ccx-step");
|
|
1588
|
+
}
|
|
1589
|
+
function bootInit() {
|
|
1590
|
+
if (booted || disposed) return;
|
|
1591
|
+
booted = true;
|
|
1592
|
+
applySettings();
|
|
1593
|
+
settings.stats = ensureStats();
|
|
1594
|
+
insertStyle();
|
|
1595
|
+
discover();
|
|
1596
|
+
observeParents();
|
|
1597
|
+
startRescan();
|
|
1598
|
+
structureDirty = true;
|
|
1599
|
+
schedule();
|
|
1600
|
+
}
|
|
1601
|
+
if (typeof doc.addEventListener === "function") doc.addEventListener("visibilitychange", onVisibility);
|
|
1602
|
+
if (doc.hidden) setActive(false);
|
|
1603
|
+
offSettings = settings.subscribe(function() {
|
|
1604
|
+
applySettings();
|
|
1605
|
+
if (statsObj !== null) {
|
|
1606
|
+
var statsOn = settings.getSnapshot().stats === true;
|
|
1607
|
+
if (statsOn && !statsObj.enabled) {
|
|
1608
|
+
statsObj.obs = 0;
|
|
1609
|
+
statsObj.obsMs = 0;
|
|
1610
|
+
statsObj.refresh = 0;
|
|
1611
|
+
statsObj.refreshMs = 0;
|
|
1612
|
+
statsObj.pass = 0;
|
|
1613
|
+
statsObj.passMs = 0;
|
|
1614
|
+
statsObj.scan = 0;
|
|
1615
|
+
statsObj.scanMs = 0;
|
|
1616
|
+
statsObj.clone = 0;
|
|
1617
|
+
statsObj.cloneMs = 0;
|
|
1618
|
+
statsObj.skip = 0;
|
|
1619
|
+
statsObj.start = Date.now();
|
|
1620
|
+
}
|
|
1621
|
+
statsObj.enabled = statsOn;
|
|
1622
|
+
}
|
|
1623
|
+
flows.forEach(function(mode, flow) {
|
|
1624
|
+
markDirty(flow, "rows");
|
|
1625
|
+
});
|
|
1626
|
+
schedule();
|
|
1627
|
+
});
|
|
1628
|
+
if (typeof requestAnimationFrame === "function") bootRaf = requestAnimationFrame(bootInit);
|
|
1629
|
+
else bootInit();
|
|
1630
|
+
return dispose;
|
|
1631
|
+
}
|
|
1632
|
+
module.exports = { installCollapseEngine };
|
|
1633
|
+
}));
|
|
1634
|
+
//#endregion
|
|
1635
|
+
//#region src/client/react-env.js
|
|
1636
|
+
var require_react_env = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1637
|
+
/**
|
|
1638
|
+
* React bridge between the two delivery channels.
|
|
1639
|
+
*
|
|
1640
|
+
* The source graph never imports React statically. Each channel provides its
|
|
1641
|
+
* own React instance and the build wrapper (intro) stamps it here before the
|
|
1642
|
+
* plugin's apply() runs:
|
|
1643
|
+
*
|
|
1644
|
+
* - installed loader channel: the factory's `require('react')` resolves
|
|
1645
|
+
* through the web shell's module table (the product's real React — the
|
|
1646
|
+
* settings card uses hooks, so instance identity matters);
|
|
1647
|
+
* - dynamic-runner channel (code.client body in a live GUI session): React
|
|
1648
|
+
* arrives as the runner's closure parameter.
|
|
1649
|
+
*
|
|
1650
|
+
* Headless harnesses (engine-smoke, live-probe without a card) provide
|
|
1651
|
+
* neither, stay null, and simply never register the settings card.
|
|
1652
|
+
*/
|
|
1653
|
+
function acquireReact() {
|
|
1654
|
+
if (typeof globalThis !== "undefined" && globalThis !== null && globalThis.__dshToolfoldReact != null) return globalThis.__dshToolfoldReact;
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1657
|
+
module.exports = { acquireReact };
|
|
1658
|
+
}));
|
|
1659
|
+
//#endregion
|
|
1660
|
+
//#region src/client/card.js
|
|
1661
|
+
var require_card = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
1662
|
+
const { acquireReact } = require_react_env();
|
|
1663
|
+
/**
|
|
1664
|
+
* One plugin card under Settings → 插件, owning the folding preferences.
|
|
1665
|
+
* @param props - injected face: useCcxSettings snapshot hook, setDur,
|
|
1666
|
+
* setThinkMode; every change applies live through the shared store.
|
|
1667
|
+
* @returns the card.
|
|
1668
|
+
*/
|
|
1669
|
+
function SettingsCard(props) {
|
|
1670
|
+
var React = acquireReact();
|
|
1671
|
+
var state = props.useCcxSettings(function(snapshot) {
|
|
1672
|
+
return snapshot;
|
|
1673
|
+
});
|
|
1674
|
+
var openCell = React.useState(false);
|
|
1675
|
+
var isOpen = openCell[0];
|
|
1676
|
+
var setOpen = openCell[1];
|
|
1677
|
+
var setNow = React.useState(0)[1];
|
|
1678
|
+
React.useEffect(function() {
|
|
1679
|
+
if (!state.stats) return;
|
|
1680
|
+
var handle = props.tick(function() {
|
|
1681
|
+
setNow(Date.now());
|
|
1682
|
+
}, 1e3);
|
|
1683
|
+
return function() {
|
|
1684
|
+
props.cancelTick(handle);
|
|
1685
|
+
};
|
|
1686
|
+
}, [state.stats]);
|
|
1687
|
+
var cardClass = isOpen ? "ccxCard ccxCardOpen" : "ccxCard";
|
|
1688
|
+
var compat = props.compat();
|
|
1689
|
+
var warnEl = null;
|
|
1690
|
+
if (compat.state === "old" || compat.state === "new") warnEl = React.createElement("span", {
|
|
1691
|
+
className: "ccxWarn",
|
|
1692
|
+
"data-tip": compat.state === "old" ? "版本不匹配:当前 DSH 版本落后。\n本插件需要 DSH >= 0.1.2-rc.1,请升级 DSH。" : "版本不匹配:当前 DSH 版本过新。\n本插件尚未适配,请等待插件更新。",
|
|
1693
|
+
"aria-label": "版本不匹配",
|
|
1694
|
+
role: "img"
|
|
1695
|
+
}, "⚠");
|
|
1696
|
+
var header = React.createElement("button", {
|
|
1697
|
+
type: "button",
|
|
1698
|
+
className: "ccxHeader",
|
|
1699
|
+
"aria-expanded": isOpen,
|
|
1700
|
+
"aria-label": (isOpen ? "收起" : "展开") + ": 工具折叠",
|
|
1701
|
+
onClick: function() {
|
|
1702
|
+
setOpen(!isOpen);
|
|
1703
|
+
}
|
|
1704
|
+
}, React.createElement("span", { className: "ccxHeadText" }, React.createElement("span", { className: "ccxName" }, "工具折叠"), React.createElement("span", { className: "ccxDescription" }, "折叠工具调用与思考的显示设置")), warnEl, React.createElement("span", { className: isOpen ? "ccxChevron ccxChevronOpen" : "ccxChevron" }, "▾"));
|
|
1705
|
+
if (!isOpen) return React.createElement("li", { className: cardClass }, header);
|
|
1706
|
+
var enabledField = React.createElement("div", { className: "ccxField" }, React.createElement("div", { className: "ccxFieldHead" }, React.createElement("label", {
|
|
1707
|
+
className: "ccxFieldLabel",
|
|
1708
|
+
htmlFor: "ccx-enabled"
|
|
1709
|
+
}, "启用插件"), React.createElement("input", {
|
|
1710
|
+
id: "ccx-enabled",
|
|
1711
|
+
type: "checkbox",
|
|
1712
|
+
className: "ccxToggle",
|
|
1713
|
+
checked: state.enabled,
|
|
1714
|
+
onChange: function(event) {
|
|
1715
|
+
props.setEnabled(event.target.checked);
|
|
1716
|
+
}
|
|
1717
|
+
})), React.createElement("p", { className: "ccxFieldHint" }, state.enabled ? "启用工具调用与思考的折叠显示(关闭后立即取消全部折叠、恢复默认显示)" : "已停用:不折叠、恢复产品默认显示;此卡片保留,随时可重新启用"));
|
|
1718
|
+
var durField = React.createElement("div", { className: "ccxField" }, React.createElement("div", { className: "ccxFieldHead" }, React.createElement("label", {
|
|
1719
|
+
className: "ccxFieldLabel",
|
|
1720
|
+
htmlFor: "ccx-dur"
|
|
1721
|
+
}, "展开动画时长"), React.createElement("span", { className: "ccxBadge" }, state.durMs + " ms")), React.createElement("input", {
|
|
1722
|
+
id: "ccx-dur",
|
|
1723
|
+
type: "range",
|
|
1724
|
+
className: "ccxRange",
|
|
1725
|
+
min: 0,
|
|
1726
|
+
max: 1e3,
|
|
1727
|
+
step: 10,
|
|
1728
|
+
disabled: !state.enabled,
|
|
1729
|
+
value: state.durMs,
|
|
1730
|
+
onChange: function(event) {
|
|
1731
|
+
props.setDur(Number(event.target.value));
|
|
1732
|
+
}
|
|
1733
|
+
}), React.createElement("p", { className: "ccxFieldHint" }, "折叠与展开的弹性动画时长,0 为瞬时切换"));
|
|
1734
|
+
var thinkMode = state.thinkMode === "keep" || state.thinkMode === "hide" ? state.thinkMode : "auto";
|
|
1735
|
+
var thinkField = React.createElement("div", { className: "ccxField" }, React.createElement("div", { className: "ccxFieldHead" }, React.createElement("label", {
|
|
1736
|
+
className: "ccxFieldLabel",
|
|
1737
|
+
htmlFor: "ccx-think-mode"
|
|
1738
|
+
}, "思考显示"), React.createElement("select", {
|
|
1739
|
+
id: "ccx-think-mode",
|
|
1740
|
+
className: "ccxSelect",
|
|
1741
|
+
disabled: !state.enabled,
|
|
1742
|
+
value: thinkMode,
|
|
1743
|
+
onChange: function(event) {
|
|
1744
|
+
props.setThinkMode(event.target.value);
|
|
1745
|
+
}
|
|
1746
|
+
}, React.createElement("option", { value: "auto" }, "自动跟随官方折叠"), React.createElement("option", { value: "keep" }, "始终保留"), React.createElement("option", { value: "hide" }, "始终隐藏"))), React.createElement("p", { className: "ccxFieldHint" }, "已完成思考的显示方式。自动(默认):官方「对话显示 = Compact」收起整块过程时保留思考,其余时候隐藏以节省空间;始终保留:思考一直可见(分隔模式下位于两条折叠条之间);始终隐藏:已完成的思考一律不显示"));
|
|
1747
|
+
var splitField = React.createElement("div", { className: "ccxField" }, React.createElement("div", { className: "ccxFieldHead" }, React.createElement("label", {
|
|
1748
|
+
className: "ccxFieldLabel",
|
|
1749
|
+
htmlFor: "ccx-split-think"
|
|
1750
|
+
}, "思考分隔调用组"), React.createElement("input", {
|
|
1751
|
+
id: "ccx-split-think",
|
|
1752
|
+
type: "checkbox",
|
|
1753
|
+
className: "ccxToggle",
|
|
1754
|
+
disabled: !state.enabled,
|
|
1755
|
+
checked: state.splitThink,
|
|
1756
|
+
onChange: function(event) {
|
|
1757
|
+
props.setSplitThink(event.target.checked);
|
|
1758
|
+
}
|
|
1759
|
+
})), React.createElement("p", { className: "ccxFieldHint" }, "开启(默认):已完成的思考把前后两组工具调用隔开、各自独立折叠;关闭:思考并入所在工具组一起折叠"));
|
|
1760
|
+
var statsField = React.createElement("div", { className: "ccxField" }, React.createElement("div", { className: "ccxFieldHead" }, React.createElement("label", {
|
|
1761
|
+
className: "ccxFieldLabel",
|
|
1762
|
+
htmlFor: "ccx-stats"
|
|
1763
|
+
}, "性能统计"), React.createElement("input", {
|
|
1764
|
+
id: "ccx-stats",
|
|
1765
|
+
type: "checkbox",
|
|
1766
|
+
className: "ccxToggle",
|
|
1767
|
+
disabled: !state.enabled,
|
|
1768
|
+
checked: state.stats,
|
|
1769
|
+
onChange: function(event) {
|
|
1770
|
+
props.setStats(event.target.checked);
|
|
1771
|
+
}
|
|
1772
|
+
})), React.createElement("p", { className: "ccxFieldHint" }, "统计插件自身耗时并实时显示在本卡片内(默认关;开启才产生极小的计时开销)"));
|
|
1773
|
+
var storageHint = props.bridgeStatus === void 0 || props.bridgeStatus() === "dsh" ? "设置保存在 DSH 主机配置(~/.dsh/settings.yaml),由 DSH 设置服务持久化" : "未检测到 DSH 设置服务,设置仅保存在本浏览器(localStorage)";
|
|
1774
|
+
var children = [header, React.createElement("div", { className: "ccxBody" }, enabledField, durField, thinkField, splitField, statsField, React.createElement("p", { className: "ccxFieldHint" }, storageHint))];
|
|
1775
|
+
if (state.stats) {
|
|
1776
|
+
var engineStats = props.stats();
|
|
1777
|
+
if (engineStats !== void 0) {
|
|
1778
|
+
var elapsed = Math.max(1, (Date.now() - engineStats.start) / 1e3);
|
|
1779
|
+
var statRows = [
|
|
1780
|
+
[
|
|
1781
|
+
"观察回调",
|
|
1782
|
+
"obs",
|
|
1783
|
+
engineStats.obs,
|
|
1784
|
+
engineStats.obsMs
|
|
1785
|
+
],
|
|
1786
|
+
[
|
|
1787
|
+
"引擎刷新",
|
|
1788
|
+
"refresh",
|
|
1789
|
+
engineStats.refresh,
|
|
1790
|
+
engineStats.refreshMs
|
|
1791
|
+
],
|
|
1792
|
+
[
|
|
1793
|
+
"合并重算",
|
|
1794
|
+
"pass",
|
|
1795
|
+
engineStats.pass,
|
|
1796
|
+
engineStats.passMs
|
|
1797
|
+
],
|
|
1798
|
+
[
|
|
1799
|
+
"安全重扫",
|
|
1800
|
+
"scan",
|
|
1801
|
+
engineStats.scan,
|
|
1802
|
+
engineStats.scanMs
|
|
1803
|
+
],
|
|
1804
|
+
[
|
|
1805
|
+
"摘要克隆",
|
|
1806
|
+
"clone",
|
|
1807
|
+
engineStats.clone,
|
|
1808
|
+
engineStats.cloneMs
|
|
1809
|
+
]
|
|
1810
|
+
].map(function(row) {
|
|
1811
|
+
return React.createElement("div", {
|
|
1812
|
+
className: "ccxStatRow",
|
|
1813
|
+
key: row[1]
|
|
1814
|
+
}, React.createElement("span", { className: "ccxFieldLabel" }, row[0]), React.createElement("span", { className: "ccxStatValue" }, row[2] + " 次 · " + row[3].toFixed(1) + " ms(" + (row[3] / elapsed).toFixed(2) + " ms/s)"));
|
|
1815
|
+
});
|
|
1816
|
+
children.push(React.createElement("div", { className: "ccxBody" }, statRows, React.createElement("p", { className: "ccxFieldHint" }, "流式变更直接忽略(零开销短路):" + engineStats.skip + " 批次"), React.createElement("p", { className: "ccxFieldHint" }, "累计自开启统计起算;标签页隐藏时引擎全部暂停,不计入")));
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
return React.createElement("li", { className: cardClass }, children);
|
|
1820
|
+
}
|
|
1821
|
+
module.exports = { SettingsCard };
|
|
1822
|
+
}));
|
|
1823
|
+
//#endregion
|
|
1824
|
+
//#region src/client/index.js
|
|
1825
|
+
/**
|
|
1826
|
+
* dsh-toolfold — CLIENT half (browser), entry module.
|
|
1827
|
+
*
|
|
1828
|
+
* Source layout mirrors the official client-modules architecture: modular
|
|
1829
|
+
* sources under src/client/, bundled by tsdown into the loader artifact
|
|
1830
|
+
* (lib/client.js) and the dynamic-runner body (lib/dynamic-body.js). See
|
|
1831
|
+
* tsdown.config.mjs for the two wrapper faces; the plugin object below is
|
|
1832
|
+
* shared verbatim by both.
|
|
1833
|
+
*/
|
|
1834
|
+
const { createSettingsStore } = require_settings();
|
|
1835
|
+
const { createSettingsBridge } = require_bridge();
|
|
1836
|
+
const { CARD_STYLE_ID, cardCss } = require_styles();
|
|
1837
|
+
const { installCollapseEngine } = require_engine();
|
|
1838
|
+
const { SettingsCard } = require_card();
|
|
1839
|
+
const { acquireReact } = require_react_env();
|
|
1840
|
+
module.exports = {
|
|
1841
|
+
name: "toolfold",
|
|
1842
|
+
inject: ["slots", "timer"],
|
|
1843
|
+
apply: function(ctx) {
|
|
1844
|
+
if (typeof document === "undefined") return;
|
|
1845
|
+
var settings = createSettingsStore();
|
|
1846
|
+
if (typeof window !== "undefined") window.__toolfoldSettings = settings;
|
|
1847
|
+
var bridge = createSettingsBridge(ctx, settings);
|
|
1848
|
+
if (typeof window !== "undefined") window.__toolfoldBridge = bridge;
|
|
1849
|
+
var timers = {
|
|
1850
|
+
after: function(callback, delay) {
|
|
1851
|
+
return ctx.timeout(callback, delay);
|
|
1852
|
+
},
|
|
1853
|
+
every: function(callback, delay) {
|
|
1854
|
+
return ctx.interval(callback, delay);
|
|
1855
|
+
},
|
|
1856
|
+
cancel: function(handle) {
|
|
1857
|
+
if (handle !== void 0 && handle !== null) handle();
|
|
1858
|
+
}
|
|
1859
|
+
};
|
|
1860
|
+
ctx.effect(function() {
|
|
1861
|
+
var cardTag = null;
|
|
1862
|
+
if (typeof document !== "undefined" && document.head !== null) {
|
|
1863
|
+
cardTag = document.createElement("style");
|
|
1864
|
+
cardTag.setAttribute("data-plugin", "dsh-toolfold");
|
|
1865
|
+
cardTag.setAttribute("data-plugin-css", CARD_STYLE_ID);
|
|
1866
|
+
cardTag.textContent = cardCss;
|
|
1867
|
+
document.head.appendChild(cardTag);
|
|
1868
|
+
}
|
|
1869
|
+
var engineInstalled = false;
|
|
1870
|
+
var offEngine = null;
|
|
1871
|
+
function syncEngine() {
|
|
1872
|
+
var on = settings.getSnapshot().enabled !== false;
|
|
1873
|
+
if (on && !engineInstalled) {
|
|
1874
|
+
offEngine = installCollapseEngine(document, settings, timers);
|
|
1875
|
+
engineInstalled = true;
|
|
1876
|
+
} else if (!on && engineInstalled) {
|
|
1877
|
+
offEngine();
|
|
1878
|
+
offEngine = null;
|
|
1879
|
+
engineInstalled = false;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
syncEngine();
|
|
1883
|
+
var offSync = settings.subscribe(function() {
|
|
1884
|
+
syncEngine();
|
|
1885
|
+
});
|
|
1886
|
+
return function() {
|
|
1887
|
+
offSync();
|
|
1888
|
+
if (engineInstalled) {
|
|
1889
|
+
offEngine();
|
|
1890
|
+
engineInstalled = false;
|
|
1891
|
+
offEngine = null;
|
|
1892
|
+
}
|
|
1893
|
+
bridge.dispose();
|
|
1894
|
+
if (cardTag !== null && cardTag.parentNode !== null) cardTag.parentNode.removeChild(cardTag);
|
|
1895
|
+
};
|
|
1896
|
+
});
|
|
1897
|
+
bridge.load();
|
|
1898
|
+
if (acquireReact() === null) return;
|
|
1899
|
+
var slots = ctx.slots;
|
|
1900
|
+
if (slots === void 0) return;
|
|
1901
|
+
try {
|
|
1902
|
+
slots.inject("settings.plugin.item", function* () {
|
|
1903
|
+
yield slots.register({
|
|
1904
|
+
name: "settings.plugin.item",
|
|
1905
|
+
key: "toolfold",
|
|
1906
|
+
id: "toolfold",
|
|
1907
|
+
order: 30,
|
|
1908
|
+
label: "工具折叠",
|
|
1909
|
+
inject: function() {
|
|
1910
|
+
return {
|
|
1911
|
+
hooks: { ccxSettings: settings },
|
|
1912
|
+
setEnabled: function(on) {
|
|
1913
|
+
bridge.write("enabled", on);
|
|
1914
|
+
},
|
|
1915
|
+
setDur: function(ms) {
|
|
1916
|
+
bridge.write("durMs", ms);
|
|
1917
|
+
},
|
|
1918
|
+
setThinkMode: function(mode) {
|
|
1919
|
+
bridge.write("thinkMode", mode);
|
|
1920
|
+
},
|
|
1921
|
+
setSplitThink: function(on) {
|
|
1922
|
+
bridge.write("splitThink", on);
|
|
1923
|
+
},
|
|
1924
|
+
setStats: function(on) {
|
|
1925
|
+
bridge.write("stats", on);
|
|
1926
|
+
},
|
|
1927
|
+
stats: function() {
|
|
1928
|
+
return settings.stats;
|
|
1929
|
+
},
|
|
1930
|
+
bridgeStatus: function() {
|
|
1931
|
+
return bridge.status();
|
|
1932
|
+
},
|
|
1933
|
+
compat: function() {
|
|
1934
|
+
return bridge.compat();
|
|
1935
|
+
},
|
|
1936
|
+
tick: function(cb, ms) {
|
|
1937
|
+
return ctx.interval(cb, ms);
|
|
1938
|
+
},
|
|
1939
|
+
cancelTick: function(handle) {
|
|
1940
|
+
if (handle !== void 0 && handle !== null) handle();
|
|
1941
|
+
}
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
}, SettingsCard);
|
|
1945
|
+
});
|
|
1946
|
+
} catch (err) {
|
|
1947
|
+
console.error("toolfold settings card failed to register:", err);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
};
|
|
1951
|
+
//#endregion
|
|
1952
|
+
return module.exports;
|
|
1953
|
+
}
|
|
1838
1954
|
});
|
|
1955
|
+
|
|
1956
|
+
//# sourceMappingURL=client.js.map
|