Files
Michael Ramos e1efea8bd4 feat(review): file comments in the diff + unified click-to-highlight comment UX (#973)
* feat(review): file comments in diff + unified click-to-highlight comment UX

Render file-scoped comments inline in the code review UI and overhaul the
comment-card UX for consistency.

File comments
- Single-file view: a full-text banner below the file path.
- All-files view: rendered in the file header (expanded files only) — Pierre's
  virtualized custom header suppresses the body-prefix slot, and its measured
  item heights make a taller header safe. Guided-review external annotations can
  now drop the line-1 anchor hack.

Click-to-highlight
- Clicking a comment (inline card or sidebar) replays its stored line range as
  the controlled selection highlight; clicking it again clears it.
- Scroll/navigate is sidebar- and findings-list-only (token-based signal) so
  clicking a comment in the diff never moves the viewport.

Unified comment cards
- Shared CommentMeta identity row (badges + author + relative time) across the
  inline, sidebar, and file-banner cards (were three hand-rolled headers).
- Shared FileNameChip (basename, full path on hover) replaces the literal
  "file" badge everywhere.
- Force the app sans font on cards (inline was inheriting the diff's monospace),
  consistent selected ring, spacing, and absolute hover actions so timestamps
  align across surfaces.

Shared helpers: annotationScope (PR-scope / file-scope / line-range projection)
and fileName (basename). Plumbs createdAt through DiffAnnotationMetadata so the
inline cards can show timestamps.

* feat(review): unify comment action row (edit · copy · delete)

Shared CommentActions component used by the inline diff card, the file-comment
banner, and the sidebar — bottom-aligned, hover-revealed, order left→right
edit · copy · delete (delete/close furthest right).

- Inline + banner: actions moved from the top (where they overlapped the
  timestamp) to the bottom, and gained the copy button.
- Sidebar: now uses the same component (copy + delete) — same icons, order, and
  styling.
- Read-only (externally-sourced) file comments show copy only.
- Extract commentCopyText() so every card's copy produces identical,
  self-describing text (location prefix + body + reasoning); drops the format
  that was hand-built in the sidebar and the agent-job panel.
- Rename PRCommentsTab's local CommentActions -> PRCommentLinkActions to avoid
  the name clash with the shared component.
- Remove the now-dead .review-comment-action(s) CSS and CommentMeta's trailing
  slot.

* fix(review): address PR review findings

- all-files: let an active compose own the line highlight (pendingSelection
  guard) so clicking/deselecting a comment mid-compose no longer clobbers the
  toolbar's range — mirrors single-file's `pendingSelection ?? annotationRange`.
- clear scrollTargetAnnotation on inline select so a remount (Refresh / base
  switch) doesn't re-scroll back to a stale sidebar/findings navigate target.
- inline diff cards: hide edit/delete for external (source-set) findings
  (matches the file-comment card) and copy with the file:line location prefix
  via a precomputed metadata.copyText (commentCopyText), unifying the copy text.
- add decorations + createdAt to the file-comment refresh signature so an
  external PATCH changing only those repaints the all-files header.
- wrap finding reasoning text (overflow-wrap/word-break) so long file:line refs
  no longer bleed past the card edge.

* refactor(review): dedupe line-annotation projection + close signature gaps

Self-review follow-ups to the PR-feedback fixes:

- Extract lineAnnotationMetadata() — the all-files and single-file diff surfaces
  built the identical 14-field DiffAnnotationMetadata; now one source of truth.
- Add reviewProfileLabel + source + createdAt to the LINE-scope refresh
  signature: the unified CommentMeta now renders those on inline cards too, so
  an external PATCH changing only them must repaint the item (same gap that was
  just fixed for the file-comment tuple).
- Wrap the agent-job panel's finding text + reasoning (overflow-wrap) so long
  file:line refs don't bleed past the panel edge, matching the inline card fix.

* fix(review): zero inter-file gap + stop file-path descender clipping

- all-files: set CodeView layout gap to 0 (Pierre default was 8) so files sit
  flush; sticky headers already mark file boundaries. Keep default edge padding.
- FileHeader: path text leading-none -> leading-normal so the overflow-hidden
  ellipsis box has room for descenders (g/y/p were clipped by the box bottom).

* fix(review): address 2nd-pass review — header re-measure, delete, highlight

- all-files file comments: re-measure the CodeView item when a comment
  expands/collapses/edits (FileCommentCard -> onHeightChange -> refreshItem) so
  Pierre repositions following items instead of overlapping them. Fixes the
  dynamic header-height bug; the remaining static sticky-offset drift is bounded
  by the new banner cap and stays the accepted trade-off.
- cap the expanded comment body (max-h + overflow) so a long guided-review note
  doesn't fill the viewport / inflate the sticky header.
- delete is now available on every surface (inline, banner) including external
  source-set findings; edit stays gated on !source.
- restore the line highlight when a compose ends: the all-files pendingSelection
  effect now re-derives the selected comment's range instead of clearing
  (mirrors single-file's pendingSelection ?? annotationRange).
- add reasoning to the file-comment refresh signature (copyText includes it).
- guard handleNavigateToAnnotation with annotationMatchesPrScope so a stale
  sidebar/finding row can't navigate to an out-of-scope annotation.

* refactor(review): share annotation→highlight derivation + smoother re-measure

Self-review follow-ups:
- Extract lineSelectionForAnnotation() — the compose-end restore and the
  selection-replay effect built the same annotation→CodeViewLineSelection by
  hand; share it so they can't diverge.
- Re-measure file-comment height via useLayoutEffect (before paint) instead of
  useEffect, so an expand/collapse lands without a one-frame overlap.

* fix(review): ignore navigate to missing annotation + re-seed on scope switch

- handleNavigateToAnnotation: return early when the annotation id is gone
  (deleted) as well as out-of-scope, so a stale row can't leave an orphan
  selection (ring with nothing to scroll to). Dismissed agent-panel rows are
  already non-clickable; this is the belt-and-suspenders for any other path.
- fileSetKey now includes prUrl/prDiffScope so a pure layer<->full-stack scope
  switch (same file set) remounts and re-seeds annotations through the current
  scope filter — the incremental sync bails on an unchanged annotations ref and
  can't otherwise detect the filter change.

* fix(review): allow editing external/source findings inline again

Drop the editable = !source gate on inline and file-comment cards. main always
showed edit for external findings (it routes through updateExternalAnnotation),
so gating it was a regression. Edit, delete, and copy are now all available on
every comment surface regardless of source.

* refactor(review): drop redundant null-check after navigate guard

annotation is guaranteed non-null past the missing/out-of-scope early return.
2026-06-26 12:19:49 -07:00

5 lines
180 B
TypeScript

/** Last path segment (the file name) for display, falling back to the full path. */
export function fileBasename(path: string): string {
return path.split('/').pop() || path;
}