dsh-smooth-stream 0.4.1 → 0.4.2

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 CHANGED
@@ -1,12 +1,30 @@
1
- # dsh-smooth-stream
1
+ # dsh-smooth-stream — a silky-smooth streaming renderer
2
+
3
+ > No more frame jumps in AI streaming output. **Silky streaming · level-free scrolling · 0-reflow follow · 0-jank reveal** — a streaming rendering plugin for the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`) Web UI.
2
4
 
3
5
  English | [中文](README.md)
4
6
 
5
- **dsh-smooth-stream** brings fluid streaming rendering and silky scrolling to the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`) Web UI. Text, Markdown, code blocks, tables, and tool results appear as the reply arrives; as the content grows, the page follows along in one continuous visual rhythm.
7
+ Model output arrives in bursts: one chunk can deliver hundreds of characters within a few milliseconds, the next one 200 ms later. Bind the renderer directly to that stream and you see the two classic failures — **text pops out in whole paragraphs, or every frame relayouts and the page judders**. dsh-smooth-stream splits rendering and scrolling into two engines, each maintains continuous state integrated once per frame, and a single ref couples them: **no step is ever discrete; everything evolves continuously frame by frame**.
6
8
 
7
9
  Project homepage: <https://laplace-bit.github.io/dsh-smooth-stream/>
8
10
 
