Bundle
dsh-message-copy-enhance
Copy dsh assistant output as Markdown, preserving links, LaTeX sources and code fences.
- Source
- Asianfleet
- stars
- 3 stars
- License
- MIT
- Updated
- Updated 2 hours ago
Readme
# dsh-message-copy-enhance [](https://www.npmjs.com/package/dsh-message-copy-enhance) [](https://www.npmjs.com/package/dsh-message-copy-enhance) [](LICENSE) [](tsconfig.json) [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 ```bash 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](https://github.com/release-it/release-it) with a Conventional Commits changelog. Run from the `main` branch with a clean working tree: ```bash 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_TOKEN` exported (a [GitHub PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) — a classic token with `repo` scope, or a fine-grained token with **Contents: Read and write** on this repo; `gh auth token` also works; `GITHUB_TOKEN` is accepted as a fallback), and `npm 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](https://www.conventionalcommits.org/); the script only recognizes those types. Useful variants: ```bash 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 ```bash dsh plugin --profile desktop add dsh-message-copy-enhance ``` ## Upgrading ```bash 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 no `href` to 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](LICENSE) — Copyright (c) 2026 Asianfleet
Install
dsh plugin --profile web add github:Asianfleet/dsh-message-copy-enhance#9d8595501224621db5942fadddcbdb0efceba686
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-message-copy-enhance from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.