# dsh-ptc-diff-card [English](README.en.md) · [简体中文](README.md) DSH Web GUI plugin: shows the standard diff card for `edit`/`write` tool calls made inside PTC programs. ## The problem In PTC (Programmatic Tool Calling) mode the agent calls `edit`/`write` from inside a `run_code` program. Those calls are recorded as nested `tool/ptc-dispatch` events and rendered as generic rows under the `run_code` card: expanding one shows the result text (`The file ... has been updated successfully`) instead of a diff card. The shipped Tool layer derives its diff card in `packages/client/ui-tool` (`diffCardModel`) and stops at root calls on its first line: ```ts if (block.parentCallId !== undefined) return null ``` Everything the card needs is already on the nested block: the call arguments (`file_path`/`old_string`/`new_string`/`content`) and the persisted `meta.diffs`. ## What you get - Nested `edit`/`write` rows become standard file cards: title, workspace-relative path, decoded input size, `+added -removed`; - expanding renders the shipped `DiffBlock`: language label, wrap/copy actions, path line, colored additions and deletions, `⋯ N more lines` folding; - failed calls render no diff: the error's first line in the summary, raw output expanded (same as the stock row); - root calls look byte-for-byte identical to the shipped row, because the whole row is a faithful re-implementation. ## How it works The plugin registers its own `edit`/`write` views on the `tool.call.toolview` keyed slot at `priority: -1`, which shadows the shipped rows while mounted and restores them on unload. The row re-implements the stock file-mutation row chrome (same `DisclosureRow`/`DiffBlock` primitives plus a vendored copy of the stock stylesheet) and reuses the stock diff derivation minus the root-only guard: | Call state | Diff source | |---|---| | settled success with non-empty `meta.diffs` | the recorded applied hunks (hunk paths rewritten to the caller's spelling) | | `write` create / call still running | whole-file diff derived from the arguments | | failed / interrupted | no diff | ## Install Mount it as a normal DSH bundle: ```sh # local directory (development) dsh plugin --profile add link:D:/dsh-plugins/dsh-ptc-diff-card # from GitHub dsh plugin --profile add "github:GrounzerLiu/dsh-ptc-diff-card" # from npm (once published) dsh plugin --profile add dsh-ptc-diff-card ``` Then add `dsh-ptc-diff-card` to that profile's `package.json` `dsh.profile.bundles` and restart the app. ## Compatibility and limits - Targets DSH `>= 0.2.1-rc.2` (`engines.dsh`) on the Web GUI (`platform: web`). - Takes over `edit` and `write` only; `str_replace_editor`/`apply_patch` are not covered. - The row stylesheet is a vendored snapshot of ui-tool 0.2.1-rc.3 chrome; re-vendor after a major restyle. - Cannot coexist with another plugin shadowing the same `edit`/`write` keys at `priority: -1` (e.g. `dsh-diff-viewer`): the slot rejects equal key + priority. This plugin catches that per key and logs, leaving the shipped row in charge instead of failing the whole plugin load. - No auto-expand; behavior matches standard mode. ## Development ```sh pnpm install pnpm run check # typecheck + pure derivation assertions + build pnpm run build # lib/index.js and lib/client.js only ``` ``` src/index.ts host half: inert Loader entry (mount point for the browser half) src/client/index.tsx slot registration (edit/write, priority -1) src/client/row.tsx the row component (preparing + dispatched/settled stages) src/client/model.ts row state, paths, and diff derivation src/client/tool-row.module.css vendored stock row stylesheet scripts/model-check.ts pure derivation assertions (Node type stripping, no extra deps) ``` The browser half is bundled with the DSH client-plugin wrapper (`window.__ModuleLoader__.load({ id, factory })`): the platform module table stays external, everything else is inlined, and CSS Modules compile into a hashed class map plus one `