From 0eb523631d6b321960ecbc3893a74d3df086a5d7 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 22 Jan 2026 13:10:51 -0600 Subject: wip(app): line selection --- packages/ui/src/components/session-review.css | 19 +- packages/ui/src/components/session-review.tsx | 381 +++++++++++--------------- 2 files changed, 175 insertions(+), 225 deletions(-) (limited to 'packages/ui') diff --git a/packages/ui/src/components/session-review.css b/packages/ui/src/components/session-review.css index 26ca73265..f34c8b446 100644 --- a/packages/ui/src/components/session-review.css +++ b/packages/ui/src/components/session-review.css @@ -75,13 +75,18 @@ overflow: hidden; } - [data-component="popover-content"] { - position: absolute !important; - } - - .session-review-comment-popover-content { - left: auto !important; - right: calc(100% + 12px) !important; + [data-slot="session-review-comment-popover-content"] { + position: absolute; + top: 0; + right: calc(100% + 12px); + z-index: 40; + min-width: 200px; + max-width: min(320px, calc(100vw - 48px)); + border-radius: var(--radius-md); + background-color: var(--surface-raised-stronger-non-alpha); + border: 1px solid color-mix(in oklch, var(--border-base) 50%, transparent); + box-shadow: var(--shadow-md); + padding: 12px; } [data-slot="session-review-trigger-content"] { diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 4096f341b..7c35c9226 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -1,6 +1,5 @@ import { Accordion } from "./accordion" import { Button } from "./button" -import { Popover } from "./popover" import { RadioGroup } from "./radio-group" import { DiffChanges } from "./diff-changes" import { FileIcon } from "./file-icon" @@ -151,6 +150,7 @@ function markerTop(wrapper: HTMLElement, marker: HTMLElement) { export const SessionReview = (props: SessionReviewProps) => { let scroll: HTMLDivElement | undefined + let focusToken = 0 const i18n = useI18n() const diffComponent = useDiffComponent() const anchors = new Map() @@ -201,6 +201,9 @@ export const SessionReview = (props: SessionReviewProps) => { const focus = props.focusedComment if (!focus) return + focusToken++ + const token = focusToken + setOpened(focus) const comment = (props.comments ?? []).find((c) => c.file === focus.file && c.id === focus.id) @@ -211,31 +214,35 @@ export const SessionReview = (props: SessionReviewProps) => { handleChange([...current, focus.file]) } - requestAnimationFrame(() => { - requestAnimationFrame(() => { - const root = scroll - if (!root) return - - const anchor = root.querySelector(`[data-comment-id="${focus.id}"]`) - if (anchor instanceof HTMLElement) { - const rootRect = root.getBoundingClientRect() - const anchorRect = anchor.getBoundingClientRect() - const offset = anchorRect.top - rootRect.top - const next = root.scrollTop + offset - rootRect.height / 2 + anchorRect.height / 2 - root.scrollTop = Math.max(0, next) - return - } - - const target = anchors.get(focus.file) - if (!target) return - - const rootRect = root.getBoundingClientRect() - const targetRect = target.getBoundingClientRect() - const offset = targetRect.top - rootRect.top - const next = root.scrollTop + offset - rootRect.height / 2 + targetRect.height / 2 - root.scrollTop = Math.max(0, next) - }) - }) + const scrollTo = (attempt: number) => { + if (token !== focusToken) return + + const root = scroll + if (!root) return + + const anchor = root.querySelector(`[data-comment-id="${focus.id}"]`) + const ready = + anchor instanceof HTMLElement && anchor.style.pointerEvents !== "none" && anchor.style.opacity !== "0" + + const target = ready ? anchor : anchors.get(focus.file) + if (!target) { + if (attempt >= 24) return + requestAnimationFrame(() => scrollTo(attempt + 1)) + return + } + + const rootRect = root.getBoundingClientRect() + const targetRect = target.getBoundingClientRect() + const offset = targetRect.top - rootRect.top + const next = root.scrollTop + offset - rootRect.height / 2 + targetRect.height / 2 + root.scrollTop = Math.max(0, next) + + if (ready) return + if (attempt >= 24) return + requestAnimationFrame(() => scrollTo(attempt + 1)) + } + + requestAnimationFrame(() => scrollTo(0)) requestAnimationFrame(() => props.onFocusedCommentChange?.(null)) }) @@ -519,207 +526,145 @@ export const SessionReview = (props: SessionReviewProps) => { - - -
- - - Loading image... - Image preview unavailable - -
- } - > - {getFilename(diff.file)} - - -
- -
- - - Loading audio... - Audio preview unavailable - -
- } +
{ + wrapper = el + anchors.set(diff.file, el) + scheduleAnchors() + }} + > + { + props.onDiffRendered?.() + scheduleAnchors() + }} + enableLineSelection={props.onLineComment != null} + onLineSelected={handleLineSelected} + onLineSelectionEnd={handleLineSelectionEnd} + selectedLines={selectedLines()} + commentedLines={commentedLines()} + before={{ + name: diff.file!, + contents: typeof diff.before === "string" ? diff.before : "", + }} + after={{ + name: diff.file!, + contents: typeof diff.after === "string" ? diff.after : "", + }} + /> + + + {(comment) => ( +
-
- - -
{ - wrapper = el - anchors.set(diff.file, el) - scheduleAnchors() - }} - > - { - props.onDiffRendered?.() - scheduleAnchors() - }} - enableLineSelection={props.onLineComment != null} - onLineSelected={handleLineSelected} - onLineSelectionEnd={handleLineSelectionEnd} - selectedLines={selectedLines()} - commentedLines={commentedLines()} - before={{ - name: diff.file!, - contents: beforeText(), - }} - after={{ - name: diff.file!, - contents: afterText(), - }} - /> - - - {(comment) => ( -
- { - if (open) { - openComment(comment) - return - } - if (!isCommentOpen(comment)) return - setOpened(null) - }} - trigger={ - - } - > -
-
- {getFilename(comment.file)}:{selectionLabel(comment.selection)} -
-
{comment.comment}
- - {(preview) =>
{preview()}
} -
+ + + +
+
+
+ {getFilename(comment.file)}:{selectionLabel(comment.selection)}
- +
{comment.comment}
+
- )} - - - - {(range) => ( - -
- { - if (open) return + +
+ )} + + + + {(range) => ( + +
+ +
+
+
+ Commenting on {getFilename(diff.file)}:{selectionLabel(range())} +
+