feat: scroll vertically to the clip's track when jumping via link/jump

goto! with follow? already recentred horizontally on the playhead. Now it also
scrolls the tracks container vertically so the clip under the landing frame is
brought into view — for link chips, annotation jump buttons, and script
highlights. No-op when the tracks already fit. Accounts for the pinned
annotation-lane header height in the scroll math.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-06-30 10:04:25 -04:00
parent 96da4a6bd8
commit 967ca97e84

View file

@ -12,6 +12,7 @@
(defonce video-el (atom nil))
(defonce raf (atom nil))
(defonce scroll-el (atom nil))
(defonce body-scroll-el (atom nil)) ; the vertical (tracks) scroll container
(defonce playhead-el (atom nil)) ; the single full-height playhead overlay
(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
@ -174,6 +175,23 @@
(rf/reg-fx :player/pause (fn [_] (when-let [v @video-el] (.pause v))))
(rf/reg-fx :player/seek (fn [secs] (when (and @video-el secs) (set! (.-currentTime @video-el) secs))))
(defn- scroll-to-seg-track!
"Scroll the tracks vertically so the clip under local frame `local` is visible.
No-op when there's no vertical overflow. The annotation lanes are a pinned
header of height `lane-h`, so the clip rows live below that in scroll space."
[segs local]
(when-let [el @body-scroll-el]
(let [tracks @(rf/subscribe [::subs/tracks])
row-h @(rf/subscribe [::subs/row-h])
anns @(rf/subscribe [::subs/annotations])
lane-h (max 18 (* 18 (count anns)))
track (:track (nth segs (seg-at segs local) nil))
i (first (keep-indexed (fn [idx t] (when (= (:id t) track) idx)) tracks))]
(when i
(let [target (+ lane-h (* i row-h) (/ row-h 2))]
(.scrollTo el #js {:top (max 0 (- target (/ (.-clientHeight el) 2)))
:behavior "smooth"}))))))
(defn- goto!
"Move the playhead to local frame `local` and seek the video to match."
([local] (goto! local false))
@ -186,7 +204,8 @@
(seek-video! fps (scene/local->source segs local))
(when @play (swap! play assoc :idx (seg-at segs local))) ; keep playback in sync
(when follow?
(r/after-render #(follow! fps zoom local))))))
(r/after-render #(do (follow! fps zoom local)
(scroll-to-seg-track! segs local)))))))
(defn video-monitor []
[:video {:src @(rf/subscribe [::subs/clip-url]) :controls true :preload "auto"
@ -322,7 +341,8 @@
^{:key (str "lbl-" (:id a))}
[:div.ann-bar-label {:style {:top (* i 18)
:left (+ 4 (px lo fps zoom))}} (:name a)])]]]
[:div.timeline-body-scroll {:style {:padding-top lane-h}}
[:div.timeline-body-scroll {:ref (fn [n] (reset! body-scroll-el n))
:style {:padding-top lane-h}}
[:div.gutter-tracks
(for [t tracks]
^{:key (:id t)}