dsh-neotui 0.0.13 → 0.0.15
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/package.json +1 -1
- package/src/views.js +46 -20
package/package.json
CHANGED
package/src/views.js
CHANGED
|
@@ -849,22 +849,37 @@ export class ChatView extends Widget {
|
|
|
849
849
|
}
|
|
850
850
|
const node = this.nodes[info.nodeIdx];
|
|
851
851
|
// Expand/collapse changes the line count ABOVE the viewport, which would
|
|
852
|
-
// leave scrollY pointing at unrelated content
|
|
853
|
-
//
|
|
854
|
-
//
|
|
855
|
-
//
|
|
856
|
-
//
|
|
857
|
-
const
|
|
858
|
-
|
|
852
|
+
// leave scrollY pointing at unrelated content. Anchor: remember the
|
|
853
|
+
// viewport-top line's identity AND its offset within that block, then
|
|
854
|
+
// restore the same position after the rebuild — the view does not move
|
|
855
|
+
// at all when the top line survives (e.g. clicking a block header), and
|
|
856
|
+
// lands on the fold's last line when the top line itself was collapsed.
|
|
857
|
+
const lineKey = (m) => (m ? `${m.nodeIdx}:${m.blockIdx ?? "n"}` : null);
|
|
858
|
+
const topKey = lineKey(this.lineMap[this.view.scrollY]);
|
|
859
|
+
let topFirst = -1;
|
|
860
|
+
if (topKey !== null) {
|
|
859
861
|
for (let i = 0; i < this.lineMap.length; i++) {
|
|
860
|
-
if (
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
862
|
+
if (lineKey(this.lineMap[i]) === topKey) { topFirst = i; break; }
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
const topOffset = topFirst >= 0 ? this.view.scrollY - topFirst : 0;
|
|
866
|
+
const reanchor = () => {
|
|
867
|
+
if (topKey === null || topFirst < 0) return;
|
|
868
|
+
let first = -1, last = -1;
|
|
869
|
+
for (let i = 0; i < this.lineMap.length; i++) {
|
|
870
|
+
if (lineKey(this.lineMap[i]) !== topKey) continue;
|
|
871
|
+
if (first < 0) first = i;
|
|
872
|
+
last = i;
|
|
866
873
|
}
|
|
867
|
-
if (
|
|
874
|
+
if (first < 0) return;
|
|
875
|
+
const target = Math.min(first + topOffset, last);
|
|
876
|
+
this.view.scrollY = Math.max(0, Math.min(target, this.view.maxScroll()));
|
|
877
|
+
// never park the viewport on a blank separator row — it reads as a
|
|
878
|
+
// spurious offset right after the click
|
|
879
|
+
while (
|
|
880
|
+
this.view.scrollY < this.lineMap.length - 1 &&
|
|
881
|
+
!(this.lines[this.view.scrollY] ?? []).some((g) => g.t.trim() !== "")
|
|
882
|
+
) this.view.scrollY++;
|
|
868
883
|
};
|
|
869
884
|
if (node?.kind === "assistant" && info.blockIdx !== null) {
|
|
870
885
|
const b = node.blocks[info.blockIdx];
|
|
@@ -880,7 +895,7 @@ export class ChatView extends Widget {
|
|
|
880
895
|
else this.collapsedBlocks.add(key);
|
|
881
896
|
}
|
|
882
897
|
this.#rebuild();
|
|
883
|
-
reanchor(
|
|
898
|
+
reanchor();
|
|
884
899
|
return true;
|
|
885
900
|
}
|
|
886
901
|
}
|
|
@@ -888,7 +903,7 @@ export class ChatView extends Widget {
|
|
|
888
903
|
if (this.expanded.has(info.nodeIdx)) this.expanded.delete(info.nodeIdx);
|
|
889
904
|
else this.expanded.add(info.nodeIdx);
|
|
890
905
|
this.#rebuild();
|
|
891
|
-
reanchor(
|
|
906
|
+
reanchor();
|
|
892
907
|
return true;
|
|
893
908
|
}
|
|
894
909
|
}
|
|
@@ -1007,12 +1022,16 @@ export class ChatView extends Widget {
|
|
|
1007
1022
|
const open = manuallyExpanded || (!manuallyCollapsed && this.thinkMode === "expanded");
|
|
1008
1023
|
beginCard("THINKBG");
|
|
1009
1024
|
// Live blocks tick (已经过…); finished blocks freeze at their
|
|
1010
|
-
// total (已完成,耗时…).
|
|
1025
|
+
// total (已完成,耗时…). Snapshot-derived blocks may lack a
|
|
1026
|
+
// per-block start (the history projection has no block-start):
|
|
1027
|
+
// show a plain 已完成 then — never a bogus ticking timer.
|
|
1011
1028
|
let timing = "";
|
|
1012
1029
|
if (b.streaming) {
|
|
1013
1030
|
timing = ` 已经过 ${fmtDuration(Date.now() - (b.startedAt ?? Date.now()))}`;
|
|
1014
1031
|
} else if (b.startedAt !== undefined && b.endedAt !== undefined) {
|
|
1015
1032
|
timing = ` 已完成,耗时 ${fmtDuration(b.endedAt - b.startedAt)}`;
|
|
1033
|
+
} else if (b.endedAt !== undefined) {
|
|
1034
|
+
timing = " 已完成";
|
|
1016
1035
|
}
|
|
1017
1036
|
const thinkMeta = `${stepTag}(${b.text?.length ?? 0} 字)${timing}`;
|
|
1018
1037
|
lines.push([{ t: "💭 思考" + (b.streaming ? "…" : "") + thinkMeta + (open ? " [t 折叠]" : " [t 展开]"), fg: K.FAINT }]);
|
|
@@ -1032,16 +1051,23 @@ export class ChatView extends Widget {
|
|
|
1032
1051
|
const key = `${realIdx}:${bi}`;
|
|
1033
1052
|
const open = this.bashMode !== "collapsed" && !this.collapsedBlocks.has(key);
|
|
1034
1053
|
const exitCode = b.view?.view?.exitCode;
|
|
1035
|
-
|
|
1036
|
-
|
|
1054
|
+
// A tool only TICKS while its turn is live. A finalized turn
|
|
1055
|
+
// whose result never matched (orphan) must freeze at 无结果 —
|
|
1056
|
+
// otherwise the timer runs forever ("timing chaos").
|
|
1057
|
+
const running = b.result == null && !b.done && node.streaming;
|
|
1058
|
+
const orphan = b.result == null && !b.done && !node.streaming;
|
|
1059
|
+
const status = orphan ? "TOOLERR" : running ? "TOOLBG" : exitCode !== undefined && exitCode !== 0 ? "TOOLERR" : "TOOLOK";
|
|
1060
|
+
const glyph = orphan ? "✗" : running ? "⏳" : exitCode !== undefined && exitCode !== 0 ? "✗" : "✓";
|
|
1037
1061
|
const card = b.view ? renderToolCard(b.view, w, open) : [];
|
|
1038
1062
|
beginCard(status);
|
|
1039
1063
|
let timing = "";
|
|
1040
|
-
if (
|
|
1064
|
+
if (running) {
|
|
1041
1065
|
timing = ` 已经过 ${fmtDuration(Date.now() - (b.startedAt ?? Date.now()))}`;
|
|
1042
1066
|
} else if (b.startedAt !== undefined && b.endedAt !== undefined) {
|
|
1043
1067
|
const failed = exitCode !== undefined && exitCode !== 0;
|
|
1044
1068
|
timing = ` ${failed ? "失败" : "已完成"},耗时 ${fmtDuration(b.endedAt - b.startedAt)}`;
|
|
1069
|
+
} else if (orphan) {
|
|
1070
|
+
timing = " 无结果";
|
|
1045
1071
|
}
|
|
1046
1072
|
lines.push([
|
|
1047
1073
|
{ t: open ? "▾ " : "▸ ", fg: K.ACCENT },
|