9
- [Install guide](https://laplace-bit.github.io/dsh-smooth-stream/install.html) · [How it works and reproducible benchmark](https://laplace-bit.github.io/dsh-smooth-stream/how-it-works.html)
11
+ [Install guide](https://laplace-bit.github.io/dsh-smooth-stream/install.html) · [How it works & reproducible benchmark](https://laplace-bit.github.io/dsh-smooth-stream/how-it-works.html) · [npm](https://www.npmjs.com/package/dsh-smooth-stream)
12
+
13
+ ## Why it feels smooth: three zeros
14
+
15
+ - **0-reflow follow.** The follow path writes only `transform` (compositor layer) with `will-change` prepared up front — it never writes `top/left/width` frame by frame. Layout stays out of the loop, so the scroll compensation never triggers a reflow storm. That is why long replies stay smooth and keep following the tail.
16
+ - **0-jank reveal and wrap.** Reveal speed adapts to queue pressure — measured for slow output, catching up with fast output — and any single-frame visual displacement from a wrap or new block is capped at **≤8px** (`FOLLOW_PAINT_SHIFT_MAX_STEP_PX`). Even high-speed output never jumps.
17
+ - **0-burst completion.** When a turn finishes, the queue drains at one fixed velocity: the ending does not "dump", it does not grab an extra frame, and body / reasoning / tool output hand off cleanly.
18
+
19
+ The animation path runs entirely on the compositor (`transform`/`opacity`), so the main thread is never repainting every frame; the experience also yields automatically to `prefers-reduced-motion` and low frame rates (<30 fps).
20
+
21
+ ## Level-free scrolling: position is integrated, not snapped
22
+
23
+ The default approach hammers `scrollTop` to the bottom on every growth event — one hard jump per write; even `scroll-behavior: smooth` restarts its easing curve on every write, so the easing never finishes and a fast stream reads like a stuttering slow-motion. dsh-smooth-stream instead is **level-free**:
24
+
25
+ - The **reveal engine** decides "how much to reveal this frame" from backlog pressure, carrying fractional character debt between frames.
26
+ - The **follow engine** holds the current height and the rendered position in a damped spring (**stiffness/damping/mass ≈ 130/24/1**) integrated every frame; wraps, code blocks, tables, and tool results all feed one continuous trajectory.
27
+ - After a long main-thread stall, physical time is clamped instead of replaying the whole missed interval in a single paint — so the settle time is nearly identical on 60 Hz and 120 Hz: **refresh-rate-independent feel**.
10
28
 
11
29
  ## Preview
12
30
 
@@ -17,10 +35,23 @@ Left: default Web UI. Right: dsh-smooth-stream.
17
35
  ## Core experience
18
36
 
19
37
  - **Fluid rendering.** Text appears as it arrives while Markdown structure stays active, keeping headings, lists, code blocks, and tables readable throughout the stream.
20
- - **Silky scrolling.** As the content grows, the page follows along one continuous scroll path and keeps the reader close to the generated output.
38
+ - **Silky scrolling.** As the content grows, the page follows one continuous scroll path and keeps the reader close to the generated output.
21
39
  - **Consistent transitions.** Line wraps, code blocks, tables, and tool results use the same motion treatment, so text, reasoning, and tools flow together.
22
40
  - **Adaptive cadence.** Reveal speed responds to arrival rate and pending content, staying measured for slow output and catching up with fast output.
23
41
 
42
+ ## Reproducible benchmark
43
+
44
+ "Silky" is not a marketing adjective here; it is a number you can reproduce. The repo ships a browser-level audit harness with gates that run locally (`run-render-audit`, `verify-overflow`, `probe-tailbob`). Full green before every release:
45
+
46
+ | Gate | Result |
47
+ | --- | --- |
48
+ | Render audit `run-render-audit` (5 streaming scenarios) | **10/10 clean**, zero movement regression |
49
+ | Overflow gate `verify-overflow` | **3/3** no over-scroll, no rebound |
50
+ | Tail smoothing `probe-tailbob` | single-frame ≤30px, 7-frame amplitude ≤32px **PASS** |
51
+ | Inline-code wrap `probe:overlap` | Issue #13 fixture across static/reveal/engine arms **PASS** |
52
+
53
+ Core ESM output gzip ≈ **4.7 kB**. Details in [How it works & benchmark](https://laplace-bit.github.io/dsh-smooth-stream/how-it-works.html).
54
+
24
55
  ## Install
25
56
 
26
57
  From a DeepSeek Harness source checkout:
@@ -65,12 +96,12 @@ In the Web UI, open **Settings → Plugins → Plugin configuration** to find a
65
96
 
66
97
  - **Enable smooth streaming** (on by default): lets this plugin own reply and tool-row rendering and follow. Turn it off to return rendering completely to the built-in Harness UI.
67
98
  - **Auto-expand thinking**: controls whether reasoning opens while it streams. This preference has no effect while the master toggle is off.
68
- - **Collapse finished work** (on by default): once a turn finishes processing, its thinking, tool calls, context injection, and intermediate output fold behind one “已处理 X秒 / Processed” summary row so only the final answer shows. Click the summary any time to expand or re-collapse the full process.
99
+ - **Collapse finished work** (on by default): once a turn finishes processing, its thinking, tool calls, context injection, and intermediate output fold behind one "Processed" summary row so only the final answer shows. Click the summary any time to expand or re-collapse the full process.
69
100
  - **Show render diagnostics** (off by default): opens a chat-side panel with live rendering, frame-rate, and scroll-follow measurements, plus controls for reveal and spring behavior.
70
101
 
71
- With “Auto-expand thinking” on, reasoning blocks open while streaming and collapse when thinking ends. With it off, reasoning stays collapsed; you can still open a block by hand, and the stream state will not wrestle it back.
102
+ With "Auto-expand thinking" on, reasoning blocks open while streaming and collapse when thinking ends. With it off, reasoning stays collapsed; you can still open a block by hand, and the stream state will not wrestle it back.
72
103
 
73
- “Collapse finished work” never interferes while a reply streams — output expands live, and folding happens only after the turn settles with all work complete. The switch works independently of “Enable smooth streaming”: folding applies whether this plugin or the built-in renderer owns the conversation. Plain replies without thinking or tools get no summary row. Content embedded by other plugins through fully custom tool views (such as `dsh-pianist`'s piano card) is never folded — only calls rendered as native tool cards participate. This feature supersedes the standalone `dsh-auto-collapse` plugin; do not run both at once, or they will fight over the same DOM nodes and overlap text.
104
+ "Collapse finished work" never interferes while a reply streams — output expands live, and folding happens only after the turn settles with all work complete. The switch works independently of "Enable smooth streaming": folding applies whether this plugin or the built-in renderer owns the conversation. Plain replies without thinking or tools get no summary row. Content embedded by other plugins through fully custom tool views (such as `dsh-pianist`'s piano card) is never folded — only calls rendered as native tool cards participate. This feature supersedes the standalone `dsh-auto-collapse` plugin; do not run both at once, or they will fight over the same DOM nodes and overlap text.
74
105
 
75
106
  These are durable, user-level preferences that apply live without a restart, and are written to the DeepSeek Harness user-settings document rather than the plugin's composed configuration.
76
107
 
@@ -94,6 +125,9 @@ dsh plugin --profile web update dsh-smooth-stream
94
125
  **Is this an official DeepSeek plugin?**
95
126
  No. It is independently maintained, MIT-licensed software for the DeepSeek Harness (`dsh`) Web UI and is not affiliated with DeepSeek.
96
127
 
128
+ **Is "0 reflow" literal?**
129
+ It refers to the animation path. The follow writes only a compositor `transform` and never reads or writes layout properties, so the follow phase causes no reflow/repaint storm; text reveal still writes character increments (the only layout that new content genuinely needs), but reveal cadence is capped so single-frame visual displacement stays ≤8px with no jumps.
130
+
97
131
  **How do I install a DeepSeek Harness plugin?**
98
132
  Use the built-in plugin command: `dsh plugin --profile web add dsh-smooth-stream` from a dsh source checkout (see [Install](#install)).
99
133
 
package/README.md CHANGED
@@ -1,12 +1,30 @@
1
- # dsh-smooth-stream
1
+ # dsh-smooth-stream — 丝滑流式渲染引擎
2
+
3
+ > AI 流式输出不再跳帧。**丝滑流式 · 无级滚动 · 0 重排跟随 · 0 跳变** —— 为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)Web UI 打造的流式渲染插件。
2
4
 
3
5
  [English](README.en.md) | 中文
4
6
 
5
- **dsh-smooth-stream** 为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)Web UI 带来流畅的流式渲染和丝滑滚动。文字、Markdown、代码块、表格以及工具结果会随着输出自然呈现;内容逐步增长时,页面平稳跟随,整轮回复保持连贯的视觉节奏。
7
+ 大模型输出是分块到达的:一个块可能在几毫秒内带来几百个字符,下一个块 200ms 后才来。把渲染直接绑在这条流上,屏幕上的表现就是两种失败——**要么整段文字猛地冒出来,要么每一帧都在重排、页面跟着抖**。dsh-smooth-stream 把「渲染」和「滚动」拆成两个独立的引擎,各自维护一段连续状态、每帧积分一次,再用一个 ref 把它们耦合起来:**没有任何一步是跳变的,一切都在逐帧连续演化**。
6
8
 
7
9
  项目主页:<https://laplace-bit.github.io/dsh-smooth-stream/>
8
10
 
9
- [安装指南](https://laplace-bit.github.io/dsh-smooth-stream/install.html) · [工作原理与可复现基准](https://laplace-bit.github.io/dsh-smooth-stream/how-it-works.html)
11
+ [安装指南](https://laplace-bit.github.io/dsh-smooth-stream/install.html) · [工作原理与可复现基准](https://laplace-bit.github.io/dsh-smooth-stream/how-it-works.html) · [npm](https://www.npmjs.com/package/dsh-smooth-stream)
12
+
13
+ ## 为什么丝滑:三个「0」
14
+
15
+ - **0 重排的滚动跟随。** 跟随路径只写 `transform`(合成器合成层)并预置 `will-change`,从不逐帧写 `top/left/width`。布局零参与,滚动补偿不会引发重排风暴——这也正是「越滚越跟手、长回复不抖」的原因。
16
+ - **0 跳变的揭示与换行。** 揭示速度按队列积压自适应,慢速输出从容呈现、快速输出及时追上;换行和新块的单帧视觉位移被限幅到 ≤8px(`FOLLOW_PAINT_SHIFT_MAX_STEP_PX`),高速输出同样无跳帧。
17
+ - **0 突进的完成收敛。** 回合结束时以固定速度匀速倾倒队列,结尾「不倾泻」、不多抓一帧,正文与思考/工具输出衔接干净。
18
+
19
+ 动画路径全程走合成器(`transform`/`opacity`),不触发主线程逐帧重绘;阅读体验对 `prefers-reduced-motion` 与低帧率(<30 fps)自动让步。
20
+
21
+ ## 无级滚动:位置是「积分」出来的,不是「拨」出来的
22
+
23
+ 在默认实现里,每次内容变高就 `scrollTop` 拨到底——一次硬跳;哪怕用 `scroll-behavior: smooth`,浏览器也会对每次写入重启一次平滑动画,缓动曲线永远跑不完,高速流看起来像卡顿的慢放。dsh-smooth-stream 的解法是**无级(level-free)**:
24
+
25
+ - **揭示引擎**负责「每帧呈现多少新内容」,用积压压力调节速度,携带跨帧的小数字符债务。
26
+ - **跟随引擎**用一个阻尼弹簧(stiffness/damping/mass ≈ **130/24/1**)持有当前高度与现实位移,逐帧积分;换行、代码块、表格、工具结果全都喂进同一条连续轨迹。
27
+ - 主线程卡顿后物理时间被 clamp,而不是把错过的一整段间隔在一帧里补完——所以 60Hz 与 120Hz 下收束时间几乎一致,**帧率无关的手感**。
10
28
 
11
29
  ## 效果
12
30
 
@@ -21,6 +39,19 @@
21
39
  - **统一过渡。** 换行、代码块、表格和工具结果使用一致的过渡方式,正文、思考过程与工具输出衔接自然。
22
40
  - **自适应节奏。** 渲染速度会根据输出速度和待显示内容调整,让慢速输出从容呈现,快速输出及时跟上。
23
41
 
42
+ ## 可复现基准
43
+
44
+ 「丝滑」不是形容词,是可以用脚本复现的数值:仓库自带浏览器级审计台架,所有闸门本地可跑(`run-render-audit`、`verify-overflow`、`probe-tailbob`)。发布前全量绿灯:
45
+
46
+ | 闸门 | 结果 |
47
+ | --- | --- |
48
+ | 渲染审计 `run-render-audit`(5 种流式场景) | **10/10 clean**,零位移突变、零回归 |
49
+ | 溢出闸门 `verify-overflow` | **3/3** 无过滚、无回弹 |
50
+ | 尾行平滑 `probe-tailbob` | 单帧位移 ≤30px、7 帧振幅 ≤32px **PASS** |
51
+ | 行内代码折行 `probe:overlap` | Issue #13 fixture,静态/揭示/引擎三臂 **PASS** |
52
+
53
+ 核心 ESM 产物 gzip 约 **4.7 kB**。详见[工作原理与基准](https://laplace-bit.github.io/dsh-smooth-stream/how-it-works.html)。
54
+
24
55
  ## 安装
25
56
 
26
57
  在 DeepSeek Harness 源码仓库里:
@@ -65,12 +96,12 @@ Host 日志里应出现 `[dsh-smooth-stream] plugin loaded!`。
65
96
 
66
97
  - **启用丝滑流式渲染**(默认开启):开启时由本插件接管回复和工具行的渲染与跟随;关闭后会撤销接管,完整使用 Harness 内置渲染。
67
98
  - **自动展开思考**:控制思考块在流式期间是否自动展开。主开关关闭时此选项不会生效。
68
- - **完成后自动折叠**(默认开启):回合处理完成后,把思考过程、工具调用、上下文注入和中间输出折叠为一行“已处理 X秒”摘要,只展示最终回复;点击摘要可随时展开或收起完整工作过程。
99
+ - **完成后自动折叠**(默认开启):回合处理完成后,把思考过程、工具调用、上下文注入和中间输出折叠为一行「已处理 X秒」摘要,只展示最终回复;点击摘要可随时展开或收起完整工作过程。
69
100
  - **显示渲染调试面板**(默认关闭):在聊天页右侧显示实时渲染、帧率和滚动跟随数据,并开放流式揭示与滚动弹簧参数。
70
101
 
71
- “自动展开思考”开启时,思考块会在流式时自动展开、思考结束后收起;关闭后思考块保持折叠,仍可手动点开,且不会被流式状态抢回控制。
102
+ 「自动展开思考」开启时,思考块会在流式时自动展开、思考结束后收起;关闭后思考块保持折叠,仍可手动点开,且不会被流式状态抢回控制。
72
103
 
73
- “完成后自动折叠”在流式期间不干预——回复实时展开输出;只有回合结束(出现结束标记且全部工作完成)才折叠。该开关独立于“启用丝滑流式渲染”:无论由本插件还是内置渲染器负责对话,折叠都照常工作。纯文本回复(无思考、无工具)不会生成摘要行。其他插件通过自定义工具视图嵌入的内容(如 `dsh-pianist` 的钢琴卡片)不会被折叠——只有渲染为原生工具卡样式的调用才参与折叠。此功能取代外挂的 `dsh-auto-collapse` 插件,二者不要同时安装,否则会互相抢夺同一批 DOM 节点造成文字重叠。
104
+ 「完成后自动折叠」在流式期间不干预——回复实时展开输出;只有回合结束(出现结束标记且全部工作完成)才折叠。该开关独立于「启用丝滑流式渲染」:无论由本插件还是内置渲染器负责对话,折叠都照常工作。纯文本回复(无思考、无工具)不会生成摘要行。其他插件通过自定义工具视图嵌入的内容(如 `dsh-pianist` 的钢琴卡片)不会被折叠——只有渲染为原生工具卡样式的调用才参与折叠。此功能取代外挂的 `dsh-auto-collapse` 插件,二者不要同时安装,否则会互相抢夺同一批 DOM 节点造成文字重叠。
74
105
 
75
106
  这些设置是用户级的持久化偏好,改完即生效,无需重启;会写进 DeepSeek Harness 的用户设置文档,而不是插件的组合配置。
76
107
 
@@ -94,6 +125,9 @@ dsh plugin --profile web update dsh-smooth-stream
94
125
  **这是 DeepSeek 官方插件吗?**
95
126
  不是。它是面向 DeepSeek Harness(`dsh`)Web UI 的独立维护项目,采用 MIT 许可证,和 DeepSeek 没有从属关系。
96
127
 
128
+ **「0 重排」是字面意思吗?**
129
+ 指动画路径本身。滚动跟随只写合成层 `transform`,不读写布局属性,因此跟随阶段不会引发重排/重绘风暴;文本揭示仍按字符增量写入(这是产生新内容的唯一必要布局),但揭示节奏被限幅,单帧视觉位移 ≤8px,避免跳帧。
130
+
97
131
  **dsh 插件怎么安装?**
98
132
  用内置插件命令:在 dsh 源码目录运行 `dsh plugin --profile web add dsh-smooth-stream`(见[安装](#安装))。
99
133