fix: deterministic playhead-driven pane scroll in playhead-fx

Move the commentary-pane auto-scroll into playhead-fx (the single per-playhead
side-effect hook): pick ONE annotation deterministically (first active in the
sorted anns) so overlapping annotations don't race per-card, and scrollIntoView
its card when that pick changes. Runs on any playhead move (playback or scrub).
Drops the unreliable commentary-scroll/active-annotation component.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-07-02 21:02:25 -04:00
parent 0022c6bedd
commit 4b4d6183c1

View file

@ -15,6 +15,7 @@
(defonce body-scroll-el (atom nil)) ; the vertical (tracks) scroll container (defonce body-scroll-el (atom nil)) ; the vertical (tracks) scroll container
(defonce playhead-el (atom nil)) ; the single full-height playhead overlay (defonce playhead-el (atom nil)) ; the single full-height playhead overlay
(defonce played-bar (atom nil)) ; [ann-id bar-idx] last auto-scrolled to during playback (defonce played-bar (atom nil)) ; [ann-id bar-idx] last auto-scrolled to during playback
(defonce scrolled-ann (atom nil)) ; ann-id the commentary pane last auto-scrolled to
(defonce scrolled-on-load? (atom nil)) ; did we scroll to deep-linked playhead on load (defonce scrolled-on-load? (atom nil)) ; did we scroll to deep-linked playhead on load
(defonce play (atom nil)) ; {:ctx :segs :fps :idx} while playing, else nil (defonce play (atom nil)) ; {:ctx :segs :fps :idx} while playing, else nil
(defonce active-insert! (atom nil)) ; the live content-editor's (insert! link) fn (defonce active-insert! (atom nil)) ; the live content-editor's (insert! link) fn
@ -487,6 +488,19 @@
(scroll-to-seg-track! segs playhead))))) (scroll-to-seg-track! segs playhead)))))
(do (reset! played-bar nil) (do (reset! played-bar nil)
(reset! scrolled-on-load? nil))) (reset! scrolled-on-load? nil)))
;; scroll the commentary pane to the annotation the playhead is inside — ONE
;; deterministic pick (first active in the sorted anns), so overlapping
;; annotations don't race. Fires on any playhead move (playback or scrub).
(let [primary (some (fn [a]
(when (some (fn [[lo hi]] (and (<= lo playhead) (< playhead hi))) (:bars a))
(:id a)))
anns)]
(when (not= primary @scrolled-ann)
(reset! scrolled-ann primary)
(when primary
(r/after-render
#(when-let [node (js/document.getElementById (str "ann-" (name primary)))]
(.scrollIntoView node #js {:block "center" :behavior "smooth"}))))))
(r/after-render #(position-playhead! fps zoom playhead)) (r/after-render #(position-playhead! fps zoom playhead))
nil)) nil))
@ -1032,15 +1046,8 @@
(rf/dispatch [::events/reparent (keyword src) (:id a)]))))})) (rf/dispatch [::events/reparent (keyword src) (:id a)]))))}))
(defn- annotation-card [a scene ctx segs nmap authed? open by-parent] (defn- annotation-card [a scene ctx segs nmap authed? open by-parent]
(r/with-let [over? (r/atom false) hov? (r/atom false) was-active? (atom false)] (r/with-let [over? (r/atom false) hov? (r/atom false)]
(let [active? @(rf/subscribe [::subs/annotation-active? (:id a)]) (let [active? @(rf/subscribe [::subs/annotation-active? (:id a)])
;; playhead just ENTERED this annotation (same active? that drives the
;; highlight) → scroll the pane to it, exactly like clicking its lane bar.
_ (when (and active? (not @was-active?))
(r/after-render
#(when-let [node (js/document.getElementById (str "ann-" (name (:id a))))]
(.scrollIntoView node #js {:block "center" :behavior "smooth"}))))
_ (reset! was-active? active?)
dragging @(rf/subscribe [::subs/dragging-ann]) dragging @(rf/subscribe [::subs/dragging-ann])
drop-ok? (and dragging (not= dragging (:id a))) drop-ok? (and dragging (not= dragging (:id a)))
drag-props (merge (ann-drag-props a authed? over?) drag-props (merge (ann-drag-props a authed? over?)