dsh-long-message-fold:把 DSH Web 裏的超長用戶消息摺疊起來

前言

DSH(DeepSeek Harness)的理念是「一切皆插件」,客戶端 UI 也走同一套路:內置能力只做必要的骨架,長尾需求靠插件補。這就帶來一個具體痛點:用戶往 Web 對話裏貼大段日誌或大文本時,一條消息就能佔滿整個會話視口,前面的上下文全被頂走。DSH 自帶的 Turn Process Folding 只摺疊過程行(tool-call / thinking),不折疊 user 消息正文,所以這塊一直是空缺。

下面介紹一個填這個空缺的客戶端插件:constraint21 維護的 dsh-long-message-fold。它把超長的用戶消息摺疊成固定高度(默認 360px,約 16 行),底部漸隱,並提供「展開全文 / 收起」切換;短消息一個像素都不變,裝上即生效。

這是什麼

一句話定位:一個純客戶端(Web 平臺)UI 插件,遮罩 shipped 的 PlainUserText 渲染器,把超長 user 消息正文摺疊成固定預覽高度。

  • 維護者:constraint21
  • 分類:客戶端(Client)
  • 許可證:MIT(© 2026 constraint21)
  • 包名/版本:@local/dsh-long-message-fold,0.1.0,package.json 裏 private: true(刻意不發 npm,只走 git 或本地目錄安裝)
  • 目錄頁:https://www.skillhub.cn/plugins/constraint21/dsh-long-message-fold
  • 倉庫:https://github.com/constraint21/dsh-long-message-fold

核心功能

1、超長用戶消息自動摺疊成固定高度(默認 MAX_HEIGHT = 360px,約 16 行),底部用 mask-image 漸隱。漸隱不猜背景色,因此在任意主題下都正確。
2、摺疊態下給出 pill 狀操作按鈕:「展開全文 · N 行」展開並顯示總行數;「收起」回到摺疊態。
3、拖動窗口寬度或側欄變寬後,原本溢出的消息會自動恢復成不折疊狀態,不需要手動操作。
4、短消息和未溢出的文本片段保持原樣。未溢出的渲染路徑返回與 shipped UserTextRun 等價的 <span style="font-size:inherit;line-height:inherit">,流式佈局與原來一致。
5、展開狀態有內存級記憶(模塊級 Map,鍵爲節點起止偏移 + 文本長度 + FNV-1a 哈希,最多 400 條),虛擬列表卸載再掛載同一行時狀態不丟;不寫 localStorage,刷新即復位。
6、沒有開關,裝上即生效。

關於「算不算超長」:插件不看文本長度,而是實測溢出。它通過 useLayoutEffect + ResizeObserver 測量:

  • 內聯態取 getBoundingClientRect().height,能反映折行後的真實高度;
  • 已成塊態取 scrollHeight,摺疊與展開兩態下判定一致,不會來回抖動。

這個做法是必需的:conversation.user.text.node 這個 key 在 QueueDock 預覽和 submission echo 中也會被調用(ellipsis: true,父級是 white-space: nowrap),nowrap 下高度恆定爲 1 行,因此靠長度判會誤傷。ResizeObserver 掛到祖先鏈上最近一個有盒子的節點(bubble),所以側欄或詳情面板寬度變化時能雙向重算。

安裝

插件走 DSH 的 Plugin Manager 安裝,三種方式任選。

第一種,讓 agent 裝(README 標註爲推薦):

安裝插件 github:constraint21/dsh-long-message-fold

對 DSH 說出這句話即可,agent 會調用 plugin_manager → install_bundle,審批通過後生效。等價的顯式調用方式是:

plugin_manager → install_bundle → target: github:constraint21/dsh-long-message-fold

第二種,本地目錄:先把源碼取到本地,然後把 install_bundle 的 target 指向該目錄的絕對路徑。

第三種,手動(不推薦):把包放進 profile 的 node_modules,並在 cordis.patch.yml 裏 insert 一個 long-message-fold 條目。手工修改 profile 的 package.json 或 patch 容易與 Plugin Manager 的狀態打架,能用前兩種就別用這個。

安裝後要刷新 Web 頁面。插件是純客戶端 UI,不重啓 Host 也可以,但頁面資源需要重新加載。

