dsh-message-copy-enhance
Manifest valid★ 4Copy dsh assistant output as Markdown, preserving links, LaTeX sources and code fences.
dsh-message-copy-enhance
A DeepSeek Harness client UI plugin: when you select text in a model message and copy it, the clipboard content is rewritten to Markdown, so links, LaTeX source, code-fence language info, and more are no longer lost.
How it works
DSH renders model output via dsh-client-ui-conversation → dsh-client-ui-primitives:
| Content | DOM shape | Recovery |
|---|---|---|
| Links | <a href="https://github.com/Asianfleet/dsh-message-copy-enhance/blob/HEAD/..."> | Take the href directly, output [label](https://github.com/Asianfleet/dsh-message-copy-enhance/blob/HEAD/url) |
| Inline / block LaTeX | KaTeX-rendered .katex / .katex-display | Retrieve the TeX source from the <annotation encoding="application/x-tex"> in the MathML branch, output $...$ / $$...$$ |
| Code blocks | .md-code-block (banner shows the language) + pre | Output a fenced code block; the language comes from the language-* class or the banner's infostring |
| Headings / emphasis / lists / quotes / tables | Standard HTML | Recovered as GFM |
The plugin listens for copy on document in the capture phase, and takes over only when the selection starts inside a [data-chat-flow-kind="assistant"] message and contains markdown-significant elements:
- Expand the selection to the full
.katexelement (selecting in the middle of a formula still gets the complete source) - Clone the selection DOM with
Range.cloneContents() - Recover the markdown with the built-in zero-dependency DOM→Markdown converter (
src/client/toMarkdown.ts, written in TypeScript, type-erased and inlined into the bundle) preventDefault()and writetext/plainandtext/markdown
Other selections (user bubbles, tool cards, plain text) and empty selections are completely unaffected; if conversion fails, the default copy behavior is restored automatically.
Project structure
dsh-message-copy-enhance/
├── package.json # dsh.client metadata (client plugin declaration)
├── tsconfig.json # typecheck config (strict, includes src/test/vite.config.ts)
├── vite.config.ts # vite build (DSH module-loader output) + vitest config
├── src/client/
│ ├── index.ts # plugin entry: copy interception + apply/inject (TS, apply(ctx) uses the official
│ │ # @deepseek-ai/cordis Context type, same as dsh-client-ui-*)
│ └── toMarkdown.ts # DOM→Markdown converter (TS, zero dependencies)
├── lib/
│ ├── index.js # Host-side no-op plugin (JS entry loaded by the DSH Loader)
│ └── index.d.ts # type declarations for the host entry
├── dist/client.js # build artifact (pre-generated)
└── test/ # vitest (.test.ts): converter / bundle / package layout
Build & test
npm install # dev deps: typescript / vite / vitest / @types/node / linkedom / @deepseek-ai/cordis (official types)
npm run typecheck # tsc -p tsconfig.json — full type check (strict)
npm run build # vite build — bundle src/client into dist/client.js in the DSH module-loader format
npm test # vitest run — 36 cases (converter / real bundle / package layout)
npm run test:watch # vitest — watch mode
npm run test:coverage # vitest run --coverage — v8 coverage report
Release
Releases are driven by release-it with a Conventional Commits changelog. Run from the main branch with a clean working tree:
pnpm release
It is interactive: first the commits since the last tag are shown as a changelog preview (grouped by type, including the conventional-commits recommended version), then you pick the next version from a prompt (patch / minor / major / pre-release variants / custom). What happens, in order:
- Checks (
npm run check:release): version is valid SemVer, the version is not already published on npm (or there are new commits since its tag),GITHUB_TOKENis set, npm is authenticated; then typecheck and the test suite run. - Prompt — the changelog preview is printed and the next version is chosen interactively (the recommended bump from conventional commits is shown;
feat→ minor,fix/perf/revert→ patch, breaking changes → major). - Changelog —
CHANGELOG.mdis regenerated from the commits since the last tag, under the chosen version. - Build —
npm run build(the DSH module-loader client bundle). - Release — commit
chore(release): vX.Y.Z, tagvX.Y.Z, push to GitHub, create a GitHub Release (release notes from the changelog), thennpm publish.
Prerequisites:
GITHUB_PAT_TOKENexported (a GitHub PAT — a classic token withreposcope, or a fine-grained token with Contents: Read and write on this repo;gh auth tokenalso works;GITHUB_TOKENis accepted as a fallback), andnpm login. The collaborator pre-check is skipped (github.skipChecks), since fine-grained tokens get a 403 on that endpoint; GitHub still enforces the real permissions when the release is created.- Commits following Conventional Commits; the script only recognizes those types.
Useful variants:
pnpm release:dry # dry-run: previews everything without changing anything
pnpm release -- --increment=patch # skip the prompt and force a patch bump (or minor/major)
pnpm release -- --ci # fully non-interactive (for CI; falls back to a patch bump)
Note: if a CI environment variable is set, release-it automatically switches to non-interactive mode.
Install
dsh plugin --profile desktop add dsh-message-copy-enhance
Upgrading
dsh plugin --profile desktop add dsh-message-copy-enhance@latest
This package is pre-1.0, and a caret range like ^0.1.0 covers patch releases only (0.1.x) — a minor bump (0.1 → 0.2) falls outside the range, so dsh plugin update will not pick it up. Upgrade explicitly with @latest (or @<version>).
Usage
Select content in any model answer and press Ctrl/Cmd+C, then paste into Typora / Obsidian / VS Code / any markdown editor to get the full markdown.
Limitations
- Selections spanning multiple messages are not intercepted (only selections starting inside an assistant message are handled).
- Links filtered out by the render whitelist (e.g.
file:, relative links) have nohrefto begin with and cannot be recovered. - Code blocks only recover the selected lines (no forced expansion of the whole block); the language tag is missing when the selected region does not include the banner.
- Elements shown as
.katex-error(KaTeX render failure) are output as$source$. - Tables are output as GFM pipe tables;
|is escaped.
License
MIT — Copyright (c) 2026 Asianfleet
Compatibility
Versions
| Latest version | Published | Size |
|---|---|---|
| 0.1.0 | — | — |
| 0.2.0 | — | — |
| 0.2.1 | — | — |
Comments
Loading…
Similar plugins
by latte03
Select text in the dsh web conversation to attach it as an annotation to your next message. The source text keeps a dashed highlight with an ordinal badge in place, and the composer summary lists, edi
★ 3
MIT
TypeScript
Oct 28, 2026
dsh plugin --profile web add dsh-select-quoteMessage and attachment editing with save-only updates, forked regeneration, and a version timeline for DSH desktop.
★ 0
dsh plugin --profile web add @sh1robana/dsh-plugin-message-editby 2310770803
One button in the DSH session header: copy a deep link, the whole conversation as Markdown, or the working directory. · DSH 会话标题栏一键复制:深度链接、整段对话 Markdown、工作目录。
★ 1
MIT
JavaScript
Oct 23, 2026
dsh plugin --profile web add dsh-session-copyby liuup
♾️ Copy and export the LaTeX in DeepSeek Harness 悬停任意 LaTeX 公式即可复制 TeX 源码或导出为独立的 SVG 文件
★ 9
↓ 276/wk
MIT
TypeScript
Oct 12, 2026
dsh plugin --profile web add dsh-latex-toolsby long6177
a dsh‑plugin for enhancing users' Markdown input experience | 一个 用于增强用户 markdown 输入体验的 dsh-plugin
★ 0
MIT
TypeScript
Oct 7, 2026
dsh plugin --profile web add dsh-markdown-inputby lingchunya
为 DeepSeek Harness 提供 Codex 风格工作区会话绑定、会话顶部快捷归档及已归档会话管理中心
★ 0
MIT
TypeScript
Sep 9, 2026
dsh plugin --profile web add dsh-workspace-enhance