feat(desktop): PR-style file diffs in chat

Render write_file/edit_file/patch as a reviewable diff instead of raw
result JSON, closer to a Cursor/T3 per-edit review.

- Unified diff via FileDiffPanel: strip git file-header + @@ hunk noise,
  drop the +/- gutter, color by line with a 2px gutter accent, full-bleed
  to the card, transparent context lines, compact scroll height.
- Header shows filename + language icon + +N/-N stats; full path moves to
  a hover tooltip (no Edited verb, no ms).
- Treat the three file-edit tools uniformly (isFileEditTool); read diff
  from inline_diff or patch's diff field; suppress raw-arg detail.
- Reusable FileTypeIcon primitive sharing the code-block icon mapping
  (codiconForFilename), codicon fallback.
- Per-row scaffolding fade (not the group wrapper, which trapped child
  opacity); expanded edits stay full, collapsed fade; keyboard-only focus
  lift. Hide diff-less rehydrated creates that read as dupes.
This commit is contained in:
Brooklyn Nicholson 2026-06-22 05:04:13 -05:00
parent fb3d31ba8b
commit a61baa9615
7 changed files with 451 additions and 50 deletions

View file

@ -0,0 +1,22 @@
import { ToolIcon, type ToolIconProps } from '@/components/ui/tool-icon'
import { codiconForFilename, codiconForLanguage } from '@/lib/markdown-code'
export interface FileTypeIconProps extends Omit<ToolIconProps, 'name'> {
/** A code-fence language tag (e.g. `ts`, `json`). Used when no `path`. */
language?: string
/** A file path or bare name; its extension selects the icon. Wins over `language`. */
path?: string
}
/**
* Icon for a file or code language, resolved through the one mapping shared
* with code blocks (`codiconForFilename` / `codiconForLanguage`). Renders via
* `ToolIcon`, so it uses a filled glyph when one exists and falls back to the
* outline codicon font otherwise. Pass a `path` for file rows or a `language`
* for fenced code.
*/
export function FileTypeIcon({ language, path, ...props }: FileTypeIconProps) {
const name = path ? codiconForFilename(path) : codiconForLanguage(language)
return <ToolIcon name={name} {...props} />
}