dsh-toolfold 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.en.md +128 -90
- package/README.md +116 -63
- package/lib/client.js +95 -10
- package/lib/dynamic-body.js +92 -10
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
所有显著变更将记录于此。格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
4
4
|
|
|
5
|
+
## [0.1.8] - 2026-08-28
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- 修复运行时收起"先动一下再突然收起":当模型产生大量工具调用、且它们是最新信息时,run 在收起动画期间仍在增长。旧实现(自 0.1.3 起)假定行集合固定:折叠条点击闭包持有的是创建时的旧 run 快照,动画只覆盖原始行,随后按旧行数计时的完成定时器把整个增长后的 run 一次性合并,新流入的行瞬间消失。现点击改为操作当前 run(`bar._ccxRun`)而非创建时快照;`syncBars` 将动画期间新增的行折入同一瀑布(`extendCollapse`:rise 类 + 高度收缩 + 各自 stagger)并重排完成定时器(`armCollapseFinish`),最后一行动画结束才应用合并。(`lib/client.js`, `lib/dynamic-body.js`)
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- npm 发布工作流增加 OIDC 权限(`id-token: write`、`contents: read`),发布改用 OIDC 身份认证(移除 `NODE_AUTH_TOKEN` 环境变量),并禁用 `package-manager-cache`。
|
|
12
|
+
- `package.json` 增加 `repository` 字段指向 GitHub 仓库。
|
|
13
|
+
- 重构 README:重组章节(安装、快速开始、默认行为、功能、设置、性能、限制),新增"默认行为 – 首次使用"说明(为什么思考会消失、为什么调用默认分组,以及快速修复指引),补充安装方法与徽章,并同步翻译为 `README.en.md`。
|
|
14
|
+
|
|
5
15
|
## [0.1.7] - 2026-08-26
|
|
6
16
|
|
|
7
17
|
### Fixed
|
|
@@ -54,6 +64,7 @@
|
|
|
54
64
|
- 首个可用版本:连续 `tool-call` 折叠为最后一条的单行摘要 + “已折叠 N 个工具调用”,支持 `durMs / keepThink / splitThink / stats` 四项设置(DSH `settings` 服务 → `~/.dsh/settings.yaml`,回退到 Host 路由与 `localStorage`)与瀑布动画。
|
|
55
65
|
- `toolfold` 远端包与一键安装、设置双半通信(Host `GET/POST /api/dsh-toolfold/settings` + Client 桥)。
|
|
56
66
|
|
|
67
|
+
[0.1.8]: https://github.com/Minecraftbe/dsh-toolfold/compare/v0.1.7...v0.1.8
|
|
57
68
|
[0.1.7]: https://github.com/Minecraftbe/dsh-toolfold/compare/v0.1.6...v0.1.7
|
|
58
69
|
[0.1.6]: https://github.com/Minecraftbe/dsh-toolfold/compare/v0.1.5...v0.1.6
|
|
59
70
|
[0.1.5]: https://github.com/Minecraftbe/dsh-toolfold/compare/v0.1.4...v0.1.5
|
package/README.en.md
CHANGED
|
@@ -1,136 +1,174 @@
|
|
|
1
1
|
# dsh-toolfold · Tool Call Folding
|
|
2
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
|
-
- **Bottom-pinned expand**: expanding near the bottom pins the bar's viewport position, so the
|
|
28
|
-
chat's auto-scroll never shoves it off the top of the screen;
|
|
29
|
-
- **Near-zero performance cost**: no page-wide observer, zero work while content streams, and
|
|
30
|
-
everything pauses when the tab is hidden — measured ≈0.03% of one core while idle
|
|
31
|
-
(see [Performance](#performance)).
|
|
32
|
-
|
|
33
|
-

|
|
34
|
-
<!-- 🖼️ IMAGE SLOT: assets/expand-collapse.gif — close-up of one 3–4 call run expanding (waterfall + label slide) and collapsing (rise + height shrink), square or 4:3, ≤2MB -->
|
|
3
|
+
[](https://awesome-dsh-plugin.com)
|
|
4
|
+
|
|
5
|
+
English | [中文](README.md)
|
|
6
|
+
|
|
7
|
+
> A DSH Web GUI plugin that provides a **Codex‑like tool folding experience**: consecutive **tool calls** are collapsed into a single compact bar showing only a one‑line summary of the last call. Click to expand/collapse.
|
|
8
|
+
> It does not replace any built‑in renderer, and uninstallation restores the UI completely.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Installation](#installation)
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
18
|
+
- [Default Behaviour – First Use](#default-behaviour--first-use)
|
|
19
|
+
- [Features](#features)
|
|
20
|
+
- [Settings](#settings)
|
|
21
|
+
- [Options](#options)
|
|
22
|
+
- [Storage Location](#storage-location)
|
|
23
|
+
- [Performance](#performance)
|
|
24
|
+
- [Limitations & Compatibility](#limitations--compatibility)
|
|
25
|
+
|
|
26
|
+
---
|
|
35
27
|
|
|
36
28
|
## Installation
|
|
37
29
|
|
|
38
|
-
###
|
|
30
|
+
### Recommended (via npm)
|
|
39
31
|
|
|
40
32
|
```sh
|
|
41
|
-
dsh plugin --profile web add
|
|
33
|
+
dsh plugin --profile web add dsh-toolfold
|
|
42
34
|
```
|
|
43
35
|
|
|
44
|
-
###
|
|
36
|
+
### Alternative Methods
|
|
45
37
|
|
|
46
38
|
```sh
|
|
39
|
+
# From GitHub
|
|
40
|
+
dsh plugin --profile web add github:Minecraftbe/dsh-toolfold
|
|
41
|
+
|
|
42
|
+
# From source
|
|
47
43
|
git clone https://github.com/Minecraftbe/dsh-toolfold.git
|
|
48
44
|
dsh plugin --profile web add ./dsh-toolfold
|
|
49
45
|
```
|
|
50
46
|
|
|
51
|
-
After
|
|
52
|
-
at boot). Verify the plugin reached the final config with:
|
|
47
|
+
After installation, **restart dsh and refresh your browser** – the plugin will be active. You can verify that it is included in the final bundle with:
|
|
53
48
|
|
|
54
49
|
```sh
|
|
55
50
|
dsh --profile web --dump-config
|
|
56
51
|
```
|
|
57
52
|
|
|
58
|
-
|
|
59
|
-
> remove that row before switching to this channel to avoid double-mounting.
|
|
60
|
-
|
|
61
|
-
### Uninstall
|
|
53
|
+
### Uninstallation
|
|
62
54
|
|
|
63
55
|
```sh
|
|
64
56
|
dsh plugin --profile web remove dsh-toolfold
|
|
65
57
|
```
|
|
66
58
|
|
|
67
|
-
After
|
|
59
|
+
After restart, the interface returns to its original state.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
Once installed, consecutive tool calls are automatically folded into a single bar in any DSH conversation.
|
|
66
|
+
|
|
67
|
+
- **Click the fold bar** to expand or collapse the tool‑call cards (keyboard `Enter` / `Space` also work)
|
|
68
|
+
- **The bar shows**: a one‑line summary of the last call plus the label “N tool calls folded · click to expand”
|
|
69
|
+
|
|
70
|
+

|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Default Behaviour – First Use
|
|
75
|
+
|
|
76
|
+
The plugin works immediately after installation, but you might notice two things that seem “unexpected” at first. Both are intentional defaults and can be changed easily.
|
|
77
|
+
|
|
78
|
+
### ❓ Why did my thinking content disappear?
|
|
79
|
+
|
|
80
|
+
**Reason**: “Keep thinking” is off by default (`keepThink: false`). Completed thinking blocks are hidden to keep the conversation cleaner.
|
|
68
81
|
|
|
69
|
-
|
|
82
|
+
**How to restore**: go to `Settings → Plugins → Tool Fold` and turn on **“Keep thinking”**.
|
|
70
83
|
|
|
71
|
-
|
|
72
|
-
|
|
84
|
+
### ❓ Why are my tool calls split into two separate fold bars?
|
|
85
|
+
|
|
86
|
+
**Reason**: “Split thinking across call groups” is on by default (`splitThink: true`). The plugin treats a completed thinking block as a logical separator, folding calls before and after it independently.
|
|
87
|
+
|
|
88
|
+
**How to merge them**: go to `Settings → Plugins → Tool Fold` and turn off **“Split thinking across call groups”**.
|
|
89
|
+
|
|
90
|
+
> Ongoing (streaming) thinking is always visible regardless of these settings, and will only be processed according to the rules once it finishes.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Features
|
|
95
|
+
|
|
96
|
+
### Core Folding Behaviour
|
|
97
|
+
|
|
98
|
+
- **Automatic folding of consecutive tool calls** – adjacent tool calls are collapsed into one bar, showing only the last call’s summary line and the number of folded items.
|
|
99
|
+
- **Thinking splits call groups (enabled by default)** – a completed thinking block separates the tool calls before and after it into two independent fold bars; they are never merged across a thinking block.
|
|
100
|
+
- **Thinking hidden by default, can be kept** – completed thinking is hidden to save space; when “Keep thinking” is enabled, thinking appears between fold bars (in split mode) or interpolated back between calls on expansion (in merged mode).
|
|
101
|
+
- **Ongoing thinking always visible** – streaming thinking remains visible until it finishes, then follows the above rules.
|
|
102
|
+
|
|
103
|
+
### Animation & Experience
|
|
104
|
+
|
|
105
|
+
- **Spring‑loaded waterfall animation** – cards drop one by one on expand (with spring bounce), and on collapse they rise while heights shrink synchronously, so content below moves continuously without jumping.
|
|
106
|
+
- **Sticks to the bottom without pushing out** – when expanding near the bottom, the fold bar’s viewport position is pinned so that automatic scrolling does not push it off screen.
|
|
107
|
+
- **Respects system preferences** – if the system has “Reduce motion” enabled, animations are automatically disabled.
|
|
108
|
+
|
|
109
|
+
### Performance & Resource Usage
|
|
110
|
+
|
|
111
|
+
- **Near‑zero performance impact** – no page‑wide observers, zero engine work during streaming, and all activity is paused when the tab is hidden. Measured idle overhead is about 0.03% of a single CPU core.
|
|
112
|
+
- **Optional real‑time stats** – enable “Performance stats” in the settings card to see per‑step timings and verify overhead.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Settings
|
|
117
|
+
|
|
118
|
+
Settings are found at: **Settings → Plugins → Tool Fold** (the card looks like other built‑in plugin cards and follows the light/dark theme).
|
|
73
119
|
|
|
74
120
|

|
|
75
|
-
<!-- 🖼️ IMAGE SLOT: assets/settings.png — screenshot of the expanded "工具折叠" card in Settings → Plugins (duration slider + keep-think toggle + stats toggle + storage-location hint), light theme, PNG/WebP ≤1MB -->
|
|
76
121
|
|
|
77
|
-
|
|
122
|
+
### Options
|
|
123
|
+
|
|
124
|
+
| Option | Description |
|
|
78
125
|
| --- | --- |
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
|
|
|
126
|
+
| **Expand animation duration** | Duration of the expand/collapse animation per card (0–1000 ms, default 240 ms; 0 = instant) |
|
|
127
|
+
| **Keep thinking** | Hide completed thinking by default; when enabled, thinking is shown – between fold bars in split mode, or interpolated back between calls on expansion in merged mode |
|
|
128
|
+
| **Split thinking across call groups** | Enabled by default: a completed thinking block separates tool calls before and after it into independent fold bars. Disabled: thinking is folded together with the surrounding call group (legacy behaviour) |
|
|
129
|
+
| **Performance stats** | Show real‑time plugin timing in the card (cumulative count and ms/s for observation callbacks, engine refresh, merge recalculation, safe rescan, summary cloning, plus the number of streaming batches short‑circuited with zero overhead) |
|
|
130
|
+
|
|
131
|
+
---
|
|
83
132
|
|
|
84
|
-
|
|
133
|
+
### Storage Location
|
|
85
134
|
|
|
86
|
-
|
|
135
|
+
Plugin settings are persisted automatically, with the following priority:
|
|
87
136
|
|
|
88
|
-
- **
|
|
89
|
-
`~/.dsh/settings.yaml` (namespace `toolfold`) — the same document the product and other
|
|
90
|
-
plugins use, host-side, so they survive browser/device changes. The card footer shows
|
|
91
|
-
"设置保存在 DSH 主机配置". The file is editable directly:
|
|
137
|
+
- **Primary (installed/bundle mode)** – stored via the DSH **settings service** in `~/.dsh/settings.yaml` (namespace `toolfold`). They follow the dsh host, so they persist across browsers/devices. You can also edit the file manually:
|
|
92
138
|
|
|
93
139
|
```yaml
|
|
94
140
|
toolfold:
|
|
95
|
-
durMs: 240 # expand animation duration
|
|
96
|
-
keepThink: false # keep
|
|
97
|
-
splitThink: true #
|
|
98
|
-
stats: false # performance
|
|
141
|
+
durMs: 240 # expand animation duration 0–2000 ms
|
|
142
|
+
keepThink: false # whether to keep completed thinking visible
|
|
143
|
+
splitThink: true # whether to split call groups by thinking
|
|
144
|
+
stats: false # whether to enable performance stats
|
|
99
145
|
```
|
|
100
146
|
|
|
101
|
-
- **
|
|
102
|
-
settings bridge): degrades to browser `localStorage` (key `dsh-toolfold.settings.v1`) and the
|
|
103
|
-
card says "仅保存在本浏览器"; the legacy key `dsh-codex-collapse.settings.v1` migrates
|
|
104
|
-
automatically on first load.
|
|
147
|
+
- **Fallback (when settings service is unavailable)** – falls back to browser `localStorage` (key `dsh-toolfold.settings.v1`), so settings are only kept in that browser. The old key `dsh-codex-collapse.settings.v1` is automatically migrated on first load.
|
|
105
148
|
|
|
106
|
-
|
|
107
|
-
|
|
149
|
+
The settings card shows the current storage method (“Settings saved in DSH host config” or “Saved only in this browser”).
|
|
150
|
+
|
|
151
|
+
---
|
|
108
152
|
|
|
109
153
|
## Performance
|
|
110
154
|
|
|
111
|
-
- **Measured on
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
|
|
124
|
-
- **
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
- Expanded state is remembered per session flow + node key; reopening a session with the same keys
|
|
128
|
-
restores it;
|
|
129
|
-
- Settings are owned by the DSH host config (`~/.dsh/settings.yaml`); browser `localStorage` is
|
|
130
|
-
only the fallback when the host bridge is unreachable (then each browser needs its own setup);
|
|
131
|
-
- Requires `MutationObserver` and `requestAnimationFrame`; without them it degrades to "fold once on
|
|
132
|
-
initial render".
|
|
155
|
+
- **Measured on a real GUI page (8 s idle window)** – total engine time ~1.6–2.8 ms (~0.3 ms/s, ~0.03% single‑core); during a 5‑s CPU profile, engine functions appeared zero times.
|
|
156
|
+
- **Zero work during streaming** – streaming thinking/text tokens are short‑circuited with O(1) checks and do not trigger any recalculation (~0.1–0.4 µs per node).
|
|
157
|
+
- **Hidden tab = literally zero** – all observers and timers are disconnected when the tab is hidden; they are re‑attached and a single reconciliation run is performed when the tab becomes visible again.
|
|
158
|
+
- Enable **“Performance stats”** in the settings card to see detailed timings for every plugin operation.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Limitations & Compatibility
|
|
163
|
+
|
|
164
|
+
- **DOM‑based** – folding relies on stable product DOM markers. If markers change, the worst case is that folding stops working – it will not break the chat.
|
|
165
|
+
- **Splitting rules**:
|
|
166
|
+
- Ongoing thinking and plain text output separate tool call groups.
|
|
167
|
+
- Completed thinking also separates groups by default (when “Split thinking across call groups” is on); when off, thinking is folded together with its surrounding call group.
|
|
168
|
+
- **State memory** – expand/collapse state is remembered per conversation flow and node key; re‑opening a conversation with the same key restores the state.
|
|
169
|
+
- **Browser requirements** – needs `MutationObserver` and `requestAnimationFrame`. If unsupported, it falls back to “fold once on initial render”.
|
|
170
|
+
- **Settings storage** – uses the DSH host config (`~/.dsh/settings.yaml`) when available; only falls back to `localStorage` when the host bridge is unreachable (in which case settings are browser‑local only).
|
|
133
171
|
|
|
134
172
|
---
|
|
135
173
|
|
|
136
|
-
This project
|
|
174
|
+
> This project is entirely built with dsh + deepseek v4 flash(max).
|
package/README.md
CHANGED
|
@@ -1,56 +1,54 @@
|
|
|
1
1
|
# dsh-toolfold · 工具调用折叠
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
[](https://awesome-dsh-plugin.com)
|
|
4
|
+
|
|
5
|
+
中文 | [English](README.en.md)
|
|
6
|
+
|
|
7
|
+
> DSH Web GUI 插件,提供**类 Codex 的工具折叠体验**:把连续的**工具调用**折叠成一条简洁的折叠条,只展示最后一个调用的一行摘要,点击即可展开/收起。不替换任何内置渲染器,卸载后界面完全恢复原样。
|
|
6
8
|
|
|
7
9
|

|
|
8
|
-
<!-- 🖼️ 图片位:assets/demo-fold.gif —— 建议录制 6~10s:多个工具调用自动折成一条 → 点击展开(卡片瀑布落下)→ 点击收起(卡片上行、高度收缩),16:9,≤2MB -->
|
|
9
|
-
|
|
10
|
-
## 特性一览
|
|
11
|
-
|
|
12
|
-
- **连续工具调用折叠为一条**:相邻的工具调用整段收起,条内直接显示**最后一个调用**的一行
|
|
13
|
-
摘要,并标注「已折叠 N 个工具调用 · 点击展开」;
|
|
14
|
-
- **思考分隔调用组(默认开启)**:已完成的思考会把前后两组工具调用**隔开、各自独立折叠**,
|
|
15
|
-
不会把思考前后的调用合并成一条;关闭后恢复原折叠方式——思考并入所在工具组一起折叠;
|
|
16
|
-
- **思考默认隐藏,可保留**:已完成的思考默认隐藏(不占版面);开启「保留思考」后思考不再
|
|
17
|
-
消失——分隔模式下显示在两条折叠条之间,合并模式下展开时按**原顺序插回调用之间**;
|
|
18
|
-
- **进行中的思考独立显示**:流式思考保持原样可见,完成后按「思考分隔」设置处理;
|
|
19
|
-
- **弹性瀑布动画**:展开时卡片自上而下逐张落下(spring 回弹),收起时逐张上行且行高度同步收缩,
|
|
20
|
-
下方内容连续上移、结尾无跳变;系统开启「减少动态效果」时动画自动关闭;
|
|
21
|
-
- **贴底不顶出**:靠近底部展开时,折叠条视口位置被钉住,不会因聊天自动滚动被顶出屏幕;
|
|
22
|
-
- **接近零性能占用**:无页面级观察器、流式期间引擎零工作、标签页隐藏时全部暂停——
|
|
23
|
-
实机测量空闲开销约 0.03% 单核(详见[性能](#性能))。
|
|
24
10
|
|
|
25
|
-
|
|
26
|
-
<!-- 🖼️ 图片位:assets/expand-collapse.gif —— 建议:单段 3~4 个调用的展开(瀑布落下 + 标签滑左)与收起(上行 + 高度收缩)特写,正方形或 4:3,≤2MB -->
|
|
11
|
+
---
|
|
27
12
|
|
|
28
|
-
##
|
|
13
|
+
## 目录
|
|
14
|
+
|
|
15
|
+
- [安装](#安装)
|
|
16
|
+
- [快速上手](#快速上手)
|
|
17
|
+
- [初次使用·默认行为说明](#初次使用默认行为说明)
|
|
18
|
+
- [功能特性](#功能特性)
|
|
19
|
+
- [设置说明](#设置说明)
|
|
20
|
+
- [设置项](#设置项)
|
|
21
|
+
- [存储位置](#存储位置)
|
|
22
|
+
- [性能](#性能)
|
|
23
|
+
- [限制与兼容性](#限制与兼容性)
|
|
29
24
|
|
|
25
|
+
---
|
|
30
26
|
|
|
27
|
+
## 安装
|
|
31
28
|
|
|
32
|
-
###
|
|
29
|
+
### 推荐(通过 npm)
|
|
33
30
|
|
|
34
31
|
```sh
|
|
35
|
-
dsh plugin --profile web add
|
|
32
|
+
dsh plugin --profile web add dsh-toolfold
|
|
36
33
|
```
|
|
37
34
|
|
|
38
|
-
###
|
|
35
|
+
### 其他方式
|
|
39
36
|
|
|
40
37
|
```sh
|
|
38
|
+
# 从 GitHub 安装
|
|
39
|
+
dsh plugin --profile web add github:Minecraftbe/dsh-toolfold
|
|
40
|
+
|
|
41
|
+
# 从源码安装
|
|
41
42
|
git clone https://github.com/Minecraftbe/dsh-toolfold.git
|
|
42
43
|
dsh plugin --profile web add ./dsh-toolfold
|
|
43
44
|
```
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
最终配置:
|
|
46
|
+
安装后**重启 dsh 并刷新浏览器**即可生效。可通过以下命令确认配置已生效:
|
|
47
47
|
|
|
48
48
|
```sh
|
|
49
49
|
dsh --profile web --dump-config
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
> 早期版本若曾在 `cordis.patch.yml` 手工加过 `toolfold` 行,先删除该行再切换,避免双挂载。
|
|
53
|
-
|
|
54
52
|
### 卸载
|
|
55
53
|
|
|
56
54
|
```sh
|
|
@@ -59,62 +57,117 @@ dsh plugin --profile web remove dsh-toolfold
|
|
|
59
57
|
|
|
60
58
|
重启后界面完全恢复原样。
|
|
61
59
|
|
|
62
|
-
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 快速上手
|
|
63
|
+
|
|
64
|
+
安装完成后,在 DSH 对话界面中,连续的工具调用会自动折叠为一条折叠条。
|
|
65
|
+
|
|
66
|
+
- **点击折叠条**:展开或收起工具调用卡片(键盘 `Enter` / `Space` 同样有效)
|
|
67
|
+
- **折叠条显示**:最后一个工具调用的一行摘要 + “已折叠 N 个工具调用 · 点击展开”
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 初次使用·默认行为说明
|
|
74
|
+
|
|
75
|
+
插件安装后立即生效,你可能首先注意到两个“异常”现象——这都是默认设置所致,可按需调整。
|
|
76
|
+
|
|
77
|
+
### ❓ 思考内容怎么消失了?
|
|
78
|
+
|
|
79
|
+
**原因**:默认关闭「保留思考」(`keepThink: false`),已完成的思考被折叠隐藏,目的是让界面更清爽。
|
|
63
80
|
|
|
64
|
-
|
|
81
|
+
**改回**:`设置 → 插件 → 工具折叠` → 开启「保留思考」。
|
|
82
|
+
|
|
83
|
+
### ❓ 工具调用为什么被切成了两段?
|
|
84
|
+
|
|
85
|
+
**原因**:默认开启「思考分隔调用组」(`splitThink: true`),插件把思考视为逻辑分界线,将前后的工具调用各自独立折叠。
|
|
86
|
+
|
|
87
|
+
**改回**:`设置 → 插件 → 工具折叠` → 关闭「思考分隔调用组」。
|
|
88
|
+
|
|
89
|
+
> 进行中的思考不受任何开关影响,始终保持可见,完成后再按规则处理。
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 功能特性
|
|
94
|
+
|
|
95
|
+
### 核心折叠行为
|
|
96
|
+
|
|
97
|
+
- **连续工具调用自动折叠**:相邻工具调用整段收起,仅显示最后一个调用的摘要行,并标注折叠数量
|
|
98
|
+
- **思考分隔调用组(默认开启)**:已完成的思考会将前后两组工具调用**隔开、各自独立折叠**,不会跨思考合并
|
|
99
|
+
- **思考默认隐藏,可保留**:已完成的思考默认不占用版面;开启「保留思考」后思考会出现在折叠条之间(分隔模式)或展开时插回调用之间(合并模式)
|
|
100
|
+
- **进行中的思考独立显示**:流式思考始终保持可见,完成后按上述规则处理
|
|
101
|
+
|
|
102
|
+
### 动画与体验
|
|
103
|
+
|
|
104
|
+
- **弹性瀑布动画**:展开时卡片逐张落下(带 spring 回弹),收起时逐张上行且高度同步收缩,下方内容连续上移、无跳变
|
|
105
|
+
- **贴底不顶出**:靠近底部展开时,折叠条视口位置固定,不会因自动滚动被顶出屏幕
|
|
106
|
+
- **尊重系统偏好**:系统开启「减少动态效果」时,动画自动关闭
|
|
107
|
+
|
|
108
|
+
### 性能与资源
|
|
109
|
+
|
|
110
|
+
- **接近零性能占用**:无页面级观察器,流式期间引擎零工作,标签页隐藏时全部暂停(空闲开销约 0.03% 单核)
|
|
111
|
+
- **实时性能统计**(可选):在设置卡片内显示插件各环节耗时,便于验证开销
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 设置说明
|
|
116
|
+
|
|
117
|
+
设置入口:**设置 → 插件 → 工具折叠**(卡片外观与内置插件一致,跟随深浅主题)。
|
|
65
118
|
|
|
66
119
|

|
|
67
|
-
|
|
120
|
+
|
|
121
|
+
### 设置项
|
|
68
122
|
|
|
69
123
|
| 设置项 | 说明 |
|
|
70
124
|
| --- | --- |
|
|
71
|
-
| **展开动画时长** |
|
|
72
|
-
| **保留思考** |
|
|
73
|
-
| **思考分隔调用组** |
|
|
74
|
-
| **性能统计** |
|
|
125
|
+
| **展开动画时长** | 单张卡片的展开/收起动画时长(0–1000ms,默认 240ms;设为 0 为瞬时切换) |
|
|
126
|
+
| **保留思考** | 默认隐藏已完成的思考;开启后思考保留(分隔模式下显示在两条折叠条之间,合并模式下展开时插回调用之间) |
|
|
127
|
+
| **思考分隔调用组** | 开启(默认):已完成的思考把前后两组工具调用隔开、各自独立折叠;关闭:思考并入所在工具组一起折叠 |
|
|
128
|
+
| **性能统计** | 在卡片内实时显示插件自身耗时(观察回调/引擎刷新/合并重算/安全重扫/摘要克隆的累计次数与耗时,以及被零开销短路忽略的流式批次) |
|
|
75
129
|
|
|
76
|
-
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
### 存储位置
|
|
77
133
|
|
|
78
|
-
|
|
134
|
+
插件设置会自动持久化,优先级如下:
|
|
79
135
|
|
|
80
|
-
-
|
|
81
|
-
`~/.dsh/settings.yaml`(命名空间 `toolfold`),与产品和其他插件的设置在同一个文档里,
|
|
82
|
-
随 dsh 主机走,换浏览器/设备不丢失;卡片底部会显示「设置保存在 DSH 主机配置」。
|
|
83
|
-
也可以直接手工编辑该文件:
|
|
136
|
+
- **优先(已安装模式)**:由 DSH **设置服务**写入 `~/.dsh/settings.yaml`(命名空间 `toolfold`),随主机走,换浏览器/设备不丢失。手工编辑示例:
|
|
84
137
|
|
|
85
138
|
```yaml
|
|
86
139
|
toolfold:
|
|
87
140
|
durMs: 240 # 展开动画时长 0–2000ms
|
|
88
|
-
keepThink: false #
|
|
89
|
-
splitThink: true #
|
|
141
|
+
keepThink: false # 是否保留已完成的思考
|
|
142
|
+
splitThink: true # 是否让已完成的思考隔开前后两组工具调用
|
|
90
143
|
stats: false # 是否开启性能统计
|
|
91
144
|
```
|
|
92
145
|
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
146
|
+
- **降级(无设置服务时)**:退化为浏览器 `localStorage`(键 `dsh-toolfold.settings.v1`),仅保存在当前浏览器。旧版键 `dsh-codex-collapse.settings.v1` 会在首次加载时自动迁移。
|
|
147
|
+
|
|
148
|
+
设置卡片底部会显示当前使用的存储位置(“设置保存在 DSH 主机配置”或“仅保存在本浏览器”)。
|
|
96
149
|
|
|
97
|
-
|
|
98
|
-
<!-- 🖼️ 图片位:assets/keep-think.gif —— 建议:同一段调用分别以「保留思考 关 / 开」各演示一次展开,展示思考不显示 / 按原顺序插回,≤2MB -->
|
|
150
|
+
---
|
|
99
151
|
|
|
100
152
|
## 性能
|
|
101
153
|
|
|
102
|
-
-
|
|
103
|
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
|
|
154
|
+
- **实测数据(真实 GUI 页面,空闲窗口 8s)**:引擎总耗时约 1.6–2.8ms(≈0.3ms/s,约 0.03% 单核);同窗口 5s CPU 采样中引擎函数零样本。
|
|
155
|
+
- **流式零工作**:思考/文本 token 流式变更被 O(1) 短路忽略,不触发任何重算(约 0.1–0.4µs/节点)。
|
|
156
|
+
- **标签页隐藏 = 零占用**:隐藏时断开全部观察器与定时器,恢复可见时重挂并补一次对账。
|
|
157
|
+
- 打开「性能统计」即可在设置卡片内查看各项耗时明细。
|
|
158
|
+
|
|
159
|
+
---
|
|
107
160
|
|
|
108
|
-
##
|
|
161
|
+
## 限制与兼容性
|
|
109
162
|
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
|
|
116
|
-
-
|
|
163
|
+
- **基于 DOM 标记**:折叠依赖产品稳定的渲染标记;若标记变动,最坏情况是折叠停止生效,但不会破坏聊天。
|
|
164
|
+
- **分隔规则**:
|
|
165
|
+
- 进行中的思考与 AI 文字输出会隔开工具调用
|
|
166
|
+
- 已完成的思考默认也隔开(「思考分隔调用组」开启时);关闭后已完成的思考并入所在工具组一起折叠
|
|
167
|
+
- **状态记忆**:展开状态按会话流 + 节点 key 记忆;会话重开且 key 相同时会恢复展开状态。
|
|
168
|
+
- **浏览器要求**:需要支持 `MutationObserver` 与 `requestAnimationFrame`;不支持时退化为“初始渲染折叠一次”。
|
|
169
|
+
- **设置存储**:以 DSH 主机配置(`~/.dsh/settings.yaml`)为准;仅当主机桥不可达时才退化为 `localStorage`(换浏览器/设备需重新设置)。
|
|
117
170
|
|
|
118
171
|
---
|
|
119
172
|
|
|
120
|
-
本项目完全由 dsh + deepseek v4 flash(max) 完成。
|
|
173
|
+
> 本项目完全由 dsh + deepseek v4 flash(max) 完成。
|
package/lib/client.js
CHANGED
|
@@ -34,6 +34,9 @@
|
|
|
34
34
|
* the content below the run follows upward continuously — no blank
|
|
35
35
|
* hole and no end snap. Cleanup runs exactly one step after the LAST
|
|
36
36
|
* row's animation finishes — no dead wait before the run disappears.
|
|
37
|
+
* A run that keeps GROWING while it collapses (the model is still
|
|
38
|
+
* streaming the newest calls into it) extends the cascade to the new
|
|
39
|
+
* rows and re-arms the finish timer, so the tail never snaps shut.
|
|
37
40
|
* When the reader is pinned near the bottom, expanding keeps the
|
|
38
41
|
* bar's viewport position fixed (the product's sticky-bottom follow
|
|
39
42
|
* would otherwise shove the bar out of the top).
|
|
@@ -983,13 +986,20 @@ window.__ModuleLoader__.load({
|
|
|
983
986
|
var toggle = function () {
|
|
984
987
|
var set = expanded.get(flow);
|
|
985
988
|
if (set === undefined || disposed) return;
|
|
986
|
-
|
|
987
|
-
|
|
989
|
+
// The closure `run` is the bar's creation-time snapshot; every
|
|
990
|
+
// pass refreshes bar._ccxRun with the CURRENT run. When the model
|
|
991
|
+
// streams more calls into the same run (the newest info at the
|
|
992
|
+
// bottom of the flow), collapsing the stale snapshot would animate
|
|
993
|
+
// only the original rows and then snap the rest shut — the "runs
|
|
994
|
+
// briefly, then suddenly collapses" symptom.
|
|
995
|
+
var current = bar._ccxRun !== undefined ? bar._ccxRun : run;
|
|
996
|
+
if (set.has(current.headKey)) {
|
|
997
|
+
startCollapse(flow, current, bar);
|
|
988
998
|
} else {
|
|
989
|
-
set.add(
|
|
990
|
-
cancelCollapse(flow,
|
|
999
|
+
set.add(current.headKey);
|
|
1000
|
+
cancelCollapse(flow, current, bar);
|
|
991
1001
|
pass(flow);
|
|
992
|
-
pinBar(bar,
|
|
1002
|
+
pinBar(bar, current);
|
|
993
1003
|
}
|
|
994
1004
|
};
|
|
995
1005
|
bar.addEventListener('click', toggle);
|
|
@@ -1080,10 +1090,34 @@ window.__ModuleLoader__.load({
|
|
|
1080
1090
|
}
|
|
1081
1091
|
for (var j = 0; j < rows.length; j++) rows[j].classList.add(COLLAPSING_CLASS);
|
|
1082
1092
|
animateRowShrink(rows, dur, step);
|
|
1093
|
+
// Track which rows the cascade has covered: a run that keeps
|
|
1094
|
+
// GROWING while it collapses (the model is still streaming the
|
|
1095
|
+
// newest calls into it) extends the cascade via extendCollapse and
|
|
1096
|
+
// re-arms the finish timer. Without this, rows added after the
|
|
1097
|
+
// collapse began would snap shut when the timer armed for the OLD
|
|
1098
|
+
// row count fires — the "runs briefly, then suddenly collapses"
|
|
1099
|
+
// symptom.
|
|
1100
|
+
bar._ccxCollapseRows = rows;
|
|
1083
1101
|
// The bar flips to collapsed visuals now; the cards stay visible for
|
|
1084
1102
|
// the rise (pass skips ccxMerged while the head key is collapsing).
|
|
1085
1103
|
pass(flow);
|
|
1104
|
+
armCollapseFinish(flow, run, bar);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* Arm (or re-arm) the collapse-finish timer. The delay covers one
|
|
1109
|
+
* full cascade — last row's stagger + rise + margin — measured from
|
|
1110
|
+
* the arm moment. `cascadeCount` defaults to the full covered row
|
|
1111
|
+
* set; extendCollapse passes only the NEW rows it just added, since
|
|
1112
|
+
* the already-covered rows have finished animating.
|
|
1113
|
+
*/
|
|
1114
|
+
function armCollapseFinish(flow, run, bar, cascadeCount) {
|
|
1086
1115
|
if (bar._ccxCollapseTimer !== undefined) timers.cancel(bar._ccxCollapseTimer);
|
|
1116
|
+
var rows = bar._ccxCollapseRows;
|
|
1117
|
+
var dur = durMs();
|
|
1118
|
+
var step = stepMs();
|
|
1119
|
+
var n = cascadeCount !== undefined ? cascadeCount : rows.length;
|
|
1120
|
+
var delay = Math.max(0, n - 1) * step + dur + 60;
|
|
1087
1121
|
bar._ccxCollapseTimer = timers.after(function () {
|
|
1088
1122
|
bar._ccxCollapseTimer = undefined;
|
|
1089
1123
|
if (disposed || !flow.isConnected) return;
|
|
@@ -1092,21 +1126,63 @@ window.__ModuleLoader__.load({
|
|
|
1092
1126
|
perNow.delete(run.headKey);
|
|
1093
1127
|
if (perNow.size === 0) collapsing.delete(flow);
|
|
1094
1128
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1129
|
+
var all = bar._ccxCollapseRows;
|
|
1130
|
+
for (var k = 0; k < all.length; k++) {
|
|
1131
|
+
all[k].classList.remove(COLLAPSING_CLASS);
|
|
1132
|
+
clearRowShrink(all[k]);
|
|
1098
1133
|
}
|
|
1134
|
+
bar._ccxCollapseRows = undefined;
|
|
1099
1135
|
pass(flow); // applies ccxMerged now
|
|
1100
|
-
},
|
|
1136
|
+
}, delay);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* A run that is mid-collapse and still GROWING (the model streams the
|
|
1141
|
+
* newest tool calls into it) folds the newly added rows into the same
|
|
1142
|
+
* rise/shrink cascade — with their own relative stagger, starting
|
|
1143
|
+
* immediately — and re-arms the finish timer so the last new row's
|
|
1144
|
+
* animation completes before the merge applies. Without this the new
|
|
1145
|
+
* rows sit at full height and then snap shut with the merge.
|
|
1146
|
+
*/
|
|
1147
|
+
function extendCollapse(flow, run, bar) {
|
|
1148
|
+
var oldRows = bar._ccxCollapseRows;
|
|
1149
|
+
var rows = run.rows;
|
|
1150
|
+
if (oldRows === undefined || rows.length <= oldRows.length) return;
|
|
1151
|
+
var dur = durMs();
|
|
1152
|
+
var step = stepMs();
|
|
1153
|
+
// The rows that joined the run since the cascade covered it. Diff by
|
|
1154
|
+
// identity so appended, inserted, or replaced rows all join — the
|
|
1155
|
+
// streaming case appends, but never assume.
|
|
1156
|
+
var added = [];
|
|
1157
|
+
for (var i = 0; i < rows.length; i++) {
|
|
1158
|
+
if (oldRows.indexOf(rows[i]) === -1) added.push(rows[i]);
|
|
1159
|
+
}
|
|
1160
|
+
if (added.length === 0) return;
|
|
1161
|
+
for (var j = 0; j < added.length; j++) {
|
|
1162
|
+
var row = added[j];
|
|
1163
|
+
row.classList.remove(FALLING_CLASS, COLLAPSING_CLASS);
|
|
1164
|
+
row.style.setProperty('--ccx-i', String(j));
|
|
1165
|
+
row.classList.add(COLLAPSING_CLASS);
|
|
1166
|
+
}
|
|
1167
|
+
animateRowShrink(added, dur, step);
|
|
1168
|
+
bar._ccxCollapseRows = rows;
|
|
1169
|
+
armCollapseFinish(flow, run, bar, added.length);
|
|
1101
1170
|
}
|
|
1102
1171
|
|
|
1103
1172
|
/** Abort a pending collapse (the user re-expanded mid-animation). */
|
|
1104
|
-
function cancelCollapse(flow, run) {
|
|
1173
|
+
function cancelCollapse(flow, run, bar) {
|
|
1105
1174
|
var per = collapsing.get(flow);
|
|
1106
1175
|
if (per !== undefined) {
|
|
1107
1176
|
per.delete(run.headKey);
|
|
1108
1177
|
if (per.size === 0) collapsing.delete(flow);
|
|
1109
1178
|
}
|
|
1179
|
+
if (bar !== undefined && bar._ccxCollapseTimer !== undefined) {
|
|
1180
|
+
timers.cancel(bar._ccxCollapseTimer);
|
|
1181
|
+
bar._ccxCollapseTimer = undefined;
|
|
1182
|
+
}
|
|
1183
|
+
if (bar !== undefined) {
|
|
1184
|
+
bar._ccxCollapseRows = undefined;
|
|
1185
|
+
}
|
|
1110
1186
|
var rows = run.rows;
|
|
1111
1187
|
for (var i = 0; i < rows.length; i++) {
|
|
1112
1188
|
rows[i].classList.remove(COLLAPSING_CLASS);
|
|
@@ -1294,6 +1370,15 @@ window.__ModuleLoader__.load({
|
|
|
1294
1370
|
registry.set(run.headKey, bar);
|
|
1295
1371
|
}
|
|
1296
1372
|
updateBar(bar, run);
|
|
1373
|
+
// A run that is mid-collapse and still GROWING (the model is
|
|
1374
|
+
// streaming the newest calls into it) extends its cascade to the
|
|
1375
|
+
// newly added rows; the finish timer is re-armed by
|
|
1376
|
+
// extendCollapse. Without this the new rows would sit at full
|
|
1377
|
+
// height and then snap shut with the merge.
|
|
1378
|
+
if (bar._ccxCollapseRows !== undefined
|
|
1379
|
+
&& run.rows.length > bar._ccxCollapseRows.length) {
|
|
1380
|
+
extendCollapse(flow, run, bar);
|
|
1381
|
+
}
|
|
1297
1382
|
// The bar is the group HEADER: it stays before the run's first row,
|
|
1298
1383
|
// so on expand the cards cascade downward below it. Guard against
|
|
1299
1384
|
// re-inserting when already in place (insertBefore always mutates).
|
package/lib/dynamic-body.js
CHANGED
|
@@ -880,13 +880,20 @@
|
|
|
880
880
|
var toggle = function () {
|
|
881
881
|
var set = expanded.get(flow);
|
|
882
882
|
if (set === undefined || disposed) return;
|
|
883
|
-
|
|
884
|
-
|
|
883
|
+
// The closure `run` is the bar's creation-time snapshot; every
|
|
884
|
+
// pass refreshes bar._ccxRun with the CURRENT run. When the model
|
|
885
|
+
// streams more calls into the same run (the newest info at the
|
|
886
|
+
// bottom of the flow), collapsing the stale snapshot would animate
|
|
887
|
+
// only the original rows and then snap the rest shut — the "runs
|
|
888
|
+
// briefly, then suddenly collapses" symptom.
|
|
889
|
+
var current = bar._ccxRun !== undefined ? bar._ccxRun : run;
|
|
890
|
+
if (set.has(current.headKey)) {
|
|
891
|
+
startCollapse(flow, current, bar);
|
|
885
892
|
} else {
|
|
886
|
-
set.add(
|
|
887
|
-
cancelCollapse(flow,
|
|
893
|
+
set.add(current.headKey);
|
|
894
|
+
cancelCollapse(flow, current, bar);
|
|
888
895
|
pass(flow);
|
|
889
|
-
pinBar(bar,
|
|
896
|
+
pinBar(bar, current);
|
|
890
897
|
}
|
|
891
898
|
};
|
|
892
899
|
bar.addEventListener('click', toggle);
|
|
@@ -977,10 +984,34 @@
|
|
|
977
984
|
}
|
|
978
985
|
for (var j = 0; j < rows.length; j++) rows[j].classList.add(COLLAPSING_CLASS);
|
|
979
986
|
animateRowShrink(rows, dur, step);
|
|
987
|
+
// Track which rows the cascade has covered: a run that keeps
|
|
988
|
+
// GROWING while it collapses (the model is still streaming the
|
|
989
|
+
// newest calls into it) extends the cascade via extendCollapse and
|
|
990
|
+
// re-arms the finish timer. Without this, rows added after the
|
|
991
|
+
// collapse began would snap shut when the timer armed for the OLD
|
|
992
|
+
// row count fires — the "runs briefly, then suddenly collapses"
|
|
993
|
+
// symptom.
|
|
994
|
+
bar._ccxCollapseRows = rows;
|
|
980
995
|
// The bar flips to collapsed visuals now; the cards stay visible for
|
|
981
996
|
// the rise (pass skips ccxMerged while the head key is collapsing).
|
|
982
997
|
pass(flow);
|
|
998
|
+
armCollapseFinish(flow, run, bar);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* Arm (or re-arm) the collapse-finish timer. The delay covers one
|
|
1003
|
+
* full cascade — last row's stagger + rise + margin — measured from
|
|
1004
|
+
* the arm moment. `cascadeCount` defaults to the full covered row
|
|
1005
|
+
* set; extendCollapse passes only the NEW rows it just added, since
|
|
1006
|
+
* the already-covered rows have finished animating.
|
|
1007
|
+
*/
|
|
1008
|
+
function armCollapseFinish(flow, run, bar, cascadeCount) {
|
|
983
1009
|
if (bar._ccxCollapseTimer !== undefined) timers.cancel(bar._ccxCollapseTimer);
|
|
1010
|
+
var rows = bar._ccxCollapseRows;
|
|
1011
|
+
var dur = durMs();
|
|
1012
|
+
var step = stepMs();
|
|
1013
|
+
var n = cascadeCount !== undefined ? cascadeCount : rows.length;
|
|
1014
|
+
var delay = Math.max(0, n - 1) * step + dur + 60;
|
|
984
1015
|
bar._ccxCollapseTimer = timers.after(function () {
|
|
985
1016
|
bar._ccxCollapseTimer = undefined;
|
|
986
1017
|
if (disposed || !flow.isConnected) return;
|
|
@@ -989,21 +1020,63 @@
|
|
|
989
1020
|
perNow.delete(run.headKey);
|
|
990
1021
|
if (perNow.size === 0) collapsing.delete(flow);
|
|
991
1022
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1023
|
+
var all = bar._ccxCollapseRows;
|
|
1024
|
+
for (var k = 0; k < all.length; k++) {
|
|
1025
|
+
all[k].classList.remove(COLLAPSING_CLASS);
|
|
1026
|
+
clearRowShrink(all[k]);
|
|
995
1027
|
}
|
|
1028
|
+
bar._ccxCollapseRows = undefined;
|
|
996
1029
|
pass(flow); // applies ccxMerged now
|
|
997
|
-
},
|
|
1030
|
+
}, delay);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* A run that is mid-collapse and still GROWING (the model streams the
|
|
1035
|
+
* newest tool calls into it) folds the newly added rows into the same
|
|
1036
|
+
* rise/shrink cascade — with their own relative stagger, starting
|
|
1037
|
+
* immediately — and re-arms the finish timer so the last new row's
|
|
1038
|
+
* animation completes before the merge applies. Without this the new
|
|
1039
|
+
* rows sit at full height and then snap shut with the merge.
|
|
1040
|
+
*/
|
|
1041
|
+
function extendCollapse(flow, run, bar) {
|
|
1042
|
+
var oldRows = bar._ccxCollapseRows;
|
|
1043
|
+
var rows = run.rows;
|
|
1044
|
+
if (oldRows === undefined || rows.length <= oldRows.length) return;
|
|
1045
|
+
var dur = durMs();
|
|
1046
|
+
var step = stepMs();
|
|
1047
|
+
// The rows that joined the run since the cascade covered it. Diff by
|
|
1048
|
+
// identity so appended, inserted, or replaced rows all join — the
|
|
1049
|
+
// streaming case appends, but never assume.
|
|
1050
|
+
var added = [];
|
|
1051
|
+
for (var i = 0; i < rows.length; i++) {
|
|
1052
|
+
if (oldRows.indexOf(rows[i]) === -1) added.push(rows[i]);
|
|
1053
|
+
}
|
|
1054
|
+
if (added.length === 0) return;
|
|
1055
|
+
for (var j = 0; j < added.length; j++) {
|
|
1056
|
+
var row = added[j];
|
|
1057
|
+
row.classList.remove(FALLING_CLASS, COLLAPSING_CLASS);
|
|
1058
|
+
row.style.setProperty('--ccx-i', String(j));
|
|
1059
|
+
row.classList.add(COLLAPSING_CLASS);
|
|
1060
|
+
}
|
|
1061
|
+
animateRowShrink(added, dur, step);
|
|
1062
|
+
bar._ccxCollapseRows = rows;
|
|
1063
|
+
armCollapseFinish(flow, run, bar, added.length);
|
|
998
1064
|
}
|
|
999
1065
|
|
|
1000
1066
|
/** Abort a pending collapse (the user re-expanded mid-animation). */
|
|
1001
|
-
function cancelCollapse(flow, run) {
|
|
1067
|
+
function cancelCollapse(flow, run, bar) {
|
|
1002
1068
|
var per = collapsing.get(flow);
|
|
1003
1069
|
if (per !== undefined) {
|
|
1004
1070
|
per.delete(run.headKey);
|
|
1005
1071
|
if (per.size === 0) collapsing.delete(flow);
|
|
1006
1072
|
}
|
|
1073
|
+
if (bar !== undefined && bar._ccxCollapseTimer !== undefined) {
|
|
1074
|
+
timers.cancel(bar._ccxCollapseTimer);
|
|
1075
|
+
bar._ccxCollapseTimer = undefined;
|
|
1076
|
+
}
|
|
1077
|
+
if (bar !== undefined) {
|
|
1078
|
+
bar._ccxCollapseRows = undefined;
|
|
1079
|
+
}
|
|
1007
1080
|
var rows = run.rows;
|
|
1008
1081
|
for (var i = 0; i < rows.length; i++) {
|
|
1009
1082
|
rows[i].classList.remove(COLLAPSING_CLASS);
|
|
@@ -1191,6 +1264,15 @@
|
|
|
1191
1264
|
registry.set(run.headKey, bar);
|
|
1192
1265
|
}
|
|
1193
1266
|
updateBar(bar, run);
|
|
1267
|
+
// A run that is mid-collapse and still GROWING (the model is
|
|
1268
|
+
// streaming the newest calls into it) extends its cascade to the
|
|
1269
|
+
// newly added rows; the finish timer is re-armed by
|
|
1270
|
+
// extendCollapse. Without this the new rows would sit at full
|
|
1271
|
+
// height and then snap shut with the merge.
|
|
1272
|
+
if (bar._ccxCollapseRows !== undefined
|
|
1273
|
+
&& run.rows.length > bar._ccxCollapseRows.length) {
|
|
1274
|
+
extendCollapse(flow, run, bar);
|
|
1275
|
+
}
|
|
1194
1276
|
// The bar is the group HEADER: it stays before the run's first row,
|
|
1195
1277
|
// so on expand the cards cascade downward below it. Guard against
|
|
1196
1278
|
// re-inserting when already in place (insertBefore always mutates).
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-toolfold",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "工具调用与思考的折叠显示:连续工具调用折叠为最后一个调用(可展开),已完成的思考随组折叠(可保留、展开时按原顺序显示),进行中的思考保持独立显示。",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/Minecraftbe/dsh-toolfold.git"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
6
10
|
"main": "lib/index.js",
|
|
7
11
|
"exports": {
|