卸載時在 Plugin Manager 裏移除 @local/dsh-long-message-fold(或執行 plugin_manager action: remove_bundle),刷新頁面後 user 消息立即恢復原樣。插件狀態只存在內存裏,無殘留。

典型用法

裝完刷新頁面後直接使用即可,沒有配置項需要打開。

1、往對話裏貼一段超過 360px 的用戶消息(例如日誌或長文)。
2、消息體現爲摺疊態:正文最高 360px、底部漸隱,下方有「展開全文 · N 行」按鈕。
3、點擊按鈕展開完整內容,按鈕文字變爲「收起」;再次點擊回到摺疊態。
4、把窗口或側欄拉寬,讓消息不再溢出,它會自行恢復成不折疊。

調參方式

參數是 client.js 頂部的四個常量,改完重新加載頁面即可,不需要重新安裝:

常量 默認 含義
MAX_HEIGHT 360 摺疊後正文高度(px,約 16 行)
FADE_HEIGHT 48 底部漸隱高度
REMEMBER_LIMIT 400 展開狀態記憶條數
OBSERVE_MIN_CHARS 120 短於此長度的文本不掛 ResizeObserver

實現要點(給要看源碼的人)

插件採用同 key 註冊的方式遮蓋 shipped 渲染器:爲 slot conversation.user.text.node 的 key 'text' 註冊自己的渲染器,外層氣泡、附件、各類控件全部保留。該 slot 在 catalog 裏聲明爲 replaceRisk: "shadows-shipped-ui";shipped 的 PlainUserText 以 priority: 100 註冊,動態安裝的插件條目由 runner 的 allocatePriority() 分到更小的數值,而數值更小者獲勝,所以同 key 註冊即取代,無需自己傳 priority。

README 記錄了幾個被否決的替代方案,理由是它們要求重寫整行或整塊(pending / echo / 時間 / 複製控件、userStack / 附件行 / bubble / JsonBlock),或者違反「不寫組件外 DOM、不讀別人 DOM」的插件紀律。

實現上的約束:文案走 Client locale service(ctx.locale.register(NS, { zh, en }),slot 註冊帶 locale: NS 以拿到框架注入的 t);配色只用 --dsw-alias-* 主題 token;不 require 任何 Harness Client 包。

兼容性方面:插件依賴 slot conversation.user.text.node 與 Client 包 @deepseek-ai/dsh-client-ui-conversation(見 package.json 的 dsh.client.inject),slot 或 shipped 實現大改時可能失效。退化是安全的:若上游把 'text' key 改名或不再有 occupant,框架會用 fallback 渲染原始文本,消息顯示不受影響,只是不再摺疊。插件只在 Web 平臺註冊(dsh.client.platform = "web"),Host 半的 index.js 是空實現。

適用場景與注意

適合:經常在 DSH Web 裏貼長日誌、長文本,且願意用插件補這塊空白的 DSH 用戶和智能體開發者;也適合想看「如何在客戶端用 slot 同 key 覆蓋 shipped 渲染器」這個做法的插件作者。

注意幾點:

1、插件以當前 DSH 進程權限運行。安裝前應打開源碼檢查一遍,並確認許可證(MIT)是否符合自身合規要求。
2、該倉庫目前 star 數爲 0,屬於很早期的個人項目。功能描述和參數以 README 爲準,缺少大規模使用反饋的跡象,建議當作小工具自用並在自己的 DSH 環境裏驗證。
3、它只處理 user 消息正文,不處理 assistant 輸出、tool-call 或 thinking 的過程行。
4、package.json 標了 private: true,所以不能通過 npm install 安裝,只能走 Plugin Manager 的 git 或本地目錄方式。
5、目錄頁(skillhub.cn)是獨立的社區站點,與 DeepSeek / 幻方無官方從屬關係,不是一個官方應用商店。

結尾

這個插件的價值很窄也很明確:把「貼大段日誌把上下文頂走」這件事用摺疊 + 漸隱 + 一鍵展開解決掉,同時不改變短消息和流式佈局。安裝一條命令、刷新頁面即生效,不寫 localStorage、卸載無殘留。目錄頁在 https://www.skillhub.cn/plugins/constraint21/dsh-long-message-fold,源碼與 README 在 https://github.com/constraint21/dsh-long-message-fold。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