DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

wangjiezhe /

wangjiezhe/dsh-md-linebreak

Verified

将软换行视为硬换行

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@0a035270

dsh-md-linebreak — 正文里的换行不再被合并

它解决的问题

Web 客户端把 Markdown 交给 MarkdownText 渲染,而 CommonMark 的软换行(正文里 单独一个换行)在渲染树里只是一个文本节点里的 \n:

case "text": return node.value

文本节点里的换行在 HTML 里是可折叠空白,所以源码写了多行的段落会被画成一行:

第一行
第二行        →   显示为「第一行 第二行」

DSH 对用户自己发的消息是保留换行的(气泡用 white-space: pre-wrap),只有渲染出来 的 Markdown 会丢——这个插件补的就是这一处。

它做了什么

宿主半边在每次采集注入表时(HTTP 服务每次响应 index.html,以及 host 启动时那一次采集) 往表里推一行 style(webserver/index-inject),规则是:

:is([class*='_markdown_'], [data-dsh-part='message-body']) :is(p, td, th) { white-space: pre-line }
:is([class*='_markdown_'], [data-dsh-part='message-body']) li:not(:has(> :is(p, ul, ol, pre, blockquote, div, table, hr, h1, h2, h3, h4, h5, h6))) { white-space: pre-line }
  • pre-line 就是全部修复:换行按换行显示,其余空白(空格、制表符)照常折叠, 长行仍然在容器边缘自动折行。等价于 GFM 的 breaks: true。 如果你想让被换行的源码连缩进也逐字保留,把 pre-line 换成 pre-wrap 即可 (就在 lib/index.js 的 STYLE 里,改一处)。
  • 代码不动:pre / code 有自己的 white-space 策略;列表项只在内容是行内 时才命中规则——渲染器会在容器的块级子节点之间插入装饰性换行文本节点,pre-line 会把那些画成空行,所以带嵌套列表 / 代码块 / 段落的列表项被排除在外。
  • 两个锚点:[class*="_markdown_"] 是 MarkdownText 的根类(CSS Modules 会写成 _markdown_<hash>_<line>,稳定的部分是本地名),[data-dsh-part="message-body"] 是皮肤中心语义属性契约里「助手消息正文」的官方锚点(启用皮肤时才存在)。任一命中即可。

为什么走 index 注入表

那张表有两个消费者:webserver 每次响应 index.html 时把行渲染进 <head>;Desktop 壳 在启动 payload 里收到同一批行。一行同时覆盖两种载体。因此:

  • 不写入 harness home,也不修改已安装的应用文件(app.asar 一个字节都不动);
  • 升级 DSH 不会把它冲掉,规则失效也只会表现成「换行又被合并」;
  • 卸掉这个 bundle,下一次 host 启动(用浏览器访问 HTTP 端口的话则是刷新)后就没有这条规则了。

怎么生效 / 怎么验证

  1. 重启 DSH(桌面端必须重启,原因见下)。
  2. 回到会话里看一条正文写了多行的回复:每一行各自占一行。
  3. 校验行确实被推入注入表(不需要浏览器):
    $node tools\dsh-env\run-mdlinebreak.mjs
    
    其中一项用真实 cordis 派发 + 官方 renderIndexInjections 渲染真实的 dist/index.html, 断言这条 <style> 落在 <head> 里、__DSH_BOOT_READY__ 未被扰动。
  4. cordis_inspect_query(platform: host, provider: Config, method: listConfigs, input: {name: '@workspace/dsh-md-linebreak'}) 应能看到 include:md-linebreak 这一行,且状态不是 inactive。

为什么桌面端要重启

注入表有两个消费者:HTTP 服务的每次 index 响应,以及桌面壳的启动 payload。浏览器直接访问 http://127.0.0.1:19387 时,刷新即可(每次响应重新收集);桌面端的窗口加载的是 dsh-app://app/(应用自有协议,Electron main 从 app.asar 里的同一份 dist 提供),注入行不是 从 index.html 里读的,而是窗口启动时经 IPC 取「host ready 那一刻采集的那一份」 (dsh-desktop-host/lib/index.js 的 ready 消息 → lib/main.js 的 ipcMain.handle(DESKTOP_IPC.boot))。新增的行因此只在下一次 host 启动时进入窗口, 刷新页面拿到的是同一份 payload。

怎么关掉

设置 → 插件里禁用这个 bundle,然后重启(或刷新,见上)。 彻底移除:plugin_manager(remove_bundle, target: "@workspace/dsh-md-linebreak")。

安装与维护

# 安装(会写入 profile 的 node_modules 与 dsh.profile.bundles)
plugin_manager(install_bundle, target: "file:C:\Users\wangjiezhe\Documents\deepseek-harness\default-workspace\dsh-md-linebreak")
# 改完源码后同步到已安装副本(pnpm 是 COPY,不是 junction)
pwsh -File dsh-md-linebreak\sync-installed.ps1
# 校验:逐文件 SHA256 + 关键标记
$node tools\dsh-env\verify-installed.mjs
dsh-md-linebreak/
  package.json        dsh.bundle.patch 指向 cordis.patch.yml
  cordis.patch.yml    插入一行 id: md-linebreak
  lib/index.js        规则本体 + 一行 style 注入(全部逻辑都在这一个文件里)
  sync-installed.ps1  按内容哈希同步到已安装副本
  README.md           这份文档

已知边界

  • 换行只在正文层面恢复:代码块本来就不折叠;表格单元格在 GFM 里本来就不能跨行 (规则对它没有副作用)。
  • 规则是 CSS,锚点依赖上游的类名 / 语义属性。上游若改名,表现是规则静默失效 (不会画错),此时更新 ANCHOR 常量即可。
  • 这条规则作用于所有 MarkdownText 表面:聊天正文、思考过程(compact 变体)、 右侧文档预览。它们共用同一个渲染器,所以行为一致。
—/ 5

No ratings yet

Verified DSH bundle

Commit 0a0352706498

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout