translucent stuff etc
This commit is contained in:
parent
5bc53c03f3
commit
96639554e4
2 changed files with 128 additions and 58 deletions
|
|
@ -54,20 +54,20 @@ body { overflow: hidden; background: var(--desktop); background-size: 2px 2px;
|
|||
press. The default action (.save) gets the heavy Mac "default button" ring. */
|
||||
.jump-btn, .add-btn, .edit-btn, .del-btn, .expand-btn, .back-btn, .crumb,
|
||||
.play-btn, .step-btn, .row-x, .add-mark, .save, .cancel, .hl-ok, .hl-cancel,
|
||||
.pane-tabs button {
|
||||
.pane-tabs button, .track-toggle {
|
||||
font-family: var(--chicago); background: var(--paper); color: var(--ink);
|
||||
border: 1px solid var(--ink); border-radius: 8px; cursor: pointer; line-height: 1.3;
|
||||
}
|
||||
.jump-btn:hover, .add-btn:hover, .edit-btn:hover, .del-btn:hover, .expand-btn:hover,
|
||||
.back-btn:hover, .crumb:hover, .play-btn:hover, .step-btn:hover, .row-x:hover,
|
||||
.add-mark:hover, .save:hover, .cancel:hover, .hl-ok:hover, .hl-cancel:hover,
|
||||
.pane-tabs button:hover {
|
||||
.pane-tabs button:hover, .track-toggle:hover {
|
||||
background: var(--hover);
|
||||
}
|
||||
.jump-btn:active, .add-btn:active, .edit-btn:active, .del-btn:active, .expand-btn:active,
|
||||
.back-btn:active, .crumb:active, .play-btn:active, .step-btn:active, .row-x:active,
|
||||
.add-mark:active, .save:active, .cancel:active, .hl-ok:active, .hl-cancel:active,
|
||||
.pane-tabs button:active {
|
||||
.pane-tabs button:active, .track-toggle:active {
|
||||
background: var(--ink); color: var(--paper);
|
||||
}
|
||||
|
||||
|
|
@ -289,10 +289,35 @@ body { overflow: hidden; background: var(--desktop); background-size: 2px 2px;
|
|||
.add-annotation:hover { background: var(--paper); color: var(--ink); }
|
||||
.add-annotation:active { box-shadow: 0 0 0 var(--ink); transform: translate(2px, 2px); }
|
||||
|
||||
.timeline-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; min-height: 0; }
|
||||
.timeline { display: flex; align-items: flex-start; background: var(--paper); }
|
||||
.timeline-scroll { flex: 1; overflow: hidden; min-height: 0; display: flex; }
|
||||
.timeline {
|
||||
flex: 1; min-width: 0; min-height: 0;
|
||||
position: relative; display: flex; flex-direction: column; background: var(--paper);
|
||||
}
|
||||
.timeline-head {
|
||||
position: absolute; top: 0; left: 0; right: 0; z-index: 12;
|
||||
display: flex; align-items: flex-start;
|
||||
background: rgba(255, 255, 255, .58);
|
||||
backdrop-filter: blur(4px);
|
||||
-webkit-backdrop-filter: blur(4px);
|
||||
}
|
||||
html.dark .timeline-head {
|
||||
background: rgba(0, 0, 0, .58);
|
||||
}
|
||||
.timeline-body-scroll {
|
||||
flex: 1; min-height: 0; display: flex; align-items: flex-start;
|
||||
overflow-y: auto; overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.gutter { flex-shrink: 0; width: 150px; user-select: none; }
|
||||
.gutter-lanes, .gutter-tracks {
|
||||
flex-shrink: 0; width: 150px; user-select: none; transition: width .2s ease;
|
||||
}
|
||||
.gutter-lanes {
|
||||
position: relative;
|
||||
box-sizing: border-box; background: transparent; border-right: 1px solid var(--ink);
|
||||
}
|
||||
.track-toggle { display: none; }
|
||||
.gutter-label {
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid var(--ink); border-right: 1px solid var(--ink);
|
||||
|
|
@ -300,8 +325,18 @@ body { overflow: hidden; background: var(--desktop); background-size: 2px 2px;
|
|||
background: var(--paper);
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.hscroll { overflow-x: auto; flex: 1; }
|
||||
.content { position: relative; cursor: ew-resize; overflow: hidden; }
|
||||
.hscroll {
|
||||
overflow-x: auto; overflow-y: hidden;
|
||||
flex: 1; min-width: 0;
|
||||
overscroll-behavior-x: contain;
|
||||
}
|
||||
.lanes-hscroll { overflow: hidden; flex: 1; min-width: 0; }
|
||||
.content { position: relative; cursor: ew-resize; }
|
||||
.track-lane {
|
||||
position: absolute; left: 0; box-sizing: border-box;
|
||||
border-bottom: 1px dotted color-mix(in srgb, var(--ink) 22%, transparent);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.track-row { position: relative; background: var(--paper); border-bottom: 1px solid var(--ink);
|
||||
overflow: hidden; transition: height .3s ease, opacity .3s ease; }
|
||||
|
|
@ -336,10 +371,15 @@ body { overflow: hidden; background: var(--desktop); background-size: 2px 2px;
|
|||
|
||||
/* --- annotation lane ---------------------------------------------------- */
|
||||
.ann-lane-row { position: relative; height: 18px; }
|
||||
.ann-lanes {
|
||||
position: relative; background: transparent;
|
||||
box-shadow: 0 1px 0 var(--ink);
|
||||
}
|
||||
.ann-bar { position: absolute; top: 2px; height: 14px; border-radius: 0; }
|
||||
.ann-bar-label { position: absolute; top: 1px; font-family: var(--chicago); font-size: 9px;
|
||||
line-height: 16px; color: var(--ink); white-space: nowrap;
|
||||
pointer-events: none; }
|
||||
.lane-playhead { bottom: 0; }
|
||||
|
||||
/* --- mobile: stack the top region vertically (video over annotation) ---- */
|
||||
@media (max-width: 720px) {
|
||||
|
|
@ -349,6 +389,17 @@ body { overflow: hidden; background: var(--desktop); background-size: 2px 2px;
|
|||
.annot-pane { width: 100%; min-width: 0; flex: 1 1 0; min-height: 110px; }
|
||||
.divider-h { height: 14px; }
|
||||
.slider input { width: 120px; }
|
||||
.track-toggle {
|
||||
display: block; position: absolute; top: 3px; left: 4px; z-index: 10;
|
||||
max-width: calc(100% - 8px); padding: 2px 6px; font-size: 10px;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
}
|
||||
.timeline.tracks-collapsed .gutter-lanes,
|
||||
.timeline.tracks-collapsed .gutter-tracks { width: 34px; }
|
||||
.timeline.tracks-collapsed .gutter-label {
|
||||
opacity: 0; padding: 0; border-right: 1px solid var(--ink);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- landing pages (project list / create) ----------------------------- */
|
||||
|
|
|
|||
|
|
@ -244,7 +244,9 @@
|
|||
(set! (.-scrollLeft el) (max 0 (- x (/ (.-clientWidth el) 2)))))))
|
||||
|
||||
(defn timeline []
|
||||
(let [content (atom nil)]
|
||||
(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])
|
||||
|
|
@ -260,58 +262,75 @@
|
|||
linking @(rf/subscribe [::subs/linking])
|
||||
len @(rf/subscribe [::subs/length])
|
||||
width (px len fps zoom)
|
||||
lane-h (* 18 (count anns))
|
||||
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))]
|
||||
(when playing? (r/after-render #(follow! fps zoom playhead)))
|
||||
[:div.timeline
|
||||
[:div.gutter
|
||||
[:div {:style {:height lane-h}}]
|
||||
(for [t tracks]
|
||||
^{:key (:id t)}
|
||||
[:div.gutter-label {:style {:height row-h :line-height (str (dec row-h) "px")}}
|
||||
(:name t)])]
|
||||
[:div.hscroll {:ref (fn [n] (reset! scroll-el n))}
|
||||
[:div.content {:ref (fn [n] (reset! content n))
|
||||
:on-mouse-down #(scrub! @content fps zoom %)
|
||||
:style {:width width :height (+ lane-h (* row-h (count tracks)))}}
|
||||
[:div.playhead {:style {:left (px playhead fps zoom)}} [:div.playhead-handle]]
|
||||
;; annotation bars — solid for saved, dashed for the in-progress draft
|
||||
(for [[i a] (map-indexed vector anns)
|
||||
[j [lo hi]] (map-indexed vector (:bars a))]
|
||||
^{:key (str (:id a) "-" j)}
|
||||
[:div.ann-bar {:title (:name a)
|
||||
:on-mouse-down (when-not (:draft a)
|
||||
(fn [e] (.stopPropagation e)
|
||||
[:div.timeline {:class (when-not @tracks-open? "tracks-collapsed")}
|
||||
[:div.timeline-head
|
||||
[:div.gutter-lanes {:style {:height lane-h}}
|
||||
[:button.track-toggle {:title (if @tracks-open? "Collapse track names" "Show track names")
|
||||
:on-click #(swap! tracks-open? not)}
|
||||
(if @tracks-open? "<- HIDE" "->")]]
|
||||
[:div.lanes-hscroll {:ref (fn [n] (reset! lanes-scroll n))}
|
||||
[:div.ann-lanes {:style {:height lane-h :width width}
|
||||
:on-mouse-down #(scrub! @content fps zoom %)}
|
||||
[:div.playhead.lane-playhead {:style {:left (px playhead fps zoom)}}
|
||||
[:div.playhead-handle]]
|
||||
;; annotation bars — solid for saved, dashed for the in-progress draft
|
||||
(for [[i a] (map-indexed vector anns)
|
||||
[j [lo hi]] (map-indexed vector (:bars a))]
|
||||
^{:key (str (:id a) "-" j)}
|
||||
[:div.ann-bar {:title (:name a)
|
||||
:on-mouse-down (when-not (:draft a)
|
||||
(fn [e] (.stopPropagation e)
|
||||
(if linking
|
||||
(do (.preventDefault e) ; pick: link to this timeline
|
||||
(commit-link! {:kind :timeline :ref (:id a)} (:name a)))
|
||||
(rf/dispatch [::events/expand (:id a)]))))
|
||||
:style {:top (+ 2 (* i 18)) :height 14
|
||||
:left (px lo fps zoom) :width (max 4 (px (- hi lo) fps zoom))
|
||||
:background (str (:color a) (if (:draft a) "44" "cc"))
|
||||
:cursor (if (:draft a) "default" "pointer")
|
||||
:border-radius 2
|
||||
:border (str (if (:draft a) "1px dashed " "1px solid ") (:color a))}}])
|
||||
(for [[i a] (map-indexed vector anns) :let [[lo _] (first (:bars a))] :when lo]
|
||||
^{: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.gutter-tracks
|
||||
(for [t tracks]
|
||||
^{:key (:id t)}
|
||||
[:div.gutter-label {:style {:height row-h :line-height (str (dec row-h) "px")}}
|
||||
(:name t)])]
|
||||
[:div.hscroll {:ref (fn [n] (reset! scroll-el n))
|
||||
:on-scroll #(when @lanes-scroll
|
||||
(set! (.-scrollLeft @lanes-scroll)
|
||||
(.. % -target -scrollLeft)))}
|
||||
[:div.content.track-content {:ref (fn [n] (reset! content n))
|
||||
:on-mouse-down #(scrub! @content fps zoom %)
|
||||
:style {:width width :height tracks-h}}
|
||||
[:div.playhead {:style {:left (px playhead fps zoom)}}]
|
||||
(for [[i t] (map-indexed vector tracks)]
|
||||
^{:key (str "lane-" (:id t))}
|
||||
[:div.track-lane {:style {:top (* i row-h) :width width :height row-h}}])
|
||||
;; clips (while authoring, click to fill the focused endpoint)
|
||||
(for [{:keys [mark track src local] :as seg} segs]
|
||||
(let [[c d] local]
|
||||
^{:key (str mark "-" (first src))}
|
||||
[:div.clip {:class (when authoring? "clickable")
|
||||
:on-mouse-down (when authoring?
|
||||
(fn [e] (.stopPropagation e) (.preventDefault e)
|
||||
(if linking
|
||||
(do (.preventDefault e) ; pick: link to this timeline
|
||||
(commit-link! {:kind :timeline :ref (:id a)} (:name a)))
|
||||
(rf/dispatch [::events/expand (:id a)]))))
|
||||
:style {:position "absolute" :top (+ 2 (* i 18)) :height 14
|
||||
:left (px lo fps zoom) :width (max 4 (px (- hi lo) fps zoom))
|
||||
:background (str (:color a) (if (:draft a) "44" "cc"))
|
||||
:cursor (if (:draft a) "default" "pointer")
|
||||
:border-radius 2
|
||||
:border (str (if (:draft a) "1px dashed " "1px solid ") (:color a))}}])
|
||||
(for [[i a] (map-indexed vector anns) :let [[lo _] (first (:bars a))] :when lo]
|
||||
^{:key (str "lbl-" (:id a))}
|
||||
[:div.ann-bar-label {:style {:position "absolute" :top (* i 18)
|
||||
:left (+ 4 (px lo fps zoom))}} (:name a)])
|
||||
;; clips (while authoring, click to fill the focused endpoint)
|
||||
(for [{:keys [mark track src local] :as seg} segs]
|
||||
(let [[c d] local]
|
||||
^{:key (str mark "-" (first src))}
|
||||
[:div.clip {:class (when authoring? "clickable")
|
||||
:on-mouse-down (when authoring?
|
||||
(fn [e] (.stopPropagation e) (.preventDefault e)
|
||||
(if linking
|
||||
(commit-link! (scene/seg-point scene seg 0)
|
||||
(clip-label scene segs (:mark seg)))
|
||||
(rf/dispatch [::events/draft-click-seg (:mark seg)]))))
|
||||
:style {:left (px c fps zoom) :width (max 1 (px (- d c) fps zoom))
|
||||
:top (+ lane-h (* (get track-y track 0) row-h)) :height (- row-h 2)
|
||||
:line-height (str (- row-h 2) "px") :position "absolute"}}
|
||||
[clip-thumbnail-layer thumbs fps zoom row-h seg]
|
||||
[:div.clip-name (clip-label scene segs mark)]]))]]]))))
|
||||
(commit-link! (scene/seg-point scene seg 0)
|
||||
(clip-label scene segs (:mark seg)))
|
||||
(rf/dispatch [::events/draft-click-seg (:mark seg)]))))
|
||||
:style {:left (px c fps zoom) :width (max 1 (px (- d c) fps zoom))
|
||||
:top (* (get track-y track 0) row-h) :height (- row-h 2)
|
||||
:line-height (str (- row-h 2) "px") :position "absolute"}}
|
||||
[clip-thumbnail-layer thumbs fps zoom row-h seg]
|
||||
[:div.clip-name (clip-label scene segs mark)]]))]]]]))))
|
||||
|
||||
;; --- links: inline markdown chips that seek the timeline ------------------
|
||||
;; A link is a ref-point named in the content as `[label](mark:ref@at)` (see
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue