dsh-context 0.21.1 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/lib/client.js +2331 -2137
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +584 -0
- package/lib/index.js +746 -636
- package/package.json +19 -12
package/lib/client.js
CHANGED
|
@@ -1,2140 +1,2334 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
function
|
|
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
|
-
function
|
|
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
|
-
function
|
|
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
|
-
function
|
|
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
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
function
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
};
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2
|
+
id: "dsh-context",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
module.exports;
|
|
6
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
7
|
+
//#region src/client/i18n.ts
|
|
8
|
+
const DICT_ZH = {
|
|
9
|
+
"tab": "上下文",
|
|
10
|
+
"cat.system": "系统提示词",
|
|
11
|
+
"cat.tools": "工具定义",
|
|
12
|
+
"cat.user": "用户消息",
|
|
13
|
+
"cat.inject": "注入内容",
|
|
14
|
+
"cat.assistant": "助手消息",
|
|
15
|
+
"cat.tool": "工具结果",
|
|
16
|
+
"overview.title": "当前构成",
|
|
17
|
+
"overview.estimate": "tokens(估算)",
|
|
18
|
+
"overview.free": "剩余窗口",
|
|
19
|
+
"overview.used": "上下文已用",
|
|
20
|
+
"overview.ofUsed": "占已用上下文",
|
|
21
|
+
"overview.compactReserve": "自动压缩预留:占用达 {pct}% 窗口时触发压缩,此区域一般不实际占用",
|
|
22
|
+
"stats.title": "上下文统计",
|
|
23
|
+
"stats.hint": "统计当前保留的历史窗口(与趋势图一致)",
|
|
24
|
+
"stats.turns": "轮次",
|
|
25
|
+
"stats.steps": "步数",
|
|
26
|
+
"stats.injects": "注入",
|
|
27
|
+
"stats.compactions": "压缩",
|
|
28
|
+
"stats.prunes": "剪枝",
|
|
29
|
+
"stats.cacheHit": "缓存命中",
|
|
30
|
+
"stats.cost": "预估费用",
|
|
31
|
+
"stats.costTip": "按 DeepSeek 官方刊例价估算整个会话的累计费用:输入区分缓存命中/未命中,输出含思考;按请求时间区分高峰(UTC 01:00–04:00、06:00–10:00)与空闲时段(半价)。适用于 deepseek-v4-flash / deepseek-v4-pro(不限 provider),价格写死在代码中,仅供参考。",
|
|
32
|
+
"stats.costPriceHead": "每百万 tokens 价格(高峰 | 空闲半价):",
|
|
33
|
+
"stats.costHit": "命中",
|
|
34
|
+
"stats.costMiss": "未命中",
|
|
35
|
+
"stats.costOut": "输出",
|
|
36
|
+
"plugin.title": "插件信息",
|
|
37
|
+
"plugin.hint": "The best DSH context plugin ⭐",
|
|
38
|
+
"plugin.name": "插件",
|
|
39
|
+
"plugin.github": "GitHub",
|
|
40
|
+
"tools.top": "工具定义 Top:",
|
|
41
|
+
"tools.more": "等 {n} 个",
|
|
42
|
+
"trend.title": "历史趋势",
|
|
43
|
+
"gran.step": "步骤",
|
|
44
|
+
"gran.turn": "轮次",
|
|
45
|
+
"trend.hint": "悬停查看详情,✂ 表示压缩/剪枝,Step/Turn 切换粒度",
|
|
46
|
+
"trend.empty": "发起一轮对话后,这里会展示每次模型请求的上下文构成",
|
|
47
|
+
"detail.step": "第 {t} 轮 · 第 {s} 步",
|
|
48
|
+
"detail.turn": "第 {t} 轮 · 共 {n} 步",
|
|
49
|
+
"detail.lastStep": "末步",
|
|
50
|
+
"detail.estTotal": "估算合计 ≈ {n}",
|
|
51
|
+
"detail.actual": "实际 prompt {n}",
|
|
52
|
+
"detail.output": "输出 {n}",
|
|
53
|
+
"events.title": "上下文事件",
|
|
54
|
+
"events.empty": "暂无上下文事件(压缩、注入、模型切换会出现在这里)",
|
|
55
|
+
"events.at": "第 {t} 轮 · 第 {s} 步",
|
|
56
|
+
"events.range": "第 {t} 轮 · 第 {a}→{b} 步",
|
|
57
|
+
"events.rangeTo": "第 {a} 轮 · 第 {as} 步 → 第 {b} 轮 · 第 {bs} 步",
|
|
58
|
+
"kind.inject": "注入",
|
|
59
|
+
"kind.compaction": "压缩",
|
|
60
|
+
"kind.prune": "剪枝",
|
|
61
|
+
"kind.model": "切换",
|
|
62
|
+
"nodes.title": "消息构成",
|
|
63
|
+
"nodes.hint": "当前模型可见的消息,最新在前",
|
|
64
|
+
"nodes.more": "… 更早的 {n} 条消息已省略",
|
|
65
|
+
"nodes.empty": "当前没有模型可见的消息",
|
|
66
|
+
"loading": "正在读取会话日志…",
|
|
67
|
+
"error": "上下文数据读取失败:",
|
|
68
|
+
"error.retry": "重试",
|
|
69
|
+
"footer": "估算口径:与 dsh 内置 tokenMeter 相同的固定密度启发式(约 4 字符 ≈ 1 token);「实际」为供应商上报用量。",
|
|
70
|
+
"tip.step": "第 {t} 轮 · 第{s}步",
|
|
71
|
+
"tip.turn": "第 {t} 轮 · 共 {n} 步",
|
|
72
|
+
"tip.total": "合计 ≈ {n}",
|
|
73
|
+
"tip.actual": "(实际 {n})",
|
|
74
|
+
"ev.compaction": "压缩上下文(摘要替换 {n} 条消息)",
|
|
75
|
+
"ev.prune": "剪枝工具输出",
|
|
76
|
+
"ev.skill": "Skill 注入({name})",
|
|
77
|
+
"ev.model": "模型切换:{a} → {b}",
|
|
78
|
+
"form.instructions": "指令注入",
|
|
79
|
+
"form.catalog": "目录更新",
|
|
80
|
+
"form.snapshot": "状态快照",
|
|
81
|
+
"form.notice": "通知",
|
|
82
|
+
"form.relay": "代理转发",
|
|
83
|
+
"form.recall": "历史召回",
|
|
84
|
+
"form.context": "上下文注入",
|
|
85
|
+
"node.toolResult": "工具结果",
|
|
86
|
+
"node.calls": "调用 ",
|
|
87
|
+
"node.empty": "(空回复)",
|
|
88
|
+
"node.nonText": "(非文本消息)",
|
|
89
|
+
"node.snapshot": "快照: ",
|
|
90
|
+
"cmd.desc": "查看当前上下文构成,浏览各步骤组成",
|
|
91
|
+
"cmd.close": "关闭",
|
|
92
|
+
"browser.title": "上下文浏览器",
|
|
93
|
+
"browser.live": "当前(下一次请求)",
|
|
94
|
+
"browser.liveNow": "当前 · 下一次请求",
|
|
95
|
+
"browser.items": "{n} 项",
|
|
96
|
+
"browser.missingLive": "… 另有 {n} 条更早的消息也在上下文中(超出展示窗口)",
|
|
97
|
+
"browser.approx": "该步骤涉及的部分已移除消息超出保留范围,以下为近似构成",
|
|
98
|
+
"browser.deltaHint": "对比上轮末步的变动",
|
|
99
|
+
"browser.noHeader": "此数据来自旧版插件:仅提供 token 估算,无实际内容",
|
|
100
|
+
"browser.noEpoch": "该步骤的头部内容(系统提示词 / 工具定义)不在保留范围内",
|
|
101
|
+
"browser.noContent": "完整内容不在当前加载的消息窗口内(在聊天页加载更早历史后可查看)",
|
|
102
|
+
"browser.loading": "正在从更早的会话历史加载完整内容…",
|
|
103
|
+
"browser.preview": "预览",
|
|
104
|
+
"tool.desc": "描述",
|
|
105
|
+
"tool.params": "参数",
|
|
106
|
+
"tool.paramsEmpty": "(无参数)",
|
|
107
|
+
"tool.jsonToggle": "查看原始 JSON",
|
|
108
|
+
"tool.jsonHide": "收起",
|
|
109
|
+
"rich.raw": "原文",
|
|
110
|
+
"rich.md": "Markdown",
|
|
111
|
+
"rich.toMd": "按 Markdown 渲染查看",
|
|
112
|
+
"rich.toRaw": "查看原始文本",
|
|
113
|
+
"block.thinking": "思考",
|
|
114
|
+
"block.answer": "回答",
|
|
115
|
+
"attach.images": "图片附件",
|
|
116
|
+
"attach.other": "其他内容",
|
|
117
|
+
"attach.image": "图片",
|
|
118
|
+
"attach.open": "查看原图",
|
|
119
|
+
"attach.loading": "…",
|
|
120
|
+
"attach.loadFailed": "加载失败 · 点击重试",
|
|
121
|
+
"attach.orig": "原图 {d}"
|
|
122
|
+
};
|
|
123
|
+
const DICT_EN = {
|
|
124
|
+
"tab": "Context",
|
|
125
|
+
"cat.system": "System Prompt",
|
|
126
|
+
"cat.tools": "Tool Schemas",
|
|
127
|
+
"cat.user": "User Messages",
|
|
128
|
+
"cat.inject": "Injected Context",
|
|
129
|
+
"cat.assistant": "Assistant Messages",
|
|
130
|
+
"cat.tool": "Tool Results",
|
|
131
|
+
"overview.title": "Current Context",
|
|
132
|
+
"overview.estimate": "tokens (estimated)",
|
|
133
|
+
"overview.free": "Free Window",
|
|
134
|
+
"overview.used": "of context used",
|
|
135
|
+
"overview.ofUsed": "of used context",
|
|
136
|
+
"overview.compactReserve": "Auto-compaction reserve: auto-compaction triggers at {pct}% of the window — this area is normally kept as headroom, not actually used",
|
|
137
|
+
"stats.title": "Context Stats",
|
|
138
|
+
"stats.hint": "Over the retained history window (same as the History chart)",
|
|
139
|
+
"stats.turns": "Turns",
|
|
140
|
+
"stats.steps": "Steps",
|
|
141
|
+
"stats.injects": "Injections",
|
|
142
|
+
"stats.compactions": "Compactions",
|
|
143
|
+
"stats.prunes": "Prunes",
|
|
144
|
+
"stats.cacheHit": "Cache Hits",
|
|
145
|
+
"stats.cost": "Est. Cost",
|
|
146
|
+
"stats.costTip": "Rough cumulative cost of the whole session at DeepSeek’s list prices: input split by cache hit/miss, output includes reasoning; each request is priced at the peak (01:00–04:00, 06:00–10:00 UTC) or half-price off-peak rate by its time. Applies to deepseek-v4-flash / deepseek-v4-pro on any provider; prices are hardcoded, for reference only.",
|
|
147
|
+
"stats.costPriceHead": "Per-1M-token rates (peak | off-peak at half price):",
|
|
148
|
+
"stats.costHit": "hit",
|
|
149
|
+
"stats.costMiss": "miss",
|
|
150
|
+
"stats.costOut": "output",
|
|
151
|
+
"plugin.title": "Plugin Info",
|
|
152
|
+
"plugin.hint": "The best DSH context plugin ⭐",
|
|
153
|
+
"plugin.name": "Plugin",
|
|
154
|
+
"plugin.github": "GitHub",
|
|
155
|
+
"tools.top": "Top Tool Schemas:",
|
|
156
|
+
"tools.more": "of {n}",
|
|
157
|
+
"trend.title": "Context History",
|
|
158
|
+
"gran.step": "Step",
|
|
159
|
+
"gran.turn": "Turn",
|
|
160
|
+
"trend.hint": "Hover to view details, ✂ marks compaction/prune, Step/Turn switches granularity",
|
|
161
|
+
"trend.empty": "Send a message and each model request’s context makeup shows up here",
|
|
162
|
+
"detail.step": "Turn {t} · Step {s}",
|
|
163
|
+
"detail.turn": "Turn {t} · {n} steps",
|
|
164
|
+
"detail.lastStep": "Last Step",
|
|
165
|
+
"detail.estTotal": "Estimated ≈ {n}",
|
|
166
|
+
"detail.actual": "Actual Prompt {n}",
|
|
167
|
+
"detail.output": "Output {n}",
|
|
168
|
+
"events.title": "Context Events",
|
|
169
|
+
"events.empty": "No context events yet (compaction, injections, model switches appear here)",
|
|
170
|
+
"events.at": "Turn {t} · Step {s}",
|
|
171
|
+
"events.range": "Turn {t} · Step {a}→{b}",
|
|
172
|
+
"events.rangeTo": "Turn {a} · Step {as} → Turn {b} · Step {bs}",
|
|
173
|
+
"kind.inject": "Inject",
|
|
174
|
+
"kind.compaction": "Compact",
|
|
175
|
+
"kind.prune": "Prune",
|
|
176
|
+
"kind.model": "Switch",
|
|
177
|
+
"nodes.title": "Messages",
|
|
178
|
+
"nodes.hint": "currently model-visible, newest first",
|
|
179
|
+
"nodes.more": "… {n} earlier messages omitted",
|
|
180
|
+
"nodes.empty": "No model-visible messages right now",
|
|
181
|
+
"loading": "Reading the session log…",
|
|
182
|
+
"error": "Failed to read context data: ",
|
|
183
|
+
"error.retry": "Retry",
|
|
184
|
+
"footer": "Estimate: same fixed-density heuristic as dsh’s built-in tokenMeter (~4 chars ≈ 1 token); “actual” is provider-reported usage.",
|
|
185
|
+
"tip.step": "Turn {t} · Step {s}",
|
|
186
|
+
"tip.turn": "Turn {t} · {n} steps",
|
|
187
|
+
"tip.total": "Total ≈ {n}",
|
|
188
|
+
"tip.actual": " (actual {n})",
|
|
189
|
+
"ev.compaction": "Context compacted (summary replaced {n} messages)",
|
|
190
|
+
"ev.prune": "Tool output pruned",
|
|
191
|
+
"ev.skill": "Skill injected ({name})",
|
|
192
|
+
"ev.model": "Model switched: {a} → {b}",
|
|
193
|
+
"form.instructions": "Instructions",
|
|
194
|
+
"form.catalog": "Catalog Update",
|
|
195
|
+
"form.snapshot": "State Snapshot",
|
|
196
|
+
"form.notice": "Notice",
|
|
197
|
+
"form.relay": "Agent Relay",
|
|
198
|
+
"form.recall": "Recall",
|
|
199
|
+
"form.context": "Context Injection",
|
|
200
|
+
"node.toolResult": "Tool Result",
|
|
201
|
+
"node.calls": "Calls ",
|
|
202
|
+
"node.empty": "(empty reply)",
|
|
203
|
+
"node.nonText": "(non-text message)",
|
|
204
|
+
"node.snapshot": "Snapshot: ",
|
|
205
|
+
"cmd.desc": "View current context makeup, browse per-step composition",
|
|
206
|
+
"cmd.close": "Close",
|
|
207
|
+
"browser.title": "Context Browser",
|
|
208
|
+
"browser.live": "Live (Next Request)",
|
|
209
|
+
"browser.liveNow": "Live · Next Request",
|
|
210
|
+
"browser.items": "{n} Items",
|
|
211
|
+
"browser.missingLive": "… {n} earlier messages are also part of the context (outside the served window)",
|
|
212
|
+
"browser.approx": "Some removed messages of this step exceed retention — the makeup below is approximate",
|
|
213
|
+
"browser.deltaHint": "vs previous turn",
|
|
214
|
+
"browser.noHeader": "Served by an older plugin build: token estimates only, no content",
|
|
215
|
+
"browser.noEpoch": "The header content (System Prompt / Tool Schemas) of this step is outside retention",
|
|
216
|
+
"browser.noContent": "Full content is outside the loaded message window (load older history in Chat to view)",
|
|
217
|
+
"browser.loading": "Loading full content from older session history…",
|
|
218
|
+
"browser.preview": "Preview",
|
|
219
|
+
"tool.desc": "Description",
|
|
220
|
+
"tool.params": "Parameters",
|
|
221
|
+
"tool.paramsEmpty": "(no parameters)",
|
|
222
|
+
"tool.jsonToggle": "View Raw JSON",
|
|
223
|
+
"tool.jsonHide": "Collapse",
|
|
224
|
+
"rich.raw": "Raw",
|
|
225
|
+
"rich.md": "Markdown",
|
|
226
|
+
"rich.toMd": "View as Markdown",
|
|
227
|
+
"rich.toRaw": "View Raw Text",
|
|
228
|
+
"block.thinking": "Reasoning",
|
|
229
|
+
"block.answer": "Response",
|
|
230
|
+
"attach.images": "Images",
|
|
231
|
+
"attach.other": "Other content",
|
|
232
|
+
"attach.image": "Image",
|
|
233
|
+
"attach.open": "Open full image",
|
|
234
|
+
"attach.loading": "…",
|
|
235
|
+
"attach.loadFailed": "Load failed · click to retry",
|
|
236
|
+
"attach.orig": "original {d}"
|
|
237
|
+
};
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/client/modalStore.ts
|
|
240
|
+
const stores = /* @__PURE__ */ new Map();
|
|
241
|
+
function modalStoreOf(sessionId) {
|
|
242
|
+
const existing = stores.get(sessionId);
|
|
243
|
+
if (existing !== void 0) return existing;
|
|
244
|
+
let open = false;
|
|
245
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
246
|
+
const store = {
|
|
247
|
+
subscribe(listener) {
|
|
248
|
+
listeners.add(listener);
|
|
249
|
+
return () => {
|
|
250
|
+
listeners.delete(listener);
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
getSnapshot: () => open,
|
|
254
|
+
set(next) {
|
|
255
|
+
if (next === open) return;
|
|
256
|
+
open = next;
|
|
257
|
+
for (const listener of listeners) listener();
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
stores.set(sessionId, store);
|
|
261
|
+
return store;
|
|
262
|
+
}
|
|
263
|
+
const pendingConsume = /* @__PURE__ */ new Map();
|
|
264
|
+
function setPendingConsume(sessionId, guard) {
|
|
265
|
+
pendingConsume.set(sessionId, guard);
|
|
266
|
+
}
|
|
267
|
+
/** Take (and clear) the guard recorded by the open path, if any. */
|
|
268
|
+
function takePendingConsume(sessionId) {
|
|
269
|
+
const guard = pendingConsume.get(sessionId);
|
|
270
|
+
if (guard !== void 0) pendingConsume.delete(sessionId);
|
|
271
|
+
return guard;
|
|
272
|
+
}
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/client/command.ts
|
|
275
|
+
/**
|
|
276
|
+
* `/context` — a client-owned slash command that opens the context modal
|
|
277
|
+
* (current composition + recent trend) in the center of the page.
|
|
278
|
+
*
|
|
279
|
+
* Implemented as the plugin's own '/' trigger source instead of a host
|
|
280
|
+
* command: nothing is dispatched to the host, no session log records are
|
|
281
|
+
* written, and nothing becomes model-visible — the invocation never enters
|
|
282
|
+
* the message history. Both paths answer `'handled'` and open the modal,
|
|
283
|
+
* leaving the `/context` token in the composer while it is open; the modal's
|
|
284
|
+
* close path consumes the token then (see modalStore.ts).
|
|
285
|
+
*/
|
|
286
|
+
const COMMAND = "context";
|
|
287
|
+
const LINE = "/context";
|
|
288
|
+
function registerContextCommand(ctx, kit) {
|
|
289
|
+
ctx.effect(() => {
|
|
290
|
+
const inputTriggers = ctx.get("inputTriggers");
|
|
291
|
+
if (inputTriggers === void 0) return () => {};
|
|
292
|
+
return inputTriggers.registerSource({
|
|
293
|
+
trigger: "/",
|
|
294
|
+
name: COMMAND,
|
|
295
|
+
order: 1,
|
|
296
|
+
candidates: (_session, req) => {
|
|
297
|
+
if (req.position !== "leading") return Promise.resolve([]);
|
|
298
|
+
const query = req.query.trim().toLowerCase();
|
|
299
|
+
if (query !== "" && !COMMAND.startsWith(query)) return Promise.resolve([]);
|
|
300
|
+
return Promise.resolve([{
|
|
301
|
+
name: COMMAND,
|
|
302
|
+
description: kit.t("cmd.desc")
|
|
303
|
+
}]);
|
|
304
|
+
},
|
|
305
|
+
onPick: (pick) => {
|
|
306
|
+
setPendingConsume(pick.session.sessionId, {
|
|
307
|
+
kind: "span",
|
|
308
|
+
span: pick.span
|
|
309
|
+
});
|
|
310
|
+
modalStoreOf(pick.session.sessionId).set(true);
|
|
311
|
+
return "handled";
|
|
312
|
+
},
|
|
313
|
+
matchEnter: (session, line) => {
|
|
314
|
+
if (line !== LINE) return Promise.resolve(void 0);
|
|
315
|
+
setPendingConsume(session.sessionId, {
|
|
316
|
+
kind: "bare-token",
|
|
317
|
+
token: LINE
|
|
318
|
+
});
|
|
319
|
+
modalStoreOf(session.sessionId).set(true);
|
|
320
|
+
return Promise.resolve("handled");
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}, "dsh-context: /context command");
|
|
324
|
+
}
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region src/client/categories.ts
|
|
327
|
+
const CATS = [
|
|
328
|
+
{
|
|
329
|
+
key: "system",
|
|
330
|
+
color: "#6366f1"
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
key: "tools",
|
|
334
|
+
color: "#f59e0b"
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
key: "user",
|
|
338
|
+
color: "#22c55e"
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
key: "inject",
|
|
342
|
+
color: "#a855f7"
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
key: "assistant",
|
|
346
|
+
color: "#3b82f6"
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
key: "tool",
|
|
350
|
+
color: "#14b8a6"
|
|
351
|
+
}
|
|
352
|
+
];
|
|
353
|
+
function partsOf(breakdown) {
|
|
354
|
+
return CATS.map((c) => {
|
|
355
|
+
return {
|
|
356
|
+
key: c.key,
|
|
357
|
+
color: c.color,
|
|
358
|
+
value: breakdown[c.key] || 0
|
|
359
|
+
};
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Reproportion heuristic parts so they sum to a provider-anchored target —
|
|
364
|
+
* the same trick the official ContextMeter uses: the heuristic breakdown
|
|
365
|
+
* supplies the composition RATIOS, the provider sample the total. Returns
|
|
366
|
+
* the parts unchanged when no anchor applies.
|
|
367
|
+
*/
|
|
368
|
+
function anchoredParts(parts, target) {
|
|
369
|
+
if (target === null || target <= 0) return parts;
|
|
370
|
+
let total = 0;
|
|
371
|
+
for (const p of parts) total += p.value;
|
|
372
|
+
if (total <= 0 || total === target) return parts;
|
|
373
|
+
const scale = target / total;
|
|
374
|
+
return parts.map((p) => ({
|
|
375
|
+
...p,
|
|
376
|
+
value: Math.round(p.value * scale)
|
|
377
|
+
}));
|
|
378
|
+
}
|
|
379
|
+
//#endregion
|
|
380
|
+
//#region src/client/headline.ts
|
|
381
|
+
function headlineOf(data, pressure = null) {
|
|
382
|
+
const current = data.current;
|
|
383
|
+
const projected = pressure !== null && typeof pressure.projectedTokens === "number" ? pressure.projectedTokens : void 0;
|
|
384
|
+
const requests = data.requests;
|
|
385
|
+
const lastReq = requests.length > 0 ? requests[requests.length - 1] : null;
|
|
386
|
+
const derived = lastReq !== null && typeof lastReq.prompt === "number" ? lastReq.prompt + (current.total - lastReq.total) : void 0;
|
|
387
|
+
const occupancyTokens = projected ?? derived ?? null;
|
|
388
|
+
const window = pressure !== null && typeof pressure.contextWindow === "number" ? pressure.contextWindow : data.contextWindow;
|
|
389
|
+
const tokens = occupancyTokens ?? current.total;
|
|
390
|
+
return {
|
|
391
|
+
tokens,
|
|
392
|
+
window,
|
|
393
|
+
pct: window !== void 0 && window > 0 ? Math.min(100, Math.round(tokens / window * 100)) : null,
|
|
394
|
+
parts: anchoredParts(partsOf(current), occupancyTokens !== null && tokens > 0 ? tokens : null)
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/client/services.ts
|
|
399
|
+
/**
|
|
400
|
+
* Narrow an unknown projection value to a string-keyed record, or null when
|
|
401
|
+
* it is not one. The boundary type is Record<string, unknown> on purpose:
|
|
402
|
+
* every field read below must re-prove itself (the no-white-screen
|
|
403
|
+
* guarantee), so no field may borrow the wire type before its check.
|
|
404
|
+
*/
|
|
405
|
+
function asRecord(value) {
|
|
406
|
+
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
407
|
+
return value;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Safe finite-number read: a missing/non-numeric/NaN field degrades to 0
|
|
411
|
+
* instead of leaking into the UI as NaN percentages or broken arithmetic.
|
|
412
|
+
*/
|
|
413
|
+
function numOf(value) {
|
|
414
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
415
|
+
}
|
|
416
|
+
/** The usable (non-null object) entries of a delivered list; [] when the field is not a list. */
|
|
417
|
+
function objectsOf(value) {
|
|
418
|
+
if (!Array.isArray(value)) return [];
|
|
419
|
+
return value.filter((v) => v !== null && typeof v === "object");
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Narrow a delivered projection value to a RENDER-SAFE context timeline —
|
|
423
|
+
* the client's no-white-screen guarantee against backend/parse failures.
|
|
424
|
+
*
|
|
425
|
+
* A value that is not a record at all (capability absent, nothing delivered
|
|
426
|
+
* yet) stays `null` and callers show the loading screen. A record that fails
|
|
427
|
+
* the wire shape (corrupt checkpoint restore, a failed/older host payload,
|
|
428
|
+
* plugin drift) is SANITIZED instead of rejected: every collection becomes
|
|
429
|
+
* an array, non-object entries are dropped, `current` becomes a numeric
|
|
430
|
+
* breakdown, and wrong-typed scalars are dropped or zeroed — so the whole
|
|
431
|
+
* tab still renders with every usable piece of data instead of throwing
|
|
432
|
+
* during render and unmounting the conversation view.
|
|
433
|
+
*/
|
|
434
|
+
function timelineOf(value) {
|
|
435
|
+
const data = asRecord(value);
|
|
436
|
+
if (data === null) return null;
|
|
437
|
+
const current = data.current;
|
|
438
|
+
if (current !== null && typeof current === "object" && [
|
|
439
|
+
"system",
|
|
440
|
+
"tools",
|
|
441
|
+
"user",
|
|
442
|
+
"inject",
|
|
443
|
+
"assistant",
|
|
444
|
+
"tool",
|
|
445
|
+
"total"
|
|
446
|
+
].every((k) => typeof current[k] === "number") && Array.isArray(data.requests) && Array.isArray(data.events) && Array.isArray(data.nodes) && Array.isArray(data.archive) && Array.isArray(data.toolList)) return data;
|
|
447
|
+
const safeCurrent = current !== null && typeof current === "object" ? current : {};
|
|
448
|
+
const cost = typeof data.cost === "object" && data.cost !== null && !Array.isArray(data.cost) ? data.cost : void 0;
|
|
449
|
+
return {
|
|
450
|
+
ok: true,
|
|
451
|
+
...typeof data.model === "string" ? { model: data.model } : {},
|
|
452
|
+
...typeof data.provider === "string" ? { provider: data.provider } : {},
|
|
453
|
+
...typeof data.contextWindow === "number" ? { contextWindow: data.contextWindow } : {},
|
|
454
|
+
current: {
|
|
455
|
+
system: numOf(safeCurrent.system),
|
|
456
|
+
tools: numOf(safeCurrent.tools),
|
|
457
|
+
user: numOf(safeCurrent.user),
|
|
458
|
+
inject: numOf(safeCurrent.inject),
|
|
459
|
+
assistant: numOf(safeCurrent.assistant),
|
|
460
|
+
tool: numOf(safeCurrent.tool),
|
|
461
|
+
total: numOf(safeCurrent.total)
|
|
462
|
+
},
|
|
463
|
+
toolList: objectsOf(data.toolList),
|
|
464
|
+
requests: objectsOf(data.requests),
|
|
465
|
+
events: objectsOf(data.events),
|
|
466
|
+
nodes: objectsOf(data.nodes),
|
|
467
|
+
droppedNodes: numOf(data.droppedNodes),
|
|
468
|
+
archive: objectsOf(data.archive),
|
|
469
|
+
...cost !== void 0 ? { cost } : {},
|
|
470
|
+
...typeof data.surfaceFloor === "number" ? { surfaceFloor: data.surfaceFloor } : {},
|
|
471
|
+
...typeof data.archiveFloor === "number" ? { archiveFloor: data.archiveFloor } : {}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Narrow a delivered projection value to the official token-meter
|
|
476
|
+
* `contextPressure` projection (provider-anchored occupancy of the next
|
|
477
|
+
* request). Absent key or value = the meter's projection is not composed
|
|
478
|
+
* (e.g. a harness without the session-projection registry) — callers fall
|
|
479
|
+
* back to their derived anchor, so the UI degrades gracefully.
|
|
480
|
+
*/
|
|
481
|
+
function contextPressureOf(value) {
|
|
482
|
+
return asRecord(value);
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Narrow a delivered projection value to the official token-meter
|
|
486
|
+
* `tokenUsage` projection (durable cumulative provider usage). Absent key or
|
|
487
|
+
* value = the meter's projection is not composed (or no request has reported
|
|
488
|
+
* usage yet) — callers drop the cache-hit cell to a dash.
|
|
489
|
+
*/
|
|
490
|
+
function tokenUsageOf(value) {
|
|
491
|
+
return asRecord(value);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Narrow a delivered projection value to the plugin's `contextHeaders`
|
|
495
|
+
* (request-header content epochs). Absent key = an older Host half without
|
|
496
|
+
* the companion unit — the Context browser degrades its system/tools
|
|
497
|
+
* sections to tokens-only with a note.
|
|
498
|
+
*
|
|
499
|
+
* Entry-level shape is checked too: a malformed epoch (corrupt payload with
|
|
500
|
+
* a missing tools list or wrong-typed system prompt) would crash the
|
|
501
|
+
* browser's tools/sections reads, so the WHOLE projection degrades to null
|
|
502
|
+
* and the card falls back to its tokens-only note.
|
|
503
|
+
*/
|
|
504
|
+
function headersOf(value) {
|
|
505
|
+
const headers = asRecord(value);
|
|
506
|
+
if (headers === null || !Array.isArray(headers.headers)) return null;
|
|
507
|
+
for (const h of headers.headers) {
|
|
508
|
+
if (h === null || typeof h !== "object") return null;
|
|
509
|
+
const entry = h;
|
|
510
|
+
if (!Array.isArray(entry.tools)) return null;
|
|
511
|
+
if (entry.system !== void 0 && typeof entry.system !== "string") return null;
|
|
512
|
+
}
|
|
513
|
+
return headers;
|
|
514
|
+
}
|
|
515
|
+
//#endregion
|
|
516
|
+
//#region src/client/assemble.ts
|
|
517
|
+
/** The header epoch in force at `seq` (last logged before it), or the newest. */
|
|
518
|
+
function headerAt(headers, seq) {
|
|
519
|
+
if (headers === null || headers.headers.length === 0) return null;
|
|
520
|
+
if (seq === null) return headers.headers[headers.headers.length - 1];
|
|
521
|
+
for (let i = headers.headers.length - 1; i >= 0; i--) if (headers.headers[i].seq < seq) return headers.headers[i];
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Reconstruct the assembled surface for one step (`seq` = the request
|
|
526
|
+
* record's seq) or the live surface (`seq` = null).
|
|
527
|
+
*/
|
|
528
|
+
function assemble(data, headers, seq) {
|
|
529
|
+
const live = seq === null;
|
|
530
|
+
let nodes;
|
|
531
|
+
if (live) nodes = data.nodes.slice();
|
|
532
|
+
else {
|
|
533
|
+
const picked = [];
|
|
534
|
+
for (const n of data.nodes) if (n.seq < seq) picked.push(n);
|
|
535
|
+
for (const n of data.archive) if (n.seq < seq && n.gone !== void 0 && n.gone > seq) picked.push(n);
|
|
536
|
+
nodes = picked;
|
|
537
|
+
}
|
|
538
|
+
nodes.sort((a, b) => a.seq - b.seq);
|
|
539
|
+
let missingLive = 0;
|
|
540
|
+
if (data.droppedNodes > 0) {
|
|
541
|
+
if (live || data.surfaceFloor !== void 0 && seq > data.surfaceFloor) missingLive = data.droppedNodes;
|
|
542
|
+
}
|
|
543
|
+
const approximate = !live && data.archiveFloor !== void 0 && seq < data.archiveFloor;
|
|
544
|
+
return {
|
|
545
|
+
live,
|
|
546
|
+
header: headerAt(headers, seq),
|
|
547
|
+
nodes,
|
|
548
|
+
missingLive,
|
|
549
|
+
approximate
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
//#endregion
|
|
553
|
+
//#region src/client/react.ts
|
|
554
|
+
const React = require("react");
|
|
555
|
+
const h = React.createElement;
|
|
556
|
+
//#endregion
|
|
557
|
+
//#region src/client/components/nodes.tsx
|
|
558
|
+
/** One-line preview of a surface node (shared by the node list and the Context browser's element rows). */
|
|
559
|
+
function makeNodeText(kit) {
|
|
560
|
+
const { t } = kit;
|
|
561
|
+
return function nodeText(n) {
|
|
562
|
+
if (n.cat === "tool") return t("node.toolResult") + (n.tool ? " ← " + n.tool : "") + (n.err ? " ⚠" : "");
|
|
563
|
+
if (n.skill) return "Skill: " + n.skill;
|
|
564
|
+
if (n.calls) return t("node.calls") + n.calls.join(", ");
|
|
565
|
+
if (n.text) return n.form === "snapshot" ? t("node.snapshot") + n.text : n.text;
|
|
566
|
+
if (n.cat === "assistant") return t("node.empty");
|
|
567
|
+
if (n.cat === "inject") return t("form." + (n.form || "context"));
|
|
568
|
+
return t("node.nonText");
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function makeNodeList(kit) {
|
|
572
|
+
const { t, fmt, fmtTime } = kit;
|
|
573
|
+
const nodeText = makeNodeText(kit);
|
|
574
|
+
return function NodeList(props) {
|
|
575
|
+
if (props.nodes.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("nodes.empty"));
|
|
576
|
+
const catColor = {};
|
|
577
|
+
for (const c of CATS) catColor[c.key] = c.color;
|
|
578
|
+
const rows = props.nodes.slice().reverse();
|
|
579
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-nodes" }, props.dropped > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-nodes-more" }, t("nodes.more", { n: props.dropped })) : null, rows.map((n) => {
|
|
580
|
+
const text = nodeText(n);
|
|
581
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
582
|
+
key: n.seq,
|
|
583
|
+
className: "lc-node"
|
|
584
|
+
}, /* @__PURE__ */ React.createElement("i", { style: { background: catColor[n.cat] || "#999" } }), /* @__PURE__ */ React.createElement("span", {
|
|
585
|
+
className: "lc-node-preview",
|
|
586
|
+
title: text
|
|
587
|
+
}, text), typeof n.time === "number" ? /* @__PURE__ */ React.createElement("span", { className: "lc-node-time" }, fmtTime(n.time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-node-tokens" }, fmt(n.tokens)));
|
|
588
|
+
}));
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region src/client/format.ts
|
|
593
|
+
/**
|
|
594
|
+
* Number/time formatting for the UI. `fmt` uses the same k/M suffix style
|
|
595
|
+
* everywhere (bars, details, stats); `fmtTime` renders a local HH:MM:SS.
|
|
596
|
+
*/
|
|
597
|
+
function fmt(n) {
|
|
598
|
+
if (n === void 0 || n === null || isNaN(n)) return "—";
|
|
599
|
+
if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
|
|
600
|
+
if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
|
|
601
|
+
return String(Math.round(n));
|
|
602
|
+
}
|
|
603
|
+
/** Byte sizes for attachment metadata (1 kB = 1000 B, matching the k/M style of `fmt`). */
|
|
604
|
+
function fmtBytes(n) {
|
|
605
|
+
if (n === void 0 || n === null || isNaN(n) || n < 0) return "—";
|
|
606
|
+
if (n >= 1e6) return (n / 1e6).toFixed(1) + " MB";
|
|
607
|
+
if (n >= 1e3) return (n / 1e3).toFixed(1) + " kB";
|
|
608
|
+
return String(Math.round(n)) + " B";
|
|
609
|
+
}
|
|
610
|
+
function fmtTime(t) {
|
|
611
|
+
const d = new Date(t);
|
|
612
|
+
if (isNaN(d.getTime())) return "—";
|
|
613
|
+
return d.toLocaleTimeString("en-GB", { hour12: false });
|
|
614
|
+
}
|
|
615
|
+
//#endregion
|
|
616
|
+
//#region src/client/components/images.tsx
|
|
617
|
+
/**
|
|
618
|
+
* Narrow an unknown content block to a durable image reference. Accepts both
|
|
619
|
+
* raw message blocks (`{ type: 'image', attachment }`) and the conversation
|
|
620
|
+
* snapshot's assistant blocks (`{ kind: 'image', attachment }`); everything
|
|
621
|
+
* else returns null. Lenient on the ref's optional facts (name/bytes/dims) —
|
|
622
|
+
* `resolveImage` reads only `attachmentId`.
|
|
623
|
+
*/
|
|
624
|
+
function imageRefOf(block) {
|
|
625
|
+
if (block === null || typeof block !== "object") return null;
|
|
626
|
+
const b = block;
|
|
627
|
+
if (b.type !== "image" && b.kind !== "image") return null;
|
|
628
|
+
const a = b.attachment;
|
|
629
|
+
if (a === null || typeof a !== "object") return null;
|
|
630
|
+
const r = a;
|
|
631
|
+
if (typeof r.attachmentId !== "string" || r.attachmentId === "") return null;
|
|
632
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : void 0;
|
|
633
|
+
const orig = r.originalDimensions !== null && typeof r.originalDimensions === "object" ? r.originalDimensions : void 0;
|
|
634
|
+
const origDims = orig !== void 0 && num(orig.width) !== void 0 && num(orig.height) !== void 0 ? {
|
|
635
|
+
width: num(orig.width),
|
|
636
|
+
height: num(orig.height)
|
|
637
|
+
} : void 0;
|
|
638
|
+
return {
|
|
639
|
+
attachmentId: r.attachmentId,
|
|
640
|
+
...typeof r.name === "string" && r.name !== "" ? { name: r.name } : {},
|
|
641
|
+
...num(r.bytes) !== void 0 ? { bytes: num(r.bytes) } : {},
|
|
642
|
+
...num(r.width) !== void 0 ? { width: num(r.width) } : {},
|
|
643
|
+
...num(r.height) !== void 0 ? { height: num(r.height) } : {},
|
|
644
|
+
...origDims !== void 0 ? { originalDimensions: origDims } : {}
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* One attachment card: a 64px cover tile (click opens the full image in a
|
|
649
|
+
* new tab; load failures retry on click) beside a metadata column — display
|
|
650
|
+
* name, normalized dimensions (plus the pre-normalization size dsh 0.1.1
|
|
651
|
+
* records), and the stored byte size.
|
|
652
|
+
*/
|
|
653
|
+
function makeImageCard(kit) {
|
|
654
|
+
const { t } = kit;
|
|
655
|
+
return function ImageCard(props) {
|
|
656
|
+
const { attachment, load } = props;
|
|
657
|
+
const [src, setSrc] = React.useState(null);
|
|
658
|
+
const [error, setError] = React.useState(false);
|
|
659
|
+
const [attempt, setAttempt] = React.useState(0);
|
|
660
|
+
React.useEffect(() => {
|
|
661
|
+
if (load === void 0) return;
|
|
662
|
+
let live = true;
|
|
663
|
+
setError(false);
|
|
664
|
+
setSrc(null);
|
|
665
|
+
load(attachment).then((url) => {
|
|
666
|
+
if (live) setSrc(url);
|
|
667
|
+
}).catch(() => {
|
|
668
|
+
if (live) setError(true);
|
|
669
|
+
});
|
|
670
|
+
return () => {
|
|
671
|
+
live = false;
|
|
672
|
+
};
|
|
673
|
+
}, [
|
|
674
|
+
attachment,
|
|
675
|
+
load,
|
|
676
|
+
attempt
|
|
677
|
+
]);
|
|
678
|
+
const name = attachment.name ?? t("attach.image");
|
|
679
|
+
const dims = attachment.width !== void 0 && attachment.height !== void 0 ? `${attachment.width}×${attachment.height}` : null;
|
|
680
|
+
const orig = attachment.originalDimensions;
|
|
681
|
+
const facts = [];
|
|
682
|
+
if (dims !== null) facts.push(dims);
|
|
683
|
+
if (orig !== void 0) facts.push(t("attach.orig", { d: `${orig.width}×${orig.height}` }));
|
|
684
|
+
if (attachment.bytes !== void 0) facts.push(fmtBytes(attachment.bytes));
|
|
685
|
+
const open = () => {
|
|
686
|
+
if (src === null) return;
|
|
687
|
+
try {
|
|
688
|
+
window.open(src, "_blank", "noopener");
|
|
689
|
+
} catch {}
|
|
690
|
+
};
|
|
691
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-att-item" }, /* @__PURE__ */ React.createElement("button", {
|
|
692
|
+
type: "button",
|
|
693
|
+
className: "lc-att-thumb",
|
|
694
|
+
title: error ? t("attach.loadFailed") : t("attach.open"),
|
|
695
|
+
onClick: error ? () => {
|
|
696
|
+
setAttempt((a) => a + 1);
|
|
697
|
+
} : open
|
|
698
|
+
}, src !== null ? /* @__PURE__ */ React.createElement("img", {
|
|
699
|
+
src,
|
|
700
|
+
alt: name
|
|
701
|
+
}) : /* @__PURE__ */ React.createElement("span", { className: error ? "lc-att-err" : "lc-att-ph" }, error ? "⚠" : load === void 0 ? "🖼" : t("attach.loading"))), /* @__PURE__ */ React.createElement("div", { className: "lc-att-meta" }, /* @__PURE__ */ React.createElement("span", {
|
|
702
|
+
className: "lc-att-name",
|
|
703
|
+
title: name
|
|
704
|
+
}, name), facts.length > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-att-dims" }, facts.join(" · ")) : null));
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
//#endregion
|
|
708
|
+
//#region src/client/components/richText.tsx
|
|
709
|
+
function makeRichText(kit) {
|
|
710
|
+
const { t } = kit;
|
|
711
|
+
function useRichMode() {
|
|
712
|
+
const [mode, setMode] = React.useState("md");
|
|
713
|
+
return [mode, setMode];
|
|
714
|
+
}
|
|
715
|
+
function RichSwitch(props) {
|
|
716
|
+
const seg = (m, label, tip) => /* @__PURE__ */ React.createElement("button", {
|
|
717
|
+
type: "button",
|
|
718
|
+
className: "lc-rich-seg-btn" + (props.mode === m ? " lc-rich-seg-on" : ""),
|
|
719
|
+
title: tip,
|
|
720
|
+
onClick: () => {
|
|
721
|
+
props.onPick(m);
|
|
722
|
+
}
|
|
723
|
+
}, label);
|
|
724
|
+
return /* @__PURE__ */ React.createElement("span", { className: "lc-rich-seg" + (props.inHead === true ? " lc-rich-seg-head" : "") }, seg("raw", t("rich.raw"), t("rich.toRaw")), seg("md", t("rich.md"), t("rich.toMd")));
|
|
725
|
+
}
|
|
726
|
+
function RichText(props) {
|
|
727
|
+
if (props.mode === "md") return /* @__PURE__ */ React.createElement("div", { className: props.mdClass ?? "lc-br-md" }, /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, { text: props.text }));
|
|
728
|
+
return /* @__PURE__ */ React.createElement("pre", { className: props.rawClass ?? "lc-br-pre" }, props.text);
|
|
729
|
+
}
|
|
730
|
+
function RichCard(props) {
|
|
731
|
+
const [mode, setMode] = useRichMode();
|
|
732
|
+
return /* @__PURE__ */ React.createElement("div", { className: props.className ?? "lc-br-content" }, /* @__PURE__ */ React.createElement(RichSwitch, {
|
|
733
|
+
mode,
|
|
734
|
+
onPick: setMode
|
|
735
|
+
}), props.render(mode));
|
|
736
|
+
}
|
|
737
|
+
return {
|
|
738
|
+
RichText,
|
|
739
|
+
RichCard,
|
|
740
|
+
RichSwitch,
|
|
741
|
+
useRichMode
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
//#endregion
|
|
745
|
+
//#region src/client/components/browser.tsx
|
|
746
|
+
/** Flatten the `anyOf` / `oneOf` alternation into one displayable string. */
|
|
747
|
+
function unionTypesOf(p) {
|
|
748
|
+
const branches = [];
|
|
749
|
+
if (Array.isArray(p.anyOf)) branches.push(...p.anyOf);
|
|
750
|
+
if (Array.isArray(p.oneOf)) branches.push(...p.oneOf);
|
|
751
|
+
if (branches.length === 0) return null;
|
|
752
|
+
const parts = [];
|
|
753
|
+
for (const b of branches) if (b !== null && typeof b === "object") parts.push(typeOf(b));
|
|
754
|
+
return parts.length > 0 ? parts.join(" | ") : null;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Derive a short, human-readable type label from a JSON Schema fragment.
|
|
758
|
+
* Arrays show their element type (`array<number>`); unions fold into
|
|
759
|
+
* `a | b`; enums collapse into `(enum)`. Returns `unknown` when the schema
|
|
760
|
+
* carries no usable signal — the row falls back to its description then.
|
|
761
|
+
*/
|
|
762
|
+
function typeOf(p) {
|
|
763
|
+
const u = unionTypesOf(p);
|
|
764
|
+
if (u !== null) return u;
|
|
765
|
+
const t = p.type;
|
|
766
|
+
if (t === "array") {
|
|
767
|
+
const items = p.items;
|
|
768
|
+
if (items !== null && typeof items === "object") return "array<" + typeOf(items) + ">";
|
|
769
|
+
return "array";
|
|
770
|
+
}
|
|
771
|
+
if (typeof t === "string") {
|
|
772
|
+
if (t === "object") {
|
|
773
|
+
const props = p.properties;
|
|
774
|
+
if (props !== null && typeof props === "object" && Object.keys(props).length > 0) return `object{${Object.keys(props).length}}`;
|
|
775
|
+
}
|
|
776
|
+
if (Array.isArray(p.enum) && p.enum.length > 0) return t + " (enum)";
|
|
777
|
+
return t;
|
|
778
|
+
}
|
|
779
|
+
if (Array.isArray(p.enum) && p.enum.length > 0) return "(enum)";
|
|
780
|
+
return "unknown";
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Locate the parameter-bearing object inside a raw tool schema. Producers
|
|
784
|
+
* may nest it under `parameters`, `input_schema`, `inputSchema`, or hand
|
|
785
|
+
* the schema as the bare JSON Schema (when `type === 'object'`).
|
|
786
|
+
*/
|
|
787
|
+
function paramsOf(schema) {
|
|
788
|
+
if (schema === null || typeof schema !== "object") return null;
|
|
789
|
+
const s = schema;
|
|
790
|
+
const candidate = (v) => v !== null && typeof v === "object" ? v : null;
|
|
791
|
+
const nested = candidate(s.parameters) ?? candidate(s.input_schema) ?? candidate(s.inputSchema);
|
|
792
|
+
if (nested !== null) return nested;
|
|
793
|
+
if (s.type === "object" && s.properties !== void 0 && typeof s.properties === "object") return s;
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* One row of the parsed parameter table — name (mono-styled, with a
|
|
798
|
+
* required chip), short type label, and the description on its own line
|
|
799
|
+
* (so a long blurb never breaks the name/type rhythm).
|
|
800
|
+
*/
|
|
801
|
+
function ParamRow(props) {
|
|
802
|
+
const typeLabel = typeOf(props.schema);
|
|
803
|
+
const desc = props.schema.description;
|
|
804
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-param-row" }, /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-name" }, props.name), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-type" }, typeLabel), /* @__PURE__ */ React.createElement("span", { className: props.required ? "lc-ts-param-req" : "lc-ts-param-req-off" }, props.required ? "✓" : "·"), typeof desc === "string" && desc !== "" ? /* @__PURE__ */ React.createElement("span", { className: "lc-ts-param-desc" }, desc) : null);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* The full body of one expanded tool row: description, a parsed parameter
|
|
808
|
+
* table (when the schema carries one), and the raw JSON behind a toggle.
|
|
809
|
+
* Owns its own open/closed state for the JSON so two expanded tools stay
|
|
810
|
+
* independent. The description card carries the shared raw/markdown view
|
|
811
|
+
* toggle in its head (per-card state via `rich.useRichMode`).
|
|
812
|
+
*/
|
|
813
|
+
function ToolSchema(props) {
|
|
814
|
+
const { rich } = props;
|
|
815
|
+
const [jsonOpen, setJsonOpen] = React.useState(false);
|
|
816
|
+
const [descMode, setDescMode] = rich.useRichMode();
|
|
817
|
+
const params = React.useMemo(() => paramsOf(props.schema), [props.schema]);
|
|
818
|
+
const rows = React.useMemo(() => {
|
|
819
|
+
if (params === null) return [];
|
|
820
|
+
const props = params.properties;
|
|
821
|
+
if (props === null || typeof props !== "object") return [];
|
|
822
|
+
const req = Array.isArray(params.required) ? new Set(params.required.filter((x) => typeof x === "string")) : /* @__PURE__ */ new Set();
|
|
823
|
+
const out = [];
|
|
824
|
+
for (const k of Object.keys(props)) {
|
|
825
|
+
const v = props[k];
|
|
826
|
+
if (v === null || typeof v !== "object") continue;
|
|
827
|
+
out.push({
|
|
828
|
+
name: k,
|
|
829
|
+
schema: v,
|
|
830
|
+
required: req.has(k)
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
return out;
|
|
834
|
+
}, [params]);
|
|
835
|
+
const schemaJson = React.useMemo(() => props.schema !== void 0 ? JSON.stringify(props.schema, null, 2) : "", [props.schema]);
|
|
836
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.description !== void 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", null, props.labels.desc), /* @__PURE__ */ React.createElement(rich.RichSwitch, {
|
|
837
|
+
mode: descMode,
|
|
838
|
+
onPick: setDescMode,
|
|
839
|
+
inHead: true
|
|
840
|
+
})), /* @__PURE__ */ React.createElement(rich.RichText, {
|
|
841
|
+
text: props.description,
|
|
842
|
+
mode: descMode,
|
|
843
|
+
rawClass: "lc-ts-desc-body",
|
|
844
|
+
mdClass: "lc-ts-desc-md"
|
|
845
|
+
})) : null, params !== null && rows.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", null, props.labels.title), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-count" }, rows.length)), rows.map((r) => /* @__PURE__ */ React.createElement(ParamRow, {
|
|
846
|
+
key: r.name,
|
|
847
|
+
name: r.name,
|
|
848
|
+
schema: r.schema,
|
|
849
|
+
required: r.required
|
|
850
|
+
}))) : params !== null ? /* @__PURE__ */ React.createElement("div", { className: "lc-ts-params-empty" }, props.labels.empty) : null, schemaJson !== "" ? /* @__PURE__ */ React.createElement("div", { className: "lc-ts-json" }, /* @__PURE__ */ React.createElement("button", {
|
|
851
|
+
type: "button",
|
|
852
|
+
className: "lc-ts-json-toggle",
|
|
853
|
+
onClick: () => {
|
|
854
|
+
setJsonOpen((o) => !o);
|
|
855
|
+
}
|
|
856
|
+
}, (jsonOpen ? "▾ " : "▸ ") + (jsonOpen ? props.labels.hide : props.labels.show)), jsonOpen ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre lc-br-dim" }, schemaJson) : null) : null);
|
|
857
|
+
}
|
|
858
|
+
/** One raw content block (text/reasoning/tool-result/image/…), rendered defensively. */
|
|
859
|
+
function RawBlocks(props) {
|
|
860
|
+
const { rich, img } = props;
|
|
861
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, props.blocks.map((b, i) => {
|
|
862
|
+
const blk = b !== null && typeof b === "object" ? b : null;
|
|
863
|
+
if (blk !== null && (blk.type === "text" || blk.type === "reasoning") && typeof blk.text === "string") {
|
|
864
|
+
if (blk.type === "reasoning") return /* @__PURE__ */ React.createElement("pre", {
|
|
865
|
+
key: i,
|
|
866
|
+
className: "lc-br-pre lc-br-dim"
|
|
867
|
+
}, blk.text);
|
|
868
|
+
return /* @__PURE__ */ React.createElement(rich.RichText, {
|
|
869
|
+
key: i,
|
|
870
|
+
text: blk.text,
|
|
871
|
+
mode: props.mode
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
const image = imageRefOf(b);
|
|
875
|
+
if (image !== null) return /* @__PURE__ */ React.createElement(img.Card, {
|
|
876
|
+
key: i,
|
|
877
|
+
attachment: image,
|
|
878
|
+
load: img.load
|
|
879
|
+
});
|
|
880
|
+
if (blk !== null && blk.type === "tool-result" && Array.isArray(blk.content)) return /* @__PURE__ */ React.createElement(RawBlocks, {
|
|
881
|
+
key: i,
|
|
882
|
+
blocks: blk.content,
|
|
883
|
+
mode: props.mode,
|
|
884
|
+
rich,
|
|
885
|
+
img
|
|
886
|
+
});
|
|
887
|
+
return /* @__PURE__ */ React.createElement("pre", {
|
|
888
|
+
key: i,
|
|
889
|
+
className: "lc-br-pre lc-br-dim"
|
|
890
|
+
}, JSON.stringify(b, null, 2));
|
|
891
|
+
}));
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* One assistant-message block (thinking / answer) as its own card: the
|
|
895
|
+
* title row carries the block label plus the block's OWN raw/markdown
|
|
896
|
+
* switch (per-card state via `rich.useRichMode`, mirroring the tool
|
|
897
|
+
* description card), so a mixed reply's parts toggle independently.
|
|
898
|
+
*/
|
|
899
|
+
function BlockCard(props) {
|
|
900
|
+
const { rich } = props;
|
|
901
|
+
const [mode, setMode] = rich.useRichMode();
|
|
902
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", null, props.label), /* @__PURE__ */ React.createElement(rich.RichSwitch, {
|
|
903
|
+
mode,
|
|
904
|
+
onPick: setMode,
|
|
905
|
+
inHead: true
|
|
906
|
+
})), /* @__PURE__ */ React.createElement(rich.RichText, {
|
|
907
|
+
text: props.text,
|
|
908
|
+
mode,
|
|
909
|
+
rawClass: "lc-ts-desc-body",
|
|
910
|
+
mdClass: "lc-ts-desc-md"
|
|
911
|
+
}));
|
|
912
|
+
}
|
|
913
|
+
/** The actual content of one surface element, joined from the conversation snapshot. */
|
|
914
|
+
function NodeContent(props) {
|
|
915
|
+
const { node, conv, rich, img } = props;
|
|
916
|
+
const richable = node.cat === "user" || node.cat === "assistant" || node.cat === "inject";
|
|
917
|
+
const wrap = (render) => richable ? /* @__PURE__ */ React.createElement(rich.RichCard, { render }) : /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, render("raw"));
|
|
918
|
+
if (conv === void 0) {
|
|
919
|
+
if (node.text === void 0 || node.text === "") return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
|
|
920
|
+
return wrap((mode) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(rich.RichText, {
|
|
921
|
+
text: node.text ?? "",
|
|
922
|
+
mode
|
|
923
|
+
}), /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint)));
|
|
924
|
+
}
|
|
925
|
+
if (conv.kind === "assistant" && Array.isArray(conv.blocks)) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, conv.blocks.map((b, i) => {
|
|
926
|
+
const blk = b;
|
|
927
|
+
if (blk.kind === "text" && typeof blk.text === "string") return /* @__PURE__ */ React.createElement(BlockCard, {
|
|
928
|
+
key: i,
|
|
929
|
+
label: props.labels.answer,
|
|
930
|
+
text: blk.text,
|
|
931
|
+
rich
|
|
932
|
+
});
|
|
933
|
+
if (blk.kind === "reasoning" && typeof blk.text === "string") return /* @__PURE__ */ React.createElement(BlockCard, {
|
|
934
|
+
key: i,
|
|
935
|
+
label: props.labels.thinking,
|
|
936
|
+
text: blk.text,
|
|
937
|
+
rich
|
|
938
|
+
});
|
|
939
|
+
if (blk.kind === "tool-call") return /* @__PURE__ */ React.createElement("div", {
|
|
940
|
+
key: i,
|
|
941
|
+
className: "lc-br-call"
|
|
942
|
+
}, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, "→ " + (blk.name ?? "?")), typeof blk.argsRaw === "string" && blk.argsRaw !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre lc-br-dim" }, blk.argsRaw) : null);
|
|
943
|
+
const image = imageRefOf(b);
|
|
944
|
+
if (image !== null) return /* @__PURE__ */ React.createElement(img.Card, {
|
|
945
|
+
key: i,
|
|
946
|
+
attachment: image,
|
|
947
|
+
load: img.load
|
|
948
|
+
});
|
|
949
|
+
return null;
|
|
950
|
+
}));
|
|
951
|
+
if (conv.kind === "tool-result") return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, conv.call != null ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-call" }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, "← " + conv.call.name), conv.call.argsRaw !== "" ? /* @__PURE__ */ React.createElement("pre", { className: "lc-br-pre lc-br-dim" }, conv.call.argsRaw) : null) : null, Array.isArray(conv.content) ? /* @__PURE__ */ React.createElement(RawBlocks, {
|
|
952
|
+
blocks: conv.content,
|
|
953
|
+
mode: "raw",
|
|
954
|
+
rich,
|
|
955
|
+
img
|
|
956
|
+
}) : null);
|
|
957
|
+
if (conv.kind === "compaction") return wrap((mode) => /* @__PURE__ */ React.createElement(React.Fragment, null, typeof conv.summary === "string" && conv.summary !== "" ? /* @__PURE__ */ React.createElement(rich.RichText, {
|
|
958
|
+
text: conv.summary,
|
|
959
|
+
mode
|
|
960
|
+
}) : null));
|
|
961
|
+
if (Array.isArray(conv.content)) {
|
|
962
|
+
const blocks = conv.content;
|
|
963
|
+
if (node.cat !== "user") return wrap((mode) => /* @__PURE__ */ React.createElement(RawBlocks, {
|
|
964
|
+
blocks,
|
|
965
|
+
mode,
|
|
966
|
+
rich,
|
|
967
|
+
img
|
|
968
|
+
}));
|
|
969
|
+
const texts = [];
|
|
970
|
+
const images = [];
|
|
971
|
+
const others = [];
|
|
972
|
+
for (const b of blocks) {
|
|
973
|
+
const blk = b;
|
|
974
|
+
if (blk !== null && typeof blk === "object" && blk.type === "text" && typeof blk.text === "string") {
|
|
975
|
+
texts.push(blk.text);
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
const image = imageRefOf(b);
|
|
979
|
+
if (image !== null) {
|
|
980
|
+
images.push(image);
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
others.push(b);
|
|
984
|
+
}
|
|
985
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, texts.length > 0 ? /* @__PURE__ */ React.createElement(rich.RichCard, {
|
|
986
|
+
className: "lc-br-rich-inline",
|
|
987
|
+
render: (mode) => /* @__PURE__ */ React.createElement(React.Fragment, null, texts.map((text, i) => /* @__PURE__ */ React.createElement(rich.RichText, {
|
|
988
|
+
key: i,
|
|
989
|
+
text,
|
|
990
|
+
mode
|
|
991
|
+
})))
|
|
992
|
+
}) : null, images.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", null, props.labels.images), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-count" }, images.length)), /* @__PURE__ */ React.createElement("div", { className: "lc-att-grid" }, images.map((a, i) => /* @__PURE__ */ React.createElement(img.Card, {
|
|
993
|
+
key: `${a.attachmentId}:${i}`,
|
|
994
|
+
attachment: a,
|
|
995
|
+
load: img.load
|
|
996
|
+
})))) : null, others.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-ts-card-head" }, /* @__PURE__ */ React.createElement("b", null, props.labels.other), /* @__PURE__ */ React.createElement("span", { className: "lc-ts-card-count" }, others.length)), others.map((b, i) => /* @__PURE__ */ React.createElement("pre", {
|
|
997
|
+
key: i,
|
|
998
|
+
className: "lc-br-pre lc-br-dim"
|
|
999
|
+
}, JSON.stringify(b, null, 2)))) : null);
|
|
1000
|
+
}
|
|
1001
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, props.hint));
|
|
1002
|
+
}
|
|
1003
|
+
/** Group an assembled surface's nodes by category (for per-category counts). */
|
|
1004
|
+
function byCatOf(asm) {
|
|
1005
|
+
const m = {};
|
|
1006
|
+
for (const n of asm.nodes) (m[n.cat] ??= []).push(n);
|
|
1007
|
+
return m;
|
|
1008
|
+
}
|
|
1009
|
+
/** Count one category's elements in an assembled surface (header cats included). */
|
|
1010
|
+
function countOf(asm, c) {
|
|
1011
|
+
if (c === "system") return asm.header !== null && asm.header.system !== void 0 ? 1 : 0;
|
|
1012
|
+
if (c === "tools") return asm.header !== null ? asm.header.tools.length : 0;
|
|
1013
|
+
return byCatOf(asm)[c]?.length ?? 0;
|
|
1014
|
+
}
|
|
1015
|
+
/** The last request of `turn` in a seq-ordered timeline, or null. */
|
|
1016
|
+
function lastOfTurn(requests, turn) {
|
|
1017
|
+
for (let i = requests.length - 1; i >= 0; i--) if ((requests[i].turn ?? 0) === turn) return requests[i];
|
|
1018
|
+
return null;
|
|
1019
|
+
}
|
|
1020
|
+
function makeContextBrowser(kit, StackedBar) {
|
|
1021
|
+
const { t, fmt, fmtTime, catLabel } = kit;
|
|
1022
|
+
const nodeText = makeNodeText(kit);
|
|
1023
|
+
const rich = makeRichText(kit);
|
|
1024
|
+
const ImageCard = makeImageCard(kit);
|
|
1025
|
+
const catColor = {};
|
|
1026
|
+
for (const c of CATS) catColor[c.key] = c.color;
|
|
1027
|
+
const MAX_AUTO_PAGES = 20;
|
|
1028
|
+
return function ContextBrowser(props) {
|
|
1029
|
+
const { data, headers } = props;
|
|
1030
|
+
const [sel, setSel] = React.useState("live");
|
|
1031
|
+
const [openCat, setOpenCat] = React.useState(null);
|
|
1032
|
+
const [openElem, setOpenElem] = React.useState(null);
|
|
1033
|
+
const convNodes = typeof props.useSession === "function" ? props.useSession((s) => s.nodes) : void 0;
|
|
1034
|
+
const bySeq = React.useMemo(() => {
|
|
1035
|
+
const m = /* @__PURE__ */ new Map();
|
|
1036
|
+
for (const n of convNodes ?? []) m.set(n.seq, n);
|
|
1037
|
+
return m;
|
|
1038
|
+
}, [convNodes]);
|
|
1039
|
+
const hasMore = typeof props.useSession === "function" ? props.useSession((s) => s.hasMore === true) : false;
|
|
1040
|
+
const loadingOlder = typeof props.useSession === "function" ? props.useSession((s) => s.loadingOlder === true) : false;
|
|
1041
|
+
const openSeq = openElem !== null && openElem.startsWith("n") ? Number(openElem.slice(1)) : null;
|
|
1042
|
+
const missingSeq = openSeq !== null && !bySeq.has(openSeq) ? openSeq : null;
|
|
1043
|
+
const [exhausted, setExhausted] = React.useState(false);
|
|
1044
|
+
const pagesRef = React.useRef(0);
|
|
1045
|
+
React.useEffect(() => {
|
|
1046
|
+
pagesRef.current = 0;
|
|
1047
|
+
setExhausted(false);
|
|
1048
|
+
}, [openElem]);
|
|
1049
|
+
const loadOlderHistory = props.loadOlderHistory;
|
|
1050
|
+
React.useEffect(() => {
|
|
1051
|
+
if (missingSeq === null || !hasMore || loadingOlder || exhausted) return;
|
|
1052
|
+
if (loadOlderHistory === void 0) return;
|
|
1053
|
+
if (pagesRef.current >= MAX_AUTO_PAGES) {
|
|
1054
|
+
setExhausted(true);
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
pagesRef.current += 1;
|
|
1058
|
+
loadOlderHistory();
|
|
1059
|
+
}, [
|
|
1060
|
+
missingSeq,
|
|
1061
|
+
hasMore,
|
|
1062
|
+
loadingOlder,
|
|
1063
|
+
exhausted,
|
|
1064
|
+
bySeq,
|
|
1065
|
+
loadOlderHistory
|
|
1066
|
+
]);
|
|
1067
|
+
React.useEffect(() => {
|
|
1068
|
+
if (!hasMore && missingSeq !== null && !exhausted) setExhausted(true);
|
|
1069
|
+
}, [
|
|
1070
|
+
hasMore,
|
|
1071
|
+
missingSeq,
|
|
1072
|
+
exhausted
|
|
1073
|
+
]);
|
|
1074
|
+
const pinSeq = props.pinSeq;
|
|
1075
|
+
React.useEffect(() => {
|
|
1076
|
+
setSel(pinSeq === null || pinSeq === void 0 ? "live" : pinSeq);
|
|
1077
|
+
setOpenCat(null);
|
|
1078
|
+
setOpenElem(null);
|
|
1079
|
+
}, [pinSeq]);
|
|
1080
|
+
const toolFocus = props.toolFocus;
|
|
1081
|
+
React.useEffect(() => {
|
|
1082
|
+
if (toolFocus === null || toolFocus === void 0) return;
|
|
1083
|
+
setSel("live");
|
|
1084
|
+
setOpenCat("tools");
|
|
1085
|
+
setOpenElem(toolFocus.tool !== void 0 ? "tool:" + toolFocus.tool : null);
|
|
1086
|
+
if (props.onToolFocusHandled !== void 0) props.onToolFocusHandled();
|
|
1087
|
+
}, [toolFocus, props.onToolFocusHandled]);
|
|
1088
|
+
const awaiting = missingSeq !== null && !exhausted && loadOlderHistory !== void 0 && hasMore;
|
|
1089
|
+
const requests = data.requests;
|
|
1090
|
+
const hoverReq = props.previewSeq !== null && props.previewSeq !== void 0 ? requests.find((r) => r.seq === props.previewSeq) ?? null : null;
|
|
1091
|
+
const req = hoverReq ?? (sel === "live" ? null : requests.find((r) => r.seq === sel) ?? null);
|
|
1092
|
+
const seq = req !== null ? req.seq : null;
|
|
1093
|
+
const linked = req === null && props.onHoverKey !== void 0;
|
|
1094
|
+
const linkKey = linked && props.hoverKey !== null && props.hoverKey !== "free" ? props.hoverKey : null;
|
|
1095
|
+
const view = assemble(data, headers, seq);
|
|
1096
|
+
const breakdown = req !== null ? req : data.current;
|
|
1097
|
+
const parts = partsOf(breakdown);
|
|
1098
|
+
const total = breakdown.total;
|
|
1099
|
+
const pick = (v) => {
|
|
1100
|
+
setSel(v === "live" ? "live" : Number(v));
|
|
1101
|
+
setOpenCat(null);
|
|
1102
|
+
setOpenElem(null);
|
|
1103
|
+
};
|
|
1104
|
+
const refReq = req === null ? requests.length > 0 ? requests[requests.length - 1] : null : lastOfTurn(requests, (req.turn ?? 0) - 1);
|
|
1105
|
+
const prevView = refReq !== null ? assemble(data, headers, refReq.seq) : null;
|
|
1106
|
+
const byCat = byCatOf(view);
|
|
1107
|
+
const toolCount = (c) => countOf(view, c);
|
|
1108
|
+
const toggleCat = (c) => {
|
|
1109
|
+
if (!(toolCount(c) > 0 || (c === "system" || c === "tools") && view.header === null)) return;
|
|
1110
|
+
setOpenCat(openCat === c ? null : c);
|
|
1111
|
+
setOpenElem(null);
|
|
1112
|
+
};
|
|
1113
|
+
const toggleElem = (key) => {
|
|
1114
|
+
setOpenElem(openElem === key ? null : key);
|
|
1115
|
+
};
|
|
1116
|
+
/** One expandable element row (preview line; content when open). */
|
|
1117
|
+
const elemRow = (key, tag, preview, tokens, time, body) => {
|
|
1118
|
+
const open = openElem === key;
|
|
1119
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1120
|
+
key,
|
|
1121
|
+
className: "lc-br-elem" + (open ? " lc-br-elem-on" : "")
|
|
1122
|
+
}, /* @__PURE__ */ React.createElement("button", {
|
|
1123
|
+
type: "button",
|
|
1124
|
+
className: "lc-br-elem-row",
|
|
1125
|
+
onClick: () => {
|
|
1126
|
+
toggleElem(key);
|
|
1127
|
+
}
|
|
1128
|
+
}, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "▸"), tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-tag" }, tag) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-preview" }, preview), time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-time" }, fmtTime(time)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens" }, "≈" + fmt(tokens))), open ? body : null);
|
|
1129
|
+
};
|
|
1130
|
+
const catBody = (c) => {
|
|
1131
|
+
if (c === "system") {
|
|
1132
|
+
if (view.header === null) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t(headers === null ? "browser.noHeader" : "browser.noEpoch"));
|
|
1133
|
+
const system = view.header.system;
|
|
1134
|
+
if (system === void 0) return null;
|
|
1135
|
+
return elemRow("sys", null, system.replace(/\s+/g, " ").trim().slice(0, 80), breakdown.system, void 0, /* @__PURE__ */ React.createElement(rich.RichCard, { render: (mode) => /* @__PURE__ */ React.createElement(rich.RichText, {
|
|
1136
|
+
text: system,
|
|
1137
|
+
mode
|
|
1138
|
+
}) }));
|
|
1139
|
+
}
|
|
1140
|
+
if (c === "tools") {
|
|
1141
|
+
if (view.header === null) return /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t(headers === null ? "browser.noHeader" : "browser.noEpoch"));
|
|
1142
|
+
const labels = {
|
|
1143
|
+
desc: t("tool.desc"),
|
|
1144
|
+
title: t("tool.params"),
|
|
1145
|
+
empty: t("tool.paramsEmpty"),
|
|
1146
|
+
show: t("tool.jsonToggle"),
|
|
1147
|
+
hide: t("tool.jsonHide")
|
|
1148
|
+
};
|
|
1149
|
+
return view.header.tools.slice().sort((a, b) => b.tokens - a.tokens).map((tool) => {
|
|
1150
|
+
return elemRow("tool:" + tool.name, null, tool.name, tool.tokens, void 0, /* @__PURE__ */ React.createElement("div", { className: "lc-br-content" }, /* @__PURE__ */ React.createElement(ToolSchema, {
|
|
1151
|
+
description: tool.description,
|
|
1152
|
+
schema: tool.schema,
|
|
1153
|
+
rich,
|
|
1154
|
+
labels
|
|
1155
|
+
})));
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
return (byCat[c] ?? []).slice().reverse().map((n) => {
|
|
1159
|
+
let tag = null;
|
|
1160
|
+
let preview = nodeText(n);
|
|
1161
|
+
if (n.cat === "tool") {
|
|
1162
|
+
tag = (n.tool ?? "?") + (n.err ? " ⚠" : "");
|
|
1163
|
+
preview = t("node.toolResult") + (n.err ? " ⚠" : "");
|
|
1164
|
+
} else if (n.cat === "inject" && !n.skill) {
|
|
1165
|
+
tag = t("form." + (n.form || "context"));
|
|
1166
|
+
if (n.text !== void 0 && n.text !== "") preview = n.form === "snapshot" ? t("node.snapshot") + n.text : n.text;
|
|
1167
|
+
}
|
|
1168
|
+
return elemRow(`n${n.seq}`, tag, preview, n.tokens, n.time, /* @__PURE__ */ React.createElement(NodeContent, {
|
|
1169
|
+
node: n,
|
|
1170
|
+
conv: bySeq.get(n.seq),
|
|
1171
|
+
rich,
|
|
1172
|
+
img: {
|
|
1173
|
+
Card: ImageCard,
|
|
1174
|
+
load: props.loadImage
|
|
1175
|
+
},
|
|
1176
|
+
labels: {
|
|
1177
|
+
thinking: t("block.thinking"),
|
|
1178
|
+
answer: t("block.answer"),
|
|
1179
|
+
images: t("attach.images"),
|
|
1180
|
+
other: t("attach.other")
|
|
1181
|
+
},
|
|
1182
|
+
hint: bySeq.get(n.seq) === void 0 && awaiting ? t("browser.loading") : t("browser.noContent")
|
|
1183
|
+
}));
|
|
1184
|
+
});
|
|
1185
|
+
};
|
|
1186
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("browser.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-br-hint" }, t("browser.deltaHint")), /* @__PURE__ */ React.createElement("select", {
|
|
1187
|
+
className: "lc-br-pick",
|
|
1188
|
+
value: seq === null ? "live" : String(seq),
|
|
1189
|
+
onChange: (e) => {
|
|
1190
|
+
pick(e.target.value);
|
|
1191
|
+
}
|
|
1192
|
+
}, /* @__PURE__ */ React.createElement("option", { value: "live" }, t("browser.live")), requests.slice().reverse().map((r) => /* @__PURE__ */ React.createElement("option", {
|
|
1193
|
+
key: r.seq,
|
|
1194
|
+
value: String(r.seq)
|
|
1195
|
+
}, t("detail.step", {
|
|
1196
|
+
t: r.turn ?? 0,
|
|
1197
|
+
s: r.step ?? 0
|
|
1198
|
+
}) + " · " + fmtTime(r.time))))), /* @__PURE__ */ React.createElement("div", { className: "lc-br-meta" }, /* @__PURE__ */ React.createElement("b", null, req !== null ? t("detail.step", {
|
|
1199
|
+
t: req.turn ?? 0,
|
|
1200
|
+
s: req.step ?? 0
|
|
1201
|
+
}) : t("browser.liveNow")), req !== null ? /* @__PURE__ */ React.createElement("span", null, fmtTime(req.time)) : null, hoverReq !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("browser.preview")) : null, /* @__PURE__ */ React.createElement("span", null, t("detail.estTotal", { n: fmt(total) })), req !== null && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, t("detail.actual", { n: fmt(req.prompt) })) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-br-bar" }, /* @__PURE__ */ React.createElement(StackedBar, {
|
|
1202
|
+
parts,
|
|
1203
|
+
height: 10,
|
|
1204
|
+
hoverKey: linked ? linkKey : void 0,
|
|
1205
|
+
onHoverKey: linked ? props.onHoverKey : void 0,
|
|
1206
|
+
tip: false
|
|
1207
|
+
})), view.missingLive > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.missingLive", { n: view.missingLive })) : null, view.approximate ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-note" }, t("browser.approx")) : null, /* @__PURE__ */ React.createElement("div", { className: "lc-br-cats" }, CATS.map((c) => {
|
|
1208
|
+
const count = toolCount(c.key);
|
|
1209
|
+
const v = breakdown[c.key] || 0;
|
|
1210
|
+
const prevCount = prevView !== null ? countOf(prevView, c.key) : null;
|
|
1211
|
+
const countDelta = prevCount !== null ? count - prevCount : null;
|
|
1212
|
+
const prevTokens = refReq !== null ? refReq[c.key] || 0 : null;
|
|
1213
|
+
const tokenDelta = prevTokens !== null ? v - prevTokens : null;
|
|
1214
|
+
const openable = count > 0 || (c.key === "system" || c.key === "tools") && view.header === null;
|
|
1215
|
+
const open = openCat === c.key && openable;
|
|
1216
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1217
|
+
key: c.key,
|
|
1218
|
+
className: "lc-br-cat" + (openable ? "" : " lc-br-cat-empty")
|
|
1219
|
+
}, /* @__PURE__ */ React.createElement("button", {
|
|
1220
|
+
type: "button",
|
|
1221
|
+
className: "lc-br-cat-row" + (linked && props.hoverKey === c.key ? " lc-br-cat-on" : ""),
|
|
1222
|
+
onMouseEnter: linked ? () => {
|
|
1223
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(c.key);
|
|
1224
|
+
} : void 0,
|
|
1225
|
+
onMouseLeave: linked ? () => {
|
|
1226
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
1227
|
+
} : void 0,
|
|
1228
|
+
onClick: () => {
|
|
1229
|
+
toggleCat(c.key);
|
|
1230
|
+
}
|
|
1231
|
+
}, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "▸"), /* @__PURE__ */ React.createElement("i", { style: { background: c.color } }), /* @__PURE__ */ React.createElement("span", { className: "lc-br-cat-label" }, catLabel(c.key)), /* @__PURE__ */ React.createElement("span", { className: "lc-br-count-grp" }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-cat-count" }, t("browser.items", { n: count })), countDelta !== null && countDelta !== 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-delta lc-br-delta-" + (countDelta > 0 ? "up" : "down") }, `${countDelta > 0 ? "+" : ""}${countDelta}`) : null), /* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens-grp" }, tokenDelta !== null && tokenDelta !== 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-br-tdelta lc-br-tdelta-" + (tokenDelta > 0 ? "up" : "down") }, (tokenDelta > 0 ? "+" : "") + fmt(tokenDelta)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-br-tokens" }, "≈" + fmt(v))), /* @__PURE__ */ React.createElement("span", { className: "lc-br-pct" }, total > 0 ? `${Math.round(v / total * 100)}%` : "")), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-br-body" }, catBody(c.key)) : null);
|
|
1232
|
+
})));
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
//#endregion
|
|
1236
|
+
//#region src/client/components/stackedBar.tsx
|
|
1237
|
+
/**
|
|
1238
|
+
* DSH's automatic compaction trigger as a fraction of the routed context
|
|
1239
|
+
* window — the default `thresholdRatio` of `@deepseek-ai/dsh-compaction-basic`
|
|
1240
|
+
* (it compacts at step boundaries once `floor(contextWindow × ratio)` is
|
|
1241
|
+
* reached). DSH does not publish the configured ratio to plugins or clients,
|
|
1242
|
+
* so the UI mirrors the default here; deployments that tune `thresholdRatio`
|
|
1243
|
+
* / `modelPolicies` can adjust it if they want the reserve band to match.
|
|
1244
|
+
*/
|
|
1245
|
+
const AUTO_COMPACT_RATIO = .8;
|
|
1246
|
+
function makeStackedBar(kit) {
|
|
1247
|
+
const { t, fmt, catLabel } = kit;
|
|
1248
|
+
return function StackedBar(props) {
|
|
1249
|
+
const [reserveOn, setReserveOn] = React.useState(false);
|
|
1250
|
+
let total = 0;
|
|
1251
|
+
for (const p of props.parts) total += p.value;
|
|
1252
|
+
const scale = props.max !== void 0 && props.max > total ? props.max : total;
|
|
1253
|
+
const free = props.max !== void 0 && props.max > total ? props.max - total : 0;
|
|
1254
|
+
const usedPct = scale > 0 ? total / scale * 100 : 0;
|
|
1255
|
+
const hovering = props.hoverKey !== null && props.hoverKey !== void 0;
|
|
1256
|
+
const showBox = free > 0 && hovering;
|
|
1257
|
+
const reserve = props.reserve !== void 0 && props.max !== void 0 && props.max > 0 ? {
|
|
1258
|
+
...props.reserve,
|
|
1259
|
+
max: props.max
|
|
1260
|
+
} : null;
|
|
1261
|
+
const reserveLeft = reserve !== null ? Math.round(reserve.max * reserve.ratio / scale * 1e3) / 10 : 0;
|
|
1262
|
+
const reserveWidth = reserve !== null ? Math.round((1 - reserve.ratio) * reserve.max / scale * 1e3) / 10 : 0;
|
|
1263
|
+
let tip = null;
|
|
1264
|
+
if (reserveOn && reserve !== null) tip = {
|
|
1265
|
+
text: reserve.label,
|
|
1266
|
+
leftPct: Math.max(12, Math.min(reserveLeft + reserveWidth / 2, 88))
|
|
1267
|
+
};
|
|
1268
|
+
else if (props.hoverKey !== null && props.hoverKey !== void 0) {
|
|
1269
|
+
if (props.hoverKey === "free" && free > 0) {
|
|
1270
|
+
const pct = scale > 0 ? free / scale * 100 : 0;
|
|
1271
|
+
tip = {
|
|
1272
|
+
text: `${t("overview.free")} ${fmt(free)} (${Math.round(pct)}%)`,
|
|
1273
|
+
leftPct: Math.max(12, Math.min(total / scale * 100 + pct / 2, 88))
|
|
1274
|
+
};
|
|
1275
|
+
} else {
|
|
1276
|
+
let acc = 0;
|
|
1277
|
+
for (const p of props.parts) {
|
|
1278
|
+
const pct = scale > 0 ? p.value / scale * 100 : 0;
|
|
1279
|
+
if (p.key === props.hoverKey && p.value > 0) {
|
|
1280
|
+
tip = {
|
|
1281
|
+
text: `${catLabel(p.key)} ≈${fmt(p.value)} (${Math.round(p.value / total * 100)}%) ` + t("overview.ofUsed"),
|
|
1282
|
+
leftPct: Math.max(12, Math.min(acc + pct / 2, 88))
|
|
1283
|
+
};
|
|
1284
|
+
break;
|
|
1285
|
+
}
|
|
1286
|
+
acc += pct;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-stacked-wrap" }, /* @__PURE__ */ React.createElement("div", {
|
|
1291
|
+
className: "lc-stacked" + (hovering ? " lc-stacked-dim" : ""),
|
|
1292
|
+
style: { height: `${props.height || 14}px` },
|
|
1293
|
+
onMouseLeave: () => {
|
|
1294
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
1295
|
+
setReserveOn(false);
|
|
1296
|
+
}
|
|
1297
|
+
}, total > 0 ? props.parts.map((p) => {
|
|
1298
|
+
if (!p.value) return null;
|
|
1299
|
+
const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
|
|
1300
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1301
|
+
key: p.key,
|
|
1302
|
+
className: "lc-stacked-seg" + (on ? " lc-stacked-seg-on" : ""),
|
|
1303
|
+
style: {
|
|
1304
|
+
width: `${p.value / scale * 100}%`,
|
|
1305
|
+
background: p.color
|
|
1306
|
+
},
|
|
1307
|
+
onMouseEnter: () => {
|
|
1308
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1311
|
+
}) : null, free > 0 ? /* @__PURE__ */ React.createElement("div", {
|
|
1312
|
+
key: "free",
|
|
1313
|
+
className: "lc-stacked-free" + (props.hoverKey === "free" ? " lc-stacked-free-on" : ""),
|
|
1314
|
+
style: { width: `${free / scale * 100}%` },
|
|
1315
|
+
onMouseEnter: () => {
|
|
1316
|
+
if (props.onHoverKey !== void 0) props.onHoverKey("free");
|
|
1317
|
+
}
|
|
1318
|
+
}) : null, reserve !== null ? /* @__PURE__ */ React.createElement("div", {
|
|
1319
|
+
className: "lc-reserve",
|
|
1320
|
+
style: {
|
|
1321
|
+
left: `${reserveLeft}%`,
|
|
1322
|
+
width: `${reserveWidth}%`
|
|
1323
|
+
},
|
|
1324
|
+
onMouseEnter: () => {
|
|
1325
|
+
setReserveOn(true);
|
|
1326
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
1327
|
+
},
|
|
1328
|
+
onMouseLeave: () => {
|
|
1329
|
+
setReserveOn(false);
|
|
1330
|
+
}
|
|
1331
|
+
}) : null, /* @__PURE__ */ React.createElement("div", {
|
|
1332
|
+
className: "lc-occupied-box" + (showBox ? " lc-occupied-box-on" : ""),
|
|
1333
|
+
style: { width: `${usedPct}%` }
|
|
1334
|
+
})), props.tip !== false ? /* @__PURE__ */ React.createElement("div", {
|
|
1335
|
+
className: "lc-bar-tip" + (tip ? " lc-bar-tip-on" : ""),
|
|
1336
|
+
style: { left: tip ? `${tip.leftPct}%` : "50%" }
|
|
1337
|
+
}, tip ? tip.text : "") : null);
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
function makeLegend(kit) {
|
|
1341
|
+
const { t, fmt, catLabel } = kit;
|
|
1342
|
+
return function Legend(props) {
|
|
1343
|
+
let total = 0;
|
|
1344
|
+
for (const p of props.parts) total += p.value;
|
|
1345
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-legend" }, props.parts.map((p) => {
|
|
1346
|
+
const on = props.hoverKey !== void 0 && props.hoverKey === p.key;
|
|
1347
|
+
return /* @__PURE__ */ React.createElement("span", {
|
|
1348
|
+
key: p.key,
|
|
1349
|
+
className: "lc-chip" + (on ? " lc-chip-on" : ""),
|
|
1350
|
+
title: t("overview.ofUsed"),
|
|
1351
|
+
onMouseEnter: () => {
|
|
1352
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
1353
|
+
},
|
|
1354
|
+
onMouseLeave: () => {
|
|
1355
|
+
if (props.onHoverKey !== void 0) props.onHoverKey(null);
|
|
1356
|
+
}
|
|
1357
|
+
}, /* @__PURE__ */ React.createElement("i", { style: { background: p.color } }), /* @__PURE__ */ React.createElement("span", { className: "lc-chip-label" }, catLabel(p.key)), /* @__PURE__ */ React.createElement("span", { className: "lc-chip-nums" }, "≈" + fmt(p.value), total > 0 ? /* @__PURE__ */ React.createElement("em", null, `${Math.round(p.value / total * 100)}%`) : null));
|
|
1358
|
+
}));
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
//#endregion
|
|
1362
|
+
//#region src/client/components/currentComposition.tsx
|
|
1363
|
+
function makeCurrentComposition(kit, StackedBar, Legend) {
|
|
1364
|
+
const { t, fmt } = kit;
|
|
1365
|
+
return function CurrentComposition(props) {
|
|
1366
|
+
const head = props.head;
|
|
1367
|
+
const reserve = head.window != null && head.window > 0 ? {
|
|
1368
|
+
ratio: AUTO_COMPACT_RATIO,
|
|
1369
|
+
label: t("overview.compactReserve", { pct: Math.round(AUTO_COMPACT_RATIO * 100) })
|
|
1370
|
+
} : void 0;
|
|
1371
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("overview.title")), props.subtitle !== void 0 && props.subtitle !== "" ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, props.subtitle) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-overview-num" }, /* @__PURE__ */ React.createElement("b", null, fmt(head.tokens)), /* @__PURE__ */ React.createElement("span", null, head.window ? " / " + fmt(head.window) + " tokens" : " " + t("overview.estimate")), head.pct !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-overview-pct" }, /* @__PURE__ */ React.createElement("b", null, `${head.pct}%`), t("overview.used")) : null), /* @__PURE__ */ React.createElement(StackedBar, {
|
|
1372
|
+
parts: head.parts,
|
|
1373
|
+
height: 16,
|
|
1374
|
+
max: head.window,
|
|
1375
|
+
hoverKey: props.hoverKey,
|
|
1376
|
+
onHoverKey: props.onHoverKey,
|
|
1377
|
+
reserve
|
|
1378
|
+
}), /* @__PURE__ */ React.createElement(Legend, {
|
|
1379
|
+
parts: head.parts,
|
|
1380
|
+
hoverKey: props.hoverKey,
|
|
1381
|
+
onHoverKey: props.onHoverKey
|
|
1382
|
+
}), props.tools !== void 0 && props.tools.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-tools" }, /* @__PURE__ */ React.createElement("button", {
|
|
1383
|
+
type: "button",
|
|
1384
|
+
className: "lc-tools-label",
|
|
1385
|
+
onClick: () => {
|
|
1386
|
+
if (props.onToolFocus !== void 0) props.onToolFocus({});
|
|
1387
|
+
}
|
|
1388
|
+
}, t("tools.top")), props.tools.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => /* @__PURE__ */ React.createElement("button", {
|
|
1389
|
+
key: tool.name,
|
|
1390
|
+
type: "button",
|
|
1391
|
+
className: "lc-tool-chip",
|
|
1392
|
+
onClick: () => {
|
|
1393
|
+
if (props.onToolFocus !== void 0) props.onToolFocus({ tool: tool.name });
|
|
1394
|
+
}
|
|
1395
|
+
}, tool.name + " " + fmt(tool.tokens))), props.tools.length > 5 ? /* @__PURE__ */ React.createElement("button", {
|
|
1396
|
+
type: "button",
|
|
1397
|
+
className: "lc-tools-more",
|
|
1398
|
+
onClick: () => {
|
|
1399
|
+
if (props.onToolFocus !== void 0) props.onToolFocus({});
|
|
1400
|
+
}
|
|
1401
|
+
}, t("tools.more", { n: props.tools.length })) : null) : null);
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
//#endregion
|
|
1405
|
+
//#region src/client/components/errorBoundary.tsx
|
|
1406
|
+
function makeErrorBoundary(t) {
|
|
1407
|
+
return class ErrorBoundary extends React.Component {
|
|
1408
|
+
constructor(props) {
|
|
1409
|
+
super(props);
|
|
1410
|
+
this.state = { error: null };
|
|
1411
|
+
}
|
|
1412
|
+
static getDerivedStateFromError(error) {
|
|
1413
|
+
return { error: error instanceof Error ? error : new Error(String(error)) };
|
|
1414
|
+
}
|
|
1415
|
+
render() {
|
|
1416
|
+
const error = this.state.error;
|
|
1417
|
+
if (error === null) return this.props.children;
|
|
1418
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-root" }, /* @__PURE__ */ React.createElement("div", { className: "lc-empty lc-error" }, /* @__PURE__ */ React.createElement("span", null, t("error")), /* @__PURE__ */ React.createElement("code", { className: "lc-error-msg" }, error.message), /* @__PURE__ */ React.createElement("button", {
|
|
1419
|
+
type: "button",
|
|
1420
|
+
className: "lc-error-retry",
|
|
1421
|
+
onClick: () => {
|
|
1422
|
+
this.setState({ error: null });
|
|
1423
|
+
}
|
|
1424
|
+
}, t("error.retry"))));
|
|
1425
|
+
}
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
//#endregion
|
|
1429
|
+
//#region src/client/components/contextModal.tsx
|
|
1430
|
+
function makeContextModal(ctx, kit) {
|
|
1431
|
+
const { t } = kit;
|
|
1432
|
+
const sessions = ctx.get("sessions");
|
|
1433
|
+
const StackedBar = makeStackedBar(kit);
|
|
1434
|
+
const CurrentComposition = makeCurrentComposition(kit, StackedBar, makeLegend(kit));
|
|
1435
|
+
const ContextBrowser = makeContextBrowser(kit, StackedBar);
|
|
1436
|
+
const ErrorBoundary = makeErrorBoundary(t);
|
|
1437
|
+
function ContextModalBody(props) {
|
|
1438
|
+
const sessionId = typeof props.sessionId === "string" ? props.sessionId : "";
|
|
1439
|
+
const open = typeof props.useContextModal === "function" ? props.useContextModal((s) => s) : false;
|
|
1440
|
+
const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
|
|
1441
|
+
const pressure = typeof props.useProjection === "function" ? contextPressureOf(props.useProjection("contextPressure")) : null;
|
|
1442
|
+
const headers = typeof props.useProjection === "function" ? headersOf(props.useProjection("contextHeaders")) : null;
|
|
1443
|
+
const [hoverCat, setHoverCat] = React.useState(null);
|
|
1444
|
+
const [toolFocus, setToolFocus] = React.useState(null);
|
|
1445
|
+
const close = React.useCallback(() => {
|
|
1446
|
+
if (sessionId === "") return;
|
|
1447
|
+
modalStoreOf(sessionId).set(false);
|
|
1448
|
+
const guard = takePendingConsume(sessionId);
|
|
1449
|
+
if (guard === void 0 || sessions === void 0) return;
|
|
1450
|
+
const scope = sessions.scope(sessionId);
|
|
1451
|
+
if (scope !== void 0) scope.bail(scope, "slash/input-consume-token", { guard });
|
|
1452
|
+
}, [sessionId]);
|
|
1453
|
+
React.useEffect(() => {
|
|
1454
|
+
if (!open) return void 0;
|
|
1455
|
+
const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
1456
|
+
const onKey = (ev) => {
|
|
1457
|
+
if (ev.key !== "Escape") return;
|
|
1458
|
+
ev.preventDefault();
|
|
1459
|
+
ev.stopPropagation();
|
|
1460
|
+
close();
|
|
1461
|
+
};
|
|
1462
|
+
window.addEventListener("keydown", onKey, true);
|
|
1463
|
+
return () => {
|
|
1464
|
+
window.removeEventListener("keydown", onKey, true);
|
|
1465
|
+
if (previous !== null && document.contains(previous)) previous.focus();
|
|
1466
|
+
};
|
|
1467
|
+
}, [open, close]);
|
|
1468
|
+
if (!open) return null;
|
|
1469
|
+
const head = data !== null ? headlineOf(data, pressure) : null;
|
|
1470
|
+
const subtitle = data !== null ? (data.model ? data.model : "") + (data.provider ? " · " + data.provider : "") : "";
|
|
1471
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1472
|
+
className: "lc-modal-backdrop",
|
|
1473
|
+
onClick: close
|
|
1474
|
+
}, /* @__PURE__ */ React.createElement("div", {
|
|
1475
|
+
className: "lc-modal-card",
|
|
1476
|
+
onClick: (ev) => {
|
|
1477
|
+
ev.stopPropagation();
|
|
1478
|
+
}
|
|
1479
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-modal-head" }, /* @__PURE__ */ React.createElement("span", { className: "lc-modal-title" }, t("tab")), /* @__PURE__ */ React.createElement("button", {
|
|
1480
|
+
className: "lc-modal-close",
|
|
1481
|
+
"aria-label": t("cmd.close"),
|
|
1482
|
+
onClick: close
|
|
1483
|
+
}, "×")), data === null || head === null ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(CurrentComposition, {
|
|
1484
|
+
head,
|
|
1485
|
+
subtitle,
|
|
1486
|
+
hoverKey: hoverCat,
|
|
1487
|
+
onHoverKey: setHoverCat,
|
|
1488
|
+
tools: data.toolList,
|
|
1489
|
+
onToolFocus: setToolFocus
|
|
1490
|
+
}), /* @__PURE__ */ React.createElement(ContextBrowser, {
|
|
1491
|
+
data,
|
|
1492
|
+
headers,
|
|
1493
|
+
useSession: props.useSession,
|
|
1494
|
+
loadOlderHistory: props.loadOlderHistory,
|
|
1495
|
+
hoverKey: hoverCat,
|
|
1496
|
+
onHoverKey: setHoverCat,
|
|
1497
|
+
toolFocus,
|
|
1498
|
+
onToolFocusHandled: () => {
|
|
1499
|
+
setToolFocus(null);
|
|
1500
|
+
}
|
|
1501
|
+
}))));
|
|
1502
|
+
}
|
|
1503
|
+
return function ContextModal(props) {
|
|
1504
|
+
return h(ErrorBoundary, null, h(ContextModalBody, props));
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
//#endregion
|
|
1508
|
+
//#region src/client/components/events.tsx
|
|
1509
|
+
const EVENT_ICONS = {
|
|
1510
|
+
compaction: "✂",
|
|
1511
|
+
prune: "✂",
|
|
1512
|
+
inject: "+",
|
|
1513
|
+
model: "⇄"
|
|
1514
|
+
};
|
|
1515
|
+
/** Build the event text helpers bound to the translate function. */
|
|
1516
|
+
function makeEventText(t) {
|
|
1517
|
+
function eventLabel(ev) {
|
|
1518
|
+
if (ev.kind === "compaction") return t("ev.compaction", { n: ev.count || 0 });
|
|
1519
|
+
if (ev.kind === "prune") return t("ev.prune");
|
|
1520
|
+
if (ev.kind === "model") return t("ev.model", {
|
|
1521
|
+
a: ev.from || "?",
|
|
1522
|
+
b: ev.to || "?"
|
|
1523
|
+
});
|
|
1524
|
+
if (ev.sub === "skill") return t("ev.skill", { name: ev.name || "?" });
|
|
1525
|
+
const base = t("form." + (ev.form || "context"));
|
|
1526
|
+
return ev.name ? base + " · " + ev.name : base;
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Where this event sits in the request timeline, as a label or null.
|
|
1530
|
+
* Boundary events (compaction/prune) show the GAP they sit in: same-turn
|
|
1531
|
+
* "Step 2→3", cross-turn "Turn 50 · Step 8 → Turn 51 · Step 1". Injections
|
|
1532
|
+
* and model switches belong to one request and keep the single point.
|
|
1533
|
+
* Events with no following request (in flight) stay unlabeled.
|
|
1534
|
+
*/
|
|
1535
|
+
function eventAt(ev) {
|
|
1536
|
+
if (ev.kind === "compaction" || ev.kind === "prune") {
|
|
1537
|
+
if (typeof ev.turn === "number" && typeof ev.step === "number") {
|
|
1538
|
+
if (typeof ev.fromTurn === "number" && typeof ev.fromStep === "number") {
|
|
1539
|
+
if (ev.fromTurn === ev.turn) return t("events.range", {
|
|
1540
|
+
t: ev.turn,
|
|
1541
|
+
a: ev.fromStep,
|
|
1542
|
+
b: ev.step
|
|
1543
|
+
});
|
|
1544
|
+
return t("events.rangeTo", {
|
|
1545
|
+
a: ev.fromTurn,
|
|
1546
|
+
as: ev.fromStep,
|
|
1547
|
+
b: ev.turn,
|
|
1548
|
+
bs: ev.step
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
return t("events.at", {
|
|
1552
|
+
t: ev.turn,
|
|
1553
|
+
s: ev.step
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
return null;
|
|
1557
|
+
}
|
|
1558
|
+
if (typeof ev.turn === "number" && typeof ev.step === "number") return t("events.at", {
|
|
1559
|
+
t: ev.turn,
|
|
1560
|
+
s: ev.step
|
|
1561
|
+
});
|
|
1562
|
+
return null;
|
|
1563
|
+
}
|
|
1564
|
+
return {
|
|
1565
|
+
eventLabel,
|
|
1566
|
+
eventAt
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
function makeEventList(kit) {
|
|
1570
|
+
const { t, fmt, fmtTime, eventLabel, eventAt } = kit;
|
|
1571
|
+
return function EventList(props) {
|
|
1572
|
+
if (props.events.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("events.empty"));
|
|
1573
|
+
const rootRef = React.useRef(null);
|
|
1574
|
+
React.useLayoutEffect(() => {
|
|
1575
|
+
const root = rootRef.current;
|
|
1576
|
+
if (!root) return;
|
|
1577
|
+
const sync = () => {
|
|
1578
|
+
for (const el of root.querySelectorAll(".lc-event-label")) el.title = el.scrollWidth > el.clientWidth ? el.textContent || "" : "";
|
|
1579
|
+
};
|
|
1580
|
+
sync();
|
|
1581
|
+
window.addEventListener("resize", sync);
|
|
1582
|
+
return () => {
|
|
1583
|
+
window.removeEventListener("resize", sync);
|
|
1584
|
+
};
|
|
1585
|
+
});
|
|
1586
|
+
const sorted = props.events.slice().reverse();
|
|
1587
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1588
|
+
className: "lc-events",
|
|
1589
|
+
ref: rootRef
|
|
1590
|
+
}, sorted.map((ev, i) => {
|
|
1591
|
+
const label = eventLabel(ev);
|
|
1592
|
+
const at = eventAt(ev);
|
|
1593
|
+
const glyph = ev.kind === "inject" ? /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, null) : ev.kind === "model" ? /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, null) : EVENT_ICONS[ev.kind] || "•";
|
|
1594
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1595
|
+
key: `${ev.seq}-${i}`,
|
|
1596
|
+
className: "lc-event"
|
|
1597
|
+
}, /* @__PURE__ */ React.createElement("span", { className: "lc-event-icon lc-event-" + ev.kind }, glyph), /* @__PURE__ */ React.createElement("span", { className: "lc-kind lc-kind-" + ev.kind }, t("kind." + ev.kind)), /* @__PURE__ */ React.createElement("span", { className: "lc-event-label" }, label), at !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-at" }, at) : null, ev.tokens ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-tokens" + (ev.kind === "inject" ? " lc-up" : " lc-down") }, (ev.kind === "inject" ? "+" : "−") + fmt(ev.tokens)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-event-time" }, fmtTime(ev.time)));
|
|
1598
|
+
}));
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
const PLUGIN_REPO = "https://github.com/bowenliang123/dsh-context";
|
|
1602
|
+
/** Short `owner/repo` form of the GitHub URL, for display only. */
|
|
1603
|
+
const PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
|
|
1604
|
+
//#endregion
|
|
1605
|
+
//#region src/client/latestVersion.ts
|
|
1606
|
+
/**
|
|
1607
|
+
* Latest published version of this plugin on the npm registry — the one
|
|
1608
|
+
* live check the Plugin info card performs. Fetched lazily on the first
|
|
1609
|
+
* card mount and cached with a 1-hour TTL, so the registry is hit at most
|
|
1610
|
+
* once per hour per page. Every failure mode (offline, CSP-blocked,
|
|
1611
|
+
* non-200, malformed body) narrows to null = no upgrade hint, so the card
|
|
1612
|
+
* silently keeps its static version.
|
|
1613
|
+
*/
|
|
1614
|
+
const REGISTRY_URL = "https://registry.npmjs.org/dsh-context/latest";
|
|
1615
|
+
/** How long a registry result stays fresh before the next check refetches. */
|
|
1616
|
+
const TTL_MS = 36e5;
|
|
1617
|
+
let cached = null;
|
|
1618
|
+
function fetchLatestVersion() {
|
|
1619
|
+
if (!cached || Date.now() - cached.at >= TTL_MS) cached = {
|
|
1620
|
+
at: Date.now(),
|
|
1621
|
+
promise: fetch(REGISTRY_URL).then((res) => res.ok ? res.json() : null).then((body) => body !== null && typeof body.version === "string" ? body.version : null).catch(() => null)
|
|
1622
|
+
};
|
|
1623
|
+
return cached.promise;
|
|
1624
|
+
}
|
|
1625
|
+
/** Numeric semver compare (pre-release suffix ignored): is `latest` strictly newer than `current`? */
|
|
1626
|
+
function isNewerVersion(latest, current) {
|
|
1627
|
+
const parse = (v) => v.replace(/^v/, "").split("-", 1)[0].split(".").map((n) => parseInt(n, 10) || 0);
|
|
1628
|
+
const a = parse(latest);
|
|
1629
|
+
const b = parse(current);
|
|
1630
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
1631
|
+
const x = a[i] || 0;
|
|
1632
|
+
const y = b[i] || 0;
|
|
1633
|
+
if (x !== y) return x > y;
|
|
1634
|
+
}
|
|
1635
|
+
return false;
|
|
1636
|
+
}
|
|
1637
|
+
//#endregion
|
|
1638
|
+
//#region src/client/components/pluginInfo.tsx
|
|
1639
|
+
function makePluginInfo(kit) {
|
|
1640
|
+
const { t } = kit;
|
|
1641
|
+
const row = (label, value, href) => /* @__PURE__ */ React.createElement("a", {
|
|
1642
|
+
className: "lc-pi-row",
|
|
1643
|
+
href,
|
|
1644
|
+
target: "_blank",
|
|
1645
|
+
rel: "noreferrer"
|
|
1646
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-pi-label" }, label), /* @__PURE__ */ React.createElement("div", { className: "lc-pi-value" }, value));
|
|
1647
|
+
return function PluginInfo() {
|
|
1648
|
+
const [latest, setLatest] = React.useState(null);
|
|
1649
|
+
React.useEffect(() => {
|
|
1650
|
+
if ("0.22.1".includes("-dev")) return;
|
|
1651
|
+
let on = true;
|
|
1652
|
+
fetchLatestVersion().then((v) => {
|
|
1653
|
+
if (on && v) setLatest(v);
|
|
1654
|
+
});
|
|
1655
|
+
return () => {
|
|
1656
|
+
on = false;
|
|
1657
|
+
};
|
|
1658
|
+
}, []);
|
|
1659
|
+
const update = latest !== null && isNewerVersion(latest, "0.22.1") ? latest : null;
|
|
1660
|
+
const nameValue = ["dsh-context (v0.22.1)"];
|
|
1661
|
+
if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
|
|
1662
|
+
key: "update",
|
|
1663
|
+
className: "lc-pi-update"
|
|
1664
|
+
}, "↑ v" + update));
|
|
1665
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("plugin.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("plugin.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-pi-grid" }, row(t("plugin.name"), nameValue, PLUGIN_REPO + "/releases"), row(t("plugin.github"), PLUGIN_REPO_SHORT, PLUGIN_REPO)));
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
//#endregion
|
|
1669
|
+
//#region src/client/components/requestDetail.tsx
|
|
1670
|
+
function makeRequestDetail(kit, StackedBar) {
|
|
1671
|
+
const { t, fmt, fmtTime, catLabel, eventLabel, eventAt } = kit;
|
|
1672
|
+
return function RequestDetail(props) {
|
|
1673
|
+
const req = props.request;
|
|
1674
|
+
if (!req) return null;
|
|
1675
|
+
const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
|
|
1676
|
+
const head = isTurn ? t("detail.turn", {
|
|
1677
|
+
t: req.turn ?? 0,
|
|
1678
|
+
n: req.stepCount ?? 0
|
|
1679
|
+
}) : t("detail.step", {
|
|
1680
|
+
t: req.turn ?? 0,
|
|
1681
|
+
s: req.step ?? 0
|
|
1682
|
+
});
|
|
1683
|
+
const marker = props.marker ?? null;
|
|
1684
|
+
const markerAt = marker !== null ? eventAt(marker) : null;
|
|
1685
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-detail" }, /* @__PURE__ */ React.createElement("div", { className: "lc-detail-head" }, /* @__PURE__ */ React.createElement("b", null, head), marker !== null && markerAt !== null ? /* @__PURE__ */ React.createElement("span", {
|
|
1686
|
+
className: "lc-detail-marker",
|
|
1687
|
+
title: eventLabel(marker)
|
|
1688
|
+
}, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, /* @__PURE__ */ React.createElement("span", null, fmtTime(req.time)), /* @__PURE__ */ React.createElement("span", null, t("detail.estTotal", { n: fmt(req.total) })), req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-actual" }, t("detail.actual", { n: fmt(req.prompt) })) : null, req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", null, t("detail.output", { n: fmt(req.output) })) : null), /* @__PURE__ */ React.createElement(StackedBar, {
|
|
1689
|
+
parts: partsOf(req),
|
|
1690
|
+
height: 10
|
|
1691
|
+
}), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c) => {
|
|
1692
|
+
const v = req[c.key] || 0;
|
|
1693
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
1694
|
+
key: c.key,
|
|
1695
|
+
className: "lc-detail-row"
|
|
1696
|
+
}, /* @__PURE__ */ React.createElement("i", { style: { background: c.color } }), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-label" }, catLabel(c.key)), /* @__PURE__ */ React.createElement("span", { className: "lc-bar-track" }, /* @__PURE__ */ React.createElement("span", {
|
|
1697
|
+
className: "lc-bar-fill",
|
|
1698
|
+
style: {
|
|
1699
|
+
width: `${req.total > 0 ? v / req.total * 100 : 0}%`,
|
|
1700
|
+
background: c.color
|
|
1701
|
+
}
|
|
1702
|
+
})), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-num" }, "≈" + fmt(v)), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-pct" }, req.total > 0 ? `${Math.round(v / req.total * 100)}%` : ""));
|
|
1703
|
+
})));
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
//#endregion
|
|
1707
|
+
//#region src/client/cost.ts
|
|
1708
|
+
const PRICES = {
|
|
1709
|
+
usd: {
|
|
1710
|
+
flash: {
|
|
1711
|
+
peak: {
|
|
1712
|
+
hit: .014,
|
|
1713
|
+
miss: .44,
|
|
1714
|
+
out: 1.32
|
|
1715
|
+
},
|
|
1716
|
+
off: {
|
|
1717
|
+
hit: .007,
|
|
1718
|
+
miss: .22,
|
|
1719
|
+
out: .66
|
|
1720
|
+
}
|
|
1721
|
+
},
|
|
1722
|
+
pro: {
|
|
1723
|
+
peak: {
|
|
1724
|
+
hit: .044,
|
|
1725
|
+
miss: 1.32,
|
|
1726
|
+
out: 3.96
|
|
1727
|
+
},
|
|
1728
|
+
off: {
|
|
1729
|
+
hit: .022,
|
|
1730
|
+
miss: .66,
|
|
1731
|
+
out: 1.98
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
},
|
|
1735
|
+
cny: {
|
|
1736
|
+
flash: {
|
|
1737
|
+
peak: {
|
|
1738
|
+
hit: .1,
|
|
1739
|
+
miss: 3,
|
|
1740
|
+
out: 9
|
|
1741
|
+
},
|
|
1742
|
+
off: {
|
|
1743
|
+
hit: .05,
|
|
1744
|
+
miss: 1.5,
|
|
1745
|
+
out: 4.5
|
|
1746
|
+
}
|
|
1747
|
+
},
|
|
1748
|
+
pro: {
|
|
1749
|
+
peak: {
|
|
1750
|
+
hit: .3,
|
|
1751
|
+
miss: 9,
|
|
1752
|
+
out: 27
|
|
1753
|
+
},
|
|
1754
|
+
off: {
|
|
1755
|
+
hit: .15,
|
|
1756
|
+
miss: 4.5,
|
|
1757
|
+
out: 13.5
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
};
|
|
1762
|
+
/**
|
|
1763
|
+
* Price the session's cumulative billed-token totals. Cache reads bill at
|
|
1764
|
+
* the hit rate; uncached input AND cache writes bill at the miss rate;
|
|
1765
|
+
* output (reasoning included) bills at the out rate. Null when nothing was
|
|
1766
|
+
* priced (no DeepSeek V4 usage folded yet), so the cell can show a dash.
|
|
1767
|
+
*/
|
|
1768
|
+
function estimateSessionCost(usage, currency) {
|
|
1769
|
+
if (usage === null || usage === void 0) return null;
|
|
1770
|
+
let total = 0;
|
|
1771
|
+
let any = false;
|
|
1772
|
+
for (const family of ["flash", "pro"]) {
|
|
1773
|
+
const fam = usage[family];
|
|
1774
|
+
if (fam === void 0) continue;
|
|
1775
|
+
for (const period of ["peak", "off"]) {
|
|
1776
|
+
const b = fam[period];
|
|
1777
|
+
if (b === void 0) continue;
|
|
1778
|
+
const p = PRICES[currency][family][period];
|
|
1779
|
+
total += (numOf(b.cacheRead) * p.hit + (numOf(b.uncached) + numOf(b.cacheWrite)) * p.miss + numOf(b.output) * p.out) / 1e6;
|
|
1780
|
+
any = true;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
return any ? total : null;
|
|
1784
|
+
}
|
|
1785
|
+
/** Short money format: two decimals at and above one unit, two significant digits below. */
|
|
1786
|
+
function formatCost(amount, currency) {
|
|
1787
|
+
return (currency === "cny" ? "¥" : "$") + (amount >= 1 ? amount.toFixed(2) : amount.toPrecision(2));
|
|
1788
|
+
}
|
|
1789
|
+
/**
|
|
1790
|
+
* All priced model families for one currency, in display order, with their
|
|
1791
|
+
* peak/off-peak rate triples — the raw material of the stats-board tooltip's
|
|
1792
|
+
* price list. The numbers stay hardcoded in PRICES above; this function only
|
|
1793
|
+
* reshapes the table for display, so what the tooltip prints can never drift
|
|
1794
|
+
* from the math that prices the session.
|
|
1795
|
+
*/
|
|
1796
|
+
function sessionPrices(currency) {
|
|
1797
|
+
return ["flash", "pro"].map((id) => ({
|
|
1798
|
+
family: id === "flash" ? "deepseek-v4-flash" : "deepseek-v4-pro",
|
|
1799
|
+
peak: PRICES[currency][id].peak,
|
|
1800
|
+
off: PRICES[currency][id].off
|
|
1801
|
+
}));
|
|
1802
|
+
}
|
|
1803
|
+
/** Price-list figure: the same money format as formatCost, trailing zeros trimmed (¥3.00 → ¥3, $0.0070 → $0.007). */
|
|
1804
|
+
function formatPriceRate(amount, currency) {
|
|
1805
|
+
return formatCost(amount, currency).replace(/0+$/, "").replace(/\.$/, "");
|
|
1806
|
+
}
|
|
1807
|
+
//#endregion
|
|
1808
|
+
//#region src/client/components/statsBoard.tsx
|
|
1809
|
+
/**
|
|
1810
|
+
* Cache-hit share of billed prompt-side input — same buckets as the harness's
|
|
1811
|
+
* chat stats line below the input box (`cacheReadTokens` over the three
|
|
1812
|
+
* disjoint billed buckets: uncached + reads + writes). Unlike that line's
|
|
1813
|
+
* whole-percent rounding, this one TRUNCATES to two decimal places (cut, not
|
|
1814
|
+
* round). Null when no input was billed. A tiny epsilon keeps a double stored
|
|
1815
|
+
* a hair below a cent boundary (e.g. 80.00 as 79.9999999999…) from losing its
|
|
1816
|
+
* last digit — with integer token counts no genuine value ever sits that close
|
|
1817
|
+
* to a boundary, so the epsilon can only absorb float noise. Bucket reads go
|
|
1818
|
+
* through numOf so a malformed payload degrades to a dash instead of NaN text.
|
|
1819
|
+
*/
|
|
1820
|
+
function cacheHitPercent(usage) {
|
|
1821
|
+
const uncached = numOf(usage.uncachedInputTokens);
|
|
1822
|
+
const reads = numOf(usage.cacheReadTokens);
|
|
1823
|
+
const writes = numOf(usage.cacheWriteTokens);
|
|
1824
|
+
const billed = uncached + reads + writes;
|
|
1825
|
+
if (billed === 0) return null;
|
|
1826
|
+
const hundredths = Math.trunc(reads / billed * 1e4 + 1e-9);
|
|
1827
|
+
return `${Math.floor(hundredths / 100)}.${String(hundredths % 100).padStart(2, "0")}`;
|
|
1828
|
+
}
|
|
1829
|
+
function makeStatsBoard(kit) {
|
|
1830
|
+
const { t, fmt } = kit;
|
|
1831
|
+
return function StatsBoard(props) {
|
|
1832
|
+
const turns = /* @__PURE__ */ new Set();
|
|
1833
|
+
let steps = 0, compactions = 0, prunes = 0, injects = 0;
|
|
1834
|
+
for (const req of props.requests) {
|
|
1835
|
+
turns.add(req.turn ?? 0);
|
|
1836
|
+
steps++;
|
|
1837
|
+
}
|
|
1838
|
+
for (const ev of props.events) if (ev.kind === "compaction") compactions++;
|
|
1839
|
+
else if (ev.kind === "prune") prunes++;
|
|
1840
|
+
else if (ev.kind === "inject") injects++;
|
|
1841
|
+
const hit = props.usage !== null ? cacheHitPercent(props.usage) : null;
|
|
1842
|
+
const currency = props.locale === "zh" ? "cny" : "usd";
|
|
1843
|
+
const cost = estimateSessionCost(props.cost, currency);
|
|
1844
|
+
const fmtRate = (n) => formatPriceRate(n, currency);
|
|
1845
|
+
const costTip = [t("stats.costTip"), /* @__PURE__ */ React.createElement("span", {
|
|
1846
|
+
key: "prices",
|
|
1847
|
+
className: "lc-stat-tip-prices"
|
|
1848
|
+
}, /* @__PURE__ */ React.createElement("span", { className: "lc-stat-tip-head" }, t("stats.costPriceHead")), sessionPrices(currency).map((r) => /* @__PURE__ */ React.createElement("span", {
|
|
1849
|
+
key: r.family,
|
|
1850
|
+
className: "lc-stat-tip-row"
|
|
1851
|
+
}, /* @__PURE__ */ React.createElement("b", { className: "lc-stat-tip-model" }, r.family), " ", t("stats.costHit"), " ", fmtRate(r.peak.hit), "/", fmtRate(r.off.hit), " · ", t("stats.costMiss"), " ", fmtRate(r.peak.miss), "/", fmtRate(r.off.miss), " · ", t("stats.costOut"), " ", fmtRate(r.peak.out), "/", fmtRate(r.off.out))))];
|
|
1852
|
+
const cell = (label, value, tip) => /* @__PURE__ */ React.createElement("div", { className: "lc-stat" + (tip === void 0 ? "" : " lc-stat-tipped") }, /* @__PURE__ */ React.createElement("span", { className: "lc-stat-label" }, label, tip !== void 0 && /* @__PURE__ */ React.createElement("i", {
|
|
1853
|
+
className: "lc-stat-q",
|
|
1854
|
+
"aria-hidden": "true"
|
|
1855
|
+
}, "?")), /* @__PURE__ */ React.createElement("b", { className: "lc-stat-value" }, value), tip !== void 0 && /* @__PURE__ */ React.createElement("span", {
|
|
1856
|
+
className: "lc-stat-tip",
|
|
1857
|
+
role: "tooltip"
|
|
1858
|
+
}, tip));
|
|
1859
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col lc-col-stats" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("stats.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("stats.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-stats" }, cell(t("stats.turns"), fmt(turns.size)), cell(t("stats.steps"), fmt(steps)), cell(t("stats.injects"), fmt(injects)), cell(t("stats.compactions"), fmt(compactions)), cell(t("stats.prunes"), fmt(prunes)), cell(t("stats.cacheHit"), hit === null ? "—" : hit + "%"), cell(t("stats.cost"), cost === null ? "—" : formatCost(cost, currency), costTip)));
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
//#endregion
|
|
1863
|
+
//#region src/client/components/trendChart.tsx
|
|
1864
|
+
/**
|
|
1865
|
+
* Collapse a per-step request timeline into one bar per turn: each turn is
|
|
1866
|
+
* represented by its LAST step's record (the context state when the turn
|
|
1867
|
+
* finished), tagged with the number of steps the turn spans so the bar can
|
|
1868
|
+
* keep the turn's column width and the detail can label it. Requests of one
|
|
1869
|
+
* turn are consecutive in the log, so a run of equal turns is replaced by
|
|
1870
|
+
* its final record.
|
|
1871
|
+
*/
|
|
1872
|
+
function aggregateByTurn(requests) {
|
|
1873
|
+
const out = [];
|
|
1874
|
+
let runSteps = 0;
|
|
1875
|
+
for (const req of requests) {
|
|
1876
|
+
const last = out.length > 0 ? out[out.length - 1] : null;
|
|
1877
|
+
if (last !== null && (last.turn ?? 0) === (req.turn ?? 0)) {
|
|
1878
|
+
runSteps++;
|
|
1879
|
+
out[out.length - 1] = {
|
|
1880
|
+
...req,
|
|
1881
|
+
stepCount: runSteps
|
|
1882
|
+
};
|
|
1883
|
+
} else {
|
|
1884
|
+
runSteps = 1;
|
|
1885
|
+
out.push({
|
|
1886
|
+
...req,
|
|
1887
|
+
stepCount: 1
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
return out;
|
|
1892
|
+
}
|
|
1893
|
+
/**
|
|
1894
|
+
* Attach each boundary event (compaction/prune) to the first request
|
|
1895
|
+
* logged after it — one entry per request index, for the ✂ marker above
|
|
1896
|
+
* the bar and the range chip in the detail header. Shared by TrendChart
|
|
1897
|
+
* and the detail panel so both show the SAME event for a request.
|
|
1898
|
+
*/
|
|
1899
|
+
function attachMarkers(requests, events) {
|
|
1900
|
+
const markers = new Array(requests.length);
|
|
1901
|
+
for (const ev of events) {
|
|
1902
|
+
if (ev.kind !== "compaction" && ev.kind !== "prune") continue;
|
|
1903
|
+
for (let r = 0; r < requests.length; r++) if (requests[r].seq >= ev.seq) {
|
|
1904
|
+
if (markers[r] === void 0) markers[r] = ev;
|
|
1905
|
+
break;
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
return markers;
|
|
1909
|
+
}
|
|
1910
|
+
function makeTrendChart(kit) {
|
|
1911
|
+
const { t, fmt, fmtTime, eventLabel, eventAt } = kit;
|
|
1912
|
+
const CHART_H = 112;
|
|
1913
|
+
const BAR_W = 14;
|
|
1914
|
+
const BAR_GAP = 2;
|
|
1915
|
+
const TURN_FILLS = ["rgba(128,128,128,0.12)", "rgba(128,128,128,0.26)"];
|
|
1916
|
+
return function TrendChart(props) {
|
|
1917
|
+
const requests = props.requests;
|
|
1918
|
+
const markers = props.markers;
|
|
1919
|
+
const anchorOf = (req) => typeof req.prompt === "number" && req.prompt > 0 && req.total > 0 ? req.prompt / req.total : 1;
|
|
1920
|
+
const barTotalOf = (req) => typeof req.prompt === "number" && req.prompt > 0 ? req.prompt : req.total;
|
|
1921
|
+
let maxTotal = 1;
|
|
1922
|
+
for (const req of requests) {
|
|
1923
|
+
const bt = barTotalOf(req);
|
|
1924
|
+
if (bt > maxTotal) maxTotal = bt;
|
|
1925
|
+
}
|
|
1926
|
+
const groups = [];
|
|
1927
|
+
for (const req of requests) {
|
|
1928
|
+
let grp = groups.length > 0 ? groups[groups.length - 1] : null;
|
|
1929
|
+
if (grp === null || grp.turn !== (req.turn ?? 0)) {
|
|
1930
|
+
grp = {
|
|
1931
|
+
turn: req.turn ?? 0,
|
|
1932
|
+
count: 0,
|
|
1933
|
+
span: 0,
|
|
1934
|
+
agg: req.stepCount !== void 0
|
|
1935
|
+
};
|
|
1936
|
+
groups.push(grp);
|
|
1937
|
+
}
|
|
1938
|
+
grp.count++;
|
|
1939
|
+
grp.span += req.stepCount ?? 1;
|
|
1940
|
+
}
|
|
1941
|
+
const scrollRef = React.useRef(null);
|
|
1942
|
+
const scrolledOnce = React.useRef(false);
|
|
1943
|
+
const lastGranRef = React.useRef(props.granularity);
|
|
1944
|
+
const [edges, setEdges] = React.useState({
|
|
1945
|
+
left: false,
|
|
1946
|
+
right: false
|
|
1947
|
+
});
|
|
1948
|
+
const edgesRef = React.useRef(edges);
|
|
1949
|
+
const updateEdges = (el) => {
|
|
1950
|
+
const left = el.scrollLeft > 4;
|
|
1951
|
+
const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 4;
|
|
1952
|
+
const prev = edgesRef.current;
|
|
1953
|
+
if (prev.left === left && prev.right === right) return;
|
|
1954
|
+
edgesRef.current = {
|
|
1955
|
+
left,
|
|
1956
|
+
right
|
|
1957
|
+
};
|
|
1958
|
+
setEdges({
|
|
1959
|
+
left,
|
|
1960
|
+
right
|
|
1961
|
+
});
|
|
1962
|
+
};
|
|
1963
|
+
React.useLayoutEffect(() => {
|
|
1964
|
+
const el = scrollRef.current;
|
|
1965
|
+
if (el === null) return;
|
|
1966
|
+
if (props.granularity !== lastGranRef.current) {
|
|
1967
|
+
lastGranRef.current = props.granularity;
|
|
1968
|
+
scrolledOnce.current = false;
|
|
1969
|
+
}
|
|
1970
|
+
if (!scrolledOnce.current) {
|
|
1971
|
+
scrolledOnce.current = true;
|
|
1972
|
+
el.scrollLeft = el.scrollWidth;
|
|
1973
|
+
} else if (el.scrollLeft + el.clientWidth >= el.scrollWidth - 24) el.scrollLeft = el.scrollWidth;
|
|
1974
|
+
updateEdges(el);
|
|
1975
|
+
});
|
|
1976
|
+
const tipOf = (req) => {
|
|
1977
|
+
return (req.stepCount !== void 0 && req.stepCount > 1 ? t("tip.turn", {
|
|
1978
|
+
t: req.turn ?? 0,
|
|
1979
|
+
n: req.stepCount
|
|
1980
|
+
}) : t("tip.step", {
|
|
1981
|
+
t: req.turn ?? 0,
|
|
1982
|
+
s: req.step ?? 0
|
|
1983
|
+
})) + " · " + fmtTime(req.time) + " · " + t("tip.total", { n: fmt(req.total) }) + (req.prompt !== void 0 ? " · " + t("tip.actual", { n: fmt(req.prompt) }) : "");
|
|
1984
|
+
};
|
|
1985
|
+
const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
|
|
1986
|
+
const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
|
|
1987
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-chartrow" }, /* @__PURE__ */ React.createElement("div", { className: "lc-axis" }, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, fmt(maxTotal)), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-mid" }, fmt(Math.round(maxTotal / 2))), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, "0")), /* @__PURE__ */ React.createElement("div", {
|
|
1988
|
+
className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
|
|
1989
|
+
ref: scrollRef,
|
|
1990
|
+
onScroll: (e) => {
|
|
1991
|
+
updateEdges(e.currentTarget);
|
|
1992
|
+
}
|
|
1993
|
+
}, edges.left ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-l" }) : null, /* @__PURE__ */ React.createElement("div", {
|
|
1994
|
+
className: "lc-chart",
|
|
1995
|
+
onMouseLeave: () => {
|
|
1996
|
+
props.onHover(null);
|
|
1997
|
+
}
|
|
1998
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-grid lc-grid-top" }), /* @__PURE__ */ React.createElement("div", { className: "lc-grid lc-grid-mid" }), requests.map((req, i) => {
|
|
1999
|
+
const marker = markers[i];
|
|
2000
|
+
const markerAt = marker !== void 0 ? eventAt(marker) : null;
|
|
2001
|
+
const selected = props.selectedSeq === req.seq;
|
|
2002
|
+
const hovered = props.hoveredSeq === req.seq;
|
|
2003
|
+
const inTurn = props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn;
|
|
2004
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
2005
|
+
key: req.seq,
|
|
2006
|
+
className: "lc-bar" + (selected ? " lc-bar-selected" : "") + (hovered ? " lc-bar-hovered" : "") + (inTurn ? " lc-bar-in-turn" : ""),
|
|
2007
|
+
style: { width: `${BAR_W}px` },
|
|
2008
|
+
onClick: () => {
|
|
2009
|
+
props.onSelect(selected ? null : req.seq);
|
|
2010
|
+
},
|
|
2011
|
+
onMouseEnter: () => {
|
|
2012
|
+
props.onHover(req.seq);
|
|
2013
|
+
}
|
|
2014
|
+
}, marker !== void 0 ? /* @__PURE__ */ React.createElement("span", {
|
|
2015
|
+
className: "lc-bar-marker",
|
|
2016
|
+
title: "✂ " + (markerAt !== null ? markerAt + " — " : "") + eventLabel(marker)
|
|
2017
|
+
}, "✂") : null, /* @__PURE__ */ React.createElement("div", { className: "lc-bar-stack" }, CATS.map((c) => {
|
|
2018
|
+
const v = (req[c.key] || 0) * anchorOf(req);
|
|
2019
|
+
if (!v) return null;
|
|
2020
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
2021
|
+
key: c.key,
|
|
2022
|
+
style: {
|
|
2023
|
+
height: `${Math.max(1, Math.round(v / maxTotal * CHART_H))}px`,
|
|
2024
|
+
background: c.color
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
})));
|
|
2028
|
+
})), hoveredReq !== null ? /* @__PURE__ */ React.createElement("div", {
|
|
2029
|
+
className: "lc-chart-tip",
|
|
2030
|
+
style: { left: `${hoveredIdx * 16 + BAR_W / 2}px` }
|
|
2031
|
+
}, tipOf(hoveredReq)) : null, /* @__PURE__ */ React.createElement("div", {
|
|
2032
|
+
className: "lc-turns",
|
|
2033
|
+
onMouseLeave: () => {
|
|
2034
|
+
props.onHoverTurn(null);
|
|
2035
|
+
}
|
|
2036
|
+
}, groups.map((grp, gi) => {
|
|
2037
|
+
const on = props.activeTurn === grp.turn;
|
|
2038
|
+
const blockW = grp.agg ? BAR_W : grp.span * 16 - BAR_GAP;
|
|
2039
|
+
return /* @__PURE__ */ React.createElement("span", {
|
|
2040
|
+
key: `turn-${gi}`,
|
|
2041
|
+
className: "lc-turn" + (on ? " lc-turn-on" : ""),
|
|
2042
|
+
style: {
|
|
2043
|
+
width: `${blockW}px`,
|
|
2044
|
+
background: TURN_FILLS[gi % TURN_FILLS.length]
|
|
2045
|
+
},
|
|
2046
|
+
title: `T${grp.turn}`,
|
|
2047
|
+
onMouseEnter: () => {
|
|
2048
|
+
props.onHoverTurn(grp.turn);
|
|
2049
|
+
}
|
|
2050
|
+
}, `T${grp.turn}`);
|
|
2051
|
+
}))), edges.right ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-r" }) : null);
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
//#endregion
|
|
2055
|
+
//#region src/client/components/contextView.tsx
|
|
2056
|
+
const viewScroll = /* @__PURE__ */ new Map();
|
|
2057
|
+
const EVENT_KINDS = [
|
|
2058
|
+
"inject",
|
|
2059
|
+
"compaction",
|
|
2060
|
+
"prune",
|
|
2061
|
+
"model"
|
|
2062
|
+
];
|
|
2063
|
+
function makeContextView(ctx, kit) {
|
|
2064
|
+
const { t } = kit;
|
|
2065
|
+
const StackedBar = makeStackedBar(kit);
|
|
2066
|
+
const CurrentComposition = makeCurrentComposition(kit, StackedBar, makeLegend(kit));
|
|
2067
|
+
const TrendChart = makeTrendChart(kit);
|
|
2068
|
+
const RequestDetail = makeRequestDetail(kit, StackedBar);
|
|
2069
|
+
const EventList = makeEventList(kit);
|
|
2070
|
+
const NodeList = makeNodeList(kit);
|
|
2071
|
+
const StatsBoard = makeStatsBoard(kit);
|
|
2072
|
+
const PluginInfo = makePluginInfo(kit);
|
|
2073
|
+
const ContextBrowser = makeContextBrowser(kit, StackedBar);
|
|
2074
|
+
const ErrorBoundary = makeErrorBoundary(t);
|
|
2075
|
+
function ContextViewBody(props) {
|
|
2076
|
+
const sessionId = props.sessionId;
|
|
2077
|
+
const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
|
|
2078
|
+
const pressure = typeof props.useProjection === "function" ? contextPressureOf(props.useProjection("contextPressure")) : null;
|
|
2079
|
+
const usage = typeof props.useProjection === "function" ? tokenUsageOf(props.useProjection("tokenUsage")) : null;
|
|
2080
|
+
const headers = typeof props.useProjection === "function" ? headersOf(props.useProjection("contextHeaders")) : null;
|
|
2081
|
+
const [selectedSeq, setSelectedSeq] = React.useState(null);
|
|
2082
|
+
const [hoveredSeq, setHoveredSeq] = React.useState(null);
|
|
2083
|
+
const [hoverTurn, setHoverTurn] = React.useState(null);
|
|
2084
|
+
const [tick, setTick] = React.useState(0);
|
|
2085
|
+
const [granularity, setGranularity] = React.useState("step");
|
|
2086
|
+
const [hoverCat, setHoverCat] = React.useState(null);
|
|
2087
|
+
const [pickedKinds, setPickedKinds] = React.useState([...EVENT_KINDS]);
|
|
2088
|
+
const toggleKind = (k) => {
|
|
2089
|
+
setPickedKinds((p) => {
|
|
2090
|
+
if (p.length === EVENT_KINDS.length) return [k];
|
|
2091
|
+
if (!p.includes(k)) return [...p, k];
|
|
2092
|
+
return p.length === 1 ? [...EVENT_KINDS] : p.filter((x) => x !== k);
|
|
2093
|
+
});
|
|
2094
|
+
};
|
|
2095
|
+
const [toolFocus, setToolFocus] = React.useState(null);
|
|
2096
|
+
const clearToolFocus = React.useCallback(() => {
|
|
2097
|
+
setToolFocus(null);
|
|
2098
|
+
}, []);
|
|
2099
|
+
const loadImage = React.useMemo(() => {
|
|
2100
|
+
if (typeof sessionId !== "string" || sessionId === "") return void 0;
|
|
2101
|
+
const conversation = ctx.get("conversation");
|
|
2102
|
+
if (conversation === void 0 || typeof conversation.resolveImage !== "function") return void 0;
|
|
2103
|
+
return (attachment) => conversation.resolveImage(sessionId, attachment);
|
|
2104
|
+
}, [sessionId]);
|
|
2105
|
+
const rootRef = React.useRef(null);
|
|
2106
|
+
const scrollerRef = React.useRef(null);
|
|
2107
|
+
const restoredRef = React.useRef(null);
|
|
2108
|
+
React.useLayoutEffect(() => {
|
|
2109
|
+
if (typeof sessionId !== "string" || sessionId === "" || data === null) return;
|
|
2110
|
+
if (restoredRef.current === sessionId) return;
|
|
2111
|
+
restoredRef.current = sessionId;
|
|
2112
|
+
const scroller = rootRef.current !== null ? rootRef.current.closest("[data-conversation-scroll]") : null;
|
|
2113
|
+
if (scroller === null) return;
|
|
2114
|
+
scrollerRef.current = scroller;
|
|
2115
|
+
scroller.scrollTop = viewScroll.get(sessionId) ?? 0;
|
|
2116
|
+
}, [sessionId, data]);
|
|
2117
|
+
React.useLayoutEffect(() => {
|
|
2118
|
+
return () => {
|
|
2119
|
+
if (typeof sessionId !== "string" || sessionId === "") return;
|
|
2120
|
+
const scroller = scrollerRef.current;
|
|
2121
|
+
if (scroller === null) return;
|
|
2122
|
+
viewScroll.set(sessionId, scroller.scrollTop);
|
|
2123
|
+
};
|
|
2124
|
+
}, [sessionId]);
|
|
2125
|
+
React.useEffect(() => {
|
|
2126
|
+
const localeSvc = ctx.get("locale");
|
|
2127
|
+
if (!localeSvc) return void 0;
|
|
2128
|
+
return localeSvc.subscribe(() => {
|
|
2129
|
+
setTick((x) => x + 1);
|
|
2130
|
+
});
|
|
2131
|
+
}, []);
|
|
2132
|
+
if (!data) return /* @__PURE__ */ React.createElement("div", {
|
|
2133
|
+
className: "lc-root",
|
|
2134
|
+
ref: rootRef
|
|
2135
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
|
|
2136
|
+
const requests = data.requests;
|
|
2137
|
+
const events = data.events;
|
|
2138
|
+
const shownEvents = pickedKinds.length === EVENT_KINDS.length ? events : events.filter((e) => pickedKinds.includes(e.kind));
|
|
2139
|
+
const nodes = data.nodes;
|
|
2140
|
+
const displayRequests = granularity === "turn" ? aggregateByTurn(requests) : requests;
|
|
2141
|
+
const markers = attachMarkers(displayRequests, events);
|
|
2142
|
+
const markerOf = (req) => {
|
|
2143
|
+
const i = displayRequests.indexOf(req);
|
|
2144
|
+
return i >= 0 ? markers[i] : void 0;
|
|
2145
|
+
};
|
|
2146
|
+
let pinnedReq = null;
|
|
2147
|
+
for (const req of displayRequests) if (req.seq === selectedSeq) pinnedReq = req;
|
|
2148
|
+
let activeReq = null;
|
|
2149
|
+
if (hoveredSeq !== null) {
|
|
2150
|
+
for (const req of displayRequests) if (req.seq === hoveredSeq) activeReq = req;
|
|
2151
|
+
}
|
|
2152
|
+
if (activeReq === null) activeReq = pinnedReq;
|
|
2153
|
+
if (activeReq === null && displayRequests.length > 0) activeReq = displayRequests[displayRequests.length - 1];
|
|
2154
|
+
let activeTurn = hoverTurn;
|
|
2155
|
+
if (activeTurn === null && hoveredSeq !== null) {
|
|
2156
|
+
for (const req of displayRequests) if (req.seq === hoveredSeq) {
|
|
2157
|
+
activeTurn = req.turn ?? null;
|
|
2158
|
+
break;
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
const head = headlineOf(data, pressure);
|
|
2162
|
+
const localeSvc = ctx.get("locale");
|
|
2163
|
+
const activeLocale = localeSvc !== void 0 && typeof localeSvc.getLocale === "function" ? localeSvc.getLocale().active : "en";
|
|
2164
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
2165
|
+
className: "lc-root",
|
|
2166
|
+
ref: rootRef
|
|
2167
|
+
}, /* @__PURE__ */ React.createElement("div", { className: "lc-cols lc-head" }, /* @__PURE__ */ React.createElement(StatsBoard, {
|
|
2168
|
+
requests,
|
|
2169
|
+
events,
|
|
2170
|
+
usage,
|
|
2171
|
+
cost: data.cost,
|
|
2172
|
+
locale: activeLocale
|
|
2173
|
+
}), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__PURE__ */ React.createElement(CurrentComposition, {
|
|
2174
|
+
head,
|
|
2175
|
+
subtitle: (data.model ? data.model : "") + (data.provider ? " · " + data.provider : ""),
|
|
2176
|
+
hoverKey: hoverCat,
|
|
2177
|
+
onHoverKey: setHoverCat,
|
|
2178
|
+
tools: data.toolList,
|
|
2179
|
+
onToolFocus: setToolFocus
|
|
2180
|
+
}), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("trend.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement("button", {
|
|
2181
|
+
className: "lc-gran-btn" + (granularity === "step" ? " lc-gran-on" : ""),
|
|
2182
|
+
onClick: () => {
|
|
2183
|
+
setGranularity("step");
|
|
2184
|
+
}
|
|
2185
|
+
}, t("gran.step")), /* @__PURE__ */ React.createElement("button", {
|
|
2186
|
+
className: "lc-gran-btn" + (granularity === "turn" ? " lc-gran-on" : ""),
|
|
2187
|
+
onClick: () => {
|
|
2188
|
+
setGranularity("turn");
|
|
2189
|
+
}
|
|
2190
|
+
}, t("gran.turn")))), displayRequests.length === 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("trend.empty")) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(TrendChart, {
|
|
2191
|
+
key: sessionId,
|
|
2192
|
+
requests: displayRequests,
|
|
2193
|
+
markers,
|
|
2194
|
+
selectedSeq: pinnedReq ? pinnedReq.seq : null,
|
|
2195
|
+
hoveredSeq,
|
|
2196
|
+
activeTurn,
|
|
2197
|
+
granularity,
|
|
2198
|
+
onSelect: setSelectedSeq,
|
|
2199
|
+
onHover: setHoveredSeq,
|
|
2200
|
+
onHoverTurn: setHoverTurn
|
|
2201
|
+
}), /* @__PURE__ */ React.createElement(RequestDetail, {
|
|
2202
|
+
request: activeReq,
|
|
2203
|
+
marker: activeReq !== null ? markerOf(activeReq) : void 0
|
|
2204
|
+
})))), /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__PURE__ */ React.createElement(ContextBrowser, {
|
|
2205
|
+
data,
|
|
2206
|
+
headers,
|
|
2207
|
+
useSession: props.useSession,
|
|
2208
|
+
loadOlderHistory: props.loadOlderHistory,
|
|
2209
|
+
previewSeq: hoveredSeq,
|
|
2210
|
+
pinSeq: pinnedReq !== null ? pinnedReq.seq : null,
|
|
2211
|
+
hoverKey: hoverCat,
|
|
2212
|
+
onHoverKey: setHoverCat,
|
|
2213
|
+
toolFocus,
|
|
2214
|
+
onToolFocusHandled: clearToolFocus,
|
|
2215
|
+
loadImage
|
|
2216
|
+
}))), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("events.title")), /* @__PURE__ */ React.createElement("div", { className: "lc-kinds" }, EVENT_KINDS.map((k) => /* @__PURE__ */ React.createElement("button", {
|
|
2217
|
+
key: k,
|
|
2218
|
+
className: "lc-gran-btn" + (pickedKinds.includes(k) ? " lc-gran-on lc-kind-" + k : ""),
|
|
2219
|
+
onClick: () => {
|
|
2220
|
+
toggleKind(k);
|
|
2221
|
+
}
|
|
2222
|
+
}, t("kind." + k))))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("nodes.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("nodes.hint"))), /* @__PURE__ */ React.createElement(NodeList, {
|
|
2223
|
+
nodes,
|
|
2224
|
+
dropped: data.droppedNodes || 0
|
|
2225
|
+
}))), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
|
|
2226
|
+
}
|
|
2227
|
+
return function ContextView(props) {
|
|
2228
|
+
return h(ErrorBoundary, null, h(ContextViewBody, props));
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
//#endregion
|
|
2232
|
+
//#region src/client/viewkit.ts
|
|
2233
|
+
function makeViewKit(t) {
|
|
2234
|
+
const { eventLabel, eventAt } = makeEventText(t);
|
|
2235
|
+
return {
|
|
2236
|
+
t,
|
|
2237
|
+
fmt,
|
|
2238
|
+
fmtTime,
|
|
2239
|
+
catLabel: (key) => t("cat." + key),
|
|
2240
|
+
eventLabel,
|
|
2241
|
+
eventAt
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
//#endregion
|
|
2245
|
+
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles.css.mjs
|
|
2246
|
+
const css = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(92px,1fr));gap:8px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:8px 10px;display:flex}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);cursor:help;border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:400}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i,.lc-node i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow .12s}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-tools{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px;display:flex}.lc-tools-label,.lc-tools-more{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;padding:0;font-family:inherit;font-size:12px}.lc-tools-label:hover,.lc-tools-more:hover{text-decoration:underline}.lc-tool-chip{background:var(--dsw-alias-bg-layer-2);font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:0;border-radius:4px;padding:1px 7px;font-size:12px}.lc-tool-chip:hover{text-decoration:underline}.lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-scroll{scrollbar-width:thin;flex:1;min-width:0;position:relative;overflow:auto hidden}.lc-chart-fade{pointer-events:none;z-index:2;width:26px;position:absolute;top:0;bottom:0}.lc-chart-fade-l{background:linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent);left:0}.lc-chart-fade-r{background:linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent);right:0}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:default;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary)}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:var(--dsw-alias-bg-layer-2);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-head .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-rows{grid-template-columns:1fr 1fr;gap:4px 24px;margin-top:10px;display:grid}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-detail-num{text-align:right;width:44px}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-head>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-events,.lc-nodes{flex-direction:column;gap:2px;max-height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-node{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-node-preview{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);flex:1;overflow:hidden}.lc-node-time{color:var(--dsw-alias-label-secondary);text-align:right;min-width:54px;font-size:12px}.lc-node-tokens{color:var(--dsw-alias-label-secondary)}.lc-nodes-more{color:var(--dsw-alias-label-secondary);padding:3px 0}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-tag{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;flex:none;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-pre{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;border-radius:6px;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-br-dim{color:var(--dsw-alias-label-secondary)}.lc-br-call{flex-direction:column;align-items:flex-start;gap:4px;display:flex}.lc-br-blk{flex-direction:column;gap:4px;display:flex}.lc-br-blk-label{color:var(--dsw-alias-label-secondary);font-size:11px;font-weight:600}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-card-count{margin-left:auto}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:160px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:140px 90px 56px 1fr;align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;align-self:flex-end;gap:2px;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-rich-seg-head{align-self:auto;margin-left:auto}.lc-br-md{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);word-break:break-word;scrollbar-width:thin;border-radius:6px;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;scrollbar-width:thin;max-height:160px;padding:4px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-br-md>div,.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-br-md div :is(h1,h2,h3,h4,h5,h6),.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-br-md div p,.lc-ts-desc-md div p,.lc-br-md div pre,.lc-ts-desc-md div pre{margin:6px 0}.lc-br-md div :not(pre)>code,.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-br-md div table code,.lc-ts-desc-md div table code{font-size:inherit}.lc-br-rich-inline{flex-direction:column;gap:6px;display:flex}.lc-ts-card .lc-br-pre{margin:8px 10px}.lc-att-grid{flex-wrap:wrap;gap:10px;padding:8px 10px;display:flex}.lc-att-item{align-items:center;gap:8px;min-width:0;display:flex}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);cursor:pointer;width:64px;height:64px;font:inherit;border-radius:6px;flex:none;justify-content:center;align-items:center;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;max-width:200px;font-size:12px;overflow:hidden}.lc-att-dims{color:var(--dsw-alias-label-secondary);font-size:11px}";
|
|
2247
|
+
const tagId = "dsh-context/styles.css";
|
|
2248
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
2249
|
+
const tag = document.createElement("style");
|
|
2250
|
+
tag.dataset.plugin = "dsh-context";
|
|
2251
|
+
tag.dataset.pluginCss = tagId;
|
|
2252
|
+
tag.textContent = css;
|
|
2253
|
+
document.head.appendChild(tag);
|
|
2254
|
+
}
|
|
2255
|
+
//#endregion
|
|
2256
|
+
//#region src/client/index.ts
|
|
2257
|
+
/**
|
|
2258
|
+
* dsh-context — Client half (installed package bundle entry).
|
|
2259
|
+
*
|
|
2260
|
+
* Registers a "上下文/Context" tab in the conversation view ring
|
|
2261
|
+
* (`conversation.view` slot, beside Chat/Trajectory) and renders the
|
|
2262
|
+
* context-composition timeline: current makeup, per-request stacked-bar
|
|
2263
|
+
* history, context events, and the live message list.
|
|
2264
|
+
*
|
|
2265
|
+
* Since v0.9 the tab needs no custom data plane: the Host half pushes its
|
|
2266
|
+
* fold through the harness's session-projection pipeline
|
|
2267
|
+
* (`contextTimeline` projection key), and this half reads the finished value
|
|
2268
|
+
* from the framework standard kit (`useProjection('contextTimeline')`, a
|
|
2269
|
+
* standard prop on every session-scope slot component). No polling, no RPC,
|
|
2270
|
+
* no client-side cache.
|
|
2271
|
+
*
|
|
2272
|
+
* This module is the body of the package's `./client` bundle: tsdown
|
|
2273
|
+
* (tsdown.config.ts) bundles it (external `react` — the browser module table
|
|
2274
|
+
* supplies it via the injected `require`) into the web boot handoff
|
|
2275
|
+
* (`window.__ModuleLoader__.load({id, factory})`). All imports from other
|
|
2276
|
+
* client modules are inlined by the bundler; everything here is zero-runtime
|
|
2277
|
+
* beyond the bundled source.
|
|
2278
|
+
*/
|
|
2279
|
+
const NS = "dsh-context";
|
|
2280
|
+
function apply(ctx) {
|
|
2281
|
+
ctx.effect(() => {
|
|
2282
|
+
return ctx.locale.register(NS, {
|
|
2283
|
+
zh: DICT_ZH,
|
|
2284
|
+
en: DICT_EN
|
|
2285
|
+
});
|
|
2286
|
+
}, "dsh-context: dictionaries");
|
|
2287
|
+
const t = ctx.locale.bind(NS);
|
|
2288
|
+
const kit = makeViewKit(t);
|
|
2289
|
+
const ContextView = makeContextView(ctx, kit);
|
|
2290
|
+
ctx.effect(() => {
|
|
2291
|
+
const noop = () => {};
|
|
2292
|
+
const sessions = ctx.get("sessions");
|
|
2293
|
+
if (sessions === void 0 || typeof sessions.provide !== "function") return noop;
|
|
2294
|
+
try {
|
|
2295
|
+
return sessions.provide({
|
|
2296
|
+
props: ["loadOlderHistory"],
|
|
2297
|
+
resolve: (binding) => ({ props: { loadOlderHistory: () => binding.session.loadOlder() } })
|
|
2298
|
+
});
|
|
2299
|
+
} catch {
|
|
2300
|
+
return noop;
|
|
2301
|
+
}
|
|
2302
|
+
}, "dsh-context: loadOlderHistory prop");
|
|
2303
|
+
ctx.slots.inject("conversation.view", () => {
|
|
2304
|
+
return ctx.slots.register({
|
|
2305
|
+
name: "conversation.view",
|
|
2306
|
+
id: "context",
|
|
2307
|
+
order: 20,
|
|
2308
|
+
locale: NS,
|
|
2309
|
+
label: () => t("tab")
|
|
2310
|
+
}, (props) => h(ContextView, props));
|
|
2311
|
+
});
|
|
2312
|
+
registerContextCommand(ctx, kit);
|
|
2313
|
+
const ContextModal = makeContextModal(ctx, kit);
|
|
2314
|
+
ctx.slots.inject("conversation.input.overlay", () => {
|
|
2315
|
+
return ctx.slots.register({
|
|
2316
|
+
name: "conversation.input.overlay",
|
|
2317
|
+
id: "context-modal",
|
|
2318
|
+
order: 10,
|
|
2319
|
+
locale: NS,
|
|
2320
|
+
inject: (sessionId) => ({ hooks: { contextModal: modalStoreOf(sessionId) } })
|
|
2321
|
+
}, (props) => h(ContextModal, props));
|
|
2322
|
+
});
|
|
2323
|
+
}
|
|
2324
|
+
module.exports = {
|
|
2325
|
+
name: "dsh-context",
|
|
2326
|
+
inject: ["slots", "locale"],
|
|
2327
|
+
apply
|
|
2328
|
+
};
|
|
2329
|
+
//#endregion
|
|
2330
|
+
return module.exports;
|
|
2331
|
+
}
|
|
2139
2332
|
});
|
|
2140
2333
|
|
|
2334
|
+
//# sourceMappingURL=client.js.map
|