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

@ -1214,19 +1214,33 @@ canvas {
background: transparent !important;
}
[data-slot='aui_assistant-message-content'] > :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure']) {
/* Fade scaffolding so the prose reading column stays primary. Two targets:
a thinking disclosure fades as one block, and each *individual* tool row
(`[data-tool-row]`) fades on its own. We deliberately do NOT fade the tool
group wrapper (`[data-tool-group]`): opacity on a parent opens a stacking
context, so a child row can never be more opaque than the group that made
it impossible to keep one row lit (an open diff) while its siblings faded.
With the fade per-row, each row hovers/focuses independently. */
[data-slot='aui_assistant-message-content'] > [data-slot='aui_thinking-disclosure'],
[data-slot='aui_assistant-message-content'] [data-slot='tool-block'][data-tool-row] {
opacity: 0.67;
transition: opacity 120ms ease-out;
}
[data-slot='aui_assistant-message-content']
> :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure']):is(:hover, :focus-within) {
/* Lift on hover or *keyboard* focus only. `:focus-within` also matches the
focus a mouse click leaves on the disclosure toggle, which kept a row lit
after you clicked to collapse it; `:has(:focus-visible)` excludes that. */
[data-slot='aui_assistant-message-content'] > [data-slot='aui_thinking-disclosure']:is(:hover, :has(:focus-visible)),
[data-slot='aui_assistant-message-content'] [data-slot='tool-block'][data-tool-row]:is(:hover, :has(:focus-visible)) {
opacity: 1;
}
/* A generated image is the deliverable, not scaffolding keep it at full
strength instead of dimming it until hover. */
[data-slot='aui_assistant-message-content'] > [data-slot='tool-block']:has([data-slot='aui_generated-image']) {
/* File edits (write_file / edit_file / patch) are the deliverable, not
scaffolding the diff is what the user reviews, like a PR. An *expanded*
edit stays at full strength; collapsed it fades like any other row. The
`data-file-edit` marker sits on the same row element and is only present
while the row is open. */
[data-slot='aui_assistant-message-content'] [data-slot='tool-block'][data-tool-row][data-file-edit] {
opacity: 1;
}