[design-doctor effort high] replacement clone of sourcebot#2: [design-doctor test] sourcebot-1154: Commit diff viewer#26
Conversation
Adds a `commit` pathType to the browse routes (`/browse/<repo>@<branch>/-/commit/<sha>[/<file>]`) that renders a placeholder CommitDiffPanel. Refactors browse path helpers into a discriminated `BrowseProps` union so commitSha is required only for pathType: 'commit'. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires up @codemirror/merge (via react-codemirror-merge) inside CommitDiffPanel with a static before/after demo. Adds a CodeDiff component that owns its language extension + view ref so each pane can reconfigure its language compartment independently. Also gates the react-grab dev scripts behind DEBUG_ENABLE_REACT_GRAP so they don't load on every dev page render. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the CodeMirror MergeView-based commit diff rendering with a DOM based split-view that renders directly from git's hunks, inspired by Chrome DevTools' DiffView. Per-file editor instances and the matching getFileSource fetches are gone — a 50-file commit drops from ~100 network requests to 0, and per-row render cost from a full editor mount to a synchronous Lezer highlight + grid emit. - New `LightweightDiffViewer` builds a single 2-column subgrid with hunk headers spanning both sides; each cell uses `subgrid` so line numbers, markers, and content align across all rows. - Pure helpers split out: `hunkParser` (body string → DiffLine[]), `splitPairing` (DiffLine[] → SplitRow[]). - `presentableDiff` from @codemirror/merge supplies character-level intra-line diff highlighting on paired modifications. - Lezer highlight code lifted from `lightweightCodeHighlighter` into `lib/codeHighlight` so both files share the helper. - Drop `react-codemirror-merge` and `commitDiffEditor`. Long lines wrap via `whitespace-pre-wrap break-words` + `minmax(0, 1fr)` on the grid. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- File path header now sticks to the top of the scroll viewport while scrolling through that file's diff, using the negative-yOffset trick to compensate for the virtualizer's translateY positioning. Same pattern as searchResultsPanel/fileMatchContainer. - Lightweight diff viewer's grid uses `minmax(<min>, max-content)` for line-number and marker columns so they don't collapse to zero width when one side of the diff is entirely blank (fully-added or fully-deleted files), keeping the right pane aligned across files. - Drop the column gap between left and right panes and instead draw a `border-r` separator on the left cells for a cleaner divider. - Hunk header gets an optional className so the first hunk renders with just `border-b` (the file header above already provides the top border), while subsequent hunks render with `border-y` between them. - Drop the per-row footer padding in the virtualizer; rows now sit flush against each other. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New `DiffStat` component renders GitHub-style additions/deletions counts with a 5-square indicator scaled log-ish to total change size. Added on the right of each file row header and on the right of the "N files changed" subheader for the commit total. Hidden when there are no line-level changes (pure renames). - Each file row gets a `CopyIconButton` next to the path (copies newPath, falling back to oldPath) and a `CommitActionLink` that opens the file at the commit. Deleted files link to the old path at the parent commit so the user lands on the file's last existing state rather than a 404. - `repoName`, `commitSha`, and `parentSha` are plumbed from the panel through `FileDiffList` to `FileDiffRow` to support the new link. - `computeChangeCounts` is memoized per file in the row. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nchoring
History panel rows in both the bottom panel and the commits page are now
clickable — they navigate to the matching commit diff via router.push,
with closest('button, a') short-circuit so inner action buttons keep
their own behavior. Bottom-panel history rows also highlight via
bg-accent when their commit is the one currently being viewed.
Commit diff header now uses AuthorsAvatarGroup + getCommitAuthors +
formatAuthorsText, matching latestCommitInfo and historyRow — co-authors
parsed from the commit body show up correctly.
When the URL trailing path matches one of the commit's files, that file
is moved to the top of the FileDiffList rather than scrolled to. Avoids
estimateSize-based scroll inaccuracy and works regardless of which side
of a rename the URL points to.
Lightweight diff viewer short-circuits with "Diff too large to display"
for files containing lines over 1000 chars, matching the cutoff in
lightweightCodeHighlighter.
PathHeader's breadcrumb measurement reserved 175px for "copy button and
padding"; the actual reservation needed is ~40px. Reduced so breadcrumbs
no longer collapse prematurely on wide layouts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Lift `truncateSha` (was a private helper in getDiffToolComponent) to `lib/utils` so PathHeader can reuse it. The branch/ref display now renders a 40-char SHA as `abc1234`, preserving any `^` / `~N` suffix. - Hide the `·` separator and the path's CopyIconButton when there's no path (repo root). Previously a dangling `·` and copy button rendered with nothing between them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a `path` query/tool parameter to restrict diff output to changes touching a single file via git's `-- <pathspec>` separator. Refactors the route handler to use the shared `getDiffRequestSchema`. Fixes SOU-1154 (sourcebot-dev#1154) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Move single-file commit diffs from `/-/commit/<sha>/<path>` to
`/-/blob/<path>?ref=<sha>&diff=true`, keeping the user's browsing
revision in the URL. `/-/commit/<sha>` still renders the full
multi-file diff.
- New `FocusedCommitDiffPanel` with status row (file status badge +
authors + relative commit date + "View full commit" + DiffStat +
exit-X) and path-filtered `getDiff` so only the single file's diff
is fetched.
- New preview banner in `CodePreviewPanel` when `?ref=` is set, with a
close button that strips the param.
- Make `PathHeader`'s revision clickable, linking to that ref's full
commit view.
- New `HoverPrefetchLink` defers Next.js prefetching until hover; used
in history rows to avoid firing many prefetches on render.
- Hide the bottom panel on `/-/commit/` views.
- Extract `getFileStatus` / `StatusBadge` to a shared `fileStatus.tsx`.
- Workaround Radix Tooltip + RSC re-render bug (drop `asChild` from
`<TooltipTrigger>`, add `key={commitSha}`) so X / Browse-files
buttons survive client navigation between commits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| <div className="flex flex-row items-center gap-2"> | ||
| <DiffStat {...computeChangeCounts(file)} /> | ||
| <Tooltip key={commitSha}> | ||
| <TooltipTrigger> |
There was a problem hiding this comment.
Diff viewer action buttons sit inside an extra invisible button
The close, exit, and browse-files controls in the new commit diff views are each wrapped in a redundant interactive layer, which can produce a doubled outline or focus box and a slightly off click/keyboard target compared with the other action buttons in the same headers. They should behave and look like the existing commit action buttons so the toolbar feels uniform and predictable.
Prompt to fix with AI
This is a comment left during a design review.
Comment:
**Diff viewer action buttons sit inside an extra invisible button**
The close, exit, and browse-files controls in the new commit diff views are each wrapped in a redundant interactive layer, which can produce a doubled outline or focus box and a slightly off click/keyboard target compared with the other action buttons in the same headers. They should behave and look like the existing commit action buttons so the toolbar feels uniform and predictable.
Issue:
<TooltipTrigger> is used without asChild, wrapping <Button asChild ...><Link .../></Button>. Because TooltipTrigger is the raw Radix Trigger (tooltip.tsx line 12), it renders its own native <button> that wraps the real Button/Link, creating a button-wrapping-an-anchor nesting and a second, default-styled interactive box around the styled control.
Suggested fix:
Add asChild to the <TooltipTrigger> in all three locations so the trigger collapses onto the existing <Button asChild><Link>, exactly as CommitActionLink/CommitBodyToggle do.
How can I resolve this? Keep the fix scoped to the PR-touched UI and preserve existing design-system patterns.
|
|
||
| export const StatusBadge = ({ status }: { status: FileStatus }) => ( | ||
| <span | ||
| className={`inline-flex items-center justify-center w-5 h-5 rounded text-xs font-mono font-bold ${STATUS_BADGE_COLORS[status]}`} |
There was a problem hiding this comment.
File change tags don't match the app's badge styling
The A/M/D/R change markers in the commit diff are built as a one-off chip rather than reusing the app's standard badge treatment, so their shape, radius, and weight can drift from tags elsewhere in the product. Aligning them keeps status indicators feeling like one consistent system.
Prompt to fix with AI
This is a comment left during a design review.
Comment:
**File change tags don't match the app's badge styling**
The A/M/D/R change markers in the commit diff are built as a one-off chip rather than reusing the app's standard badge treatment, so their shape, radius, and weight can drift from tags elsewhere in the product. Aligning them keeps status indicators feeling like one consistent system.
Issue:
StatusBadge renders a raw <span className='inline-flex items-center justify-center w-5 h-5 rounded text-xs font-mono font-bold ...'> with per-status raw color maps (bg-green-500/20, bg-yellow-500/20, bg-red-500/20, bg-blue-500/20), bypassing the Badge primitive.
Suggested fix:
Render StatusBadge via the Badge component (e.g. an outline/custom variant with size override) or add a Badge variant for diff status, keeping the A/M/D/R letters and status colors but inheriting Badge's base structure.
How can I resolve this? Keep the fix scoped to the PR-touched UI and preserve existing design-system patterns.
| })} | ||
| aria-label="Close preview" | ||
| > | ||
| <X className="h-4 w-4" /> |
There was a problem hiding this comment.
Close and exit icons look slightly oversized next to nearby buttons
The close and exit icons in the diff and preview headers are drawn larger than the copy and action icons sitting right beside them, so the small toolbar looks slightly uneven. Matching their size makes the header controls feel balanced.
Prompt to fix with AI
This is a comment left during a design review.
Comment:
**Close and exit icons look slightly oversized next to nearby buttons**
The close and exit icons in the diff and preview headers are drawn larger than the copy and action icons sitting right beside them, so the small toolbar looks slightly uneven. Matching their size makes the header controls feel balanced.
Issue:
Close/Exit buttons render <X className='h-4 w-4' /> inside Button ... className='h-6 w-6 text-muted-foreground'.
Suggested fix:
Render the X glyph at h-3 w-3 (or align all header icon buttons to a single glyph size) to match CopyIconButton/CommitActionLink.
How can I resolve this? Keep the fix scoped to the PR-touched UI and preserve existing design-system patterns.
| <CommitMessage subject={subject} body={commitResponse.body} /> | ||
| </div> | ||
| <Tooltip key={commitSha}> | ||
| <TooltipTrigger> |
There was a problem hiding this comment.
Browse files button has a doubled-up control wrapper
The Browse files action at the top of the commit view is wrapped in an extra invisible control, so it can grab keyboard focus and show its highlight in the wrong place instead of cleanly on the button itself. It should behave as a single, crisp control like the other action buttons in the product.
Prompt to fix with AI
This is a comment left during a design review.
Comment:
**Browse files button has a doubled-up control wrapper**
The Browse files action at the top of the commit view is wrapped in an extra invisible control, so it can grab keyboard focus and show its highlight in the wrong place instead of cleanly on the button itself. It should behave as a single, crisp control like the other action buttons in the product.
Issue:
<Tooltip><TooltipTrigger><Button asChild variant="outline" size="sm">...<Link>Browse files</Link></Button></TooltipTrigger> — TooltipTrigger has no asChild, so Radix renders its own native <button> wrapping the styled Button/Link, nesting interactive elements and moving the focus target to the wrapper.
Suggested fix:
Add asChild to the TooltipTrigger so the Button is the trigger: <TooltipTrigger asChild><Button asChild variant="outline" size="sm">...</Button></TooltipTrigger>.
How can I resolve this? Keep the fix scoped to the PR-touched UI and preserve existing design-system patterns.
| <div className="flex flex-row items-center gap-2"> | ||
| <DiffStat {...computeChangeCounts(file)} /> | ||
| <Tooltip key={commitSha}> | ||
| <TooltipTrigger> |
There was a problem hiding this comment.
Exit-diff close control has a doubled-up wrapper
The close control on the single-file diff view is wrapped in an extra invisible control, so keyboard focus and its highlight can land on empty space around the icon instead of on the button. It should focus and highlight as one tidy icon button.
Prompt to fix with AI
This is a comment left during a design review.
Comment:
**Exit-diff close control has a doubled-up wrapper**
The close control on the single-file diff view is wrapped in an extra invisible control, so keyboard focus and its highlight can land on empty space around the icon instead of on the button. It should focus and highlight as one tidy icon button.
Issue:
<Tooltip><TooltipTrigger><Button asChild variant="ghost" size="icon" className="h-6 w-6 ..."><Link aria-label="Exit diff view"><X/></Link></Button></TooltipTrigger> — TooltipTrigger lacks asChild, nesting a native button around the ghost icon button.
Suggested fix:
Add asChild to the TooltipTrigger wrapping the Exit diff view Button.
How can I resolve this? Keep the fix scoped to the PR-touched UI and preserve existing design-system patterns.
| </Link> | ||
| </span> | ||
| <Tooltip key={previewRef}> | ||
| <TooltipTrigger> |
There was a problem hiding this comment.
Close-preview control has a doubled-up wrapper
The close control on the file preview banner is wrapped in an extra invisible control, so keyboard focus and its highlight can fall on the area around the icon rather than the button. It should focus and highlight as a single clean icon button like other dismiss controls.
Prompt to fix with AI
This is a comment left during a design review.
Comment:
**Close-preview control has a doubled-up wrapper**
The close control on the file preview banner is wrapped in an extra invisible control, so keyboard focus and its highlight can fall on the area around the icon rather than the button. It should focus and highlight as a single clean icon button like other dismiss controls.
Issue:
<Tooltip><TooltipTrigger><Button asChild variant="ghost" size="icon" className="h-6 w-6 text-muted-foreground"><Link aria-label="Close preview"><X/></Link></Button></TooltipTrigger> — TooltipTrigger lacks asChild.
Suggested fix:
Add asChild to the TooltipTrigger wrapping the Close preview Button.
How can I resolve this? Keep the fix scoped to the PR-touched UI and preserve existing design-system patterns.
|
Closing contaminated Design Doctor effort A/B clone; replacing with a fresh PR for the same high variant. |
Replacement Design Doctor reasoning-effort A/B clone for #2.
Variant:
high.This branch adds a no-op commit on top of the original PR head so the file contents are equivalent while GitHub checks stay isolated to this test PR.
Replaces contaminated setup PR: #19.