diff --git a/tl/resources/public/css/app.css b/tl/resources/public/css/app.css index 431a333..9939258 100644 --- a/tl/resources/public/css/app.css +++ b/tl/resources/public/css/app.css @@ -199,11 +199,14 @@ body { overflow: hidden; } flex-shrink: 0; } .slider { display: flex; align-items: center; gap: 6px; font-size: 11px; color: #9ab; } .slider input { width: 150px; touch-action: none; } -.play-btn { +.transport { display: flex; align-items: center; gap: 4px; } +.play-btn, .step-btn { background: #1c2a38; color: #cfe3f5; border: 1px solid #3a6ea5; - border-radius: 4px; padding: 2px 12px; cursor: pointer; font-size: 14px; line-height: 1.4; + border-radius: 4px; cursor: pointer; line-height: 1.4; } -.play-btn:hover { background: #24384e; } +.play-btn { padding: 2px 12px; font-size: 14px; } +.step-btn { padding: 2px 7px; font-size: 11px; letter-spacing: -1px; } +.play-btn:hover, .step-btn:hover { background: #24384e; } .timeline-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; min-height: 0; } .timeline { display: flex; align-items: flex-start; background: #111; } diff --git a/tl/src/tl/views.cljs b/tl/src/tl/views.cljs index 99b56bb..f0a0454 100644 --- a/tl/src/tl/views.cljs +++ b/tl/src/tl/views.cljs @@ -116,6 +116,15 @@ (when-let [v @video-el] (if (.-paused v) (.play v) (.pause v)))) ; play/pause events drive :playing? + rAF +(defn- step-frame! + "Pause and nudge the playhead one frame in `dir` (-1 / +1). Seeking is the + only move — the rAF clock reports the new position back into :playhead." + [fps dir] + (when-let [v @video-el] + (when-not (.-paused v) (.pause v)) + (let [frame (js/Math.round (* (.-currentTime v) fps))] + (set! (.-currentTime v) (secs (max 0 (+ frame dir)) fps))))) + (defn- editable-target? "True for fields where Space should type a space, not toggle play." [el] @@ -486,10 +495,14 @@ (defn zoom-controls [] (let [zoom @(rf/subscribe [::subs/zoom]) row-h @(rf/subscribe [::subs/row-h]) + fps (or @(rf/subscribe [::subs/fps]) (/ 24000 1001)) playing? @(rf/subscribe [::subs/playing?])] [:div.toolbar - [:button.play-btn {:on-click toggle-play! :title "Play / pause (Space)"} - (if playing? "⏸" "▶")] + [:div.transport + [:button.step-btn {:on-click #(step-frame! fps -1) :title "Previous frame"} "|◀"] + [:button.play-btn {:on-click toggle-play! :title "Play / pause (Space)"} + (if playing? "⏸" "▶")] + [:button.step-btn {:on-click #(step-frame! fps 1) :title "Next frame"} "▶|"]] [slider "Zoom X" zoom 2 400 zoom-x!] [slider "Zoom Y" row-h 8 120 #(rf/dispatch [::events/set-row-h %])]]))