dsh-auto-collapse 0.1.3 → 0.1.4
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.en.md +97 -80
- package/README.md +98 -80
- package/lib/client.js +835 -123
- package/lib/index.js +32 -8
- package/lib/types/client/index.d.ts +16 -15
- package/lib/types/index.d.ts +22 -10
- package/package.json +61 -54
package/lib/client.js
CHANGED
|
@@ -5,6 +5,12 @@ var __dshcfBundle = (() => {
|
|
|
5
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
8
14
|
var __export = (target, all) => {
|
|
9
15
|
for (var name2 in all)
|
|
10
16
|
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
@@ -29,6 +35,9 @@ var __dshcfBundle = (() => {
|
|
|
29
35
|
|
|
30
36
|
// src/fold.ts
|
|
31
37
|
var STYLE_ID = "dshcf-style";
|
|
38
|
+
var DEFAULT_STATUS_TEXT = "Deep sleeping...";
|
|
39
|
+
var ANIM_DURATION_MS = 180;
|
|
40
|
+
var ANIM_EASING = "ease-out";
|
|
32
41
|
var TOOL_LABELS = {
|
|
33
42
|
bash: "Bash",
|
|
34
43
|
pwsh: "Pwsh",
|
|
@@ -68,6 +77,12 @@ var __dshcfBundle = (() => {
|
|
|
68
77
|
text-align: left;
|
|
69
78
|
cursor: pointer;
|
|
70
79
|
user-select: none;
|
|
80
|
+
/* \u5C55\u5F00\u6001\u8865\u7684 margin-bottom 16px \u7531 aria-expanded/has-body \u7FFB\u8F6C\u9A71\u52A8\uFF0C
|
|
81
|
+
\u4E00\u5E27\u77AC\u5F00\uFF08\u4E0E\u4E09\u7EA7\u884C display \u7FFB\u8F6C\u540C pass \u540C\u5E27\uFF0C\u65E0\u4E0B\u63A8\uFF09\uFF1B\u6536\u8D77\u65B9\u5411
|
|
82
|
+
\u7531 JS \u4FA7\u9489\u4F4F\u95F4\u8DDD\uFF08\u6536\u8D77 fade \u671F\u95F4\u5185\u8054 16px\uFF0C\u6700\u540E\u4E00\u6761\u5728\u9014\u6E10\u9690 settle
|
|
83
|
+
\u540E\u5F52\u96F6\uFF0C\u89C1 reconcileBlock / hasPendingCollapse\uFF09\u3002\u4E0D\u8BBE CSS transition
|
|
84
|
+
\u2014\u2014v13 \u7684\u8FC7\u6E21\u4E0E chip \u5143\u7D20\u751F\u547D\u5468\u671F\u968F\u673A\u4EA4\u4E92\uFF0C\u4EA7\u751F\u5C55\u5F00\u65B9\u5411\u53CC\u91CD\u4EBA\u683C
|
|
85
|
+
\uFF08\u590D\u7528\u5143\u7D20\u7F13\u52A8\u4E0B\u63A8\u4E09\u7EA7\u884C vs \u65B0\u5EFA\u5143\u7D20\u77AC\u5F00\uFF09\uFF0C\u540C\u7C7B\u578B\u5757\u4E0D\u4E00\u81F4\u3002 */
|
|
71
86
|
}
|
|
72
87
|
.dshcf-chip[aria-expanded="true"],
|
|
73
88
|
.dshcf-chip.dshcf-has-body {
|
|
@@ -328,7 +343,7 @@ var __dshcfBundle = (() => {
|
|
|
328
343
|
}
|
|
329
344
|
`;
|
|
330
345
|
var FoldController = class {
|
|
331
|
-
constructor() {
|
|
346
|
+
constructor(statusTextProvider2) {
|
|
332
347
|
this.observer = null;
|
|
333
348
|
this.raf = 0;
|
|
334
349
|
this.timer = 0;
|
|
@@ -351,18 +366,45 @@ var __dshcfBundle = (() => {
|
|
|
351
366
|
this.segmentStates = /* @__PURE__ */ new Map();
|
|
352
367
|
/** segment 首次观察到 running 的时间,用于没有官方时长的实时回合。 */
|
|
353
368
|
this.runningSince = /* @__PURE__ */ new Map();
|
|
369
|
+
/** 曾完成过的 segment key:段恢复运行时据此重开本地计时,防止重新结算
|
|
370
|
+
* 的本地时长吞掉完成间隙。 */
|
|
371
|
+
this.completedOnce = /* @__PURE__ */ new Set();
|
|
354
372
|
/** 插件改写 display 前的精确原值;受控集合用于分类漂移和 stop() 恢复。 */
|
|
355
373
|
this.originalDisplay = /* @__PURE__ */ new WeakMap();
|
|
356
374
|
this.controlledDisplay = /* @__PURE__ */ new Set();
|
|
357
|
-
/**
|
|
375
|
+
/** 被改写为状态提示词的原生状态文本,卸载时按节点恢复。 */
|
|
376
|
+
/** 被改写为状态提示词的原生状态文本:original = 宿主原文(卸载还原用),
|
|
377
|
+
* written = 插件最后一次写入的值(仅当节点仍等于它时才还原,避免覆盖
|
|
378
|
+
* 宿主在插件写入之后的状态更新)。 */
|
|
358
379
|
this.turnStatusTexts = /* @__PURE__ */ new Map();
|
|
380
|
+
/** 正文判定缓存(消息元素 → 有无正文):流式期间只有被 mutation 命中的
|
|
381
|
+
* 消息失效重算,历史消息跨 pass 复用,避免每帧全量 TreeWalker。 */
|
|
382
|
+
this.bodyTextCache = /* @__PURE__ */ new WeakMap();
|
|
383
|
+
/** 自上次 pass 以来子树发生变化的 flow 顶层消息;pass 开头统一失效。 */
|
|
384
|
+
this.dirtyMessages = /* @__PURE__ */ new Set();
|
|
385
|
+
/** 在途显示动画(元素 → 记录):冲突仲裁、记账对齐与生命周期清理的依据。
|
|
386
|
+
* 用 Map 不用 WeakMap——switchFlow/stop 需要遍历全量 cancel。 */
|
|
387
|
+
this.pendingAnims = /* @__PURE__ */ new Map();
|
|
388
|
+
/** 手势点击的一次性可动画 block key;segment 级点击另保留中间正文的门控。 */
|
|
389
|
+
this.animatableKeys = /* @__PURE__ */ new Set();
|
|
390
|
+
/** segment 点击时只让点击前已存在的 block 播放 reveal;流式中新出现的
|
|
391
|
+
* 临时分裂块直接显示,避免分类收敛时留下半透明 stale chip。 */
|
|
392
|
+
this.animatableSegmentBlocks = /* @__PURE__ */ new Map();
|
|
393
|
+
this.statusTextProvider = statusTextProvider2 ?? (() => DEFAULT_STATUS_TEXT);
|
|
394
|
+
}
|
|
395
|
+
/** 设置变更后重跑一轮,让状态提示词立即生效。 */
|
|
396
|
+
refresh() {
|
|
397
|
+
this.schedule();
|
|
359
398
|
}
|
|
360
399
|
start() {
|
|
361
400
|
if (this.disposed) return;
|
|
362
401
|
injectStyle();
|
|
363
402
|
try {
|
|
364
403
|
this.observer = new MutationObserver((records) => {
|
|
365
|
-
if (this.shouldSchedule(records))
|
|
404
|
+
if (this.shouldSchedule(records)) {
|
|
405
|
+
this.markDirty(records);
|
|
406
|
+
this.schedule();
|
|
407
|
+
}
|
|
366
408
|
});
|
|
367
409
|
this.observer.observe(document.body, {
|
|
368
410
|
childList: true,
|
|
@@ -394,6 +436,29 @@ var __dshcfBundle = (() => {
|
|
|
394
436
|
if (records.length === 0 || this.flow === null || !this.flow.isConnected) return true;
|
|
395
437
|
return records.some((record) => nodeWithin(record.target, this.flow) || nodeWithin(this.flow, record.target));
|
|
396
438
|
}
|
|
439
|
+
/** 记录本批 mutation 命中的 flow 顶层消息,供正文判定缓存定向失效。
|
|
440
|
+
* 从 record.target 沿 parentNode 走到 flow 的直接子级即所属消息;
|
|
441
|
+
* 归属不到单一顶层消息(flow 直挂层结构变化、flow 外节点、文本直接
|
|
442
|
+
* 子节点)时全量失效——保守正确且罕见。 */
|
|
443
|
+
markDirty(records) {
|
|
444
|
+
const flow = this.flow;
|
|
445
|
+
if (flow === null || !flow.isConnected) return;
|
|
446
|
+
if (records.length === 0) {
|
|
447
|
+
this.bodyTextCache = /* @__PURE__ */ new WeakMap();
|
|
448
|
+
this.dirtyMessages.clear();
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
for (const record of records) {
|
|
452
|
+
let current = record.target;
|
|
453
|
+
while (current !== null && current.parentNode !== flow) current = current.parentNode;
|
|
454
|
+
if (!(current instanceof HTMLElement)) {
|
|
455
|
+
this.bodyTextCache = /* @__PURE__ */ new WeakMap();
|
|
456
|
+
this.dirtyMessages.clear();
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
this.dirtyMessages.add(current);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
397
462
|
schedule() {
|
|
398
463
|
if (this.disposed || this.raf !== 0) return;
|
|
399
464
|
this.raf = requestAnimationFrame(() => {
|
|
@@ -424,6 +489,9 @@ var __dshcfBundle = (() => {
|
|
|
424
489
|
style?.removeAttribute("data-dshcf-error");
|
|
425
490
|
} catch (error) {
|
|
426
491
|
this.reportError(error);
|
|
492
|
+
} finally {
|
|
493
|
+
this.animatableKeys.clear();
|
|
494
|
+
this.animatableSegmentBlocks.clear();
|
|
427
495
|
}
|
|
428
496
|
}
|
|
429
497
|
reportError(error) {
|
|
@@ -442,12 +510,19 @@ var __dshcfBundle = (() => {
|
|
|
442
510
|
if (nextFlow !== this.flow) this.switchFlow(nextFlow);
|
|
443
511
|
const flow = this.flow;
|
|
444
512
|
if (flow === null) return;
|
|
445
|
-
const
|
|
513
|
+
for (const el of this.dirtyMessages) this.bodyTextCache.delete(el);
|
|
514
|
+
this.dirtyMessages.clear();
|
|
515
|
+
const blocks = findBlocks(flow, (el) => this.hasBodyCached(el));
|
|
446
516
|
this.currentBlocks = new Map(blocks.map((block) => [block.key, block]));
|
|
447
|
-
const segments = buildSegments(flow, blocks);
|
|
517
|
+
const segments = buildSegments(flow, blocks, (el) => this.hasBodyCached(el));
|
|
448
518
|
const liveSegmentKeys = new Set(segments.map((segment) => segment.key));
|
|
449
519
|
for (const segment of segments) {
|
|
450
|
-
if (segment.running
|
|
520
|
+
if (!segment.running) continue;
|
|
521
|
+
if (this.completedOnce.has(segment.key)) {
|
|
522
|
+
this.completedOnce.delete(segment.key);
|
|
523
|
+
this.runningSince.delete(segment.key);
|
|
524
|
+
}
|
|
525
|
+
if (!this.runningSince.has(segment.key)) {
|
|
451
526
|
this.runningSince.set(segment.key, Date.now());
|
|
452
527
|
}
|
|
453
528
|
}
|
|
@@ -455,6 +530,7 @@ var __dshcfBundle = (() => {
|
|
|
455
530
|
for (const snapshot of segments) {
|
|
456
531
|
if (!snapshot.closed || snapshot.running || !snapshot.hasWork) continue;
|
|
457
532
|
completedKeys.add(snapshot.key);
|
|
533
|
+
this.completedOnce.add(snapshot.key);
|
|
458
534
|
let state = this.segmentStates.get(snapshot.key);
|
|
459
535
|
if (state === void 0) {
|
|
460
536
|
state = { key: snapshot.key, row: null, expanded: false, snapshot };
|
|
@@ -465,7 +541,7 @@ var __dshcfBundle = (() => {
|
|
|
465
541
|
const started = this.runningSince.get(snapshot.key);
|
|
466
542
|
const parsed = snapshot.boundary === null ? void 0 : parseTurnDuration(snapshot.boundary);
|
|
467
543
|
if (parsed !== void 0) state.duration = parsed;
|
|
468
|
-
else if (started !== void 0) state.duration = Date.now() - started;
|
|
544
|
+
else if (state.duration === void 0 && started !== void 0) state.duration = Date.now() - started;
|
|
469
545
|
if (state.row === null || !state.row.isConnected) state.row = this.createProcessedRow(state);
|
|
470
546
|
this.syncProcessedRow(state);
|
|
471
547
|
}
|
|
@@ -487,9 +563,10 @@ var __dshcfBundle = (() => {
|
|
|
487
563
|
for (const segment of segments) {
|
|
488
564
|
const state = this.segmentStates.get(segment.key);
|
|
489
565
|
const collapse = state !== void 0 && !state.expanded;
|
|
566
|
+
const animate = this.animatableKeys.has(segment.key);
|
|
490
567
|
for (const middle of segment.middleSteps) {
|
|
491
|
-
if (collapse) this.hideElement(middle, desiredHidden);
|
|
492
|
-
else this.restoreElement(middle);
|
|
568
|
+
if (collapse) this.hideElement(middle, desiredHidden, animate);
|
|
569
|
+
else this.restoreElement(middle, animate);
|
|
493
570
|
}
|
|
494
571
|
if (segment.finalStep !== null) this.restoreElement(segment.finalStep);
|
|
495
572
|
}
|
|
@@ -510,14 +587,32 @@ var __dshcfBundle = (() => {
|
|
|
510
587
|
for (const key of [...this.runningSince.keys()]) {
|
|
511
588
|
if (!liveSegmentKeys.has(key)) this.runningSince.delete(key);
|
|
512
589
|
}
|
|
590
|
+
for (const key of [...this.completedOnce]) {
|
|
591
|
+
if (!liveSegmentKeys.has(key)) this.completedOnce.delete(key);
|
|
592
|
+
}
|
|
513
593
|
for (const [node] of [...this.turnStatusTexts]) {
|
|
514
594
|
if (!node.isConnected) this.turnStatusTexts.delete(node);
|
|
515
595
|
}
|
|
516
|
-
|
|
596
|
+
for (const [el] of [...this.pendingAnims]) {
|
|
597
|
+
if (!el.isConnected) this.pendingAnims.delete(el);
|
|
598
|
+
}
|
|
599
|
+
const statusText = this.statusTextProvider();
|
|
600
|
+
if (statusText === void 0 || statusText === "") {
|
|
601
|
+
restoreTurnStatus(this.turnStatusTexts);
|
|
602
|
+
} else {
|
|
603
|
+
replaceTurnStatus(flow, this.turnStatusTexts, statusText);
|
|
604
|
+
}
|
|
517
605
|
}
|
|
518
606
|
/** flow 元素变化即视为会话切换:完整恢复旧 flow,再从新 DOM 重建。 */
|
|
519
607
|
switchFlow(next) {
|
|
520
608
|
if (next === this.flow) return;
|
|
609
|
+
for (const [el, record] of this.pendingAnims) {
|
|
610
|
+
record.anim.cancel();
|
|
611
|
+
if (record.kind === "height") this.clearCollapseLock(el);
|
|
612
|
+
}
|
|
613
|
+
this.pendingAnims.clear();
|
|
614
|
+
this.animatableKeys.clear();
|
|
615
|
+
this.animatableSegmentBlocks.clear();
|
|
521
616
|
for (const record of this.chips.values()) record.chip.remove();
|
|
522
617
|
this.chips.clear();
|
|
523
618
|
for (const host of [...this.mergedThinks.keys()]) this.removeMergedThink(host);
|
|
@@ -526,6 +621,9 @@ var __dshcfBundle = (() => {
|
|
|
526
621
|
this.currentBlocks.clear();
|
|
527
622
|
this.blockExpanded.clear();
|
|
528
623
|
this.runningSince.clear();
|
|
624
|
+
this.completedOnce.clear();
|
|
625
|
+
this.bodyTextCache = /* @__PURE__ */ new WeakMap();
|
|
626
|
+
this.dirtyMessages.clear();
|
|
529
627
|
this.restoreAllDisplays();
|
|
530
628
|
restoreTurnStatus(this.turnStatusTexts);
|
|
531
629
|
this.flow = next;
|
|
@@ -534,6 +632,8 @@ var __dshcfBundle = (() => {
|
|
|
534
632
|
const row = createProcessedRowElement(state.duration);
|
|
535
633
|
row.addEventListener("click", () => {
|
|
536
634
|
state.expanded = !state.expanded;
|
|
635
|
+
this.animatableKeys.add(state.key);
|
|
636
|
+
this.animatableSegmentBlocks.set(state.key, new Set(state.snapshot.blocks.map((block) => block.key)));
|
|
537
637
|
if (state.expanded) {
|
|
538
638
|
for (const block of state.snapshot.blocks) {
|
|
539
639
|
this.blockExpanded.set(block.key, false);
|
|
@@ -564,7 +664,11 @@ var __dshcfBundle = (() => {
|
|
|
564
664
|
return;
|
|
565
665
|
}
|
|
566
666
|
let target = state.snapshot.firstWork ?? state.snapshot.finalStep ?? state.snapshot.boundary;
|
|
567
|
-
if (target === null || target.parentElement !== flow)
|
|
667
|
+
if (target === null || target.parentElement !== flow) {
|
|
668
|
+
row.remove();
|
|
669
|
+
state.row = null;
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
568
672
|
while (target.previousElementSibling?.classList.contains("dshcf-flow-chip") === true) {
|
|
569
673
|
target = target.previousElementSibling;
|
|
570
674
|
}
|
|
@@ -572,40 +676,68 @@ var __dshcfBundle = (() => {
|
|
|
572
676
|
}
|
|
573
677
|
reconcileBlock(block, segment, desiredHidden) {
|
|
574
678
|
const state = segment === null ? void 0 : this.segmentStates.get(segment.key);
|
|
679
|
+
const segmentAnimatableBlocks = segment === null ? void 0 : this.animatableSegmentBlocks.get(segment.key);
|
|
680
|
+
const animate = this.animatableKeys.has(block.key) || segment !== null && this.animatableKeys.has(segment.key) && (segmentAnimatableBlocks === void 0 || segmentAnimatableBlocks.has(block.key));
|
|
575
681
|
const levelCollapsed = state !== void 0 && !state.expanded;
|
|
576
682
|
if (levelCollapsed) {
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
for (const row of block.rows) this.hideElement(row, desiredHidden);
|
|
580
|
-
for (const container of block.containers) this.hideElement(container, desiredHidden);
|
|
581
|
-
const keepHost = segment?.finalStep === block.host && hasBodyContent(block.host);
|
|
683
|
+
const keepHost = segment?.finalStep === block.host && this.hasBodyCached(block.host);
|
|
684
|
+
let hostFade = false;
|
|
582
685
|
if (keepHost) this.restoreElement(block.host);
|
|
583
|
-
else this.hideElement(block.host, desiredHidden);
|
|
584
|
-
|
|
686
|
+
else hostFade = this.hideElement(block.host, desiredHidden, animate);
|
|
687
|
+
for (const container of block.containers) this.hideElement(container, desiredHidden, animate);
|
|
688
|
+
for (const row of block.rows) this.hideElement(row, desiredHidden, animate);
|
|
689
|
+
const existing = this.chips.get(block.key)?.chip;
|
|
690
|
+
if (existing !== void 0 && existing.style.display !== "none") {
|
|
691
|
+
existing.style.marginBottom = "";
|
|
692
|
+
if (block.mount === "before" || keepHost) {
|
|
693
|
+
if (animate && this.canAnimate(existing)) this.startFadeCollapse(existing);
|
|
694
|
+
else existing.style.display = "none";
|
|
695
|
+
} else if (!hostFade) {
|
|
696
|
+
existing.style.display = "none";
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
this.releaseMergedThink(block.host, animate);
|
|
585
700
|
return;
|
|
586
701
|
}
|
|
587
|
-
const chip = this.ensureChip(block);
|
|
588
|
-
this.restoreElement(block.host);
|
|
589
|
-
if (chip.style.display !== "") chip.style.display = "";
|
|
590
702
|
let expanded = this.blockExpanded.get(block.key) ?? false;
|
|
591
703
|
if (!expanded && block.rows.some((row) => row.hasAttribute("data-selected"))) {
|
|
592
704
|
expanded = true;
|
|
593
705
|
this.blockExpanded.set(block.key, true);
|
|
594
706
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
707
|
+
const chip = this.ensureChip(block);
|
|
708
|
+
const hostIsCollapsedRow = !expanded && block.rows.includes(block.host);
|
|
709
|
+
const hostWasHidden = block.host.style.display === "none";
|
|
710
|
+
const hostAnimate = !hostIsCollapsedRow && hostWasHidden && animate;
|
|
711
|
+
if (!hostIsCollapsedRow) this.restoreElement(block.host, hostAnimate);
|
|
712
|
+
const pendingChip = this.pendingAnims.get(chip);
|
|
713
|
+
if (pendingChip?.target === "hidden") this.cancelPendingSync(chip);
|
|
714
|
+
const chipWasHidden = chip.style.display === "none";
|
|
715
|
+
if (chip.style.display !== "") chip.style.display = "";
|
|
716
|
+
if (chipWasHidden && animate && !(hostAnimate && block.mount === "inside")) this.revealVisual(chip);
|
|
717
|
+
if (expanded || !this.hasPendingCollapse(block)) this.unpinChipMargin(chip);
|
|
718
|
+
const chipSettle = () => {
|
|
719
|
+
if (!this.hasPendingCollapse(block)) this.unpinChipMargin(chip);
|
|
720
|
+
};
|
|
599
721
|
for (const container of block.containers) {
|
|
600
|
-
if (expanded) this.restoreElement(container);
|
|
601
|
-
else
|
|
722
|
+
if (expanded) this.restoreElement(container, animate);
|
|
723
|
+
else {
|
|
724
|
+
const started = this.hideElement(container, desiredHidden, animate, chipSettle);
|
|
725
|
+
if (started) this.pinChipMargin(chip);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
for (const row of block.rows) {
|
|
729
|
+
if (expanded) this.restoreElement(row, animate);
|
|
730
|
+
else {
|
|
731
|
+
const started = this.hideElement(row, desiredHidden, animate, chipSettle);
|
|
732
|
+
if (started) this.pinChipMargin(chip);
|
|
733
|
+
}
|
|
602
734
|
}
|
|
603
735
|
if (expanded && block.rows.length > 1 && block.rows.every((row) => isThinkRow(row))) {
|
|
604
|
-
this.syncMergedThink(block.host, block.rows, desiredHidden);
|
|
736
|
+
this.syncMergedThink(block.host, block.rows, desiredHidden, animate);
|
|
605
737
|
} else {
|
|
606
|
-
this.
|
|
738
|
+
if (this.releaseMergedThink(block.host, animate, chipSettle)) this.pinChipMargin(chip);
|
|
607
739
|
}
|
|
608
|
-
chip.classList.toggle("dshcf-has-body", block.mount === "inside" &&
|
|
740
|
+
chip.classList.toggle("dshcf-has-body", block.mount === "inside" && this.hasBodyCached(block.host));
|
|
609
741
|
updateChip(chip, block.rows, expanded);
|
|
610
742
|
}
|
|
611
743
|
ensureChip(block) {
|
|
@@ -623,14 +755,16 @@ var __dshcfBundle = (() => {
|
|
|
623
755
|
chip2.setAttribute("data-dshcf-block-key", block.key);
|
|
624
756
|
const leading = document.createElement("span");
|
|
625
757
|
leading.className = "dshcf-leading";
|
|
626
|
-
leading.appendChild(
|
|
758
|
+
leading.appendChild(createCommandIcon());
|
|
627
759
|
chip2.appendChild(leading);
|
|
628
760
|
chip2.appendChild(createSpan("dshcf-chip-title"));
|
|
629
761
|
chip2.appendChild(createSpan("dshcf-chip-sep"));
|
|
630
762
|
chip2.appendChild(createSpan("dshcf-chip-summary"));
|
|
631
763
|
chip2.appendChild(createChevronIcon("dshcf-chevron"));
|
|
764
|
+
chip2.style.display = "none";
|
|
632
765
|
chip2.addEventListener("click", () => {
|
|
633
766
|
this.blockExpanded.set(block.key, !(this.blockExpanded.get(block.key) ?? false));
|
|
767
|
+
this.animatableKeys.add(block.key);
|
|
634
768
|
this.schedule();
|
|
635
769
|
});
|
|
636
770
|
record = { host: block.host, chip: chip2 };
|
|
@@ -648,7 +782,10 @@ var __dshcfBundle = (() => {
|
|
|
648
782
|
}
|
|
649
783
|
suppressBlock(block, desiredHidden) {
|
|
650
784
|
const existing = this.chips.get(block.key)?.chip;
|
|
651
|
-
if (existing !== void 0 && existing.style.display !== "none")
|
|
785
|
+
if (existing !== void 0 && existing.style.display !== "none") {
|
|
786
|
+
existing.style.marginBottom = "";
|
|
787
|
+
existing.style.display = "none";
|
|
788
|
+
}
|
|
652
789
|
this.removeMergedThink(block.host);
|
|
653
790
|
this.retainDisplayControl(block.host, desiredHidden);
|
|
654
791
|
for (const row of block.rows) this.retainDisplayControl(row, desiredHidden);
|
|
@@ -668,7 +805,7 @@ var __dshcfBundle = (() => {
|
|
|
668
805
|
}
|
|
669
806
|
/** 连续思考合并行:插在第一个思考行前,标题用第一行思考内容;
|
|
670
807
|
* 点击切换显示/隐藏全部原始思考行。 */
|
|
671
|
-
syncMergedThink(host, rows, desiredHidden) {
|
|
808
|
+
syncMergedThink(host, rows, desiredHidden, animate = false) {
|
|
672
809
|
let row = this.mergedThinks.get(host);
|
|
673
810
|
if (row === void 0 || !row.isConnected) {
|
|
674
811
|
row = document.createElement("button");
|
|
@@ -682,14 +819,21 @@ var __dshcfBundle = (() => {
|
|
|
682
819
|
title.className = "dshcf-merged-title";
|
|
683
820
|
const chevron = createChevronIcon("dshcf-chevron");
|
|
684
821
|
row.append(leading, title, chevron);
|
|
822
|
+
row.style.display = "none";
|
|
685
823
|
const btn = row;
|
|
686
824
|
btn.addEventListener("click", () => {
|
|
825
|
+
if (this.mergedThinks.get(host) !== btn) return;
|
|
687
826
|
const next = !this.mergedExpanded.has(host);
|
|
688
|
-
if (next)
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
827
|
+
if (next) {
|
|
828
|
+
if (this.expandMergedBody(host, btn)) {
|
|
829
|
+
this.mergedExpanded.add(host);
|
|
830
|
+
btn.setAttribute("aria-expanded", "true");
|
|
831
|
+
}
|
|
832
|
+
} else {
|
|
833
|
+
this.mergedExpanded.delete(host);
|
|
834
|
+
btn.setAttribute("aria-expanded", "false");
|
|
835
|
+
this.collapseMergedBody(host);
|
|
836
|
+
}
|
|
693
837
|
});
|
|
694
838
|
rows[0].before(row);
|
|
695
839
|
this.mergedThinks.set(host, row);
|
|
@@ -711,42 +855,155 @@ var __dshcfBundle = (() => {
|
|
|
711
855
|
}
|
|
712
856
|
const expanded = this.mergedExpanded.has(host);
|
|
713
857
|
if (row.getAttribute("aria-expanded") !== String(expanded)) row.setAttribute("aria-expanded", String(expanded));
|
|
858
|
+
const rowWasHidden = row.style.display === "none";
|
|
714
859
|
if (row.style.display !== "") row.style.display = "";
|
|
860
|
+
if (rowWasHidden && animate) this.revealVisual(row);
|
|
715
861
|
for (const r of rows) this.hideElement(r, desiredHidden);
|
|
716
|
-
if (expanded)
|
|
862
|
+
if (expanded) {
|
|
863
|
+
const result = this.ensureMergedBody(host, row, false);
|
|
864
|
+
if (result !== null && result.created && animate) this.revealMergedBody(result.body);
|
|
865
|
+
}
|
|
717
866
|
}
|
|
718
867
|
/** 展开合并行:直接读各思考行文本合成内容块(不依赖原生行展开:
|
|
719
|
-
* 程序化 click 不触发 React 展开,且后台 tab 的 rAF 不执行)。
|
|
868
|
+
* 程序化 click 不触发 React 展开,且后台 tab 的 rAF 不执行)。
|
|
869
|
+
* 返回是否成功——思考行已不可读(parts 为空)时返回 false,调用方
|
|
870
|
+
* 据此保持收起态,避免展开状态与内容块脱节。 */
|
|
720
871
|
expandMergedBody(host, btn) {
|
|
721
872
|
const cached = this.mergedBodyTexts.get(host);
|
|
722
|
-
if (cached
|
|
723
|
-
this.
|
|
724
|
-
return;
|
|
873
|
+
if (cached === void 0) {
|
|
874
|
+
const parts = this.currentThinkRows(host).map((r) => r.textContent.replace(/^Think\s*/, "").trim()).filter(Boolean);
|
|
875
|
+
if (parts.length === 0) return false;
|
|
876
|
+
this.mergedBodyTexts.set(host, parts.join("\n\n"));
|
|
877
|
+
}
|
|
878
|
+
const result = this.ensureMergedBody(host, btn, true);
|
|
879
|
+
if (result === null) return false;
|
|
880
|
+
if (result.created) {
|
|
881
|
+
this.revealMergedBody(result.body);
|
|
882
|
+
} else {
|
|
883
|
+
this.cancelPendingSync(result.body);
|
|
725
884
|
}
|
|
726
|
-
|
|
727
|
-
if (parts.length === 0) return;
|
|
728
|
-
this.mergedBodyTexts.set(host, parts.join("\n\n"));
|
|
729
|
-
this.ensureMergedBody(host, btn, true);
|
|
885
|
+
return true;
|
|
730
886
|
}
|
|
731
|
-
/** 创建/更新合并内容块(缓存优先,不重新展开原生行)。
|
|
887
|
+
/** 创建/更新合并内容块(缓存优先,不重新展开原生行)。
|
|
888
|
+
* 返回内容块与其是否为本次新建(新建才走展开动画)。 */
|
|
732
889
|
ensureMergedBody(host, btn, force) {
|
|
733
890
|
const cached = this.mergedBodyTexts.get(host);
|
|
734
|
-
if (cached === void 0) return;
|
|
891
|
+
if (cached === void 0) return null;
|
|
735
892
|
let body = btn.nextElementSibling;
|
|
893
|
+
let created = false;
|
|
736
894
|
if (body === null || !body.classList.contains("dshcf-merged-body")) {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
btn.after(
|
|
895
|
+
const next = document.createElement("div");
|
|
896
|
+
next.className = "dshcf-merged-body";
|
|
897
|
+
btn.after(next);
|
|
898
|
+
body = next;
|
|
899
|
+
created = true;
|
|
740
900
|
}
|
|
741
901
|
if (force || body.textContent !== cached) body.textContent = cached;
|
|
902
|
+
return { body, created };
|
|
903
|
+
}
|
|
904
|
+
/** 清理合并 think 行(v12):状态 map 立即清除;DOM 在手势动画路径下
|
|
905
|
+
* 渐隐后移除(settle 回调),其余路径瞬删。渐隐中途被反向取消时元素
|
|
906
|
+
* 保留,由后续 pass 的 syncMergedThink 重建/复用。
|
|
907
|
+
* settle 透传给每个渐隐目标的移除回调之后(chip 间距钉住的结算探测点,
|
|
908
|
+
* AI 评审 P0:merged 行渐隐不走 block.rows,必须纳入同一钉住体系)。 */
|
|
909
|
+
releaseMergedThink(host, animate = false, settle) {
|
|
910
|
+
const row = this.mergedThinks.get(host);
|
|
911
|
+
this.mergedExpanded.delete(host);
|
|
912
|
+
this.mergedBodyTexts.delete(host);
|
|
913
|
+
if (row === void 0) return false;
|
|
914
|
+
this.mergedThinks.delete(host);
|
|
915
|
+
const body = row.nextElementSibling;
|
|
916
|
+
const targets = body !== null && body.classList.contains("dshcf-merged-body") ? [row, body] : [row];
|
|
917
|
+
if (animate && this.canAnimate(row)) {
|
|
918
|
+
for (const t of targets) this.startFadeCollapse(t, () => {
|
|
919
|
+
t.remove();
|
|
920
|
+
settle?.();
|
|
921
|
+
});
|
|
922
|
+
return true;
|
|
923
|
+
} else {
|
|
924
|
+
for (const t of targets) t.remove();
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
742
927
|
}
|
|
743
|
-
/**
|
|
928
|
+
/** merged-body 展开高度动画(机制样板:插件全资 DOM)。
|
|
929
|
+
* 关键帧含 marginBottom 0→16px——其 CSS 有常量 margin-bottom:16px,
|
|
930
|
+
* 高度从 0 起步时这 16px 会先占位产生小跳变。fill:'forwards' 托住终态,
|
|
931
|
+
* onfinish 清内联后 cancel 释放,无闪烁窗口。收起由 collapseMergedBody
|
|
932
|
+
* 做镜像高度卷下(同款账本与身份守卫),开合对称。 */
|
|
933
|
+
revealMergedBody(body) {
|
|
934
|
+
if (!this.canAnimate(body)) return;
|
|
935
|
+
this.cancelPendingSync(body);
|
|
936
|
+
const targetHeight = body.getBoundingClientRect().height;
|
|
937
|
+
if (!(targetHeight > 0)) return;
|
|
938
|
+
body.style.height = "0px";
|
|
939
|
+
body.style.overflow = "hidden";
|
|
940
|
+
body.style.marginBottom = "0px";
|
|
941
|
+
const anim = body.animate(
|
|
942
|
+
[
|
|
943
|
+
{ height: "0px", marginBottom: "0px" },
|
|
944
|
+
{ height: `${targetHeight}px`, marginBottom: "16px" }
|
|
945
|
+
],
|
|
946
|
+
{ duration: ANIM_DURATION_MS, easing: ANIM_EASING, fill: "forwards" }
|
|
947
|
+
);
|
|
948
|
+
const record = { anim, target: "visible", kind: "height" };
|
|
949
|
+
this.pendingAnims.set(body, record);
|
|
950
|
+
anim.onfinish = () => {
|
|
951
|
+
if (this.pendingAnims.get(body) !== record) return;
|
|
952
|
+
this.pendingAnims.delete(body);
|
|
953
|
+
body.style.height = "";
|
|
954
|
+
body.style.overflow = "";
|
|
955
|
+
body.style.marginBottom = "";
|
|
956
|
+
anim.cancel();
|
|
957
|
+
this.schedule();
|
|
958
|
+
};
|
|
959
|
+
anim.oncancel = () => {
|
|
960
|
+
if (this.pendingAnims.get(body) !== record) return;
|
|
961
|
+
this.pendingAnims.delete(body);
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
/** 收起合并行:内容块高度卷下后移除——镜像 revealMergedBody 的唯一几何动画,
|
|
965
|
+
* 开合对称。插件全资静态文本 DOM、无 React 协调竞争,可安全做几何收起
|
|
966
|
+
* (与 seat 级拒绝盲卷的场景不同:那里是 React 混杂多卡片)。
|
|
967
|
+
* reduced-motion / 无 WAAPI / 零高度降级为同步 remove()。 */
|
|
744
968
|
collapseMergedBody(host) {
|
|
745
969
|
const btn = this.mergedThinks.get(host);
|
|
746
|
-
if (btn
|
|
747
|
-
|
|
748
|
-
|
|
970
|
+
if (btn === void 0) return;
|
|
971
|
+
const body = btn.nextElementSibling;
|
|
972
|
+
if (body === null || !body.classList.contains("dshcf-merged-body")) return;
|
|
973
|
+
const el = body;
|
|
974
|
+
if (!this.canAnimate(el)) {
|
|
975
|
+
el.remove();
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
this.cancelPendingSync(el);
|
|
979
|
+
const current = el.getBoundingClientRect().height;
|
|
980
|
+
if (!(current > 0)) {
|
|
981
|
+
el.remove();
|
|
982
|
+
return;
|
|
749
983
|
}
|
|
984
|
+
el.style.height = `${current}px`;
|
|
985
|
+
el.style.overflow = "hidden";
|
|
986
|
+
const anim = el.animate(
|
|
987
|
+
[
|
|
988
|
+
{ height: `${current}px`, marginBottom: "16px" },
|
|
989
|
+
{ height: "0px", marginBottom: "0px" }
|
|
990
|
+
],
|
|
991
|
+
{ duration: ANIM_DURATION_MS, easing: ANIM_EASING, fill: "forwards" }
|
|
992
|
+
);
|
|
993
|
+
const record = { anim, target: "hidden", kind: "height" };
|
|
994
|
+
this.pendingAnims.set(el, record);
|
|
995
|
+
anim.onfinish = () => {
|
|
996
|
+
if (this.pendingAnims.get(el) !== record) return;
|
|
997
|
+
this.pendingAnims.delete(el);
|
|
998
|
+
el.remove();
|
|
999
|
+
anim.cancel();
|
|
1000
|
+
this.schedule();
|
|
1001
|
+
};
|
|
1002
|
+
anim.oncancel = () => {
|
|
1003
|
+
if (this.pendingAnims.get(el) !== record) return;
|
|
1004
|
+
this.pendingAnims.delete(el);
|
|
1005
|
+
this.clearCollapseLock(el);
|
|
1006
|
+
};
|
|
750
1007
|
}
|
|
751
1008
|
/** 当前宿主内的思考行(现取,React 重渲染后引用仍然有效)。 */
|
|
752
1009
|
currentThinkRows(host) {
|
|
@@ -767,19 +1024,180 @@ var __dshcfBundle = (() => {
|
|
|
767
1024
|
this.mergedExpanded.delete(host);
|
|
768
1025
|
this.mergedBodyTexts.delete(host);
|
|
769
1026
|
}
|
|
770
|
-
|
|
771
|
-
|
|
1027
|
+
/** 正文判定(带缓存):同一消息子树未变时直接复用上次结果。失效由
|
|
1028
|
+
* markDirty(mutation 定向)与 switchFlow(整体重置)驱动;缓存的是
|
|
1029
|
+
* 纯文本/媒体存在性判定,与 display 状态无关,插件自身的显隐切换
|
|
1030
|
+
* 不会产生脏数据。 */
|
|
1031
|
+
hasBodyCached(el) {
|
|
1032
|
+
const cached = this.bodyTextCache.get(el);
|
|
1033
|
+
if (cached !== void 0) return cached;
|
|
1034
|
+
const value = hasBodyContent(el);
|
|
1035
|
+
this.bodyTextCache.set(el, value);
|
|
1036
|
+
return value;
|
|
1037
|
+
}
|
|
1038
|
+
/** 本块是否有在途收起渐隐(rows/containers/merged 行/body 任一)。
|
|
1039
|
+
* 基于 pendingAnims 账本无状态判定:onfinish/oncancel 都会即时清账,
|
|
1040
|
+
* 取消路径天然解锁(计数器/最后注册者会卡死)。merged 行渐隐时已被
|
|
1041
|
+
* releaseMergedThink 摘出 mergedThinks,按 DOM 类名现查。 */
|
|
1042
|
+
hasPendingCollapse(block) {
|
|
1043
|
+
const check = (el) => el !== null && el !== void 0 && this.pendingAnims.get(el)?.target === "hidden";
|
|
1044
|
+
if (block.containers.some(check)) return true;
|
|
1045
|
+
if (block.rows.some(check)) return true;
|
|
1046
|
+
const mergedRow = block.host.querySelector(".dshcf-merged-think");
|
|
1047
|
+
if (check(mergedRow)) return true;
|
|
1048
|
+
const mergedBody = mergedRow?.nextElementSibling;
|
|
1049
|
+
if (mergedBody instanceof HTMLElement && check(mergedBody)) return true;
|
|
1050
|
+
return false;
|
|
1051
|
+
}
|
|
1052
|
+
/** 钉住 chip 与首行的 16px 间距(收起 fade 期间;内联优先于 aria=false 的 0)。
|
|
1053
|
+
* flow-chip(context 等 before-mounted)豁免:其间距由宿主 row-gap 16px
|
|
1054
|
+
* 提供、自身 CSS 恒 0,钉住 16px 会叠加成 32px(真机实测:收起上下文
|
|
1055
|
+
* 注入时二级与三级间距瞬间扩大)。
|
|
1056
|
+
*/
|
|
1057
|
+
pinChipMargin(chip) {
|
|
1058
|
+
if (chip.classList.contains("dshcf-flow-chip")) return;
|
|
1059
|
+
if (chip.style.marginBottom !== "16px") chip.style.marginBottom = "16px";
|
|
1060
|
+
}
|
|
1061
|
+
/** 解除钉住(aria=true 的 16px 或 aria=false 的 0 由 CSS 接管)。 */
|
|
1062
|
+
unpinChipMargin(chip) {
|
|
1063
|
+
if (chip.style.marginBottom !== "") chip.style.marginBottom = "";
|
|
1064
|
+
}
|
|
1065
|
+
/** 返回 true 表示启动了渐隐动画(调用方可据此决定内部元素的处置)。
|
|
1066
|
+
* settle 在渐隐自然结束时调用(onfinish 链;反向取消不触发)。 */
|
|
1067
|
+
hideElement(el, desired, animate = false, settle) {
|
|
1068
|
+
desired.add(el);
|
|
1069
|
+
const pending = this.pendingAnims.get(el);
|
|
1070
|
+
if (pending !== void 0) {
|
|
1071
|
+
if (pending.target === "hidden") return false;
|
|
1072
|
+
this.cancelPendingSync(el);
|
|
1073
|
+
}
|
|
1074
|
+
if (!this.originalDisplay.has(el) && !isDisplayed(el)) return false;
|
|
1075
|
+
if (this.hasAnimatingAncestor(el)) return false;
|
|
772
1076
|
if (!this.originalDisplay.has(el)) this.originalDisplay.set(el, el.style.display);
|
|
773
1077
|
this.controlledDisplay.add(el);
|
|
774
|
-
|
|
775
|
-
if (
|
|
1078
|
+
if (el.style.display === "none") return false;
|
|
1079
|
+
if (animate && this.canAnimate(el)) {
|
|
1080
|
+
this.startFadeCollapse(el, settle);
|
|
1081
|
+
return true;
|
|
1082
|
+
}
|
|
1083
|
+
el.style.display = "none";
|
|
1084
|
+
return false;
|
|
776
1085
|
}
|
|
777
|
-
restoreElement(el) {
|
|
1086
|
+
restoreElement(el, animate = false) {
|
|
1087
|
+
const pending = this.pendingAnims.get(el);
|
|
1088
|
+
if (pending !== void 0) {
|
|
1089
|
+
if (pending.target === "visible") return;
|
|
1090
|
+
this.cancelPendingSync(el);
|
|
1091
|
+
}
|
|
778
1092
|
if (!this.originalDisplay.has(el)) return;
|
|
779
1093
|
const original = this.originalDisplay.get(el);
|
|
1094
|
+
if (!animate || !this.canAnimate(el) || this.hasAnimatingAncestor(el)) {
|
|
1095
|
+
if (el.style.display !== original) el.style.display = original;
|
|
1096
|
+
this.originalDisplay.delete(el);
|
|
1097
|
+
this.controlledDisplay.delete(el);
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
780
1100
|
if (el.style.display !== original) el.style.display = original;
|
|
781
|
-
this.
|
|
782
|
-
|
|
1101
|
+
this.startReveal(el);
|
|
1102
|
+
}
|
|
1103
|
+
/** 是否可动画:WAAPI 特性检测 + reduced-motion 门控(均做 typeof 防桩缺失)。 */
|
|
1104
|
+
canAnimate(el) {
|
|
1105
|
+
if (typeof el.animate !== "function") return false;
|
|
1106
|
+
if (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches) return false;
|
|
1107
|
+
return true;
|
|
1108
|
+
}
|
|
1109
|
+
/** 展开方向淡入(opacity + 4px 微位移):无高度分量、零布局读取。
|
|
1110
|
+
* onfinish 按终态可见对齐账本(双删除)并 schedule() 幂等重同步;
|
|
1111
|
+
* oncancel 只做身份守卫删除——取消方的终态写入自己负责。 */
|
|
1112
|
+
startReveal(el) {
|
|
1113
|
+
const anim = el.animate(
|
|
1114
|
+
[
|
|
1115
|
+
{ opacity: "0", transform: "translateY(4px)" },
|
|
1116
|
+
{ opacity: "1", transform: "translateY(0)" }
|
|
1117
|
+
],
|
|
1118
|
+
{ duration: ANIM_DURATION_MS, easing: ANIM_EASING }
|
|
1119
|
+
);
|
|
1120
|
+
const record = { anim, target: "visible", kind: "fade" };
|
|
1121
|
+
this.pendingAnims.set(el, record);
|
|
1122
|
+
anim.onfinish = () => {
|
|
1123
|
+
if (this.pendingAnims.get(el) !== record) return;
|
|
1124
|
+
this.pendingAnims.delete(el);
|
|
1125
|
+
this.originalDisplay.delete(el);
|
|
1126
|
+
this.controlledDisplay.delete(el);
|
|
1127
|
+
this.schedule();
|
|
1128
|
+
};
|
|
1129
|
+
anim.oncancel = () => {
|
|
1130
|
+
if (this.pendingAnims.get(el) !== record) return;
|
|
1131
|
+
this.pendingAnims.delete(el);
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
/** 同步取消在途动画并清账:收起动画需同时清锁高内联(height/overflow/
|
|
1135
|
+
* marginBottom),否则取消方写完终态后元素仍被锁高裁剪一帧以上。 */
|
|
1136
|
+
cancelPendingSync(el) {
|
|
1137
|
+
const pending = this.pendingAnims.get(el);
|
|
1138
|
+
if (pending === void 0) return;
|
|
1139
|
+
pending.anim.cancel();
|
|
1140
|
+
this.pendingAnims.delete(el);
|
|
1141
|
+
if (pending.kind === "height") this.clearCollapseLock(el);
|
|
1142
|
+
}
|
|
1143
|
+
clearCollapseLock(el) {
|
|
1144
|
+
el.style.height = "";
|
|
1145
|
+
el.style.overflow = "";
|
|
1146
|
+
el.style.marginBottom = "";
|
|
1147
|
+
el.style.boxSizing = "";
|
|
1148
|
+
}
|
|
1149
|
+
/** 祖先 seat 在途动画检测:沿 parentNode 走到 flow,任一祖先在 pendingAnims
|
|
1150
|
+
* 即视为在途。分层规则——同一视觉变化只动画一层。 */
|
|
1151
|
+
hasAnimatingAncestor(el) {
|
|
1152
|
+
const flow = this.flow;
|
|
1153
|
+
if (flow === null) return false;
|
|
1154
|
+
let node = el.parentElement;
|
|
1155
|
+
while (node !== null && node !== flow) {
|
|
1156
|
+
if (this.pendingAnims.has(node)) return true;
|
|
1157
|
+
node = node.parentElement;
|
|
1158
|
+
}
|
|
1159
|
+
return false;
|
|
1160
|
+
}
|
|
1161
|
+
/** 收起方向渐隐动画(v11 定稿):镜像 reveal 的 opacity + 4px 微位移,
|
|
1162
|
+
* 淡完 onfinish 写 display:none 并保持双条目(镜像 hideElement 终态契约)。
|
|
1163
|
+
* fill:'forwards' 占位到终态写入后释放;无几何锁、无 gap 补偿。 */
|
|
1164
|
+
startFadeCollapse(el, settle) {
|
|
1165
|
+
const anim = el.animate(
|
|
1166
|
+
[
|
|
1167
|
+
{ opacity: "1", transform: "translateY(0)" },
|
|
1168
|
+
{ opacity: "0", transform: "translateY(4px)" }
|
|
1169
|
+
],
|
|
1170
|
+
{ duration: ANIM_DURATION_MS, easing: ANIM_EASING, fill: "forwards" }
|
|
1171
|
+
);
|
|
1172
|
+
const record = { anim, target: "hidden", kind: "fade" };
|
|
1173
|
+
this.pendingAnims.set(el, record);
|
|
1174
|
+
anim.onfinish = () => {
|
|
1175
|
+
if (this.pendingAnims.get(el) !== record) return;
|
|
1176
|
+
this.pendingAnims.delete(el);
|
|
1177
|
+
if (el.style.display !== "none") el.style.display = "none";
|
|
1178
|
+
settle?.();
|
|
1179
|
+
anim.cancel();
|
|
1180
|
+
this.schedule();
|
|
1181
|
+
};
|
|
1182
|
+
anim.oncancel = () => {
|
|
1183
|
+
if (this.pendingAnims.get(el) !== record) return;
|
|
1184
|
+
this.pendingAnims.delete(el);
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
/** 轻量视觉 reveal(opacity + 4px 微位移):用于插件全资元素的即时显示
|
|
1188
|
+
* 路径——chip(一级展开时出现)与 merged-think 行(二级展开时出现)。
|
|
1189
|
+
* 这些元素的 display 完全由插件直写、无 React 协调竞争,因此不入
|
|
1190
|
+
* pendingAnims 账本、无仲裁;收起同为直写 display:none,无 fill 的在途
|
|
1191
|
+
* 动画残留在隐藏元素上自然失效。门控沿用 animate 布尔(手势路径才调)。 */
|
|
1192
|
+
revealVisual(el) {
|
|
1193
|
+
if (!this.canAnimate(el)) return;
|
|
1194
|
+
el.animate(
|
|
1195
|
+
[
|
|
1196
|
+
{ opacity: "0", transform: "translateY(4px)" },
|
|
1197
|
+
{ opacity: "1", transform: "translateY(0)" }
|
|
1198
|
+
],
|
|
1199
|
+
{ duration: ANIM_DURATION_MS, easing: ANIM_EASING }
|
|
1200
|
+
);
|
|
783
1201
|
}
|
|
784
1202
|
restoreUnusedDisplays(desired) {
|
|
785
1203
|
for (const el of [...this.controlledDisplay]) {
|
|
@@ -797,28 +1215,20 @@ var __dshcfBundle = (() => {
|
|
|
797
1215
|
span.className = cls;
|
|
798
1216
|
return span;
|
|
799
1217
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
prompt.setAttribute("x", "2");
|
|
815
|
-
prompt.setAttribute("y", "7.5");
|
|
816
|
-
prompt.setAttribute("font-size", "7");
|
|
817
|
-
prompt.setAttribute("fill", "currentColor");
|
|
818
|
-
prompt.textContent = ">_";
|
|
819
|
-
svg.append(rect, prompt);
|
|
820
|
-
return svg;
|
|
821
|
-
}
|
|
1218
|
+
var COMMAND_ICON_PATHS = [
|
|
1219
|
+
{
|
|
1220
|
+
transform: "translate(0.6689 1.073)",
|
|
1221
|
+
d: "M11.4818 5.57813C11.4818 4.45301 11.4807 3.66237 11.4075 3.05908C11.3359 2.46953 11.2024 2.13852 10.9939 1.89441C10.9247 1.81341 10.8493 1.73801 10.7683 1.66882C10.5242 1.46033 10.1932 1.32686 9.60364 1.25525C9.00034 1.18198 8.20974 1.18091 7.0846 1.18091L5.57813 1.18091C4.45301 1.18091 3.66238 1.18198 3.05908 1.25525C2.46953 1.32686 2.13852 1.46033 1.89441 1.66882C1.81341 1.73801 1.73801 1.81341 1.66882 1.89441C1.46033 2.13852 1.32686 2.46953 1.25525 3.05908C1.18198 3.66238 1.18091 4.45301 1.18091 5.57813L1.18091 6.2771C1.18091 7.40218 1.18197 8.19288 1.25525 8.79614C1.32687 9.38553 1.46036 9.71674 1.66882 9.96082C1.73797 10.0417 1.81347 10.1173 1.89441 10.1864C2.13851 10.3948 2.13965 10.5275 3.05908 10.5991C3.66238 10.6724 4.45298 10.6735 5.57813 10.6735L7.0846 10.6735C8.20977 10.6735 9.00033 10.6724 9.60364 10.5991C10.1931 10.5275 10.5242 10.3948 10.7683 10.1864C10.8493 10.1173 10.9247 10.0417 10.9939 9.96082C11.2024 9.71674 11.3358 9.38553 11.4075 8.79614C11.4808 8.19288 11.4818 7.40218 11.4818 6.2771L11.4818 5.57813ZM12.6627 6.2771C12.6627 7.37222 12.6637 8.247 12.5798 8.93799C12.4942 9.64284 12.3133 10.2359 11.8928 10.7282C11.7834 10.8562 11.6637 10.9751 11.5356 11.0845C11.0434 11.5049 10.4511 11.6867 9.74634 11.7723C9.05525 11.8563 8.17999 11.8552 7.0846 11.8552L5.57813 11.8552C4.48273 11.8552 3.60747 11.8563 2.91638 11.7723C2.21157 11.6867 1.61933 11.5049 1.12708 11.0845C0.99901 10.9751 0.879281 10.8562 0.769898 10.7282C0.349454 10.2359 0.168506 9.64284 0.0828864 8.93799C-0.00101964 8.247 4.88512e-07 7.37222 6.47206e-07 6.2771L6.47206e-07 5.57813C6.47206e-07 4.48273 -0.00106163 3.60747 0.0828864 2.91638C0.168502 2.21168 0.349594 1.61928 0.769898 1.12708C0.879302 0.998981 0.998981 0.879302 1.12708 0.769898C1.61928 0.349594 2.21168 0.168502 2.91638 0.0828864C3.60747 -0.00106163 4.48273 6.47206e-07 5.57813 6.47206e-07L7.0846 6.47206e-07C8.17999 6.47206e-07 9.05525 -0.00106163 9.74634 0.0828864C10.451 0.168505 11.0434 0.349587 11.5356 0.769898C11.6637 0.879302 11.7834 0.998981 11.8928 1.12708C12.3131 1.61928 12.4942 2.21169 12.5798 2.91638C12.6638 3.60747 12.6627 4.48273 12.6627 5.57813L12.6627 6.2771Z"
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
transform: "translate(0.6689 1.073)",
|
|
1225
|
+
d: "M6.02607 5.50955L6.44306 5.9274L3.84284 8.52762L3.425 8.11063L3.00715 7.69278L4.77253 5.9274L3.00715 4.16202L3.84284 3.32633L6.02607 5.50955Z"
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
transform: "translate(0.6689 1.073)",
|
|
1229
|
+
d: "M9.23789 7.35397L9.23789 8.53488L6.96238 8.53488L6.96238 7.35397L9.23789 7.35397Z"
|
|
1230
|
+
}
|
|
1231
|
+
];
|
|
822
1232
|
var NATIVE_CHEVRON_DOWN_PATH = "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z";
|
|
823
1233
|
function createChevronIcon(className) {
|
|
824
1234
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
@@ -886,10 +1296,31 @@ var __dshcfBundle = (() => {
|
|
|
886
1296
|
const vb = (svg.getAttribute("viewBox") ?? "").trim().split(/\s+/);
|
|
887
1297
|
return vb.length === 4 && Number(vb[2]) === 14 && Number(vb[3]) === 14;
|
|
888
1298
|
}
|
|
1299
|
+
var cachedNativeCommandSvg = null;
|
|
889
1300
|
function createCommandIcon() {
|
|
1301
|
+
if (cachedNativeCommandSvg !== null) return cachedNativeCommandSvg.cloneNode(true);
|
|
890
1302
|
const native = findNativeCommandSvg();
|
|
891
|
-
if (native !== null)
|
|
892
|
-
|
|
1303
|
+
if (native !== null) {
|
|
1304
|
+
cachedNativeCommandSvg = native;
|
|
1305
|
+
return native.cloneNode(true);
|
|
1306
|
+
}
|
|
1307
|
+
return createCommandIconFallback();
|
|
1308
|
+
}
|
|
1309
|
+
function createCommandIconFallback() {
|
|
1310
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1311
|
+
svg.setAttribute("viewBox", "0 0 14 14");
|
|
1312
|
+
svg.setAttribute("width", "14");
|
|
1313
|
+
svg.setAttribute("height", "14");
|
|
1314
|
+
svg.setAttribute("fill", "none");
|
|
1315
|
+
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
1316
|
+
for (const p of COMMAND_ICON_PATHS) {
|
|
1317
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1318
|
+
path.setAttribute("transform", p.transform);
|
|
1319
|
+
path.setAttribute("d", p.d);
|
|
1320
|
+
path.setAttribute("fill", "currentColor");
|
|
1321
|
+
svg.appendChild(path);
|
|
1322
|
+
}
|
|
1323
|
+
return svg;
|
|
893
1324
|
}
|
|
894
1325
|
var CONTEXT_ICON_PATHS = [
|
|
895
1326
|
{
|
|
@@ -928,13 +1359,41 @@ var __dshcfBundle = (() => {
|
|
|
928
1359
|
}
|
|
929
1360
|
return svg;
|
|
930
1361
|
}
|
|
1362
|
+
var WRITE_ICON_PATH = "M9.94076 1.34942C10.7047 0.90231 11.6503 0.902415 12.4143 1.34942C12.7061 1.52015 12.9688 1.79118 13.3104 2.13284C13.6521 2.47448 13.9231 2.73721 14.0939 3.02894C14.5408 3.79294 14.5409 4.73856 14.0939 5.50251C13.9231 5.79415 13.652 6.05704 13.3104 6.39861L6.65932 13.0497C6.28068 13.4284 6.00695 13.7108 5.66543 13.9097C5.32391 14.1085 4.94315 14.2074 4.42705 14.3498L3.24394 14.6761C2.77527 14.8054 2.34538 14.9262 2.00131 14.9684C1.65196 15.0112 1.17964 15.0013 0.810764 14.6325C0.441921 14.2637 0.432107 13.7913 0.47486 13.442C0.517035 13.0979 0.6379 12.668 0.767181 12.1993L1.09352 11.0162C1.23588 10.5001 1.33481 10.1193 1.5336 9.77784C1.7325 9.43632 2.0149 9.1626 2.39355 8.78395L9.04466 2.13284C9.38625 1.79126 9.64911 1.52016 9.94076 1.34942ZM15.5427 14.8398H7.55223L8.96707 13.425H15.5427V14.8398ZM3.39382 9.78422C2.965 10.213 2.84244 10.3436 2.75709 10.49C2.67183 10.6366 2.61862 10.8079 2.45733 11.3925L2.13099 12.5756C2.00183 13.0439 1.92194 13.3419 1.88863 13.5536C2.10041 13.5204 2.39872 13.4416 2.86764 13.3123L4.05075 12.9859C4.63544 12.8246 4.80669 12.7715 4.95323 12.6862C5.09968 12.6008 5.23022 12.4783 5.65905 12.0494L10.721 6.98644L8.45577 4.72121L3.39382 9.78422ZM11.7 2.57079C11.3774 2.38198 10.9777 2.38198 10.6551 2.57079C10.5602 2.62647 10.4487 2.72931 10.0449 3.13311L9.45604 3.72094L11.7213 5.98617L12.3102 5.39833C12.7139 4.99457 12.8168 4.88307 12.8725 4.78818C13.0613 4.46561 13.0612 4.06585 12.8725 3.74326C12.8169 3.64827 12.7146 3.53752 12.3102 3.13311C11.9057 2.72863 11.795 2.6264 11.7 2.57079Z";
|
|
1363
|
+
function findNativeWriteSvg() {
|
|
1364
|
+
const selector = '[data-tool="write"] [data-disclosure-row], [data-tool="edit"] [data-disclosure-row]';
|
|
1365
|
+
for (const drow of document.querySelectorAll(selector)) {
|
|
1366
|
+
for (const svg of drow.querySelectorAll("svg")) {
|
|
1367
|
+
if (svg.querySelectorAll("path").length === 1 && isIcon16(svg)) return svg;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
return null;
|
|
1371
|
+
}
|
|
1372
|
+
function isIcon16(svg) {
|
|
1373
|
+
const vb = (svg.getAttribute("viewBox") ?? "").trim().split(/\s+/);
|
|
1374
|
+
return vb.length === 4 && Number(vb[2]) === 16 && Number(vb[3]) === 16;
|
|
1375
|
+
}
|
|
1376
|
+
function createWriteIcon() {
|
|
1377
|
+
const native = findNativeWriteSvg();
|
|
1378
|
+
if (native !== null) return native.cloneNode(true);
|
|
1379
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1380
|
+
svg.setAttribute("viewBox", "0 0 16 16");
|
|
1381
|
+
svg.setAttribute("width", "14");
|
|
1382
|
+
svg.setAttribute("height", "14");
|
|
1383
|
+
svg.setAttribute("fill", "none");
|
|
1384
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1385
|
+
path.setAttribute("d", WRITE_ICON_PATH);
|
|
1386
|
+
path.setAttribute("fill", "currentColor");
|
|
1387
|
+
svg.appendChild(path);
|
|
1388
|
+
return svg;
|
|
1389
|
+
}
|
|
931
1390
|
function syncLeadingIcon(chip, kind) {
|
|
932
1391
|
const leading = chip.querySelector(".dshcf-leading");
|
|
933
1392
|
if (leading === null) return;
|
|
934
1393
|
const existing = leading.querySelector("svg");
|
|
935
1394
|
if (existing !== null && existing.getAttribute("data-dshcf-icon") === kind) return;
|
|
936
1395
|
for (const child of [...leading.childNodes]) child.remove();
|
|
937
|
-
const svg = kind === "think" ? createThinkIcon() : kind === "context" ? createContextIcon() : createCommandIcon();
|
|
1396
|
+
const svg = kind === "think" ? createThinkIcon() : kind === "context" ? createContextIcon() : kind === "write" ? createWriteIcon() : createCommandIcon();
|
|
938
1397
|
svg.setAttribute("data-dshcf-icon", kind);
|
|
939
1398
|
leading.appendChild(svg);
|
|
940
1399
|
}
|
|
@@ -969,7 +1428,7 @@ var __dshcfBundle = (() => {
|
|
|
969
1428
|
return kind === "assistant-step" || kind === "assistant" || kind === "tool-call" || kind === "command" || kind === "manual-compaction";
|
|
970
1429
|
});
|
|
971
1430
|
}
|
|
972
|
-
function buildSegments(flow, blocks) {
|
|
1431
|
+
function buildSegments(flow, blocks, hasBody) {
|
|
973
1432
|
const items = flowItems(flow);
|
|
974
1433
|
const itemIndex = new Map(items.map((el, index) => [el, index]));
|
|
975
1434
|
const snapshots = [];
|
|
@@ -982,7 +1441,7 @@ var __dshcfBundle = (() => {
|
|
|
982
1441
|
const segmentBlocks = blocks.filter((block) => inRange.has(block.host));
|
|
983
1442
|
const bodySteps = range.filter((el) => {
|
|
984
1443
|
const kind = el.getAttribute("data-chat-flow-kind");
|
|
985
|
-
return (kind === "assistant-step" || kind === "assistant") &&
|
|
1444
|
+
return (kind === "assistant-step" || kind === "assistant") && hasBody(el);
|
|
986
1445
|
});
|
|
987
1446
|
const finalStep = bodySteps.length > 0 ? bodySteps[bodySteps.length - 1] : null;
|
|
988
1447
|
const middleSteps = new Set(bodySteps.slice(0, -1));
|
|
@@ -1043,7 +1502,7 @@ var __dshcfBundle = (() => {
|
|
|
1043
1502
|
if (segment.finalStep !== null) workHosts.add(segment.finalStep);
|
|
1044
1503
|
return [...workHosts].some(isDisplayed);
|
|
1045
1504
|
}
|
|
1046
|
-
function findBlocks(flow) {
|
|
1505
|
+
function findBlocks(flow, hasBody) {
|
|
1047
1506
|
const blocks = [];
|
|
1048
1507
|
const children = flowItems(flow);
|
|
1049
1508
|
let run = null;
|
|
@@ -1080,7 +1539,7 @@ var __dshcfBundle = (() => {
|
|
|
1080
1539
|
const workRows = [...callRowsIn(el), ...commandRowsIn(el)];
|
|
1081
1540
|
const isToolPile = workRows.length > 0;
|
|
1082
1541
|
const isContext = kind === "context";
|
|
1083
|
-
const
|
|
1542
|
+
const msgHasBody = !isToolPile ? hasBody(el) : false;
|
|
1084
1543
|
if (isContext) {
|
|
1085
1544
|
flushCarry();
|
|
1086
1545
|
if (run === null || run.category !== "context") run = makeBlock(el, "context");
|
|
@@ -1088,7 +1547,7 @@ var __dshcfBundle = (() => {
|
|
|
1088
1547
|
run.mount = "before";
|
|
1089
1548
|
continue;
|
|
1090
1549
|
}
|
|
1091
|
-
if (isToolPile || thinkRows.length > 0 && !
|
|
1550
|
+
if (isToolPile || thinkRows.length > 0 && !msgHasBody) {
|
|
1092
1551
|
if (run === null || run.category !== "work") run = makeBlock(el, "work");
|
|
1093
1552
|
if (carry.length > 0) {
|
|
1094
1553
|
run.rows.push(...carry);
|
|
@@ -1100,7 +1559,7 @@ var __dshcfBundle = (() => {
|
|
|
1100
1559
|
run.containers.push(el);
|
|
1101
1560
|
}
|
|
1102
1561
|
if (workRows.includes(el)) run.mount = "before";
|
|
1103
|
-
} else if (el.hasAttribute("data-chat-anchor-key") && (thinkRows.length > 0 ||
|
|
1562
|
+
} else if (el.hasAttribute("data-chat-anchor-key") && (thinkRows.length > 0 || msgHasBody) || msgHasBody && kind !== null) {
|
|
1104
1563
|
flushCarry();
|
|
1105
1564
|
if (thinkRows.length > 0) {
|
|
1106
1565
|
const segments = splitThinkByBody(el, thinkRows);
|
|
@@ -1128,11 +1587,29 @@ var __dshcfBundle = (() => {
|
|
|
1128
1587
|
return blocks;
|
|
1129
1588
|
}
|
|
1130
1589
|
function splitThinkByBody(el, rows) {
|
|
1590
|
+
const texts = [];
|
|
1591
|
+
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
|
1592
|
+
let node;
|
|
1593
|
+
while ((node = walker.nextNode()) !== null) {
|
|
1594
|
+
if (node.data.trim() === "") continue;
|
|
1595
|
+
const parent = node.parentElement;
|
|
1596
|
+
if (parent !== null && parent.closest('[data-variant="think"], [data-chat-call-id], .dshcf-chip, .dshcf-merged-think, .dshcf-merged-body') !== null) continue;
|
|
1597
|
+
texts.push(node);
|
|
1598
|
+
}
|
|
1599
|
+
const hasBetween = (a, b) => {
|
|
1600
|
+
for (const t of texts) {
|
|
1601
|
+
const posA = a.compareDocumentPosition(t);
|
|
1602
|
+
if ((posA & Node.DOCUMENT_POSITION_FOLLOWING) === 0) continue;
|
|
1603
|
+
const posB = b.compareDocumentPosition(t);
|
|
1604
|
+
return (posB & Node.DOCUMENT_POSITION_PRECEDING) !== 0;
|
|
1605
|
+
}
|
|
1606
|
+
return false;
|
|
1607
|
+
};
|
|
1131
1608
|
const segments = [];
|
|
1132
1609
|
let current = [];
|
|
1133
1610
|
for (let i = 0; i < rows.length; i++) {
|
|
1134
1611
|
current.push(rows[i]);
|
|
1135
|
-
if (i + 1 < rows.length &&
|
|
1612
|
+
if (i + 1 < rows.length && hasBetween(rows[i], rows[i + 1])) {
|
|
1136
1613
|
segments.push(current);
|
|
1137
1614
|
current = [];
|
|
1138
1615
|
}
|
|
@@ -1140,21 +1617,6 @@ var __dshcfBundle = (() => {
|
|
|
1140
1617
|
if (current.length > 0) segments.push(current);
|
|
1141
1618
|
return segments.length > 0 ? segments : [rows];
|
|
1142
1619
|
}
|
|
1143
|
-
function hasBodyBetween(el, a, b) {
|
|
1144
|
-
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
|
1145
|
-
let node;
|
|
1146
|
-
while ((node = walker.nextNode()) !== null) {
|
|
1147
|
-
if (node.data.trim() === "") continue;
|
|
1148
|
-
const parent = node.parentElement;
|
|
1149
|
-
if (parent !== null && parent.closest('[data-variant="think"], [data-chat-call-id], .dshcf-chip, .dshcf-merged-think, .dshcf-merged-body') !== null) continue;
|
|
1150
|
-
const posA = a.compareDocumentPosition(node);
|
|
1151
|
-
const posB = b.compareDocumentPosition(node);
|
|
1152
|
-
if ((posA & Node.DOCUMENT_POSITION_FOLLOWING) !== 0 && (posB & Node.DOCUMENT_POSITION_PRECEDING) !== 0) {
|
|
1153
|
-
return true;
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
return false;
|
|
1157
|
-
}
|
|
1158
1620
|
function hasBodyText(el) {
|
|
1159
1621
|
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
|
1160
1622
|
let node;
|
|
@@ -1269,7 +1731,6 @@ var __dshcfBundle = (() => {
|
|
|
1269
1731
|
runningTool,
|
|
1270
1732
|
runningThink,
|
|
1271
1733
|
tools,
|
|
1272
|
-
count: rows.length,
|
|
1273
1734
|
hasError: infos.some((i) => i.state === "error"),
|
|
1274
1735
|
hasStopped: infos.some((i) => i.state === "stopped"),
|
|
1275
1736
|
allContext: infos.length > 0 && infos.every((i) => i.label === "\u4E0A\u4E0B\u6587\u6CE8\u5165")
|
|
@@ -1298,14 +1759,19 @@ var __dshcfBundle = (() => {
|
|
|
1298
1759
|
titleText = "\u5DF2\u601D\u8003";
|
|
1299
1760
|
summaryText = "";
|
|
1300
1761
|
}
|
|
1301
|
-
|
|
1762
|
+
let kind = running !== null ? running.kind : info.allContext ? "context" : info.tools.length > 0 ? "tool" : "think";
|
|
1763
|
+
if (running === null && info.tools.some((tool) => tool === "Edit" || tool === "Write")) kind = "write";
|
|
1302
1764
|
if (title.textContent !== titleText) title.textContent = titleText;
|
|
1303
1765
|
if (summary.textContent !== summaryText) summary.textContent = summaryText;
|
|
1304
1766
|
if (sep !== null) {
|
|
1305
1767
|
const sepDisplay = summaryText === "" ? "none" : "";
|
|
1306
1768
|
if (sep.style.display !== sepDisplay) sep.style.display = sepDisplay;
|
|
1307
1769
|
}
|
|
1308
|
-
|
|
1770
|
+
if (running !== null) {
|
|
1771
|
+
summary.scrollLeft = summary.scrollWidth - summary.clientWidth;
|
|
1772
|
+
} else if (chip.classList.contains("running") && summary.scrollLeft !== 0) {
|
|
1773
|
+
summary.scrollLeft = 0;
|
|
1774
|
+
}
|
|
1309
1775
|
const expandedAttr = String(expanded);
|
|
1310
1776
|
if (chip.getAttribute("aria-expanded") !== expandedAttr) {
|
|
1311
1777
|
chip.setAttribute("aria-expanded", expandedAttr);
|
|
@@ -1403,21 +1869,29 @@ var __dshcfBundle = (() => {
|
|
|
1403
1869
|
style.textContent = CHIP_CSS;
|
|
1404
1870
|
document.head.appendChild(style);
|
|
1405
1871
|
}
|
|
1406
|
-
function replaceTurnStatus(flow, originals) {
|
|
1872
|
+
function replaceTurnStatus(flow, originals, statusText) {
|
|
1407
1873
|
const statuses = flow.matches('[role="status"]') ? [flow, ...flow.querySelectorAll('[role="status"]')] : [...flow.querySelectorAll('[role="status"]')];
|
|
1408
1874
|
for (const status of statuses) {
|
|
1409
1875
|
for (const node of status.childNodes) {
|
|
1410
1876
|
if (node instanceof Text && node.data.includes("Deep diving")) {
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1877
|
+
let record = originals.get(node);
|
|
1878
|
+
if (record === void 0) {
|
|
1879
|
+
record = { original: node.data, written: "" };
|
|
1880
|
+
originals.set(node, record);
|
|
1881
|
+
}
|
|
1882
|
+
if (node.data !== record.written) record.original = node.data;
|
|
1883
|
+
const next = node.data.replace(/Deep diving[.…]*/, statusText);
|
|
1884
|
+
if (node.data !== next) {
|
|
1885
|
+
node.data = next;
|
|
1886
|
+
record.written = next;
|
|
1887
|
+
}
|
|
1414
1888
|
}
|
|
1415
1889
|
}
|
|
1416
1890
|
}
|
|
1417
1891
|
}
|
|
1418
1892
|
function restoreTurnStatus(originals) {
|
|
1419
|
-
for (const [node,
|
|
1420
|
-
if (node.isConnected && node.data.
|
|
1893
|
+
for (const [node, record] of originals) {
|
|
1894
|
+
if (node.isConnected && node.data === record.written && node.data !== record.original) node.data = record.original;
|
|
1421
1895
|
}
|
|
1422
1896
|
originals.clear();
|
|
1423
1897
|
}
|
|
@@ -1425,15 +1899,253 @@ var __dshcfBundle = (() => {
|
|
|
1425
1899
|
document.getElementById(STYLE_ID)?.remove();
|
|
1426
1900
|
}
|
|
1427
1901
|
|
|
1902
|
+
// src/settings.ts
|
|
1903
|
+
var AUTO_COLLAPSE_NS = "dsh-auto-collapse";
|
|
1904
|
+
var DEFAULT_STATUS_TEXT2 = "Deep sleeping...";
|
|
1905
|
+
function statusTextProvider(scope) {
|
|
1906
|
+
return () => {
|
|
1907
|
+
if (scope === void 0) return DEFAULT_STATUS_TEXT2;
|
|
1908
|
+
const snapshot = scope.getSnapshot();
|
|
1909
|
+
const value = snapshot.value;
|
|
1910
|
+
return value?.statusText ?? DEFAULT_STATUS_TEXT2;
|
|
1911
|
+
};
|
|
1912
|
+
}
|
|
1913
|
+
var CARD_CSS = `
|
|
1914
|
+
.dshcf-settings-card {
|
|
1915
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
1916
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
1917
|
+
border-radius: 12px;
|
|
1918
|
+
list-style: none;
|
|
1919
|
+
transition: border-color .16s, background .16s;
|
|
1920
|
+
}
|
|
1921
|
+
.dshcf-settings-card:hover { border-color: var(--dsw-alias-label-dimmed); }
|
|
1922
|
+
.dshcf-settings-cardOpen {
|
|
1923
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
1924
|
+
border-color: var(--dsw-alias-label-dimmed);
|
|
1925
|
+
}
|
|
1926
|
+
.dshcf-settings-header {
|
|
1927
|
+
appearance: none;
|
|
1928
|
+
width: 100%;
|
|
1929
|
+
font: inherit;
|
|
1930
|
+
color: inherit;
|
|
1931
|
+
text-align: left;
|
|
1932
|
+
cursor: pointer;
|
|
1933
|
+
background: 0 0;
|
|
1934
|
+
border: 0;
|
|
1935
|
+
border-radius: 12px;
|
|
1936
|
+
align-items: center;
|
|
1937
|
+
gap: 12px;
|
|
1938
|
+
padding: 14px 16px;
|
|
1939
|
+
display: flex;
|
|
1940
|
+
}
|
|
1941
|
+
.dshcf-settings-header:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: -2px; }
|
|
1942
|
+
.dshcf-settings-headText { flex-direction: column; flex: 1; gap: 4px; min-width: 0; display: flex; }
|
|
1943
|
+
.dshcf-settings-name { color: var(--dsw-alias-label-primary); font-size: 15px; font-weight: 600; line-height: 1.4; }
|
|
1944
|
+
.dshcf-settings-description { color: var(--dsw-alias-label-tertiary); font-size: 13px; line-height: 1.5; }
|
|
1945
|
+
.dshcf-settings-chevron { color: var(--dsw-alias-label-tertiary); flex: none; transition: transform .16s; }
|
|
1946
|
+
.dshcf-settings-chevronOpen { transform: rotate(180deg); }
|
|
1947
|
+
.dshcf-settings-pending {
|
|
1948
|
+
white-space: nowrap;
|
|
1949
|
+
background: var(--dsw-alias-bg-module-platform);
|
|
1950
|
+
color: var(--dsw-alias-label-secondary);
|
|
1951
|
+
border-radius: 999px;
|
|
1952
|
+
flex: none;
|
|
1953
|
+
padding: 1px 8px;
|
|
1954
|
+
font-size: 11px;
|
|
1955
|
+
font-weight: 500;
|
|
1956
|
+
line-height: 17px;
|
|
1957
|
+
}
|
|
1958
|
+
.dshcf-settings-body { border-top: 1px solid var(--dsw-alias-border-l2); margin: 0 16px; padding-bottom: 8px; }
|
|
1959
|
+
.dshcf-settings-readOnly { color: var(--dsw-alias-label-tertiary); margin: 12px 0 0; font-size: 12px; line-height: 1.5; }
|
|
1960
|
+
.dshcf-settings-field { flex-direction: column; gap: 6px; padding: 12px 0; display: flex; }
|
|
1961
|
+
.dshcf-settings-fieldHead { align-items: center; gap: 8px; display: flex; }
|
|
1962
|
+
.dshcf-settings-fieldLabel { min-width: 0; color: var(--dsw-alias-label-primary); flex: 1; font-size: 13px; font-weight: 500; line-height: 1.5; }
|
|
1963
|
+
.dshcf-settings-badges { align-items: center; gap: 8px; display: inline-flex; }
|
|
1964
|
+
.dshcf-settings-badge {
|
|
1965
|
+
white-space: nowrap;
|
|
1966
|
+
background: var(--dsw-alias-bg-module-platform);
|
|
1967
|
+
color: var(--dsw-alias-label-secondary);
|
|
1968
|
+
border-radius: 999px;
|
|
1969
|
+
padding: 1px 8px;
|
|
1970
|
+
font-size: 11px;
|
|
1971
|
+
font-weight: 500;
|
|
1972
|
+
line-height: 17px;
|
|
1973
|
+
}
|
|
1974
|
+
.dshcf-settings-reset { font: inherit; color: var(--dsw-alias-label-secondary); cursor: pointer; background: 0 0; border: none; padding: 0; font-size: 12px; line-height: 1.5; }
|
|
1975
|
+
.dshcf-settings-reset:hover:not(:disabled) { color: var(--dsw-alias-label-primary); }
|
|
1976
|
+
.dshcf-settings-reset:disabled { cursor: default; }
|
|
1977
|
+
.dshcf-settings-input {
|
|
1978
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
1979
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
1980
|
+
height: 34px;
|
|
1981
|
+
font: inherit;
|
|
1982
|
+
color: var(--dsw-alias-label-primary);
|
|
1983
|
+
border-radius: 8px;
|
|
1984
|
+
padding: 0 12px;
|
|
1985
|
+
font-size: 13px;
|
|
1986
|
+
line-height: 1.5;
|
|
1987
|
+
box-sizing: border-box;
|
|
1988
|
+
width: 100%;
|
|
1989
|
+
}
|
|
1990
|
+
.dshcf-settings-input:focus-visible { border-color: var(--dsw-alias-brand-primary); outline: none; }
|
|
1991
|
+
.dshcf-settings-input:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }
|
|
1992
|
+
.dshcf-settings-hint { color: var(--dsw-alias-label-tertiary); margin: 0; font-size: 12px; line-height: 1.5; }
|
|
1993
|
+
.dshcf-settings-footer { border-top: 1px solid var(--dsw-alias-border-l2); justify-content: flex-end; align-items: center; gap: 8px; padding: 12px 0 4px; display: flex; }
|
|
1994
|
+
.dshcf-settings-failed { min-width: 0; color: var(--dsw-alias-label-error); flex: 1; margin: 0; font-size: 12px; line-height: 1.5; }
|
|
1995
|
+
.dshcf-settings-discard,
|
|
1996
|
+
.dshcf-settings-save { appearance: none; font: inherit; cursor: pointer; border: 1px solid #0000; border-radius: 8px; padding: 5px 14px; font-size: 13px; line-height: 1.5; }
|
|
1997
|
+
.dshcf-settings-discard { border-color: var(--dsw-alias-border-l2); color: var(--dsw-alias-label-secondary); background: 0 0; }
|
|
1998
|
+
.dshcf-settings-discard:hover:not(:disabled) { color: var(--dsw-alias-label-primary); border-color: var(--dsw-alias-label-dimmed); }
|
|
1999
|
+
.dshcf-settings-save { background: var(--dsw-alias-label-primary); color: var(--dsw-alias-bg-layer-3); }
|
|
2000
|
+
.dshcf-settings-discard:disabled,
|
|
2001
|
+
.dshcf-settings-save:disabled { opacity: .4; cursor: default; }
|
|
2002
|
+
.dshcf-settings-discard:focus-visible,
|
|
2003
|
+
.dshcf-settings-save:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 1px; }
|
|
2004
|
+
`;
|
|
2005
|
+
var STYLE_ID2 = "dshcf-settings-style";
|
|
2006
|
+
function injectCardStyle() {
|
|
2007
|
+
if (typeof document === "undefined" || document.getElementById(STYLE_ID2) !== null) return;
|
|
2008
|
+
const style = document.createElement("style");
|
|
2009
|
+
style.id = STYLE_ID2;
|
|
2010
|
+
style.textContent = CARD_CSS;
|
|
2011
|
+
document.head.appendChild(style);
|
|
2012
|
+
}
|
|
2013
|
+
function ChevronIcon(open) {
|
|
2014
|
+
const React = __require("react");
|
|
2015
|
+
const className = open ? "dshcf-settings-chevron dshcf-settings-chevronOpen" : "dshcf-settings-chevron";
|
|
2016
|
+
return React.createElement(
|
|
2017
|
+
"svg",
|
|
2018
|
+
{ width: 14, height: 14, viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, className },
|
|
2019
|
+
React.createElement("path", {
|
|
2020
|
+
d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z",
|
|
2021
|
+
fill: "currentColor"
|
|
2022
|
+
})
|
|
2023
|
+
);
|
|
2024
|
+
}
|
|
2025
|
+
function StatusTextCard(props) {
|
|
2026
|
+
const React = __require("react");
|
|
2027
|
+
const scope = props.scope;
|
|
2028
|
+
const [open, setOpen] = React.useState(false);
|
|
2029
|
+
const [snapshot, setSnapshot] = React.useState(scope.getSnapshot());
|
|
2030
|
+
const [pending, setPending] = React.useState(null);
|
|
2031
|
+
const [saving, setSaving] = React.useState(false);
|
|
2032
|
+
const [failed, setFailed] = React.useState(false);
|
|
2033
|
+
React.useEffect(() => scope.subscribe(() => setSnapshot(scope.getSnapshot())), [scope]);
|
|
2034
|
+
if (snapshot.status !== "ready") return null;
|
|
2035
|
+
const value = snapshot.value;
|
|
2036
|
+
const base = snapshot.base;
|
|
2037
|
+
const user = snapshot.user;
|
|
2038
|
+
const currentText = value?.statusText ?? "";
|
|
2039
|
+
const defaultText = base?.statusText ?? DEFAULT_STATUS_TEXT2;
|
|
2040
|
+
const text = pending ? pending.text : currentText;
|
|
2041
|
+
const userHas = user !== void 0 && Object.prototype.hasOwnProperty.call(user, "statusText");
|
|
2042
|
+
const overridden = pending ? !pending.reset : userHas;
|
|
2043
|
+
const dirty = pending !== null && (pending.reset ? userHas : pending.text.trim() !== currentText);
|
|
2044
|
+
const writable = snapshot.writable;
|
|
2045
|
+
const discard = () => {
|
|
2046
|
+
setPending(null);
|
|
2047
|
+
setFailed(false);
|
|
2048
|
+
};
|
|
2049
|
+
const resetField = () => {
|
|
2050
|
+
setPending({ text: defaultText, reset: true });
|
|
2051
|
+
setFailed(false);
|
|
2052
|
+
};
|
|
2053
|
+
const edit = (next) => {
|
|
2054
|
+
setPending({ text: next, reset: false });
|
|
2055
|
+
setFailed(false);
|
|
2056
|
+
};
|
|
2057
|
+
const save = async () => {
|
|
2058
|
+
if (pending === null) return;
|
|
2059
|
+
setSaving(true);
|
|
2060
|
+
setFailed(false);
|
|
2061
|
+
try {
|
|
2062
|
+
if (pending.reset) await scope.unset("statusText");
|
|
2063
|
+
else await scope.set("statusText", pending.text.trim());
|
|
2064
|
+
setPending(null);
|
|
2065
|
+
} catch {
|
|
2066
|
+
setFailed(true);
|
|
2067
|
+
} finally {
|
|
2068
|
+
setSaving(false);
|
|
2069
|
+
}
|
|
2070
|
+
};
|
|
2071
|
+
const blocked = !dirty || saving;
|
|
2072
|
+
const cardClass = `dshcf-settings-card${open ? " dshcf-settings-cardOpen" : ""}`;
|
|
2073
|
+
return React.createElement("li", { className: cardClass }, [
|
|
2074
|
+
React.createElement(
|
|
2075
|
+
"button",
|
|
2076
|
+
{
|
|
2077
|
+
type: "button",
|
|
2078
|
+
className: "dshcf-settings-header",
|
|
2079
|
+
"aria-expanded": open,
|
|
2080
|
+
"aria-label": `${open ? "\u6536\u8D77\u8BBE\u7F6E" : "\u5C55\u5F00\u8BBE\u7F6E"}: \u72B6\u6001\u63D0\u793A\u8BCD`,
|
|
2081
|
+
onClick: () => setOpen(!open)
|
|
2082
|
+
},
|
|
2083
|
+
[
|
|
2084
|
+
React.createElement("span", { className: "dshcf-settings-headText" }, [
|
|
2085
|
+
React.createElement("span", { className: "dshcf-settings-name" }, "\u72B6\u6001\u63D0\u793A\u8BCD"),
|
|
2086
|
+
React.createElement("span", { className: "dshcf-settings-description" }, "\u81EA\u5B9A\u4E49\u72B6\u6001\u63D0\u793A\u8BCD\uFF0C\u53EF\u4EE5\u66FF\u6362\u539F\u6709\u7684Deep diving...\u4E00\u884C\uFF0C\u7531\u63D2\u4EF6dsh-auto-collapse\u63D0\u4F9B")
|
|
2087
|
+
]),
|
|
2088
|
+
dirty ? React.createElement("span", { className: "dshcf-settings-pending" }, "\u672A\u4FDD\u5B58") : null,
|
|
2089
|
+
ChevronIcon(open)
|
|
2090
|
+
]
|
|
2091
|
+
),
|
|
2092
|
+
open ? React.createElement("div", { className: "dshcf-settings-body" }, [
|
|
2093
|
+
!writable ? React.createElement("p", { className: "dshcf-settings-readOnly", role: "status" }, "\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\u3002") : null,
|
|
2094
|
+
React.createElement("div", { className: "dshcf-settings-field" }, [
|
|
2095
|
+
React.createElement("div", { className: "dshcf-settings-fieldHead" }, [
|
|
2096
|
+
React.createElement("label", { className: "dshcf-settings-fieldLabel", htmlFor: "dshcf-status-text" }, "\u81EA\u5B9A\u4E49\u72B6\u6001\u63D0\u793A\u8BCD"),
|
|
2097
|
+
overridden ? React.createElement("span", { className: "dshcf-settings-badges" }, [
|
|
2098
|
+
React.createElement("span", { className: "dshcf-settings-badge" }, "\u5DF2\u8986\u76D6"),
|
|
2099
|
+
React.createElement("button", { type: "button", className: "dshcf-settings-reset", disabled: !writable, onClick: resetField }, "\u6062\u590D\u9ED8\u8BA4")
|
|
2100
|
+
]) : null
|
|
2101
|
+
]),
|
|
2102
|
+
React.createElement("input", {
|
|
2103
|
+
id: "dshcf-status-text",
|
|
2104
|
+
className: "dshcf-settings-input",
|
|
2105
|
+
type: "text",
|
|
2106
|
+
value: text,
|
|
2107
|
+
placeholder: "Deep diving...",
|
|
2108
|
+
disabled: !writable,
|
|
2109
|
+
onChange: (event) => edit(event.target.value)
|
|
2110
|
+
}),
|
|
2111
|
+
React.createElement("p", { className: "dshcf-settings-hint" }, "\u4E3A\u7A7A\u65F6\u6062\u590D\u9ED8\u8BA4Deep diving...\u63D0\u793A\u8BCD\u72B6\u6001")
|
|
2112
|
+
]),
|
|
2113
|
+
React.createElement("div", { className: "dshcf-settings-footer" }, [
|
|
2114
|
+
failed ? React.createElement("p", { className: "dshcf-settings-failed", role: "status" }, "\u672C\u90E8\u7F72\u6CA1\u6709\u63A5\u53D7\u8FD9\u4E9B\u503C\uFF0C\u5DF2\u4FDD\u7559\u4F9B\u4F60\u4FEE\u6539\u3002") : null,
|
|
2115
|
+
React.createElement("button", { type: "button", className: "dshcf-settings-discard", disabled: !dirty || saving, onClick: discard }, "\u653E\u5F03\u4FEE\u6539"),
|
|
2116
|
+
React.createElement("button", { type: "button", className: "dshcf-settings-save", disabled: blocked, onClick: save }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58")
|
|
2117
|
+
])
|
|
2118
|
+
]) : null
|
|
2119
|
+
]);
|
|
2120
|
+
}
|
|
2121
|
+
function setupSettingsCard(ctx, scope) {
|
|
2122
|
+
injectCardStyle();
|
|
2123
|
+
return ctx.slots.inject("settings.plugin.item", () => ctx.slots.register(
|
|
2124
|
+
{
|
|
2125
|
+
name: "settings.plugin.item",
|
|
2126
|
+
key: AUTO_COLLAPSE_NS,
|
|
2127
|
+
inject: () => ({ scope })
|
|
2128
|
+
},
|
|
2129
|
+
StatusTextCard
|
|
2130
|
+
));
|
|
2131
|
+
}
|
|
2132
|
+
|
|
1428
2133
|
// src/client.ts
|
|
1429
2134
|
var name = "dsh-auto-collapse";
|
|
1430
|
-
var inject = [];
|
|
2135
|
+
var inject = ["slots", "settingsScope"];
|
|
1431
2136
|
function apply(ctx) {
|
|
1432
2137
|
ctx.effect(() => {
|
|
1433
|
-
const
|
|
2138
|
+
const scope = ctx.settingsScope?.bind({ namespace: AUTO_COLLAPSE_NS });
|
|
2139
|
+
const controller = new FoldController(statusTextProvider(scope));
|
|
1434
2140
|
controller.start();
|
|
1435
|
-
|
|
1436
|
-
|
|
2141
|
+
const offScope = scope?.subscribe(() => controller.refresh());
|
|
2142
|
+
const offSettings = ctx.slots === void 0 || scope === void 0 ? void 0 : setupSettingsCard(ctx, scope);
|
|
2143
|
+
return () => {
|
|
2144
|
+
offScope?.();
|
|
2145
|
+
offSettings?.();
|
|
2146
|
+
controller.stop();
|
|
2147
|
+
};
|
|
2148
|
+
}, "dsh-auto-collapse: fold observer + settings card");
|
|
1437
2149
|
}
|
|
1438
2150
|
return __toCommonJS(client_exports);
|
|
1439
2151
|
})();
|