Bundle
dsh-conversation-jump
Circular jump controls that step through a DSH Web conversation one user message at a time, paging older history in automatically. 为 DSH Web 对话区提供上一条/下一条用户消息、回到顶部/底部的圆形导航按钮,并自动处理更早历史分页。
- Source
- uigdwunm
- License
- MIT
- Updated
- Updated yesterday
Readme
# dsh-conversation-jump [](https://github.com/uigdwunm/dsh-conversation-jump/releases) [](https://github.com/topics/dsh-plugin) [](LICENSE) English | [中文](#中文) A rail of four circular buttons for the DSH Web conversation: **to top**, **previous user message**, **next user message**, **to bottom**. It floats above the composer at the right edge of the conversation, and replaces the product's single built-in "to bottom" button. ## What makes it different Sibling plugins mostly draw a **minimap**, a **table of contents**, or **inline anchors**. This one does none of that. It steps through the conversation **one user message at a time** — a click lands exactly on the previous or next thing *you* asked — and pages older history in automatically when the target sits outside the loaded window. Navigation targets are only `user` and `steering` entries. Tool calls, context injections, compaction markers and other process nodes are skipped, so "previous" means "my previous question", not "the previous DOM block". ## Install ```sh # From npm dsh plugin --profile web add dsh-conversation-jump # From GitHub, when you want the source dsh plugin --profile web add github:uigdwunm/dsh-conversation-jump # From the prebuilt release tarball (nothing to build locally) dsh plugin --profile web add https://github.com/uigdwunm/dsh-conversation-jump/releases/download/v0.1.5/dsh-conversation-jump-0.1.5.tgz ``` Restart `dsh web` afterwards. `dsh plugin` is a thin forwarder to `pnpm` in the profile directory, so a plain registry name, a `github:` spec and a remote tarball URL all work. This package declares no `prepare` script, so no `allowBuilds` entry is needed for the GitHub or tarball route. ### Local development ```sh npm install npm run build # install into the web profile; restart dsh web afterwards dsh plugin --profile web add file:/absolute/path/dsh-conversation-jump ``` A rebuild alone does not reach the running page. The profile keeps its own copy under `~/.dsh/profiles/<profile>/node_modules/`, and `npm run build` replaces `lib/client.js` by rename — which breaks the hard link the install created. Re-run the `dsh plugin … add file:…` command after each build (or copy the built file into that package) and then reload the page. ## Capabilities | Capability | Behaviour | |---|---| | To top | Scrolls to the current top, then keeps paging older history in until the real top is reached | | Previous | Jumps to the previous user message; if it lies beyond the loaded window, it goes to the top and pages older history in first | | Next | Jumps to the next user message | | To bottom | Goes straight to the newest position | | Message anchor | The target message settles about `1/12` down from the top of the viewport | | When it appears | Hidden by default; appears when the average upward scroll speed exceeds `200 px/s`, hides again as soon as you scroll down | | Auto-hide | Disappears `3 s` after appearing; clicking any button restarts that countdown | | Position | Aligned to the right edge of the conversation and `16 px` above the composer, positioned synchronously before paint so it does not flash | | State | Page-memory only — nothing persisted | Button labels are Chinese (`回到顶部` / `上一个` / `下一个` / `回到底部`); each button carries the same text as both its `title` and `aria-label`, and the rail is a `role="toolbar"` labelled `会话导航`. ## Implementation notes - **Targeting is attribute-based, not hash-based.** The scrollport is `[data-conversation-scroll]`, user messages are `[data-chat-anchor-key]` filtered by `data-chat-flow-kind`, and the composer seat is `[data-composer-seat]`. The built-in "to bottom" button is hidden by matching the stable `_toBottomSlot` class **suffix** rather than a CSS-module hash, so a DSH upgrade that rehashes class names does not break it. - **Paging drives the product's own control.** It locates the "load older" button inside `[data-chat-flow]` — the first button that is not inside a message row and precedes the first row — clicks it, then polls the anchor count until it grows. Attempts are bounded, so a host that never grows the list cannot spin forever. - **Only the `slots` seat is injected.** `cordis-plugin-timer` is host-only and absent in the web client, so the plugin declares `inject: ['slots']` and uses native `setTimeout` / `setInterval`, tracking every handle so disposal cancels them. - **The stylesheet heals itself.** It lives in a `<style data-dsh-conversation-nav>` node owned by the fiber, but every 100 ms sample checks the view and recreates the node when it is missing. A fiber re-apply that removes the styles (for example the dev-mode HMR driver rebuilding this plugin) therefore cannot leave the rail unstyled in the page's top-left corner with the product's own "to bottom" button reappearing while the rail is still mounted — it recovers on the next sample instead of needing a page refresh. - **Everything is cleaned up on unload**: the style element, every pending timer, and the scroll listener. ## Boundaries - This is a **Web UI plugin only**. The host half (`index.mjs`) is an empty `apply` — no host services, events, tools or storage. - It keeps **page-memory state only**: no `localStorage`, IndexedDB, cookies, files or backend database. - If the host never renders a "load older" button, paging cannot proceed and the jump stops there rather than looping. ## Development ```sh npm run build # esbuild bundles src/client.ts into lib/client.js npm run typecheck # tsc --noEmit ``` ## Publishing ```sh npm login # requires an npm account npm publish # prepublishOnly runs the build first ``` > The `repository` field must point back at this GitHub repo. `awesome-dsh-plugin`'s npm probe only claims a package when the package exists **and** its `repository` points at the same repo — otherwise there is no download-count sorting and no npm install entry. ## Getting listed - **GitHub `dsh-plugin` topic** — already set; plugin markets sync from it. - **awesome-dsh-plugin** — open a PR adding `data/plugins/uigdwunm__dsh-conversation-jump.yml` (category `ui`) to [awesome-dsh-plugin/awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin). That repo's READMEs are generated from the data files — do not edit them by hand. ## Naming The npm name `dsh-conversation-nav` is taken by another author (not this repo), so this package is published as `dsh-conversation-jump`. Repo name and package name match. ## Structure | File | Purpose | |---|---| | `package.json` | DSH bundle/client declaration, exports, metadata and build scripts | | `cordis.patch.yml` | Mounts the plugin into the web profile composition | | `index.mjs` | Host half (browser-only plugin, empty apply) | | `src/client.ts` | Browser-side navigation, paging, positioning and visibility policy | | `build.mjs` | esbuild browser build and ModuleLoader wrapper | | `lib/client.js` | Build output (shipped with the package) | ## License MIT — see [LICENSE](LICENSE). --- ## 中文 DSH Web 对话区的一组圆形导航按钮:**回到顶部**、**上一条用户消息**、**下一条用户消息**、**回到底部**。它悬浮在输入框上方、对话内容右缘,并替换产品自带的单个“回到底部”按钮。 ### 与同类插件的区别 同类插件大多在做**缩略图(minimap)**、**目录(TOC)**或**行内锚点**。本插件都不做,而是**按「用户消息」这个语义单位逐条前后跳转**——一次点击精确落到上一条或下一条你提过的问题,并在目标超出已加载窗口时自动翻页加载更早历史。 导航目标只包含 `user` 与 `steering` 两类条目,会跳过工具调用、上下文注入、压缩标记和其他过程节点。所以“上一条”指的是“我上一个问题”,而不是“上一个 DOM 块”。 ### 安装 ```sh # 从 npm 安装 dsh plugin --profile web add dsh-conversation-jump # 从 GitHub 安装(需要源码时) dsh plugin --profile web add github:uigdwunm/dsh-conversation-jump # 从预构建 Release 包安装(无需本地构建) dsh plugin --profile web add https://github.com/uigdwunm/dsh-conversation-jump/releases/download/v0.1.5/dsh-conversation-jump-0.1.5.tgz ``` 安装后重启 `dsh web` 生效。 `dsh plugin` 只是把参数转发给 profile 目录里的 `pnpm`,因此「registry 包名」「`github:` 规格」「远程 tarball URL」三种写法都可用。本包没有声明 `prepare` 脚本,所以走 GitHub 或 tarball 路线都不需要额外配置 `allowBuilds`。 ### 本地开发安装 ```sh npm install npm run build # 安装到 web profile,重启 dsh web 后生效 dsh plugin --profile web add file:/绝对路径/dsh-conversation-jump ``` **只重新构建不会让运行中的页面生效。** profile 在 `~/.dsh/profiles/<profile>/node_modules/` 下保存自己的一份副本,而 `npm run build` 是用「改名替换」的方式写 `lib/client.js`,会切断安装时建立的硬链接。因此每次构建后都要重新执行一次上面的 `dsh plugin … add file:…`(或把构建产物复制进 profile 里的那个包),再刷新页面。 ### 能力 | 能力 | 行为 | |---|---| | 回到顶部 | 先滚到当前顶部,再持续翻页加载更早历史,直到真正的顶部 | | 上一个 | 跳到上一条用户消息;若它在已加载窗口之外,会先到顶部并自动加载更早历史 | | 下一个 | 跳到下一条用户消息 | | 回到底部 | 直接到最新位置 | | 消息锚点 | 目标消息停在视口上方约 `1/12` 处 | | 显示策略 | 默认隐藏;往上平均速度超过 `200 px/s` 时显示,往下滚动立即隐藏 | | 自动隐藏 | 出现后 `3 秒`无点击自动消失;点击任一按钮重新计时 | | 定位 | 对齐对话内容右缘、输入框上方 `16 px`;绘制前同步定位以避免闪动 | | 状态 | 仅页面内存状态,不做持久化 | ### 实现说明 - **定位基于数据属性而非哈希类名。** 滚动容器是 `[data-conversation-scroll]`,用户消息是 `[data-chat-anchor-key]` 并按 `data-chat-flow-kind` 过滤,输入区是 `[data-composer-seat]`。产品自带的“回到底部”按钮通过匹配稳定的 `_toBottomSlot` 类名**后缀**隐藏,而不是写死 CSS-module 哈希,因此 DSH 升级重新生成类名不会失效。 - **翻页走产品自身的控件。** 在 `[data-chat-flow]` 中定位“加载更早”按钮(第一个不在消息行内、且位于首行之前的按钮)并点击它,然后轮询锚点数量直到增长;尝试次数有上限,宿主始终不增长列表时也不会空转。 - **只注入 `slots` 一个席位。** `cordis-plugin-timer` 是纯 Host 服务,在 Web 端不存在,因此插件声明 `inject: ['slots']` 并使用原生 `setTimeout` / `setInterval`,同时记录每个句柄以便卸载时取消。 - **样式会自愈。** 样式挂在 fiber 拥有的 `<style data-dsh-conversation-nav>` 节点上,但每 100ms 采样都会检查它是否还在,缺失就重新创建。因此当 fiber 被重新 apply 而移除样式时(例如 dev 模式下 HMR 重建本插件),不会出现“导航条仍挂着却变成页面左上角的无样式按钮、产品自带『回到底部』同时回来”的状态——下一个采样周期就恢复,不需要刷新页面。 - **卸载时全部清理**:样式元素、所有待执行定时器、滚动监听。 ### 边界 - 这是**纯 Web UI 插件**。Host 半边(`index.mjs`)是空的 `apply`——不需要 Host 服务、事件、工具或存储。 - 只维护**页面内存状态**:不使用 `localStorage`、IndexedDB、Cookie、文件或后端数据库。 - 若宿主始终没有渲染“加载更早”按钮,翻页无法进行,跳转会就此停止,不会死循环。 ### 开发 ```sh npm run build # 用 esbuild 把 src/client.ts 打包到 lib/client.js npm run typecheck # tsc --noEmit ``` ### 发布 ```sh npm login # 需要 npm 账号 npm publish # prepublishOnly 会先执行构建 ``` > `repository` 字段必须指回本 GitHub 仓库。awesome-dsh-plugin 的 npm 探测只在「npm 包存在 **且** 其 `repository` 指向同一仓库」时才认领,否则拿不到下载量排序与 npm 安装入口。 ### 收录渠道 - **GitHub `dsh-plugin` topic**:已添加,各类插件市场会据此同步收录。 - **awesome-dsh-plugin**:向 [awesome-dsh-plugin/awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) 提 PR,新增 `data/plugins/uigdwunm__dsh-conversation-jump.yml` 一个文件(分类 `ui`)。**该仓库的 README 由数据文件生成,不要手工编辑。** ### 命名说明 npm 上 `dsh-conversation-nav` 这一名称已被其他作者占用(非本仓库),因此本包发布名为 `dsh-conversation-jump`。仓库名与包名一致。 ### 结构 | 文件 | 作用 | |---|---| | `package.json` | DSH bundle/client 声明、exports、元数据与构建脚本 | | `cordis.patch.yml` | 把插件挂进 web profile 组合 | | `index.mjs` | Host 半边(浏览器 UI 插件,空 apply) | | `src/client.ts` | 浏览器端导航、分页、定位与显示策略 | | `build.mjs` | esbuild 浏览器构建与 ModuleLoader 包装 | | `lib/client.js` | 构建产物(发布时包含) | ### 许可证 MIT,见 [LICENSE](LICENSE)。
Install
dsh plugin --profile web add github:uigdwunm/dsh-conversation-jump
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-conversation-jump from the hub
- 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.