前言¶
DSH (DeepSeek Harness) 裏跑智能體,長回答是常態。回答結束時頁面停留位置在末尾:要麼從末尾往上手動翻半天,要麼重新生成一次。dsh-answer-top 這個插件解決的就是這件事:AI 回答結束後,把滾動容器自動滾回到這一輪迴答的第一行。
下面介紹這個插件:它是誰維護的、裝上去之後具體做什麼、怎麼安裝、有哪些開關可以配置,以及在什麼場景下不要指望它。
這是什麼¶
dsh-answer-top 是 DSH 生態的客戶端插件,維護者是 DSH-USER-GTB。它不改變 DSH 的行爲,只監聽 DSH 客戶端渲染出來的對話 DOM,在檢測到「本輪迴答結束」時執行一次滾動。
一句話定位:回答結束 → 自動滾到這一輪迴答的第一行,不用每次往上翻。
它屬於客戶端插件:全部邏輯在瀏覽器側(lib/client.js),宿主端 lib/index.js 是空插件,apply 不做任何事。
核心功能¶
自動回滾的觸發條件¶
插件監聽 DSH 客戶端的 DOM 契約,判斷本輪迴答是否結束:
MutationObserver監聽document.body上data-actions-reveal屬性的變化,另有 800ms 定時器兜底(漏事件或會話切換時補檢)。[data-turn-tail]元素出現data-actions-reveal,說明這一輪迴答結束了。- 找出本輪第一個
[data-chat-flow-kind="assistant-step"]節點,把[data-conversation-scroll]滾到它的頂部。
用到的 DOM 契約是從 DSH 客戶端裏逆向出來的:
[data-conversation-scroll] 滾動容器
[data-chat-turn="N"] 第 N 輪的所有流節點
[data-chat-flow-kind] 節點類型:user / assistant-step / tool-call / turn-process
[data-turn-tail] 輪次末尾容器
data-actions-reveal 該屬性存在 = 本輪已結束
各情況下的行爲:
| 情況 | 行爲 |
|---|---|
| 回答比一屏長 | 滾到回答開頭 |
回答短於 max(視口 25%, 80px) |
不滾(避免頁面亂跳;閾值可調) |
| 歷史消息加載 | 不滾(只對最後一輪生效) |
| turn 號識別失敗 | 不滾(不 fallback 到全部回答,避免誤滾到對話開頭) |
| 流式輸出中 | 不滾(等真正結束才動) |
| 啓動後首次進入會話 | 跳到最後一輪迴答開頭(整個頁面生命週期只一次,切會話不重複) |
觸發閾值(v0.12.0+)¶
默認行爲是「回答短就不滾」,由兩條規定同時生效、取更高一條:回答高度低於 max(視口 × 25%, 80px) 時不滾。
閾值在 DSH 設置 → 左側導航「自動回滾」→「觸發閾值」兩個滑塊裏調,拖動立即生效並自動保存:
| 控件 | 範圍 | 默認 | 說明 |
|---|---|---|---|
| 最小回答高度 | 視口的 5%~100% | 25% | 回答低於「視口 × 該比例」時不滾 |
| 絕對下限 | 0~200px | 80px | 無論窗口多大,回答低於這個高度都不滾 |
設置持久化在瀏覽器 localStorage 的 dsh-answer-top:config 鍵裏,結構如下:
{
"v": 1,
"enabled": true,
"sessions": {},
"threshold": { "ratio": 0.25, "px": 80 }
}
| 字段 | 說明 |
|---|---|
v |
配置版本,仍爲 1 |
enabled |
總開關,默認 true |
sessions |
預留空對象,首版恆空 |
threshold.ratio |
最小回答高度佔視口的比例,0.05~1,默認 0.25 |
threshold.px |
絕對下限像素,0~200,默認 80 |
舊版本寫出的配置沒有 threshold 字段,讀取時自動回填 { ratio: 0.25, px: 80 },不改寫原存儲,下次拖動滑塊時才寫回。
總開關¶
默認開啓。關閉後完全不滾——不是滾少一點,是一次都不滾。開關放在 DSH 設置頁「自動回滾」分區,也可以在瀏覽器控制台執行:
__dshAnswerTop.setEnabled(false) // 關閉
__dshAnswerTop.setEnabled(true) // 打開
__dshAnswerTop.config() // 看當前配置
改了立即生效,不用刷新。控制台裏改閾值也可以,但不持久化:
__dshAnswerTop.setThreshold(0.15, 40)
手動回滾按鈕(v0.12.0+)¶
輸入框下方的 dock(conversation.composer.dock)裏常駐一個「⤒ 回滾」小按鈕,始終顯示:
- 點擊即滾到最後一輪迴答開頭,即使自動回滾關閉、或檢測到共存插件衝突;
- 手動路徑跳過總開關、300ms 交互窗口和高度閾值;
- DOM 標記:
data-dsh-answer-top-manual="1",title="回滾到回答開頭"。
也可以用控制台手動觸發一次:
__dshAnswerTop.forceScroll()
與同類插件共存¶
如果同時啓用了同樣會「自動滾動/跳轉」的插件,兩個一起動會打架。插件讀 ctx.modules 的模塊名單(純內存,不走 loopback、不聯網):
// v0.12.0 起只把 dsh-chat-answer-anchor 視爲衝突
["dsh-chat-answer-anchor"]
命中後只關自動路徑:回答結束不再自動滾,設置頁顯示「檢測到 dsh-chat-answer-anchor,已暫停自動回滾」。手動按鈕和命令行 forceScroll() 不受影響。禁用 anchor 後會自動恢復。探測每 800ms 做一次輕量重檢;ctx.modules 不可用或形狀不符時保持上一次結論,寧可不動也不誤傷。dsh-jump-button 自 v0.12.0 起不再算衝突。
看探測結果:
__dshAnswerTop.info().coexistence // { anchor, checked, probe }
__dshAnswerTop.diagnose().coexistence
__dshAnswerTop.moduleIds() // 覈對目標插件 id 是否帶 /client 後綴
數據與隱私¶
插件不聯網、不上傳。源碼裏沒有任何 fetch / XMLHttpRequest / WebSocket / sendBeacon,不向 DSH、GitHub 或第三方發送數據。
只在本機 localStorage 寫兩個鍵:dsh-answer-top:config(設置)和 dsh-answer-top:events(脫敏事件日誌,最多 200 條 / 50KB,超出丟最舊)。事件日誌只記布爾或枚舉字段(errorType、at、v、count、anchor、probe 等),不記消息正文、會話 id、用戶輸入、文件路徑、URL、頁面內容、截圖和原始錯誤文本。清理方式:
localStorage.removeItem('dsh-answer-top:events') // 只清日誌
localStorage.removeItem('dsh-answer-top:config') // 連設置一起重置
location.reload()
安裝與啓用¶
插件是鏈接安裝串(link)形式,裝完需要重啓 DSH(bundle 層插件)。在 DSH 裏進入 設置 → 插件 → 添加插件,輸入本地目錄的 link 安裝串:
link:<你的插件目錄>
命令行等價寫法:
dsh plugin --profile desktop add link:<你的插件目錄>
卸載:
dsh plugin --profile desktop remove dsh-answer-top
下載途徑:社區目錄頁 https://www.skillhub.cn/plugins/DSH-USER-GTB/dsh-answer-top 和源碼倉庫 https://github.com/DSH-USER-GTB/dsh-answer-top。本地開發用同一條 link 命令,重新添加時保持目錄不變即可。
package.json 裏的版本是 0.12.1,許可證 MIT。按包內聲明,dsh.client.inject 是 [],但 client 端運行時代碼的 exports.inject 必須聲明爲 ["slots", "modules"] 才能拿到 ctx.modules,否則共存探測不可用。
典型用法¶
1、裝好插件,啓動 DSH。
2、打開任意會話,發一條會觸發長回答的請求。
3、回答結束後,頁面自動回到這一輪迴答的第一行;如果回答短於 max(視口 25%, 80px),則停在末尾不動。
4、想臨時調閾值,在控制台執行 __dshAnswerTop.setThreshold(0.15, 40)(不持久化)。
5、想長期調整,在設置頁「自動回滾 → 觸發閾值」裏拖兩個滑塊。
6、自動回滾被共存插件暫停時,點輸入框下方的「⤒ 回滾」按鈕手動跳一次。
適用場景與注意¶
適合:以長回答爲主的 DSH 對話場景,特別是每次回答結束都要從頭讀一遍的用戶;以及想改成手動控制、只要一個常駐按鈕的用戶。設置頁開關和手動按鈕是兩個獨立槽位,開關切首選/備選位置時手動按鈕不受影響。
需要注意:
- 插件以當前 dsh 進程權限運行,安裝前應打開源碼倉庫覈對代碼與許可證(MIT)。
- 只對最後一輪生效,翻歷史消息時不會跟着滾。
- 依賴 DSH 的 DOM 契約。升級 DSH 後如果失效,多半是屬性名變了——先按下面的方法確認契約元素是否存在。
- v0.12.x 起滾動是瞬時跳轉(直接賦值
scrollTop),在 DSH 的新滾動容器上 smooth 動畫不生效。
調試¶
如果沒生效,在瀏覽器控制台逐條確認契約元素:
// 看契約元素在不在
document.querySelector('[data-conversation-scroll]')
document.querySelectorAll('[data-chat-turn]').length
document.querySelectorAll('[data-turn-tail]').length
// 看最後一輪結束沒
const tails = document.querySelectorAll('[data-turn-tail]');
tails[tails.length-1].hasAttribute('data-actions-reveal')
元素都在但不滾,看控制台報錯。設置頁裏看不到開關時,v0.9.1 起可以用一條命令定位問題在哪一層:
__dshAnswerTop.diagnose()
| 輸出 | 含義 | 下一步 |
|---|---|---|
injectCallbackRuns: 0 |
slot 還沒被聲明,注入回調沒跑 | 確認 DSH 版本里 settings.section 仍存在 |
injectCallbackRuns: 1, registerRuns: 0 |
回調跑了但註冊失敗 | 看 registerError |
slotEntryCount: 0 |
註冊了但列表裏沒有 | 看 registerError / 是否被 abdicate |
slotEntryCount: 1, slotMounted: 0 |
已註冊,只是沒打開對應分區 | 打開 設置 →「自動回滾」 |
slotEntryCount: 1, slotMounted: 1 |
一切正常 | 看組件是否被 CSS 隱藏 |
注意 teardown() 會註銷 slot,之後必須刷新頁面纔會重新註冊。
總結¶
dsh-answer-top 是一個功能單一的客戶端插件:自動回滾、閾值可調、有手動按鈕兜底、不聯網不落敏感數據。裝好重啓 DSH 即可用;如果以後 DSH 改了 DOM 契約導致失效,先在控制台覈對上面那幾個契約元素是否存在。目錄頁:https://www.skillhub.cn/plugins/DSH-USER-GTB/dsh-answer-top ;源碼:https://github.com/DSH-USER-GTB/dsh-answer-top 。