summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-26 20:41:24 -0600
committerAdam <[email protected]>2026-02-26 20:41:35 -0600
commitf2100dcfd8ed0786ead6e7ae0b22cec23a79dab5 (patch)
tree63e0a54139d9a3616b7334f6838dd30ef5aa88a8
parentb0b88f679216d1b7e16bb1098cef0d91f911d9dd (diff)
downloadopencode-f2100dcfd8ed0786ead6e7ae0b22cec23a79dab5.tar.gz
opencode-f2100dcfd8ed0786ead6e7ae0b22cec23a79dab5.zip
fix(app): scroll jacking
-rw-r--r--packages/ui/src/components/session-review.tsx79
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) => {