From 4b4d6183c188da8ab1b8a33c1662f595369abc3f Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 2 Jul 2026 21:02:25 -0400 Subject: [PATCH] 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 --- tl/src/tl/views.cljs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tl/src/tl/views.cljs b/tl/src/tl/views.cljs index 4d61caa..e0288e2 100644 --- a/tl/src/tl/views.cljs +++ b/tl/src/tl/views.cljs @@ -15,6 +15,7 @@ (defonce body-scroll-el (atom nil)) ; the vertical (tracks) scroll container (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 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 play (atom nil)) ; {:ctx :segs :fps :idx} while playing, else nil (defonce active-insert! (atom nil)) ; the live content-editor's (insert! link) fn @@ -487,6 +488,19 @@ (scroll-to-seg-track! segs playhead))))) (do (reset! played-bar 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)) nil)) @@ -1032,15 +1046,8 @@ (rf/dispatch [::events/reparent (keyword src) (:id a)]))))})) (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)]) - ;; 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]) drop-ok? (and dragging (not= dragging (:id a))) drag-props (merge (ann-drag-props a authed? over?)