diff options
| author | Adam <[email protected]> | 2026-02-26 20:41:24 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-02-26 20:41:35 -0600 |
| commit | f2100dcfd8ed0786ead6e7ae0b22cec23a79dab5 (patch) | |
| tree | 63e0a54139d9a3616b7334f6838dd30ef5aa88a8 | |
| parent | b0b88f679216d1b7e16bb1098cef0d91f911d9dd (diff) | |
| download | opencode-f2100dcfd8ed0786ead6e7ae0b22cec23a79dab5.tar.gz opencode-f2100dcfd8ed0786ead6e7ae0b22cec23a79dab5.zip | |
fix(app): scroll jacking
| -rw-r--r-- | packages/ui/src/components/session-review.tsx | 79 |
1 files changed, 46 insertions, 33 deletions
diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index 679935f61..5829401eb 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -16,7 +16,18 @@ import { useFileComponent } from "../context/file" import { useI18n } from "../context/i18n" import { getDirectory, getFilename } from "@opencode-ai/util/path" import { checksum } from "@opencode-ai/util/encode" -import { createEffect, createMemo, createSignal, For, Match, onCleanup, Show, Switch, type JSX } from "solid-js" +import { + createEffect, + createMemo, + createSignal, + For, + Match, + onCleanup, + Show, + Switch, + untrack, + type JSX, +} from "solid-js" import { createStore } from "solid-js/store" import { type FileContent, type FileDiff } from "@opencode-ai/sdk/v2" import { PreloadMultiFileDiffResult } from "@pierre/diffs/ssr" @@ -442,50 +453,52 @@ export const SessionReview = (props: SessionReviewProps) => { const focus = props.focusedComment if (!focus) return - focusToken++ - const token = focusToken + untrack(() => { + focusToken++ + const token = focusToken - setOpened(focus) + setOpened(focus) - const comment = (props.comments ?? []).find((c) => c.file === focus.file && c.id === focus.id) - if (comment) setSelection({ file: comment.file, range: cloneSelectedLineRange(comment.selection) }) + const comment = (props.comments ?? []).find((c) => c.file === focus.file && c.id === focus.id) + if (comment) setSelection({ file: comment.file, range: cloneSelectedLineRange(comment.selection) }) - const current = open() - if (!current.includes(focus.file)) { - handleChange([...current, focus.file]) - } + const current = open() + if (!current.includes(focus.file)) { + handleChange([...current, focus.file]) + } + + const scrollTo = (attempt: number) => { + if (token !== focusToken) return - const scrollTo = (attempt: number) => { - if (token !== focusToken) return + const root = scroll + if (!root) return - const root = scroll - if (!root) return + const wrapper = anchors.get(focus.file) + const anchor = wrapper?.querySelector(`[data-comment-id="${focus.id}"]`) + const ready = anchor instanceof HTMLElement + + const target = ready ? anchor : wrapper + if (!target) { + if (attempt >= 120) return + requestAnimationFrame(() => scrollTo(attempt + 1)) + return + } - const wrapper = anchors.get(focus.file) - const anchor = wrapper?.querySelector(`[data-comment-id="${focus.id}"]`) - const ready = anchor instanceof HTMLElement + 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 target = ready ? anchor : wrapper - if (!target) { + if (ready) return if (attempt >= 120) 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 >= 120) return - requestAnimationFrame(() => scrollTo(attempt + 1)) - } + requestAnimationFrame(() => scrollTo(0)) - requestAnimationFrame(() => scrollTo(0)) - - requestAnimationFrame(() => props.onFocusedCommentChange?.(null)) + requestAnimationFrame(() => props.onFocusedCommentChange?.(null)) + }) }) const handleReviewKeyDown = (event: KeyboardEvent) => { |
