feat(desktop): let a tool say how it should be drawn - #271
Open
oratis wants to merge 1 commit into
Open
Conversation
Every tool call rendered as the same grey text blob. An Edit showed its result sentence but never the change; a Bash showed its output with no sign of which command produced it. The information was all present in the arguments — nothing was reading it. A tool now declares a render intent alongside its schema, and clients read that instead of each hardcoding tool names. Bash declares terminal, Edit/Write/ NotebookEdit declare diff, everything else stays generic. The alternative was `name === 'Edit'` in the desktop, again in the CLI, again in the VS Code extension — three copies of one fact, and a fourth to write for the next tool. Declaring it once on the tool is why the mapping is in core. Presentation is a pure function of the call's arguments. It never reads the result or the filesystem, so a session replayed from its log renders exactly as it did live. That constraint is also why Write renders as wholly added: its arguments genuinely do not say what the file held before, and inventing a before-side would be a nicer-looking lie. A declared intent is a request, not a guarantee. A call declaring diff with no usable path or text falls back to generic rather than handing the client an empty diff to draw. Verified in a dev-only preview harness: real +/- colouring on both diff cases, the command as a shell prompt above its output, running and error states, and generic left untouched. The harness is excluded from the production bundle the same way the FilePanel one is. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourth implementation PR from
docs/DSH_ADOPTION_PLAN.md§1.6 — the UI half of the DeepSeek Harness adoption.THREE_WAY_REVIEW.mdconcluded the next stage's ROI is almost entirely in UI outlets rather than the feature list. This is the most concentrated one: users spend most of their time looking at tool cards, and every card looked identical.Before
An
Editshowed its result sentence but never the change. ABashshowed output with no indication which command produced it. All of it was already in the arguments — nothing read them.After
A tool declares a render intent next to its schema, and clients read that:
Bashterminal$prompt, output below a ruleEdit,Write,NotebookEditdiff+/-lines, colouredgenericWhy the mapping lives in core
The opponent case in the plan was that presentation does not belong in
packages/core, which advertises no UI dependency — and thatToolCardalready had adiffboolean, so the desktop could just checkname === 'Edit'.It gains no UI dependency: the intent is a string enum and the payload is plain data, no React or DOM types anywhere near it. And the alternative is that one fact — "an Edit is a diff" — gets written in the desktop, again in the CLI, again in the VS Code extension, with a fourth copy owed for every new tool. Declared once on the tool, each client reads the same answer. The desktop's own duplicate
pickTargetkey list is deleted in this PR for the same reason.Purity, and the honest Write
Presentation is a pure function of the arguments. It never reads the result or the filesystem, so a session replayed from its log renders exactly as it did live.
That constraint is why
Writerenders as wholly added. Its arguments genuinely do not say what the file held before, and synthesising a before-side would be a better-looking lie.Editstates both sides, so it gets a true two-sided diff.A declared intent is a request, not a guarantee: a call declaring
diffwith no usable path or text falls back togenericrather than handing the client an empty diff to draw. Tested.Verification
Visually verified in a dev-only preview harness (screenshot below) across all six states — both diff cases, terminal with output, terminal with an error, generic untouched, and a running command with no output yet. Console clean on a fresh tab. The harness is excluded from the production bundle exactly as the FilePanel one is;
pnpm --filter @deepcode/desktop buildconfirms the output still contains onlyindex.html.pnpm typecheck,lint,format:check,buildclean. Full suite green — core 1045 passed / 28 skipped, desktop 104, cli 242, server 49, protocol 34, lsp 13, vscode 12, scripts 42. 11 new tests, including one asserting the built-in intent table and the tool definitions cannot drift apart.One small fix included:
Bashno longer repeats its command in the header, since the terminal body already opens with it in full.