From 2b07291e17856a2b014537542f79cc8c059487c9 Mon Sep 17 00:00:00 2001 From: adamelmore <2363879+adamdottv@users.noreply.github.com> Date: Sun, 25 Jan 2026 07:58:24 -0600 Subject: [PATCH] fix(app): scroll to comment on click --- packages/app/src/components/prompt-input.tsx | 7 +++- packages/app/src/pages/session.tsx | 42 +++++++++++++++++++ packages/ui/src/components/session-review.tsx | 4 +- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index ccff04efe..5ec0eb1ea 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -185,23 +185,26 @@ export const PromptInput: Component = (props) => { const openComment = (item: { path: string; commentID?: string; commentOrigin?: "review" | "file" }) => { if (!item.commentID) return - comments.setFocus({ file: item.path, id: item.commentID }) - comments.setActive({ file: item.path, id: item.commentID }) + const focus = { file: item.path, id: item.commentID } + comments.setActive(focus) view().reviewPanel.open() if (item.commentOrigin === "review") { tabs().open("review") + requestAnimationFrame(() => comments.setFocus(focus)) return } if (item.commentOrigin !== "file" && commentInReview(item.path)) { tabs().open("review") + requestAnimationFrame(() => comments.setFocus(focus)) return } const tab = files.tab(item.path) tabs().open(tab) files.load(item.path) + requestAnimationFrame(() => comments.setFocus(focus)) } const recent = createMemo(() => { diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 2016870e1..5f743dcba 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -1860,6 +1860,7 @@ export default function Page() { let scrollFrame: number | undefined let pending: { x: number; y: number } | undefined let codeScroll: HTMLElement[] = [] + let focusToken = 0 const path = createMemo(() => file.pathFromTab(tab)) const state = createMemo(() => { @@ -2036,9 +2037,50 @@ export default function Page() { const target = fileComments().find((comment) => comment.id === focus.id) if (!target) return + focusToken++ + const token = focusToken + setOpenedComment(target.id) setCommenting(null) file.setSelectedLines(p, target.selection) + + const scrollTo = (attempt: number) => { + if (token !== focusToken) return + + const root = scroll + if (!root) { + if (attempt >= 120) return + requestAnimationFrame(() => scrollTo(attempt + 1)) + return + } + + const anchor = root.querySelector(`[data-comment-id="${target.id}"]`) + const ready = + anchor instanceof HTMLElement && + anchor.style.pointerEvents !== "none" && + anchor.style.opacity !== "0" + + const shadow = getRoot() + const marker = shadow ? findMarker(shadow, target.selection) : undefined + const node = (ready ? anchor : (marker ?? wrap)) as HTMLElement | undefined + if (!node) { + if (attempt >= 120) return + requestAnimationFrame(() => scrollTo(attempt + 1)) + return + } + + const rootRect = root.getBoundingClientRect() + const targetRect = node.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 || marker) return + if (attempt >= 120) return + requestAnimationFrame(() => scrollTo(attempt + 1)) + } + + requestAnimationFrame(() => scrollTo(0)) requestAnimationFrame(() => comments.clearFocus()) }) diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 42c0bc2aa..60ac0d516 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -238,7 +238,7 @@ export const SessionReview = (props: SessionReviewProps) => { const target = ready ? anchor : anchors.get(focus.file) if (!target) { - if (attempt >= 24) return + if (attempt >= 120) return requestAnimationFrame(() => scrollTo(attempt + 1)) return } @@ -250,7 +250,7 @@ export const SessionReview = (props: SessionReviewProps) => { root.scrollTop = Math.max(0, next) if (ready) return - if (attempt >= 24) return + if (attempt >= 120) return requestAnimationFrame(() => scrollTo(attempt + 1)) }