dsh-streamfold 0.5.7 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,73 @@
1
+ # Changelog
2
+
3
+ 本项目按 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/) 的组织方式记录用户可感知的变化;版本号遵循语义化版本。
4
+
5
+ ## [0.7.1] — 2026-10-04
6
+
7
+ **纯文档修正(无代码变更)**:0.7.0 的 npm 包内 README 仍是发布前的安装段——写着「0.7.0 尚未发布到 npm」,与仓库首页的最终版不一致(发布 commit `029df7e` 之后 50 秒才提交 README 终稿 `9f3206a`)。本版重新发布,让包内 README 与实际发布状态一致。
8
+
9
+ ### 文档
10
+
11
+ - 包内 `README.md` / `README.en.md` 安装段为最终版:**DSH 插件面板为首选方式**(npm `latest` 即本版,含 provenance),命令行三种写法并列;移除已过期的「尚未发布到 npm」警告。
12
+ - 版本号引用不再硬编码具体版本,避免下次发布再次滞后。
13
+ - `lib/` 无任何改动(`lib/client.js` 仍为 `9404096f`)。
14
+
15
+ ## [0.7.0] — 2026-10-04
16
+
17
+ 0.6 → 0.7 的架构重建之后的**首个对外版本**(npm 上此前最新为 0.5.7,0.6 / 0.7 均未发布;本文描述的全部内容都是首次对外)。除架构重建外,本版还并入 2026-10-04 的审计修复批次:输入/卸载语义(P0)、折叠与导航(P1)、性能去重、诊断口径,以及用户点名的「瞬时判据改场景 + 展开也错峰」。
18
+
19
+ ### 行为变化
20
+
21
+ - **展开也错峰,方向与收起相反。** 一次展开多行时**从最上面一组往下**、每 3 行一组、组间 70ms;收起仍是**从最下面一组往上**、每 3 行一组、组间 70ms。收/展共用同一节奏(`FOLD_CHUNK = 3`、`FOLD_CHUNK_MS = 70`)与同一判据。
22
+ - **「瞬时」判据从行数改为场景。** 本批覆盖 **≥2 个轮次**(加载、切会话、全量重排等批量场景)⇒ 瞬时;**单轮一律错峰**,哪怕 40+ 行。单轮只保留时长护栏:`ceil(行数 / 3) × 70ms > 2000ms`(即 ≥85 行)才瞬时。行折叠与行内思考摘要批同判据。
23
+ - **小窗触底吸附收窄到「当前活动的思考窗」。** 只有**本轮 running 的最新思考正文**跟尾/贴底;被取代、已跑完、上一轮的窗一律不再吸附,避免多个小窗争抢主容器滚动。
24
+ - **回到提问处(settle)更可靠。**
25
+ - 滑行途中出现外来写(JS 赋值 / `scrollIntoView` / 官方导航)不再把 element 目标顶成 position,继续滑到提问处;
26
+ - 目标行节点断开时,按「最后一条 user 消息」重试一次,而不是放弃导航停在原地;
27
+ - 到位后开启 **2s 有界观察窗口**(每 2 帧观察、最多纠正 4 次、两次纠正间隔 ≥100ms),纠正图片/代码块/摘要行等**迟到落版**;窗口到点或次数用尽后立即退出,零 rAF / 定时器残留。
28
+ - **嵌套滚动不再被误判为主容器接管。** 在思考小窗、工具正文、上下文注入正文里滚动**不再**让主容器撒手。滚到该嵌套区域的滚动边界后继续滚,才把滚动交给主容器(链式滚动);触摸、无 delta、纯横向 wheel 因方向不可知,维持早退。
29
+ - **不可达目标不再永久卡住。** 导航在 `navBudget(距离) + ≤1.2s` 无进展时由看门狗有界终止(清导航目标并走既有超时清理),随后该轮折叠照常发生。
30
+ - **Ctrl+F 搜索撒手。** 搜索期间主容器与小窗一起交出写权、不贴底;滚回窗尾或点「回到底部」恢复跟随。
31
+ - **会话切换复位。** 提问卡登记跨会话复位;短会话(内容不足一屏)里读者滚轮接管后仍能取回跟随。
32
+ - **「恢复默认」同时把官方档停到「完全展开」**,避免官方自己折叠与本插件折叠叠加成双重折叠。
33
+
34
+ ### 修复
35
+
36
+ - **卸载即净。** `dispose` 现在恢复插件自身的视觉改动:清错峰定时器、还原折叠行行内 `max-height` 与 `hidden="until-found"`、摘掉小窗标记/回底按钮/chip/搜索标记/`html[data-dshsf-anim]`。热重载或真卸载后不再残留 0 高行与隐藏正文。
37
+ - **`dispose` 幂等 + 全局副作用所有权。** 二次调用直接返回;还原实例级 `scrollTop/scrollTo/scrollBy` 与 `Element.prototype.scrollIntoView` 前先确认 `window.__dshStreamfold` 仍属本实例,避免拆掉新实例的吸收器。
38
+ - **元素滑行守卫的零步停机回归。** 守卫分支放行外来写时同时作废方向速度态,修掉「外来写落在目标另一侧 ⇒ 帧循环停摆、协调被永久压住」的回归;另加导航看门狗兜底。
39
+ - **settle 预算与滑行距离同源。** 长滑行的协调预算改为 `max(1400ms, navBudget(剩余距离))`,折叠不再插进滑行中段。
40
+ - **置顶提问文案随行文本刷新。** 缓存失效并入结构观察(MutationObserver 按行定向删除)并以原文兜底比较,等长改写也会刷新,不再永久陈旧。
41
+ - **老轮正文变化后行判定失效。** 结构观察会把变化行上溯到所属轮,删掉文本缓存;热集从「最近 2 轮」扩到「运行中的轮 ∪ 最近 3 轮」。
42
+ - **设置页文案与真实默认同源。** 「跟随速度上限 / 加速度上限」的说明文字从 `DEFAULTS` 插值,修正旧的「默认 480 / 20000」。
43
+ - **短会话取回跟随。** 内容不足一屏(`floor === 0`)时读者滚轮接管后视为贴底并可接回,不再永久停跟。
44
+
45
+ ### 性能
46
+
47
+ - **滑行帧强制布局读下降。** 独立复测(同一 34 帧 element 滑行):`strongReads` 7.76 → **4.76 /帧(−39%)**,`scrollTop` 读 12.71 → **5.59 /帧(−56%)**;到位精度不变(提问行顶 12px,无往返)。另一套装置对 29 帧滑行统计:总强排读 619 → 328(−47%),每帧 21.34 → 11.31。
48
+ - **一次扫描去重。** 行列表与 running 标记扫描内只查一次;卡片计数按行缓存:一次扫描 `querySelector` 153 → **130(−15%)**,`countCards` 的 think+tool 整树查询 48 → **0**,RUNNING 查询 2 → 1。
49
+ - **穿插正文判定去重。** 一次扫描总查询 300 → **263(−12%)**,`assistantBody` 的 `[data-turn-process-inline]` 查询 48 → **12**。
50
+ - **帧路径不再每窗重查回底按钮。** 复用已有登记表:60 帧窗口期该查询 60 → **0**。
51
+ - **观察窗口降频。** 2s 窗口内每 2 帧复算一次目标位置:窗口期 rect 读 119 → **59**;窗口仍保活 120 帧/2000ms,到期后 rAF 与定时器均为 0。
52
+
53
+ ### 诊断
54
+
55
+ - `probe().mainFollow.settle = { turn, until, last }`:当前协调的轮次、预算截止与最后一次清理原因。
56
+ - `probe().mainFollow.abort = { count, last }`:看门狗有界终止的次数与详情(`last.why = "stuck"`,含 `armedAt / at / budgetMs / top`),可与正常的 `lastSettle.why = "timeout"` 区分。
57
+ - **`readCount` 口径校正。** 只统计插件主动发起的显式强排读(`scrollTop / scrollHeight / clientHeight`);滑行帧读数 2 → 5,不再虚报「只读 2 次」。仍轻微低估(约 2.9/帧的 `scrollTop` 读未计入)。
58
+ - trace 首帧 `dt`:0 → 16.67。
59
+ - `tickPhases` 重复键 `pin` 删除;`releaseBy.external` 标注为死字段。
60
+
61
+ ### 0.6 → 0.7 架构重建(概括)
62
+
63
+ - **写权与目标分离**:`writer ∈ {us, reader}`(谁写 `scrollTop`)、`goal ∈ {tail, element, position, none}`(插件目标)。
64
+ - **一个滑行器**:`slide()` 是唯一移动律(指数追赶 + 精确落点),主容器与小窗共用;删除原生 `scrollTo({behavior:"smooth"})`、意图窗、距离门槛、几何回补等旧机制。
65
+ - **settle 有 9 个具名清理点**(session / reader / nav / lost / timeout / done / manual / official / dispose);协调未清时该轮折叠被压后,避免折叠与滑行互相打断。
66
+ - **设置页方案 A**:插件自己的「工作步骤展示」行(可用时以可见性方式隐藏官方那一行、可逆),不再向官方下拉注入任何项。
67
+ - **回尾判据改为「出现新 user 消息」**(`returnToTail`);删除 `autoFollow` / `injectMenu` 两个设置项。
68
+ - **默认值按真机固化**:窗口高度 360、跟随速度上限 240 px/s、加速度上限 10000 px/s²、保留穿插正文开。
69
+ - **自动展开移出扫描帧**:提问卡/思考/工具只登记,合成点击在下一帧 rAF 执行。
70
+ - **折叠内容用 `hidden="until-found"`**,兼顾长会话性能与 Ctrl+F 可搜。
71
+
72
+ [0.7.1]: https://github.com/rezon-aki/dsh-streamfold/releases/tag/v0.7.1
73
+ [0.7.0]: https://github.com/rezon-aki/dsh-streamfold/releases/tag/v0.7.0
package/README.en.md CHANGED
@@ -1,95 +1,111 @@
1
1
  # dsh-streamfold
