Introduction¶
The philosophy of DSH (DeepSeek Harness) is “everything is a plugin,” and the client UI follows the same pattern: built-in capabilities only provide the necessary skeleton, while long-tail needs are filled by plugins. This brings a specific pain point: when users paste large chunks of logs or text into a Web chat, a single message can fill the entire conversation viewport, pushing all preceding context away. DSH’s built-in Turn Process Folding only folds process lines (tool-call / thinking) and does not fold the user message body, so this gap has always existed.
Below is a client plugin that fills this gap: dsh-long-message-fold maintained by constraint21. It folds extremely long user messages into a fixed height (default 360px, about 16 lines) with a fade-out at the bottom, and provides an “Expand All / Collapse” toggle; short messages remain unchanged by a single pixel, and it works as soon as it is installed.
What is it¶
One-sentence positioning: A pure client-side (Web platform) UI plugin that masks the shipped PlainUserText renderer, folding the super-long user message body into a fixed preview height.
- Maintainer: constraint21
- Category: Client
- License: MIT (© 2026 constraint21)
- Package Name/Version:
@local/dsh-long-message-fold, 0.1.0,private: trueinpackage.json(intentionally not published on npm, only via git or local directory installation) - Directory Page: https://www.skillhub.cn/plugins/constraint21/dsh-long-message-fold
- Repository: https://github.com/constraint21/dsh-long-message-fold
Core Features¶
- Super-long user messages are automatically folded into a fixed height (default
MAX_HEIGHT = 360px, about 16 lines), with amask-imagefade-out at the bottom. The fade-out does not guess the background color, so it is correct under any theme. - In the folded state, a pill-shaped action button is provided: “Expand All · N lines” to expand and show the total line count; “Collapse” to return to the folded state.
- After dragging the window width or widening the sidebar, messages that originally overflowed automatically restore to the un-folded state; no manual operation is required.
- Short messages and non-overflowing text fragments remain unchanged. The un-overflowed rendering path returns a
<span style="font-size:inherit;line-height:inherit">equivalent to the shippedUserTextRun, keeping the streaming layout consistent. - The expanded state has memory-level persistence (module-level Map, key is node start/end offset + text length + FNV-1a hash, max 400 entries); state is not lost when a virtual list unmounts and remounts the same line; no
localStorageis written, so it resets on refresh. - There is no toggle switch; it works as soon as it is installed.
Regarding “Is it considered super long?”: The plugin does not look at text length but actually measures overflow. It uses useLayoutEffect + ResizeObserver:
- For inline state, it takes
getBoundingClientRect().height, reflecting the true height after wrapping. - For block state, it takes
scrollHeight. The judgment is consistent between the folded and expanded states, preventing back-and-forth jitter.
This approach is necessary: the key conversation.user.text.node is also called in QueueDock preview and submission echo (ellipsis: true, parent is white-space: nowrap). Under nowrap, the height is constantly 1 line, so judging by length would cause false positives. ResizeObserver is attached to the nearest ancestor node with a box (bubble), so it can recalculate bidirectionally when the sidebar or detail panel width changes.
Installation¶
The plugin is installed via DSH’s Plugin Manager. Choose any of the three methods.
The first method is to let the agent install it (marked as recommended in the README):
安装插件 github:constraint21/dsh-long-message-fold
Simply say this to DSH, and the agent will call plugin_manager → install_bundle, taking effect after approval. The equivalent explicit call is:
plugin_manager → install_bundle → target: github:constraint21/dsh-long-message-fold
The second method is local directory: first download the source code locally, then point the target of install_bundle to the absolute path of that directory.
The third method is manual (not recommended): put the package in the profile’s node_modules and insert a long-message-fold entry in cordis.patch.yml. Manually modifying the profile’s package.json or patch is prone to conflicting with the Plugin Manager’s state; if you can use the first two methods, don’t use this one.
After installation, you must refresh the Web page. The plugin is a pure client-side UI and does not require restarting the Host, but page resources need to be reloaded.
To uninstall, remove @local/dsh-long-message-fold in the Plugin Manager (or execute plugin_manager action: remove_bundle) and refresh the page; the user message immediately returns to its original state. Plugin state exists only in memory and leaves no residue.
Typical Usage¶
After refreshing the page, you can use it directly; there are no configuration items to enable.
- Paste a user message longer than 360px into the conversation (e.g., logs or long text).
- The message appears in a folded state: the body is max 360px with a fade-out at the bottom, and there is an “Expand All · N lines” button below.
- Click the button to expand the full content, and the button text changes to “Collapse”; click again to return to the folded state.
- Drag the window or sidebar wider so the message no longer overflows, and it will automatically restore to the un-folded state.
Tuning / Configuration¶
The parameters are four constants at the top of client.js. After changing them, reload the page; no reinstallation is needed:
| Constant | Default | Meaning |
|---|---|---|
MAX_HEIGHT |
360 |
Height of the folded body (px, approx. 16 lines) |
FADE_HEIGHT |
48 |
Height of the bottom fade-out |
REMEMBER_LIMIT |
400 |
Number of expanded state entries to remember |
OBSERVE_MIN_CHARS |
120 |
Text shorter than this length will not have a ResizeObserver attached |
Implementation Notes (For those reading source code)¶
The plugin uses the same key registration method to mask the shipped renderer: it registers its own renderer for the key 'text' of the slot conversation.user.text.node, preserving the outer bubble, attachments, and various controls. This slot is declared as replaceRisk: "shadows-shipped-ui" in the catalog; the shipped PlainUserText is registered with priority: 100, while dynamically installed plugin entries are allocated a smaller value by the runner’s allocatePriority(). Since the smaller value wins, registering with the same key replaces the shipped one, so there is no need to pass priority manually.
The README documents several rejected alternative solutions, as they required rewriting entire rows or blocks (pending / echo / time / copy controls, userStack / attachment rows / bubble / JsonBlock), or violated the plugin discipline of “not writing DOM outside components and not reading other people’s DOM.”
Implementation constraints: Copywriting goes through the Client locale service (ctx.locale.register(NS, { zh, en }), the slot registration includes locale: NS to get the injected t from the framework); colors only use --dsw-alias-* theme tokens; no external Harness Client packages are required.
Regarding compatibility: The plugin depends on the slot conversation.user.text.node and the Client package @deepseek-ai/dsh-client-ui-conversation (see dsh.client.inject in package.json). It may become ineffective if the slot or shipped implementation undergoes major changes. The degradation is safe: if upstream renames the 'text' key or no longer has an occupant, the framework will use the fallback renderer to render the original text, and the message display is not affected—it just won’t be folded. The plugin is only registered on the Web platform (dsh.client.platform = "web"); the Host’s index.js is an empty implementation.
Applicable Scenarios & Notes¶
Suitable for: DSH users and AI agent developers who frequently paste long logs or text in the DSH Web interface and are willing to use a plugin to fill this gap; also suitable for plugin authors who want to see “how to use a slot with the same key to override a shipped renderer” on the client side.
Points to note:
- The plugin runs with the current DSH process permissions. You should open the source code and check before installing, and confirm that the license (MIT) meets your compliance requirements.
- The current star count for this repository is 0, making it a very early personal project. Function descriptions and parameters are based on the README; there are no signs of large-scale usage feedback, so it is recommended to treat it as a small tool for personal use and verify it in your own DSH environment.
- It only handles user message bodies and does not handle assistant output, tool-call, or process lines of thinking.
package.jsonis markedprivate: true, so it cannot be installed vianpm install; only the Plugin Manager’s git or local directory methods are available.- The directory page (skillhub.cn) is an independent community site with no official affiliation to DeepSeek / Huaxin, and is not an official app store.
Conclusion¶
The value of this plugin is narrow but clear: it solves the problem of “pasting large chunks of logs pushing away context” with folding + fade-out + one-click expand, while not changing short messages or streaming layouts. Install with a single command and refresh the page to take effect, without writing localStorage or leaving residue after uninstall. The directory page is at https://www.skillhub.cn/plugins/constraint21/dsh-long-message-fold, and the source code and README are at https://github.com/constraint21/dsh-long-message-fold.