dsh-markdown-copy
在 DSH Web 会话里用鼠标选中对话内容并按 Ctrl+C(或右键菜单“复制”)时,把选区还原成 Markdown 源码写入剪贴板。
DSH 的消息本来就是由 Markdown 渲染出来的:标题、列表、代码块(含语言)、GFM 表格、粗体/斜体/删除线、链接、KaTeX 公式等信息都保留在 DOM 里。本插件在浏览器 copy 事件(捕获阶段)里读取选区,遍历选中的 DOM 片段重新生成 Markdown,然后覆盖 text/plain。粘贴到编辑器或笔记里得到的是可继续编辑的 Markdown,而不是渲染后的纯文本。
行为
- 只处理选区完全落在对话流
[data-chat-flow]内的复制操作。 - 输入框、多行编辑框、可编辑区域里的复制不受影响。
- 选区可跨多条消息,按文档顺序输出。
- 内置的“复制”按钮保持原样(DSH 自带的复制按钮输出的已经是 Markdown 源码)。
- 选区落在表格或代码块内部时,会扩展到整个表格 / 整个代码块,保证粘贴后的结构完整。
- 选区落在行内构件内部(加粗、斜体、行内代码、链接、公式、标题、引用、列表项)时,按祖先链补回对应标记,只输出选中的文字。
转换规则
| DOM | Markdown |
|---|---|
h1–h6 |
# … ###### |
p、块级容器 |
段落,之间空一行 |
ul / ol / 嵌套列表 |
- / 1. ,嵌套缩进 2 空格,任务列表输出 [x] / [ ] |
blockquote |
每行加 > |
pre / .md-code-block |
围栏代码块,语言取自代码块 banner 的语言标签 |
table |
GFM 表格,按单元格对齐方式(textAlign 样式或 align 属性)输出对齐行 |
strong / em / del |
** / * / ~~ |
code |
反引号,按内容自适应围栏长度 |
a / img |
[text](href) /  |
.katex / .katex-display |
$…$ / $$…$$,TeX 取自 KaTeX 的 application/x-tex 注解 |
hr、br |
---、\ 换行(与渲染器的软、硬换行设置无关) |
[data-clock] 操作行、代码块 banner |
跳过 |
转义与等价性
正文文本会被逐字符转义(*、`、[、]、<、成对 $、行首的块语法、&entity; 等,按上下文判断),保证粘贴后重新渲染的结果与屏幕上选中的内容一致;代码围栏、行内代码、公式与链接目标属于各自的语法,不做转义。
这条契约是渲染等价,不是逐字节还原:渲染阶段已经消耗掉的源写法(\# 与 #、* 与 - 列表标记等)在 DOM 里无法区分,任何实现都恢复不了。需要与模型原文逐字节一致时,请使用每条消息自带的复制按钮。
安装
dsh plugin add https://github.com/DrinkerqvB/dsh-markdown-copy
安装到桌面客户端后刷新会话页面即可生效,无需重启。
开发
node build.mjs # 由 src/copy-markdown.js 生成 client.js
cd test && npm install && node serialize.test.mjs
src/copy-markdown.js 是纯函数脚本(无 import),build.mjs 会把它内联进 client.js(Client bundle 的 window.__ModuleLoader__.load 包装)。测试在 jsdom 里直接加载源码和 client.js,不依赖其他构建步骤。提交前请运行 node build.mjs 重新生成 client.js。
已知限制
- 逐字节还原不可行,见上一节。
- 选中表格或代码块的一部分时会把整个构件输出,内容多于选中的部分。
- 模型原文里的原始 HTML 标签在 DSH 里本就不渲染,自然也不会出现在输出里。
English
Copies the selected DSH conversation content as Markdown source when you select text with the mouse and press Ctrl+C (or use the context-menu copy).
DSH renders messages from Markdown: headings, lists, code blocks (with language), GFM tables, bold/italic/strikethrough, links and KaTeX math are all preserved in the DOM. The plugin listens for the browser copy event in the capture phase, keeps the event only when the whole selection lives inside the conversation flow ([data-chat-flow]), serializes the cloned selection back into Markdown and replaces text/plain. Pasting into an editor or note app yields editable Markdown instead of rendered plain text.
- Selections that start or end outside the conversation flow, and copies inside inputs, textareas or contenteditable regions, are left untouched.
- Selections spanning several messages are emitted in document order.
- The built-in per-message copy button is untouched: it already writes Markdown.
Install with dsh plugin add https://github.com/DrinkerqvB/dsh-markdown-copy.
Prose is escaped by context (*, backticks, [, ], <, paired $, line-start block syntax and &entity;), so pasting re-renders exactly what was on screen; code blocks, inline code, math and link destinations keep their own syntax untouched. The contract is render-equivalence, not byte-exact source: syntax already consumed by rendering (such as a literal \*) cannot be recovered — use the built-in per-message copy button when you need the original bytes.
Selections inside a table or code block are widened to the whole construct; selections inside an inline construct (bold, inline code, link, heading, quote, list item) get their marker rebuilt around the selected text only.
No comments yet. Be the first to write one.