fix: reliable playhead-driven pane scroll to the active annotation

Rewrite commentary-scroll as a lifecycle component that scrolls in
component-did-update via scrollIntoView (offsetParent-agnostic, so it works for
nested cards too), replacing the fragile after-render + manual offsetTop math.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-07-02 20:46:34 -04:00
parent 28bdac1142
commit f6e887c14d

View file

@ -1095,15 +1095,19 @@
;; Renders nothing: owns the active-annotation subscription and scrolls the active ;; Renders nothing: owns the active-annotation subscription and scrolls the active
;; card into view, so `commentary` itself no longer re-renders on every crossing. ;; card into view, so `commentary` itself no longer re-renders on every crossing.
(defn- commentary-scroll [el] (defn- commentary-scroll [el]
(let [active @(rf/subscribe [::subs/active-annotation])] ;; Form-3 so the scroll runs in component-did-update — reliably after the DOM is
(r/after-render ;; committed on every active-annotation change — and via scrollIntoView, which
(fn [] (when-let [c @el] ;; (unlike offsetTop math) is correct even when the active card is nested.
(when-let [node (and active (.querySelector c (str "#ann-" (name active))))] (letfn [(scroll! [& _]
;; scrollIntoView (not manual offsetTop math): a nested card's (let [active @(rf/subscribe [::subs/active-annotation])]
;; offsetParent is its parent .ann (position:relative), so offsetTop (when-let [c @el]
;; would be measured inside the parent, not the scroll container. (when-let [node (and active (.querySelector c (str "#ann-" (name active))))]
(.scrollIntoView node #js {:block "center" :behavior "smooth"}))))) (.scrollIntoView node #js {:block "center" :behavior "smooth"})))))]
nil)) (r/create-class
{:display-name "commentary-scroll"
:component-did-mount scroll!
:component-did-update scroll!
:reagent-render (fn [_] @(rf/subscribe [::subs/active-annotation]) nil)})))
(defn commentary [] (defn commentary []
(let [el (atom nil) open (r/atom nil)] (let [el (atom nil) open (r/atom nil)]