dsh-message-copy-enhance
Copy selections from assistant messages as Markdown, preserving links, LaTeX sources, and code-fence language info.
Install
dsh plugin --profile web add dsh-message-copy-enhance
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-message-copy-enhance




[English](README.md) · [中文](README_zh.md)
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="..."> · Take the href directly, output [label](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:
1. Expand the selection to the full .katex element (selecting in the middle of a formula still gets the complete source)
2. Clone the selection DOM with Range.cloneContents()
3. 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)
4. preventDefault() and write text/plain and text/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:
1. 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_TOKEN is set, npm is authenticated; then typecheck and the test suite run.
2. 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).
3. Changelog — CHANGELOG.md is regenerated from the commits since the last tag, under the chosen version.
4. Build — npm run build (the DSH module-loader client bundle).
5. Release — commit chore(release): vX.Y.Z, tag vX.Y.Z, push to GitHub, create a GitHub Release (release notes from the changelog), then npm 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 bu …(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::3,最后更新 2026-08-19