前言¶
在 DSH 裏跑長對話,回查一個關鍵詞通常靠滾動加肉眼找。dsh-content-search 做的事很直接:把已經寫入日誌的對話變成可檢索的,在對話區右上角放一個搜索按鈕,右邊欄出搜索框,命中的字標黃,點一條結果跳過去。維護者是 appthin,歸類爲「記憶」,插件目錄頁顯示 2 星。它是社區插件,和 DeepSeek / 幻方沒有官方從屬關係。
這是什麼¶
一句話定位:給當前這段對話加搜索,不產生任何新內容。
插件明確說自己不碰對話窗口、不寫會話事件、不改補丁層,只做兩件事——讀,和定位。它把已有的日誌變成可檢索的,而不是在對話裏新增一層結構。
核心功能¶
下面列出的能力都來自 README。
1、搜索按鈕:放在對話區頭部右側(conversation.session.header.utilities,order 15)。面板已開着時點擊是聚焦並展開右邊欄,不會再開一個。
2、右邊欄搜索框:作爲右邊欄的正式標籤頁(類型 contentSearch)打開,位置會隨會話記住。
3、兩種範圍:「已加載」是當前持有的事件窗口,純前端即時出結果;「整段歷史」走宿主日誌全量。
4、黃色高亮:對話區裏所有命中的字加黃色底,用戶消息和助手回覆一視同仁。
5、命中定位:點結果 → 翻歷史到那條 → 滾到該行 → 落淡黃底,2 秒後淡出。
6、可選中複製:每條結果是 div role="button" 而不是 <button>,文字可以拖選複製。
7、展開更多上下文:懸停時右下角浮出「展開更多」,點擊前後各多 300 字左右。
8、鍵盤:Enter 跳到第一個結果,↑/↓ 移動,Esc 清空。
9、中英雙語:命名空間 content-search,跟隨界面語言。
兩種範圍,區別是真區別¶
「已加載」和「整段歷史」不是同一個功能的兩個開關,數據來源和代價都不同。
| 已加載 | 整段歷史 | |
|---|---|---|
| 數據來源 | 對話當前持有的事件窗口 | 宿主日誌的全量事件 |
| 實現 | 瀏覽器內直接掃 | GET /api/content-search.search → 宿主 |
| 延遲 | 即時 | 一次網絡往返 |
| 上限 | 結果 300 條 | 每頁 200 條,累計 1000 條 |
| 會不會改對話 | 不會 | 不會 |
默認停在「已加載」。README 的解釋是:慢的那條不適合設成默認,否則每次搜索都要先付一次網絡代價。需要全量時再切。
安裝與啓用¶
版本要求:DSH >= 0.1.6-alpha.1 且 < 0.3.0,README 註明已在 0.2.1-rc.5 上測試。DSH 自身依賴 Node.js >= 24.2.0 與 Git >= 2.31.0。
Desktop 版在應用內安裝,因爲 Desktop 的 profile 由 Electron 獨佔管理,dsh plugin --profile desktop 會被拒絕。兩種方式:插件頁「插件 → 添加」,來源填 https://github.com/appthin/dsh-content-search.git;或在會話裏讓助手調 plugin_manager 的 install_bundle,目標填本目錄絕對路徑。
web / tui 等 profile 用命令安裝:
# 本地目錄(link 方式,改動即時生效,適合開發調試)
dsh plugin --profile web add file:E:/dev/deepseek_harness/dsh-content-search
dsh plugin --profile web ls # 查看已裝
dsh plugin --profile web remove dsh-content-search # 卸載
裝完打開「插件 → dsh-content-search」,卡片上應有標題與描述,取自 locale/*.json 的 meta。
同路徑下改動後,remove_bundle + install_bundle 即可熱更新,不必重啓。注意:改了插件目錄路徑必須重啓桌面端,否則宿主緩存裏仍是舊路徑,表現爲「插件裝了、按鈕沒了」;解決辦法是重啓或改包名。
卸載:
dsh plugin --profile web remove dsh-content-search
插件不寫 profile 文件、不裝依賴、不改 cordis.patch.yml,移除即清除。
典型用法:裝完按這個順序覈對¶
README 給了驗證步驟。先做語法檢查,再動界面:
node --check index.js
node --check client.js
界面裏依次確認:
1、對話區右上角出現放大鏡按鈕;
2、點擊 → 右邊欄出現「搜索」標籤頁(已存在時會聚焦並展開);
3、輸入關鍵詞 → 對話區命中的字變黃,用戶消息和助手回覆都有;
4、點一條結果 → 對話滾到該位置並短暫泛黃;
5、懸停一條結果 → 右下角浮出「展開更多」→ 點擊後前後各多 300 字左右,鏈接變「收起」;
6、切到「整段歷史」→ 提示行顯示命中來源,結果條數變爲「條命中(整段歷史)」。
它怎麼實現的¶
瀏覽器半邊負責按鈕、面板、高亮和定位。高亮用 CSS Custom Highlight API(CSS.highlights),不碰對話的 DOM:
registry.set('dsh-content-search', new Highlight(...ranges))
::highlight(dsh-content-search) { background-color: rgba(255, 206, 0, .55); color: inherit }
::highlight(dsh-content-search-hit) { background-color: rgba(255, 206, 0, .18) }
不包 <mark>,是因爲那要改別的插件的 DOM。高亮只覆蓋已經渲染出來的行,摺疊收起的內容不會憑空被點亮。
宿主半邊只有一個職責:讀會話的完整持久日誌,通過一條只讀路由暴露給瀏覽器:
GET /api/content-search.search?sessionId=…&query=…&limit=…&cursor=…
每頁默認 100 條、最多 300 條,游標是十進制偏移。每條命中帶回關鍵詞前後各 480 字左右的原文。定位則走 session.loadThrough(hit.seq) 翻歷史,再按 Chat 的渲染屬性(data-chat-node-key 等)找行;這套屬性是渲染約定而非公開 API,插件做了特性檢測,找不到時提示「已翻到該位置,但這一條當前沒有渲染出來」。
一個刻意的取捨是:整段歷史不用宿主的全文索引 sessionQuery.searchEvents,因爲它可以被部署關掉(openAt: "never" 時拋 SESSION_QUERY_SEARCH_DISABLED),插件改用不帶開關的 sessionQuery.filterEvents 做字面掃描。代價是大會話第一頁比索引慢,理由是「能搜到比搜得快重要」。
適用場景與注意¶
適合在長對話裏按關鍵詞回查、跳回某條消息。它只搜當前會話,跨會話不通。
注意事項:
- 插件以當前 dsh 進程的權限運行。安裝前先檢查源碼與許可證再裝。
- 整段歷史是字面掃描,大會話第一頁會慢;不做模糊匹配、不分詞、不支持正則。
- 每條命中上下文窗口是 ±480 字,展開上限 300 字是從這個窗口裏切的。
- 高亮只覆蓋已渲染的行,摺疊收起的內容不會被強制點亮。
- 流式回覆期間可能出現短暫的重複行。
- 定位依賴 Chat 渲染契約,上游改了那幾個
data-chat-*屬性會退化。 - 宿主日誌不可讀時,面板會顯示宿主原話,並退回「加載更早的內容」把窗口撐大(最多 2 次、每次 5 秒超時)。
許可證:MIT,不含第三方代碼,不打包第三方二進制,不引入依賴。
結尾¶
它的價值在於輕:不往對話里加東西,只把已有的日誌變得能搜、能跳。目錄頁在 https://www.skillhub.cn/plugins/appthin/dsh-content-search,源碼在 https://github.com/appthin/dsh-content-search。