Skip to content
dsh.fish
Bundle

dsh-web-scroll-flow

DSH Web 对话滚动动效插件:自动跟随滚动平滑过渡(内容向上顶/向下拉的流畅滚动),边缘滚动物理回弹(小幅橡皮筋效果),手动滚动保持原生手感

Source
TYOPXN360
License
MIT
Updated
Updated 20 days ago

Readme

# dsh-web-scroll-flow

DSH Web 对话滚动动效插件:自动跟随滚动动画、边缘橡皮筋回弹、流式逐字打字机。

纯客户端插件,源码 + 预构建产物都在仓库里,安装后无需自己构建即可直接看到效果。

## 效果

1. **自动跟随滚动动画**:内容流入 / 自动换行时,贴底跟随从"瞬间跳变"变成自然的平滑推送。小增量(流式逐字)近乎瞬时,大距离(回到底部、展开思维链)平滑过渡;底部状态行(Deep diving、时间、待插话消息)保持固定不漂移。
2. **边缘橡皮筋回弹**:手动滚动到顶部 / 底部后继续向外滚,内容跟手持续拉动(无硬上限),滚轮停止后才弹簧回弹归位,类似手机的橡皮筋手感。消息内部可滚动的子容器(详情、代码块)不触发页面回弹。
3. **流式逐字打字机(原生 / 覆盖层两种模式)**:思维链与正文在流式输出时以字为单位逐字显示,带闪烁光标。
   - 原生:直接截断原始 Markdown 文本节点,无双份 DOM,段落 / 行距天然一致。
   - 覆盖层:在原内容上叠加模拟层逐字显示。
   段落间距与完成后的 Markdown 一致,打完字不跳变;大段文本按公式自动提速。初次加载 / 刷新页面时历史消息不会全部重新打字。
4. **设置面板可调**:General 设置中新增四项,选择持久化到浏览器:
   - 自动滚动动画:关闭 / 优雅(慢速上推)/ 适中(默认)
   - 边缘回弹:开关
   - 打字机效果:开关
   - 打字机模式:原生 / 覆盖层

`prefers-reduced-motion`(系统减动效)下自动跟随动画与回弹禁用,保持瞬时行为。

## 安装到 DSH Web

仓库自带预构建产物(`lib/index.js`、`lib/client.js`),克隆后**无需构建**,直接安装即可看到效果:

```sh
git clone https://github.com/<owner>/dsh-web-scroll-flow.git
cd dsh-web-scroll-flow
pnpm install            # 安装 link 解析需要的依赖(不构建插件)
pnpm dsh plugin --profile web add "$PWD"
```

这会向 `$DSH_HOME/profiles/web/package.json` 写入
`"dsh-web-scroll-flow": "link:<本目录>"` 依赖,并把包加入
`dsh.profile.bundles` 层列表。首次安装需要重启 `pnpm dsh web`:

```sh
# 停掉当前 dsh web,然后:
pnpm dsh web
```

打开 `http://127.0.0.1:3080`:`window.__DSH_BOOT__` 中出现
`dsh-web-scroll-flow` 条目,`/plugins/dsh-web-scroll-flow/client.js`
可访问,即加载成功。

### 热重载(开发时)

每次 `pnpm run build` 后刷新 `http://127.0.0.1:3080` 即可(服务器实时读取
`lib/client.js`),无需重启服务器;只有首次安装需要重启 `pnpm dsh web`
使 profile 组合生效。

## 开发

```sh
pnpm install          # 安装构建 / 测试依赖
pnpm run typecheck    # tsc --noEmit
pnpm run test         # vitest(jsdom)
pnpm run build        # tsdown → lib/(index.js + client.js)
pnpm run check        # 三者一起
```

仓库提交了预构建产物(`lib/index.js`、`lib/client.js`、`lib/index.d.ts`),
sourcemap 与依赖目录不入库。修改源码后请重新 `pnpm run build` 再提交,
确保 `lib/client.js` 与源码同步。

## 目录结构

```
src/
  index.ts                       节点半入口(空 apply,使行成为合法 loader 条目)
  client/
    index.ts                     浏览器半入口:扫描 + 挂载 + 设置接线
    scroll-flow-controller.ts    滚动控制器:跟随动画 / 橡皮筋回弹(零依赖,可独立测试)
    scroll-flow-settings.ts      设置类型 / 持久化 policy
    settings-row.tsx             General 设置行(动画档位 + 回弹 + 打字机开关)
    typewriter.ts                逐字打字机:思维链 + 正文,段落间距 / 光标 / 提速
tests/                           vitest + jsdom 测试
cordis.patch.yml                 bundle patch:插入 dsh.client 行
tsdown.config.ts                 构建配置(node 半 ESM + client 半 CJS bundle)
lib/                             预构建产物(client.js 供运行时加载,index.js 供 loader 解析)
```

## 原理

插件是纯 DOM 层增强,不修改 DSH 本体代码:

- 用 `MutationObserver` 监听文档中的对话滚动容器
  `[data-conversation-scroll]`(ConversationRoot 的 scrollBody),挂载
  `ScrollFlowController` 与 `TypewriterController`。
- **自动跟随动画**:对容器实例覆写 `scrollTop`(configurable,dispose 时
  删除还原)。程序性"贴底跟随"写入(目标 == `scrollHeight`)被拦截:
  小增量贴底跟随瞬时落底并播放整列入场推升(ChatAnimation 式,新内容
  从底部出现、旧消息整体上推);大距离跳转(回到底部)保留平滑缓动。
  其它写入(恢复位置、prepend 锚定)原样瞬时通过。只有明确的用户输入
  (滚轮 / 触摸 / 鼠标按下 / 键盘滚动)会打断动画。
- **边缘橡皮筋**:监听滚轮事件,仅在顶 / 底边缘继续向外滚时跟手累积
  位移(软增益递减、无硬上限),滚轮停止 `releaseDelay` 后弹簧回中。
- **打字机**:MutationObserver 只对"文本实际增长"的 Markdown 启动打字
  (历史消息不打字);打字期间目标 Markdown `display:none` 不占高,覆盖层
  正常流逐字显示,按 `\n\n` 拆段并复制段落 margin,光标跟随正在输入的
  段落;停止增长后恢复原始 Markdown,并在恢复瞬间抑制入场推升避免回弹。
- **设置**:`ctx.settingsScope` 绑定 `dsh-web-scroll-flow` 命名空间,
  General 设置行通过 `settings.general.item` 插槽注册;切换档位 / 开关时
  实时更新已挂载的所有控制器。

## 调参

默认参数在 `src/client/scroll-flow-controller.ts` 顶部的
`DEFAULT_FOLLOW` / `DEFAULT_BOUNCE`,以及 `src/client/typewriter.ts` 的
`DEFAULT_OPTIONS`:

- 跟随动画最大时长(200ms,满 200px 距离)
- 回弹软增益参考距离(16px)、弹簧刚度 / 阻尼 / 滚轮灵敏度 / 松手延迟
- 打字机基础速度(约 60 字/秒)、停止判定、光标保持、加载宽限期

修改后重新 `pnpm run build` 并刷新页面。

用户也可以在 General 设置里直接切换动画档位(优雅 = 380ms 慢速上推)、
回弹开关、打字机开关,无需改代码。

## 卸载

```sh
pnpm dsh plugin --profile web remove dsh-web-scroll-flow
```

重启 `dsh web` 后插件不再加载,浏览器行为完全还原。

## License

MIT

Install

dsh plugin --profile web add github:TYOPXN360/dsh-web-scroll-flow

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source