2
2
 
3
- [简体中文](README.md) · **English**
4
-
5
3
  [![license](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
6
4
  [![format](https://img.shields.io/badge/format-DSH%20bundle-blueviolet.svg)](cordis.patch.yml)
7
- [![tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](test/spark.mjs)
5
+ [![npm](https://img.shields.io/badge/npm-dsh--streamfold-CB3837.svg)](https://www.npmjs.com/package/dsh-streamfold)
6
+
7
+ > A better conversation window: the newest thinking window opens automatically while a turn runs, the other process rows fold into a single summary, and when the turn finishes the thinking and tool calls fold away while interleaved prose stays. Everything advances per frame, like water.
8
8
 
9
- > A better conversation window: **the newest thinking window opens automatically while a turn runs**, older windows fold themselves, and when the turn finishes the thinking and tool calls fold away — interleaved prose can stay unfolded. Water-like motion throughout.
9
+ ## Why it exists
10
10
 
11
- **Why this project exists**:
11
+ In a long conversation, tool calls and reasoning drown out the prose. The official Compact mode also stops folding while a session still has unloaded history, so it cannot help until everything is loaded. This plugin enhances the official transcript and folds on its own terms, independent of whether history has finished loading:
12
12
 
13
- - **Peek at what dsh is thinking right now.** The newest thinking window opens automatically while it works, and the transcript still stays tidy.
14
- - **Motion.** We wanted an interface that simply feels good to look at: window height, folding and scroll-follow all advance per frame, like water — no jumps. The "back to bottom" button keeps the official look, but **when it shows is decided here** — normal follow lag no longer makes it flicker, and clicking it glides back smoothly.
15
- - **Sparks.** Blue sparks rise from the bottom edge of the running thinking window, like a grinding wheel on metal: the faster the content scrolls, the **brighter and denser** they fly (brightness follows scroll speed — dim on slow output, hot on fast), and they stop when it stops. Colour and density are configurable; turning it off means it never runs.
16
- - **Forging.** While the answer streams, every new chunk throws a small spray of sparks from the writing head (the end of the last line), drifting outwards like metal struck on an anvil. Written text only; pause and the hammer stops, and the sparks burn out on their own. Colour, density, speed and lifetime are configurable.
17
- - **Settle on the prompt.** When a turn finishes the view glides back to **the message that started it**, so you never have to scroll up to remember what you asked (skipped if you scrolled away yourself; on by default, can be turned off).
18
- - **Pin the prompt.** The prompt of the turn you are reading stays pinned to the top of the conversation, one line with an ellipsis; it steps aside while the real row is on screen, and **clicking it glides back to that message** (on by default, can be turned off).
13
+ - **Process stops drowning the prose**: past turns collapse into a single "N rows folded · click to expand" line.
14
+ - **What you want to watch stays visible**: while a turn runs, only the newest thinking window stays open — capped height, scrolling inside itself.
15
+ - **No scrolling back after a turn**: when it finishes, the view glides back to the message that started it.
19
16
 
20
- ![Fold mode: only the newest thinking window stays open, older and finished process rows fold into one line](https://github.com/rezon-aki/dsh-streamfold/blob/main/screenshots/01-fold.png?raw=true)
17
+ ## What it looks like once installed
21
18
 
22
- ## Why it is light
19
+ - **You can watch it think.** The newest thinking window of the running turn opens automatically (height configurable); older ones fold back into a one-line summary after a grace period. Superseded windows and past turns no longer fight for the scroll position.
20
+ - **Folding keeps a rhythm and does not flash.** Collapsing runs bottom-up, 3 rows per group with a 70 ms stagger; expanding runs top-down, 3 rows per group. Cross-turn batches (page load, session switch) complete instantly; even a very long single turn advances group by group (only a single turn over 84 rows takes the instant guard rail).
21
+ - **Settle on the prompt.** When a turn finishes, the view glides back to **the message that started it** (skipped if you scrolled away yourself). For 2 seconds after arrival it also corrects late layout shifts from images or code blocks.
22
+ - **Pin the prompt.** The prompt of the turn you are reading stays pinned to the top of the conversation, one line with an ellipsis; it steps aside while the real row is on screen, and clicking it glides back to that message.
23
+ - **Steady follow.** The bottom follow advances per frame; scrolling up stops it, and a "back to bottom" button appears once you are away. Scrolling inside a thinking window or a tool body no longer stops the follow by mistake.
24
+ - **Search is handled too.** Folded content is still findable with Ctrl+F; while searching the plugin releases the scroll and does not pin to the bottom, and returning to the window tail or pressing back-to-bottom resumes the follow.
25
+ - **Window sparks / forging.** Sparks along the bottom edge of the thinking window get brighter and denser with scroll speed; while the answer streams, each new chunk throws a spray of sparks from the writing head. Colour, density, speed and lifetime are configurable, and turning them off means they never run.
23
26
 
24
- - **No separate view.** It enhances the official transcript through semantic attributes instead of shipping its own conversation shell, taking over the render pipeline or depending on internal renderer contracts — a small upgrade surface.
25
- - **No dependencies, no build.** Hand-written `lib/`; no runtime dependencies and no file I/O — the host half is just an entry so the client manifest can hand the browser half to the page, and settings live in browser localStorage.
26
- - **No changes to DSH source; clean uninstall.**
27
+ ![A folded conversation: process rows collapsed into a summary, interleaved prose kept](https://github.com/rezon-aki/dsh-streamfold/blob/main/screenshots/01-fold.png?raw=true)
28
+
29
+ ![A running thinking window with sparks along its bottom edge](https://github.com/rezon-aki/dsh-streamfold/blob/main/screenshots/02-sparks.png?raw=true)
27
30
 
28
31
  ## Install
29
32
 
30
33
  **Option 1: the DSH plugin panel (recommended)** — sidebar → **Plugins** → **Add plugin**, paste the line below, then hit “Enable now”:
31
34
 
32
35
  ```
33
- github:rezon-aki/dsh-streamfold
36
+ dsh-streamfold
34
37
  ```
35
38
 
36
- **Option 2: the command line** (two equivalent forms):
39
+ > The panel installs this version (npm `latest` is this release, with provenance). Option 2 is for tracking the repository's latest code.
40
+
41
+ **Option 2: the command line** (three forms):
37
42
 
38
43
  ```bash
39
- dsh plugin --profile web add github:rezon-aki/dsh-streamfold
44
+ dsh plugin --profile web add dsh-streamfold # npm package (recommended)
45
+ dsh plugin --profile web add github:rezon-aki/dsh-streamfold # track the repository
40
46
  dsh plugin --profile web add https://github.com/rezon-aki/dsh-streamfold
41
47
  ```
42
48
 
43
49
  Restart the profile, then **refresh the browser page** (client code is injected at page load).
44
50
 
45
- Requires DSH **>= 0.1.7-alpha.1** (the official transcript view is now owned by the `configForms` service; for the DSH 0.1.5 series use 0.4.x — v0.4.0 itself declares `>=0.1.5-rc.2`).
51
+ Requires DSH **>= 0.2.0-rc.2** (the official transcript view is owned by the `configForms` service; on the 0.1.7 series use 0.5.0, and on 0.1.5 and earlier use 0.4.x).
46
52
 
47
- > **What changed in 0.5:** settings are no longer written to a host file (`~/.dsh/streamfold.json` and the `/streamfold/api/settings` route are gone) — they live in browser localStorage only, so values you already tuned stay put. The 0.1.7 transcript view now has four modes (Compact/Standard/Detailed/Verbose); this plugin appends its own "Fold" entry to that official dropdown and parks the official mode on Verbose while folding, so the two never fight.
48
-
49
- Uninstall:
53
+ Uninstall: remove it in the plugin panel, or
50
54
 
51
55
  ```bash
52
56
  dsh plugin --profile web remove dsh-streamfold
53
57
  ```
54
58
 
59
+ Restart the profile and the official behaviour is back.
60
+
55
61
  ## Usage
56
62
 
57
- Settings → General → **Work details**: the official four modes stay as they are (**Compact / Standard / Detailed / Verbose**) and a fifth, **Fold**, is appended — the one this plugin owns:
63
+ Settings → General → **Work details**: this row is provided by the plugin (the official row is hidden while ours is available; visibility only, reversible), with the official four modes plus **Fold**:
58
64
 
59
65
  | Option | Behaviour |
60
66
  | --- | --- |
61
67
  | Compact / Standard / Detailed / Verbose | The official modes, with the official labels and behaviour |
62
- | Fold | This plugin: one window for the running turn, everything else folds into one line |
63
-
64
- Choosing **Fold** parks the official mode on **Verbose**, so the official side neither folds nor groups anything and folding is done by this plugin alone (no double folding). The four official labels come from the official dictionary, so they follow the official wording; the fifth has no official entry and uses the label shipped with this plugin (Fold).
65
-
66
- Dedicated settings page: Settings → **Streamfold** (all switches below, applied immediately).
67
-
68
- While running, only the newest thinking window opens automatically; **windows you opened yourself are never auto-collapsed**, and a superseded window folds back into a one-line summary after the "superseded grace" (2s by default). Scrolling up stops the follow, and a "↓ back to bottom" button appears once you are away from the bottom.
69
-
70
- ## Settings
71
-
72
- | Setting | Default | Meaning |
73
- | --- | --- | --- |
74
- | Fold history turns | on | Off: do not fold past turns on page load (only while a turn runs) |
75
- | Keep interleaved prose | off | Keep prose rows while folding, laid out naturally (no capped window; the final answer is never wrapped either) |
76
- | Window height | 260 | Max height of the running thinking window (px) |
77
- | Auto-expand thinking while running | on | Only the newest block of the running turn; superseded windows fold after the grace period |
78
- | Superseded grace | 2 s | How long an older window waits before folding into a summary line (0–60, decimals allowed) |
79
- | Auto-expand tools while running | off | Expand the newest tool card using the official card's own disclosure/scroll |
80
- | Auto-follow the bottom | on | Stick to the bottom while content grows; scrolling up stops it |
81
- | Smoothing factor | 0.15 | Share of the remaining gap consumed per frame (0.05–0.9) |
82
- | Minimum step | 1 | Minimum pixels advanced per 16 ms (refresh-rate independent) |
83
- | Show "back to bottom" | on | Shown when away from the bottom (hidden for follow lag, to avoid flicker) |
84
- | Settle on the prompt | on | When a turn finishes, glide back to your message that started it; if you scrolled away yourself, nothing moves |
85
- | Pin the prompt | on | Keep the prompt of the turn you are reading pinned to the top of the conversation: it follows you as you scroll up, one line with ellipsis; it steps aside while the real row is in view, click to glide back to it |
86
- | Animated transitions | on | Off makes folding / expanding / jumping instant |
87
- | Window sparks | on | Sparks along the bottom edge of the running thinking window (standalone module: off = no canvas, no frames) |
88
- | Spark colour | #4fa8ff | Spark colour; the core is brightened towards white heat |
89
- | Spark density | 1 | Spark count multiplier (0.2–3): denser costs more to draw (shared by both spark kinds) |
90
- | Forging sparks | on | Sparks thrown from the writing head while the answer streams; nothing new written, no hammer |
91
- | Forging spark speed | 1 | Speed multiplier for the thrown sparks (0.2–4) |
92
- | Forging spark life | 1.3 | How long one spark lives (0.2–5 s, randomised by ±30%) |
68
+ | Fold | This plugin: one window for the running turn, everything else folds into one summary |
69
+
70
+ Choosing **Fold** parks the official mode on **Verbose**, so the official side neither folds nor groups anything and folding is done by this plugin alone (no double folding). The labels come from the official dictionary, so they follow the official wording.
71
+
72
+ Dedicated settings page: Settings → **Streamfold** (every switch below lives there and applies immediately).
73
+
74
+ While running, only the newest thinking window opens automatically; **windows you opened yourself are never auto-collapsed**, and a superseded window folds back into a one-line summary after the "superseded grace" (2 s by default). Scrolling up stops the follow, and a "back to bottom" button appears once you are away (the same button serves the thinking windows and the main view).
75
+
76
+ ## Safety
77
+
78
+ - **Client-side presentation only**: it reads the conversation DOM, makes no network requests, touches no credentials and never modifies DSH source.
79
+ - **The host half writes nothing**: no file I/O, no routes, no exported Config; settings live in browser localStorage only (key `dsh-streamfold`, scoped to the page origin).
80
+ - **Official mode changes are reversible**: the transcript view is read and written through the DSH settings service, and while our row is available only the official row is hidden (visibility, reversible).
81
+ - **Clean uninstall**: the settings row, window markers, back-to-bottom button and injected styles are all removed, with no leftover inline `max-height` or hidden attributes.
82
+
83
+ ## Settings (22 items)
84
+
85
+ | Setting | Key | Default | Range | Meaning |
86
+ | --- | --- | --- | --- | --- |
87
+ | Work details | `mode` | Fold | Fold / official four modes | Choosing Fold parks the official mode on Verbose, avoiding double folding |
88
+ | Fold history turns | `foldHistory` | on | toggle | Off: do not fold past turns on page load (only while a turn runs) |
89
+ | Keep interleaved prose | `keepInterleavedText` | on | toggle | Keep prose rows while folding and separate them from the final answer with a rule (no capped window) |
90
+ | Keep LLM questions unfolded | `keepUserQuestions` | on | toggle | The `ask_user_question` card and its reply node never fold; off folds them with everything else |
91
+ | Follow speed cap | `followMaxSpeed` | 240 | 60–2000 px/s | Cap on our own catch-up speed; lower is softer and lags more on fast output. Content growth is not capped |
92
+ | Follow acceleration cap | `followMaxAccel` | 10000 | 2000–120000 px/s² | Cap on speed change; lower is softer at start/stop, higher is more responsive |
93
+ | Window height | `windowHeight` | 360 | 80–1200 px | Max height of a thinking / interleaved-prose window |
94
+ | Auto-expand thinking while running | `autoExpandReasoning` | on | toggle | Only the newest block of the running turn; superseded windows fold after the grace period |
95
+ | Superseded grace | `supersedeDelay` | 2 | 0–60 s | How long an older window waits before folding into a summary line (decimals allowed) |
96
+ | Auto-expand tools while running | `autoExpandTools` | off | toggle | Expand the newest tool card using the official card's own disclosure/scroll |
97
+ | Return to tail on new message | `returnToTail` | on | toggle | While stopped mid-transcript, sending a new message glides back to the bottom; off keeps the view where it is until you scroll to the bottom or press back-to-bottom |
98
+ | Smoothing factor | `smoothGrow` | 0.15 | 0.05–0.9 | Share of the remaining gap consumed per frame |
99
+ | Show "back to bottom" | `showJumpButton` | on | toggle | Shown when away from the bottom (hidden for follow lag, to avoid flicker) |
100
+ | Settle on the prompt | `settleToPrompt` | on | toggle | When a turn finishes, glide back to your message that started it; if you scrolled away yourself, nothing moves |
101
+ | Pin the prompt | `pinPrompt` | on | toggle | Keep the prompt of the turn you are reading pinned to the top: one line with ellipsis, steps aside while the real row is in view, click to glide back |
102
+ | Animated transitions | `animations` | on | toggle | Off makes folding / expanding / jumping instant |
103
+ | Window sparks | `sparks` | on | toggle | Sparks along the bottom edge of the running thinking window (off = no canvas, no frames) |
104
+ | Spark colour | `sparkColor` | `#4fa8ff` | colour | Spark colour; the core is brightened towards white heat |
105
+ | Forging sparks | `forgeSparks` | on | toggle | Sparks thrown from the writing head while the answer streams; nothing new written, no hammer |
106
+ | Forging spark speed | `forgeSpeed` | 1 | 0.2–4 × | Drift speed multiplier |
107
+ | Forging spark life | `forgeLife` | 1.3 | 0.2–5 s | How long one spark lives (±30% random) |
108
+ | Spark density | `sparkDensity` | 1 | 0.2–3 × | Spark count multiplier, shared by both spark kinds |
93
109
 
94
110
  Settings live in browser localStorage, scoped to the DSH page origin.
95
111
 
@@ -99,25 +115,19 @@ Browser console:
99
115
 
100
116
  ```js
101
117
  __dshStreamfold.stats() // window / fold / chip counts (incl. animation state)
102
- __dshStreamfold.probe() // row state, still-visible thinking rows, window animation, jump button, spark cost (probe().sparks)
103
- __dshStreamfold.state() // current settings + official "conversation display" snapshot
118
+ __dshStreamfold.probe() // row state, still-visible thinking rows, window animation, jump button, spark cost, follow detail
119
+ __dshStreamfold.state() // current settings + official "Work details" snapshot
104
120
  __dshStreamfold.set({ smoothGrow: 0.1, supersedeDelay: 3 })
105
121
  ```
106
122
 
107
123
  When reporting an issue, attach the `probe()` output and your DSH version.
108
124
 
109
- ## Safety
110
-
111
- - Client-side presentation only: it reads the conversation DOM, makes no network requests, touches no credentials and never modifies DSH source.
112
- - The host half reads no files and registers no routes: settings stay in browser localStorage, and the official transcript view is read/written through DSH's own settings service (`configForms`).
113
- - Clean uninstall: the settings row, window markers and injected styles are all removed.
114
-
115
- ## Development
125
+ ## Development and verification
116
126
 
117
- - Hand-written, no build: `lib/client.js` (browser half, wrapped in `window.__ModuleLoader__`) and `lib/index.js` (host half).
127
+ - Hand-written, no build: `lib/client.js` (browser half, wrapped in `window.__ModuleLoader__`) and `lib/index.js` (host half, 12 lines).
118
128
  - After editing `lib/*.js`, reload the plugin and **refresh the page**.
119
129
  - Row anchors use official semantic attributes only: `[data-chat-flow]`, `[data-chat-flow-kind]`, `[data-chat-turn]`, `[data-disclosure-row][aria-expanded]`, `[data-variant=think]`, `[data-tool]`, `[data-sample=bash]`, `[class*=_thinkBody]`, `[class*=_bodyWrap]`, `[data-context-injection-body]` — never CSS module hashes.
120
- - Verified on DSH 0.1.7-rc.1 / 0.1.7-rc.2.
130
+ - Tests: `npm test` (`test/spark.mjs` 28 assertions + `test/contract.mjs` + `test/client-apply.mjs`), currently EXIT=0.
121
131
 
122
132
  ## License
123
133
 
package/README.md CHANGED
@@ -1,52 +1,56 @@
1
1
  # dsh-streamfold · 流式折叠
2
2
 
3
- **简体中文** · [English](README.en.md)
4
-
5
3
  [![license](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
6
4
  [![format](https://img.shields.io/badge/format-DSH%20bundle-blueviolet.svg)](cordis.patch.yml)
7
- [![tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](test/spark.mjs)
5
+ [![npm](https://img.shields.io/badge/npm-dsh--streamfold-CB3837.svg)](https://www.npmjs.com/package/dsh-streamfold)
6
+
7
+ > 一个更好的会话窗口:运行中自动展开最新的思考小窗,其它过程行折成一条摘要;一轮跑完,思考与工具调用自动折起,穿插正文保留。全程按帧推进,像水流一样。
8
8
 
9
- > 一个更好的会话窗口:**运行时自动展开最新的思考小窗**,旧小窗自动折叠,跑完自动将思考过程与工具调用折起,并且可以保留穿插的正文不折叠,全程水流般的动效。
9
+ ## 为什么需要它
10
10
 
11
- **为什么有这个项目**:
11
+ 长会话里,工具调用与思考过程会把正文淹掉;官方的「紧凑」档在会话还有未加载历史时会折叠失效,历史加载完之前帮不上忙。本插件在官方对话流上做展示增强,折叠按自己的规则走,不依赖历史是否加载完:
12
12
 
13
- - **顺便「视奸」dsh 现在在想什么。** 运行中自动展开最新的思考小窗——它此刻在想什么你看得见,界面又保持整洁。
14
- - **动效。** 我们要的是一个看着就很舒服的界面:小窗高度、折叠与滚动跟随都按帧推进,水流一样,不跳不顿。右下角那颗「回到底部」外观仍是官方的,但**何时出现由本插件决定**——跟随中的正常落后不会再让它一闪一闪,点它是平滑滑回底部。
15
- - **小窗火花。** 运行中思考小窗底部往上冒火星——像滚筒碾过金属:小窗滚得越快,火星**越亮也越密**(亮度跟着滚动速度走,慢输出暗、快输出烫),停住就不磨了;底边左右两端更密,火星朝窗口内迸。颜色与密度可调,关掉即完全不运行。
16
- - **正文锻打。** 正文流式输出时,每写一段就从写头(最后一行末尾)向四周飘出一扇火星,像溅出来的一小团火。写了才有,停笔就不发,火星烧完自动收手。颜色、密度、速度、寿命都可调。
17
- - **跑完回到提问处。** 一轮结束,视图平滑滑回**你这轮那句话**的位置——不用自己往上翻找回"我当初问的是什么"(你已经自己上滚过就不动你;可关,默认开)。
18
- - **置顶显示提问。** 读到哪一轮,那一轮的提问就钉在会话顶端(一行,超出省略);它自己在屏幕上时自动让位,**点它一下平滑滑回那句发言**(可关,默认开)。
13
+ - **过程不再淹没正文**:非运行轮次收成一条「已折叠 N 行 · 点击展开」;
14
+ - **想看的随时看得见**:运行中只留最新那一个思考窗,限高、内部自滚;
15
+ - **读完一轮不用往回翻**:跑完自动回到你这轮的提问处。
19
16
 
20
- ![折叠模式:运行中只留一个思考小窗,旧窗与跑完的过程收成一行](https://github.com/rezon-aki/dsh-streamfold/blob/main/screenshots/01-fold.png?raw=true)
17
+ ## 装完是什么样
21
18
 
22
- ## 它轻在哪
19
+ - **运行中看得到思考。** 自动展开本轮最新的思考窗(限高可调),旧的按宽限折回一行摘要;被取代的、上一轮的窗不再抢滚动。
20
+ - **折叠有节奏,不闪。** 收起从下往上、一组 3 行、组间 70ms;展开从最上面往下、同样一组 3 行。加载页面、切会话这类**跨轮批量**场景直接瞬时完成;单轮再长也逐组推进(只有单轮超过 84 行才走瞬时护栏)。
21
+ - **跑完回到提问处。** 一轮结束,视图平滑滑回**你这轮那句话**的位置(你自己上滚过就不动你)。到位后 2 秒内还会纠正图片/代码块迟到落版造成的偏移。
22
+ - **置顶显示提问。** 读到哪一轮,那一轮的提问就钉在会话顶端(一行,超出省略);它自己在屏幕上时自动让位,点它滑回那句发言。
23
+ - **跟随够稳。** 底部跟随按帧推进;上滚即停跟,离底出现「回到底部」;在思考小窗或工具正文里滚动不会误触发「停止跟随」。
24
+ - **搜索也照顾。** 折叠的内容仍可被 Ctrl+F 搜到;搜索期间不抢滚动,滚回窗尾或点回底恢复。
25
+ - **小窗火花 / 正文锻打。** 思考窗底部的火星随滚动速度变亮变密;正文流式写头每写一段砸出一扇火星。颜色、密度、速度、寿命都可调,关掉即完全不运行。
23
26
 
24
- - **不另开视图。** 直接在官方对话流上做 DOM 增强(靠语义属性定位行),不自己实现会话壳、不接管渲染管线、不依赖内部渲染器契约——升级面小。
25
- - **零依赖、零构建。** 手写 `lib/`,没有构建步骤、没有运行时依赖,也不读写任何文件:宿主半区只是一个占位条目(客户端清单据它把浏览器半区交给页面),设置存在浏览器 localStorage。
26
- - **不碰官方源码,卸载即净。**
27
+ ![折叠后的会话:过程行收成一条摘要,穿插正文保留](https://github.com/rezon-aki/dsh-streamfold/blob/main/screenshots/01-fold.png?raw=true)
28
+
29
+ ![运行中的思考窗口与底部火花](https://github.com/rezon-aki/dsh-streamfold/blob/main/screenshots/02-sparks.png?raw=true)
27
30
 
28
31
  ## 安装
29
32
 
30
- **方式一:DSH 插件面板(推荐)** —— 侧栏 →「**插件**」→「**添加插件**」,粘贴下面这行,装完点「立即启用」:
33
+ **方式一:DSH 插件面板(推荐)** —— 侧栏 →「**插件**」→「**添加插件**」,粘贴下面这一行,装完点「立即启用」:
31
34
 
32
35
  ```
33
- github:rezon-aki/dsh-streamfold
36
+ dsh-streamfold
34
37
  ```
35
38
 
36
- **方式二:命令行**(两种等价写法):
39
+ > 面板装的就是本版(npm 上 `latest` 即本版,含 provenance)。方式二适合想跟仓库最新代码的人。
40
+
41
+ **方式二:命令行**(三种写法):
37
42
 
38
43
  ```bash
39
- dsh plugin --profile web add github:rezon-aki/dsh-streamfold
44
+ dsh plugin --profile web add dsh-streamfold # npm 包(推荐)
45
+ dsh plugin --profile web add github:rezon-aki/dsh-streamfold # 跟仓库最新
40
46
  dsh plugin --profile web add https://github.com/rezon-aki/dsh-streamfold
41
47
  ```
42
48
 
43
- 装完重启 profile,然后**刷新浏览器页面**(客户端代码在页面加载时注入)。
49
+ 装完**重启 profile**,然后**刷新浏览器页面**(客户端代码在页面加载时注入)。
44
50
 
45
- 要求 DSH **>= 0.1.7-alpha.1**(官方「对话显示」档位改由 `configForms` 服务托管;DSH 0.1.5 系列请用 0.4.x —— v0.4.0 自己声明的是 `>=0.1.5-rc.2`)。
51
+ 要求 DSH **>= 0.2.0-rc.2**(官方「对话显示」档位由 `configForms` 服务托管;0.1.7 系列请用 0.5.0,0.1.5 及更早请用 0.4.x)。
46
52
 
47
- > **0.4 → 0.5 的变化**:设置不再写宿主文件(`~/.dsh/streamfold.json` 与 `/streamfold/api/settings` 路由已移除),改为只存浏览器 localStorage——你在页面上已经调好的值不受影响;0.1.7 把「对话显示」换成四档(简洁/标准/详细/完全展开),本插件把「折叠」作为第五项接在官方下拉里,选它时官方自动停在「完全展开」,两层折叠不会打架。
48
-
49
- 卸载:
53
+ 卸载:在插件面板里移除,或
50
54
 
51
55
  ```bash
52
56
  dsh plugin --profile web remove dsh-streamfold
@@ -56,42 +60,52 @@ dsh plugin --profile web remove dsh-streamfold
56
60
 
57
61
  ## 用法
58
62
 
59
- 设置 → 通用设置 → **工作步骤展示**:官方那四档原样保留(**简洁 / 标准 / 详细 / 完全展开**),末尾多一项「**折叠**」——本插件接管的那一档:
63
+ 设置 → 通用设置 → **工作步骤展示**:这一行由本插件提供(官方那一行在本插件的设置行可用时隐藏,只改可见性、可逆),里面是官方四档 + 本插件的「**折叠**」:
60
64
 
61
65
  | 选项 | 行为 |
62
66
  | --- | --- |
63
67
  | 简洁 / 标准 / 详细 / 完全展开 | 官方原本的四档,文案与行为都跟官方一致 |
64
- | 折叠 | 本插件:运行中的一轮只留一个窗,其余过程收成一行 |
65
-
66
- 选「折叠」时,官方那一档会被自动停在 **完全展开**(等价于 0.1.5 时代的「标准」语义)——官方自己不折、不分组,折叠只由本插件做,避免两层折叠打架;官方四档的档位名取自官方词典(官方改了文案这边跟着变);「折叠」官方没有对应词条,用本插件自带的标签(中文「折叠」/ 英文 Fold)。
67
-
68
- 专属设置页:设置 → **流式折叠**(下面所有开关都在这里,改动即时生效)。
69
-
70
- 运行中只会自动展开最新那一个思考窗;**你自己点开的窗不会被自动收起**,被取代的旧窗按「旧窗折叠宽限」(默认 2 秒)折回一行摘要;上滚即停跟,离底时出现回底按钮(箭头图标;小窗与主窗口是同一个按钮)。
71
-
72
- ## 设置
73
-
74
- | 设置项 | 默认 | 说明 |
75
- | --- | --- | --- |
76
- | 折叠历史轮次 | 开 | 关掉后:加载页面不再自动折叠历史轮次(只在跑动时折前面的) |
77
- | 保留穿插正文 | 关 | 折叠时保留带正文的行,直接自然排版(不套窗;正式回答本身也不套窗) |
78
- | 窗口高度 | 260 | 运行中思考小窗的最大高度(px) |
79
- | 运行中自动展开思考 | 开 | 只自动展开本轮最新的那一个;被取代的旧窗按「旧窗折叠宽限」折回一行摘要 |
80
- | 旧窗折叠宽限 | 2 秒 | 新的思考窗出现后,旧窗再等多久才自动折回(0~60,支持小数) |
81
- | 运行中自动展开工具 | 关 | 开:运行中最新的工具卡自动展开(用官方卡片自己的折叠/内滚,不套限高小窗) |
82
- | 触底自动跟随 | 开 | 内容增长时自动贴底;你上滚即停跟 |
83
- | 平滑系数 | 0.15 | 跟随时每帧吃掉多少差距,越小越柔(0.05~0.9) |
84
- | 平滑最小步长 | 1 | 每 16ms 至少推进多少像素(与屏幕刷新率无关) |
85
- | 离底显示「回到底部」 | 开 | 离开底部时出现回底按钮(跟随中的轻微落后不显示,避免闪烁) |
86
- | 跑完回到提问处 | 开 | 一轮结束后平滑移到本轮你那句话的位置;你已经自己上滚过就不动你(动效关掉时直接到位) |
87
- | 置顶显示提问 | 开 | 把「你正在看的那一轮」的提问钉在会话顶端:往上滚会跟着换成那一轮,超过一行只显示一行;它自己在视口里时自动让位,点它滑回那句话 |
88
- | 动效过渡 | 开 | 关掉则折叠/展开/回底全部瞬时 |
89
- | 小窗火花 | 开 | 运行中思考小窗底部的火星(独立模块:关掉后不建画布、不排帧) |
90
- | 火花颜色 | #4fa8ff | 火星颜色;核心自动提亮成白热 |
91
- | 火花密度 | 1 | 火星数量倍率(0.2~3),越高越密、绘制开销越大(两种火花共用) |
92
- | 正文锻打火花 | 开 | 正文流式写头砸出的火星;写头不动就不砸,跑完自动收手 |
93
- | 锻打火花速度 | 1 | 火星飘出去的速度倍率(0.2~4) |
94
- | 锻打火花寿命 | 1.3 | 单颗火星活多久(0.2~5 秒,实际在 ±30% 内随机) |
68
+ | 折叠 | 本插件:运行中的一轮只留一个窗,其余过程收成一条摘要 |
69
+
70
+ 选「折叠」时,官方那一档会被自动停在 **完全展开**——官方自己不折、不分组,折叠只由本插件做,避免两层折叠打架;档位名与官方设置页同源(取自官方词典),官方改了文案这边跟着变。
71
+
72
+ 专属设置页:设置 → **流式折叠**(本文所有开关都在这里,改动即时生效)。
73
+
74
+ 运行中只会自动展开最新那一个思考窗;**你自己点开的窗不会被自动收起**,被取代的旧窗按「旧窗折叠宽限」(默认 2 秒)折回一行摘要;上滚即停跟,离底时出现回底按钮(小窗与主窗口是同一个按钮)。
75
+
76
+ ## 安全边界
77
+
78
+ - **纯客户端展示增强**:只读对话 DOM,不发网络请求、不接触凭据、不改官方源码。
79
+ - **宿主半区不落任何东西**:不读写文件、不注册路由、不导出 Config;设置只存浏览器 localStorage(key `dsh-streamfold`,按页面源站隔离)。
80
+ - **对官方档位只做可逆改动**:档位读写走 DSH 自己的设置服务;本插件的设置行可用时只隐藏官方那一行(可见性、可逆)。
81
+ - **卸载即净**:设置行、小窗标记、回底按钮与注入样式全部撤掉,不留行内 `max-height` 与隐藏属性。
82
+
83
+ ## 配置(22 项)
84
+
85
+ | 设置项 | 键 | 默认 | 取值/范围 | 说明 |
86
+ | --- | --- | --- | --- | --- |
87
+ | 工作步骤展示 | `mode` | 折叠 | 折叠 / 官方四档 | 选「折叠」时官方档自动停到「完全展开」,避免两层折叠 |
88
+ | 折叠历史轮次 | `foldHistory` | 开 | 开关 | 关掉后:加载页面不再自动折叠历史轮次(只在跑动时折前面的) |
89
+ | 保留穿插正文 | `keepInterleavedText` | 开 | 开关 | 折叠时保留带正文的行,用一条横线与正式回答分开(正文不套限高小窗) |
90
+ | 提问不入折叠 | `keepUserQuestions` | 开 | 开关 | LLM 的提问工具(`ask_user_question`)与其回复节点始终保留、不折进摘要;关掉则随其余内容一起折 |
91
+ | 跟随速度上限 | `followMaxSpeed` | 240 | 60~2000 px/s | 主动追赶时的速度上限;越小越柔、高速输出时落后越多。内容自身增长的速度不受它限制 |
92
+ | 跟随加速度上限 | `followMaxAccel` | 10000 | 2000~120000 px/s² | 速度变化的上限;调小起步/刹车更柔,调大更跟手 |
93
+ | 窗口高度 | `windowHeight` | 360 | 80~1200 px | 单个思考/穿插正文小窗的最大高度 |
94
+ | 运行中自动展开思考 | `autoExpandReasoning` | 开 | 开关 | 只自动展开本轮最新的那一个;被取代的旧窗按「旧窗折叠宽限」折回一行摘要 |
95
+ | 旧窗折叠宽限 | `supersedeDelay` | 2 | 0~60 秒 | 新的思考窗出现后,旧窗再等多久才自动折回(支持小数) |
96
+ | 运行中自动展开工具 | `autoExpandTools` | 关 | 开关 | 开:运行中最新的工具卡自动展开(用官方卡片自己的折叠/内滚,不套限高小窗) |
97
+ | 发消息回到尾部 | `returnToTail` | 开 | 开关 | 你停在中途(看过置顶、跳转过轮次)时,自己再发一条消息 ⇒ 视图滑回尾部;关掉后一律停在原地,只有滚到底或点「回到底部」才恢复跟随 |
98
+ | 平滑系数 | `smoothGrow` | 0.15 | 0.05~0.9 | 跟随时每帧吃掉多少差距,越小越柔 |
99
+ | 离底显示「回到底部」 | `showJumpButton` | 开 | 开关 | 离开底部时出现回底按钮(跟随中的轻微落后不显示,避免闪烁) |
100
+ | 跑完回到提问处 | `settleToPrompt` | 开 | 开关 | 一轮结束后平滑移到本轮你那句话的位置;你已经自己上滚过就不动你(动效关掉时直接到位) |
101
+ | 置顶显示提问 | `pinPrompt` | 开 | 开关 | 把「你正在看的那一轮」的提问钉在会话顶端:往上滚会跟着换成那一轮,超过一行只显示一行;它自己在视口里时自动让位,点它滑回那句话 |
102
+ | 动效过渡 | `animations` | 开 | 开关 | 关掉则折叠/展开/回底全部瞬时 |
103
+ | 小窗火花 | `sparks` | 开 | 开关 | 运行中思考小窗底部的火星(关掉后不建画布、不排帧) |
104
+ | 火花颜色 | `sparkColor` | `#4fa8ff` | 颜色 | 火星颜色;核心自动提亮成白热 |
105
+ | 正文锻打火花 | `forgeSparks` | 开 | 开关 | 正文流式写头砸出的火星;写头不动就不砸,跑完自动收手 |
106
+ | 锻打火花速度 | `forgeSpeed` | 1 | 0.2~4 × | 火星飘出去的速度倍率 |
107
+ | 锻打火花寿命 | `forgeLife` | 1.3 | 0.2~5 秒 | 单颗火星活多久(实际在 ±30% 内随机) |
108
+ | 火花密度 | `sparkDensity` | 1 | 0.2~3 × | 火星数量倍率,两种火花共用;越高越密、绘制开销越大 |
95
109
 
96
110
  设置存在浏览器 localStorage(按 DSH 页面源站隔离)。
97
111
 
@@ -101,25 +115,19 @@ dsh plugin --profile web remove dsh-streamfold
101
115
 
102
116
  ```js
103
117
  __dshStreamfold.stats() // 窗口 / 折叠 / 折叠条计数(含动效状态)
104
- __dshStreamfold.probe() // 行状态、还看得见的思考行、小窗动画、回底按钮状态、火星开销(probe().sparks)
105
- __dshStreamfold.state() // 当前设置 + 官方「对话显示」取值快照
118
+ __dshStreamfold.probe() // 行状态、还看得见的思考行、小窗动画、回底按钮、火花开销、跟随明细
119
+ __dshStreamfold.state() // 当前设置 + 官方「工作步骤展示」取值快照
106
120
  __dshStreamfold.set({ smoothGrow: 0.1, supersedeDelay: 3 })
107
121
  ```
108
122
 
109
123
  报问题时附上 `probe()` 的输出与 DSH 版本,定位会快很多。
110
124
 
111
- ## 安全边界
112
-
113
- - 纯客户端展示增强:只读对话 DOM,不发网络请求、不接触凭据、不改官方源码。
114
- - 宿主半区不读写文件、不注册路由:设置只落浏览器 localStorage;对官方「对话显示」档位的读写走 DSH 自己的设置服务(`configForms`)。
115
- - 卸载即净:设置行、小窗标记与注入的样式全部撤掉。
116
-
117
- ## 开发
125
+ ## 开发与验收
118
126
 
119
- - 手写、无构建:`lib/client.js`(浏览器半区,包在 `window.__ModuleLoader__` 里)、`lib/index.js`(宿主半区)。
120
- - 改完 `lib/*.js` 重载插件并**刷新页面**。
127
+ - 手写、无构建:`lib/client.js`(浏览器半区,包在 `window.__ModuleLoader__` 里)、`lib/index.js`(宿主半区,12 行占位)。
128
+ - 改完 `lib/*.js` 后重载插件,并**刷新浏览器页面**。
121
129
  - 行锚点全部用官方语义属性:`[data-chat-flow]`、`[data-chat-flow-kind]`、`[data-chat-turn]`、`[data-disclosure-row][aria-expanded]`、`[data-variant=think]`、`[data-tool]`、`[data-sample=bash]`、`[class*=_thinkBody]`、`[class*=_bodyWrap]`、`[data-context-injection-body]`——不用 CSS module 哈希。
122
- - 已在 DSH 0.1.7-rc.1 / 0.1.7-rc.2 上验证。
130
+ - 测试:`npm test`(`test/spark.mjs` 28 断言 + `test/contract.mjs` + `test/client-apply.mjs`),当前 EXIT=0。
123
131
 
124
132
  ## License
125
133