From ab16763f8d57633b1d91af27b60de10cac7ccf5a Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 2 Jul 2026 10:29:58 -0400 Subject: [PATCH] fix: label jump-popover targets from the mark's clip ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The jump dropdown described each target with clip-at, which re-derived the clip by looking up the (floored) bar-edge frame in the content segments — at a clip boundary that lands one clip early, so a mark on clip B could read "clip A " even though the jump seeks correctly. The annotation editor labels each mark straight from its :ref via clip-label, so the two disagreed. Add scene/jump-targets (built on runs, keyed by the owning mark) and label the popover with clip-label + the mark-time frame — the same basis as the editor. Drops the now-unused clip-at. Co-Authored-By: Claude Opus 4.8 --- tl/src/tl/scene.cljs | 11 +++++++++++ tl/src/tl/subs.cljs | 3 +++ tl/src/tl/views.cljs | 26 +++++++++----------------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/tl/src/tl/scene.cljs b/tl/src/tl/scene.cljs index 0f8cc45..bc47af9 100644 --- a/tl/src/tl/scene.cljs +++ b/tl/src/tl/scene.cljs @@ -394,6 +394,17 @@ [{:id id :lo lo :hi hi :len (inc (- hi lo))}])) [] pts))) +(defn jump-targets + "One target per discontinuity for an annotation's jump popover — labelled from + the owning mark's clip ref + frame, the SAME basis the annotation editor uses + (clip-label of :ref), so the jump label can never disagree with the mark row. + Each: {:local :seg :f }." + [scene ctx gid] + (mapv (fn [{:keys [id lo]}] + (let [st (:start (some #(when (= id (:id %)) %) (:marks (grp scene gid))))] + {:local lo :seg (:ref st) :f (at->frame scene (:ref st) (:at st))})) + (runs scene ctx gid))) + (defn linkables "Pickable link targets within `ctx`, grouped for the autocomplete: one group per video track (its clips, by start) and per child annotation (its run diff --git a/tl/src/tl/subs.cljs b/tl/src/tl/subs.cljs index e3afa64..8903fd2 100644 --- a/tl/src/tl/subs.cljs +++ b/tl/src/tl/subs.cljs @@ -112,6 +112,9 @@ (filterv #(= :script-note (get-in scene [:groups % :type])))) :broken (boolean reason) :reason reason :hidden (boolean hidden) + ;; jump targets labelled from the marks' clip refs (same as + ;; the editor) — not re-derived from a floored bar frame + :jumps (scene/jump-targets scene ctx gid) :start (or (ffirst bars) 0) :bars bars})))) (sort-by (juxt :broken :start)) ; broken annotations sink to the bottom vec)))) diff --git a/tl/src/tl/views.cljs b/tl/src/tl/views.cljs index 3dd23dd..93d3369 100644 --- a/tl/src/tl/views.cljs +++ b/tl/src/tl/views.cljs @@ -384,15 +384,6 @@ (: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 - clip name plus its crop-adjusted mark-time frame, e.g. \"CU Tashi (1) 45f\"." - [scene segs lf] - (if-let [seg (some (fn [{[c d] :local :as s}] (when (and (<= c lf) (< lf d)) s)) segs)] - (str (clip-label scene segs (:mark seg)) - " " (:at (scene/seg-point scene seg (js/Math.round (- lf (first (:local seg)))))) "f") - (str (js/Math.round lf) "f"))) - (defn frame-readout "Bottom-right of the video: the timeline (context-absolute) frame, and the track + clip + mark-time frame under the playhead. While authoring, the clip @@ -881,21 +872,22 @@ (defn- jump-control "One jump button, or — when the annotation's marks have discontinuities (more - than one bar) — a button that opens a popover of one target per piece, each - labelled with the clip + frame it lands on (as the annotation editor shows it)." + than one piece) — a button that opens a popover of one target per piece. Each + is labelled straight from its mark's clip ref via clip-label, exactly like the + annotation editor's mark rows, so the two never disagree." [open a scene segs] - (let [bars (:bars a)] - (if (< (count bars) 2) + (let [jumps (:jumps a)] + (if (< (count jumps) 2) [:button.jump-btn {:on-click #(goto! (:start a) true)} "↪ jump"] [:span {:style {:position "relative"}} [:button.jump-btn {:on-click #(swap! open (fn [x] (when (not= x (:id a)) (:id a))))} - (str "↪ jump (" (count bars) ") ▾")] + (str "↪ jump (" (count jumps) ") ▾")] (when (= @open (:id a)) [:div.jump-pop - (for [[i [lo _]] (map-indexed vector bars)] + (for [[i {:keys [local seg f]}] (map-indexed vector jumps)] ^{:key i} - [:button {:on-click #(do (goto! lo true) (reset! open nil))} - (str "▸ " (clip-at scene segs lo))])])]))) + [:button {:on-click #(do (goto! local true) (reset! open nil))} + (str "▸ " (clip-label scene segs seg) " " f "f")])])]))) ;; A bound-note chip on an annotation card. Owns its own »»» state so a bar ;; crossing only re-renders the chip that flipped, not the whole card list.