optimization: perf improvements

This commit is contained in:
Your Name 2026-07-01 19:53:57 -04:00
parent 40ff2ecf52
commit d22bff7654
2 changed files with 81 additions and 38 deletions

View file

@ -68,7 +68,7 @@
(defonce ^:private last-replaced (atom nil))
(def ^:private last-url-key "tl/last-url")
(defn replace-project-state! [{:keys [id stack playhead]}]
(defn- do-replace! [{:keys [id stack playhead]}]
(when id
(let [url (project-url id stack playhead)]
(when (not= url @last-replaced)
@ -77,6 +77,24 @@
;; remember where we were so an installed PWA can come back here
(.setItem js/localStorage last-url-key url)))))
;; Playback moves the playhead ~60fps, and set-playhead syncs it into the URL's
;; ?f= param — but rebuilding the URL + history.replaceState + a synchronous
;; localStorage write every frame hitches the main thread. Trailing-throttle so
;; the deep-link / PWA-resume state stays roughly current (leading edge keeps
;; navigation snappy) without per-frame writes.
(defonce ^:private replace-timer (atom nil))
(defonce ^:private pending-args (atom nil))
(defn replace-project-state! [args]
(reset! pending-args args)
(when-not @replace-timer
(do-replace! args) ; leading edge: immediate
(reset! replace-timer
(js/setTimeout (fn []
(reset! replace-timer nil)
(do-replace! @pending-args)) ; trailing: final position
400))))
(defn- standalone?
"Running as an installed PWA (added to homescreen), not a browser tab."
[]

View file

@ -359,17 +359,30 @@
:ref (fn [n] (when n (reset! video-el n)))}]
[drawing-layer]]))))
(defn- build-label-map
"{mark-id → \"Track (occurrence)\"} for every segment, in one pass over `segs`."
[scene segs]
(into {}
(mapcat (fn [[t tsegs]]
(map-indexed (fn [i {m :mark}]
[m (str (get-in scene [:tracks t :name] (name t)) " (" (inc i) ")")])
(sort-by (comp first :local) tsegs)))
(group-by :track segs))))
;; clip-label ran group-by + sort over ALL segments on every call, and it's called
;; once per clip per render → O(segs²) on each timeline re-render (zoom, jump, …).
;; The labels are stable per `segs`, so memoize the whole map on segs identity
;; (::segments returns a stable ref until scene/context changes) → O(1) lookups.
(defonce ^:private label-cache (atom {:segs nil :map nil}))
(defn- clip-label
"Track-local clip label for the content segment whose :mark is `mid`."
[scene segs mid]
(or (some (fn [[t tsegs]]
(some (fn [[i {m :mark}]]
(when (= m mid)
(str (get-in scene [:tracks t :name] (name t))
" (" (inc i) ")")))
(map-indexed vector (sort-by (comp first :local) tsegs))))
(group-by :track segs))
"clip"))
(let [c @label-cache
m (if (identical? segs (:segs c))
(:map c)
(:map (reset! label-cache {:segs segs :map (build-label-map scene segs)})))]
(get m mid "clip")))
(defn- clip-at
"Describe local frame `lf` the way the annotation editor does: the underlying
@ -449,28 +462,15 @@
(.addEventListener js/document "mousemove" move)
(.addEventListener js/document "mouseup" up)))
(defn timeline []
(let [content (atom nil)
lanes-scroll (atom nil)
tracks-open? (r/atom true)]
(fn []
(let [scene @(rf/subscribe [::subs/scene])
fps @(rf/subscribe [::subs/fps])
zoom @(rf/subscribe [::subs/zoom])
row-h @(rf/subscribe [::subs/row-h])
segs @(rf/subscribe [::subs/segments])
tracks @(rf/subscribe [::subs/tracks])
anns @(rf/subscribe [::subs/annotations])
thumbs @(rf/subscribe [::subs/thumbnails])
playhead @(rf/subscribe [::subs/playhead])
playing? @(rf/subscribe [::subs/playing?])
authoring? (some? @(rf/subscribe [::subs/draft-group]))
linking @(rf/subscribe [::subs/linking])
len @(rf/subscribe [::subs/length])
width (px len fps zoom)
lane-h (max 18 (* 18 (count anns)))
tracks-h (* row-h (count tracks))
track-y (into {} (map-indexed (fn [i t] [(:id t) i]) tracks))]
(defn- playhead-fx
"Renders nothing. Isolates the playhead-driven side effects — positioning the
(imperative) playhead line and the follow-scroll during playback — so a moving
playhead re-renders only this empty component, not the whole timeline DOM (all
clips + per-clip clip-label) every animation frame. The timeline's hiccup never
depended on the playhead; only these effects did."
[fps zoom segs anns scene]
(let [playhead @(rf/subscribe [::subs/playhead])
playing? @(rf/subscribe [::subs/playing?])]
(if playing?
(r/after-render
(fn []
@ -479,10 +479,10 @@
(when (and (nil? @scrolled-on-load?) (> playhead 0) (seq segs))
(reset! scrolled-on-load? true)
(scroll-to-seg-track! segs playhead))
;; clip-following: only while the playhead is actually inside a bar of
;; an annotation that opts in (:meta :scroll-to) and is not hidden —
;; scroll each of its clips into view as the playhead crosses onto a new bar (discontinuity).
;; Track [ann-id bar-idx] to avoid scrolling within the same bar.
;; clip-following: only while the playhead is inside a bar of an annotation
;; that opts in (:meta :scroll-to) and is not hidden — scroll each clip into
;; view as the playhead crosses onto a new bar. Track [ann-id bar-idx] to
;; avoid scrolling within the same bar.
(when-let [current-bar (some (fn [a]
(when (and (not (get-in (get-in scene [:groups (:id a)]) [:meta :hidden]))
(ann-scroll-to? (get-in scene [:groups (:id a)]))
@ -497,7 +497,30 @@
(do (reset! played-bar nil)
(reset! scrolled-on-load? nil)))
(r/after-render #(position-playhead! fps zoom playhead))
nil))
(defn timeline []
(let [content (atom nil)
lanes-scroll (atom nil)
tracks-open? (r/atom true)]
(fn []
(let [scene @(rf/subscribe [::subs/scene])
fps @(rf/subscribe [::subs/fps])
zoom @(rf/subscribe [::subs/zoom])
row-h @(rf/subscribe [::subs/row-h])
segs @(rf/subscribe [::subs/segments])
tracks @(rf/subscribe [::subs/tracks])
anns @(rf/subscribe [::subs/annotations])
thumbs @(rf/subscribe [::subs/thumbnails])
authoring? (some? @(rf/subscribe [::subs/draft-group]))
linking @(rf/subscribe [::subs/linking])
len @(rf/subscribe [::subs/length])
width (px len fps zoom)
lane-h (max 18 (* 18 (count anns)))
tracks-h (* row-h (count tracks))
track-y (into {} (map-indexed (fn [i t] [(:id t) i]) tracks))]
[:div.timeline {:class (when-not @tracks-open? "tracks-collapsed")}
[playhead-fx fps zoom segs anns scene]
[:div.playhead.timeline-playhead {:ref (fn [n] (reset! playhead-el n))}
[:div.playhead-handle]]
[:div.timeline-head
@ -567,7 +590,9 @@
:on-scroll #(let [sl (.. % -target -scrollLeft)]
(when @lanes-scroll
(set! (.-scrollLeft @lanes-scroll) sl))
(position-playhead! fps zoom playhead))}
;; deref at scroll time (not render) so this
;; handler needs no render-time playhead dep
(position-playhead! fps zoom @(rf/subscribe [::subs/playhead])))}
[:div.content.track-content {:ref (fn [n] (reset! content n))
:on-mouse-down #(scrub! @content fps zoom %)
:style {:width width :height tracks-h}}