DeepSeek Harness Plugin

senyayume/dsh-edit-diff

Stars ★ 0 Category UI Enhancements Added 2026-09-21

Redraws line-level diffs on edit, write, insert and str_replace_editor tool cards — unchanged lines render once and changed characters are underlined — covers run_code (PTC) sub-calls, adds a per-turn changed-files card, and opens or copies any of those paths from the card rows and the tool rows through a host route the plugin serves itself.

Install

# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)

dsh plugin --profile web add github:senyayume/dsh-edit-diff

Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).

README

English | 中文

A DeepSeek Harness client plugin that redraws line-level diffs on file-mutation tool cards: unchanged context lines render once, changed lines carry a full-width red/green background rather than coloured text, and the characters that actually changed take a darker shade of that same background. The code itself is syntax-highlighted by a tokenizer shipped inside this plugin, using the same --shiki-token-* theme variables the built-in code and read blocks colour their tokens with — so the palette matches, both themes are followed, and no DSH core file is touched. It takes over the edit / write / insert / str_replace_editor rows and the edit / write sub-calls that run_code (PTC) makes — the two shapes the built-in diffCardModel refuses outright. A long hunk is not folded: it lives in a max-height: 320px scroll container with every row in the DOM, while the copy button and the footer stay outside the scroll area. A zero half of +N -M is dropped on the header and on each per-file row (+87, not +87 -0). At the end of a turn it adds a changed-files card for that turn (per-file +N -M, collapse, review, open).

Right-clicking a row — on the turn card or on a tool row — offers reveal in file manager, copy file path and copy folder path. Reveal runs through a host route the plugin serves itself (explorer.exe /n,/select,… on Windows, open -R on macOS, xdg-open on Linux), with the official session/openWorkspacePath opener as a fallback that reports why it was used.

This page is a summary. The Chinese README (README.md) is the canonical document: it carries the measured numbers, the data contracts that were checked against the shipped bundles, and the reasons behind what we do not do. Where the two disagree, the Chinese one wins.

Screenshots

Diff body on a tool row

Turn change card with its context menu

The same context menu on a tool row

Install

dsh plugin add github:senyayume/dsh-edit-diff --profile desktop

The package declares dsh.bundlecordis.patch.yml, so it mounts itself as a profile layer — no manual entry to write.

Two halves, two reload rules. lib/client.js is read from disk on every page load, so a window refresh is enough. lib/index.js is loaded when the harness process starts, and DSH Desktop only hides to the tray when its window is closed — restart it from the tray. Reloading only the window leaves the reveal route unregistered, and the client's POST comes back as 405 from the static frontend.

Requirements

  • Tested on the DSH Desktop harness 0.1.5-rc.2 (@deepseek-ai/dsh-client-ui-primitives 0.1.5-rc.2); route registration was also checked on CLI harness 0.1.1. On a harness without uiConversation.events and the conversation.chat.turnTail slot the plugin degrades quietly to taking over the tool rows only.
  • Node ≥ 18 and React 18 to run this repository's tests.
  • Reveal is verified on Windows 11. The macOS and Linux commands exist in the code but are untested on real machines.

Verify

npm test                    # = node test/smoke.mjs && node test/host.mjs
node --check lib/client.js

Limits

No undo/redo and no real file line numbers; each is left out for a concrete reason recorded in the Chinese README's 已知限制 section, including why a DSH core patch is not an option here.

Syntax highlighting uses a local tokenizer shipped with this plugin, because @deepseek-ai/dsh-client-ui-primitives@0.1.5-rc.2 does not export its highlighter: highlightLines, subscribeGrammarLoaded and grammarLoadCount are defined but not exported — they are visible inside the module and used by the package's own CodeBlock, yet absent from its export {} list. So the plugin cannot reuse the built-in highlighter, and patching the shipped primitives is not an option either (that is an official file).

The palette is still shared with the built-in blocks: CodeBlock tokenizes through shiki's createCssVariablesTheme (variablePrefix: "--shiki-"), so every token colour is a --shiki-token-* custom property that the theme package defines in :root for light and body[data-ds-dark-theme] for dark. The local tokenizer maps its own token classes onto those same variables, so a token in a diff and the same token in a code block render the same colour, in both themes. No colour literal is hard-coded in the plugin. Note those variable names belong to the theme package: renaming them there means updating TOKEN_COLOR here.

The tokenizer is an approximation, coarser than shiki: it recognizes comments, strings, keywords, numbers, call names and punctuation from small literal tables rather than a grammar, so nested interpolation, regex-versus-division and heredocs are not understood. An unrecognized word simply keeps the default foreground — it is never mis-coloured. Multi-line strings (Python docstrings, JS template literals) are threaded across lines as scanner state, and all offsets are code-point based so an astral glyph cannot shift a token boundary.

Prose is not tokenized, and data is coloured lexically only — added after a user screenshot showed the first cut colouring English at random. A code tokenizer reading prose highlights in, as, with, this, for, is, not, and, or and package (all keywords), lets an apostrophe in shiki's open a string that swallows the rest of the line, and paints MIT / WASM / TODO as constants. So:

  • plain (md, mdx): markdown is prose and is not tokenized at all; the red/green wash and the inline change marks still apply. The built-in CodeBlock handles markdown because shiki's markdown grammar separates prose from fenced code — an approximation cannot, so it colours nothing rather than colouring wrongly.
  • lexical only (yaml, toml, ini): comments, strings and numbers are coloured, but words are never classified, so description: install in the for as is not is not speckled with keyword colours.
  • full (every other code language): comments, strings, keywords, numbers, call names, punctuation.

Two more rules keep prose readable everywhere: a single-character quote only opens a string when the preceding code point is not a word character (shiki's, don't, 12" wide no longer swallow a line, while 'abc' in real code still works), and SCREAMING_CASE must be longer than one character (a lone A is no longer painted as a constant).

A file whose extension is not in the allowlist — Makefile, .gitignore, no extension at all — renders as plain text rather than guessing a language. Nothing is lazily loaded, so there is no first-frame plain render and no grammar subscription.

License

MIT — see LICENSE.

Content from the project README on GitHub ↗

Links

More in this category

View the whole category →

Community comments

Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.