diff --git a/tl/resources/public/css/app.css b/tl/resources/public/css/app.css index 926038d..6fadd50 100644 --- a/tl/resources/public/css/app.css +++ b/tl/resources/public/css/app.css @@ -436,12 +436,45 @@ html.dark .timeline-head { .upload-pct { font-family: var(--mono); font-size: 11px; min-width: 36px; text-align: right; } /* editor: project bar above the timeline — a Mac window title bar (selected) */ -.project-bar { display: flex; gap: 10px; align-items: center; padding: 4px 8px; - background: var(--ink); color: var(--paper); font-family: var(--chicago); - font-size: 12px; } -.project-bar a { color: var(--paper); text-decoration: underline; } -.project-bar .project-name { color: var(--paper); } -.project-bar .logout-link { margin-left: auto; color: var(--paper); text-decoration: underline; } +/* --- top bar: HyperCard window title bar -------------------------------- */ +.titlebar { + flex-shrink: 0; display: flex; align-items: center; gap: 8px; + padding: 3px 8px; background: var(--paper); color: var(--ink); + border-bottom: 2px solid var(--ink); + font-family: var(--chicago); font-size: 12px; user-select: none; +} +.titlebar-left, .titlebar-right { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; } +.titlebar-center { flex: 1; min-width: 0; display: flex; align-items: center; + justify-content: center; gap: 8px; } +/* the signature racing-stripes flanking the centered window title */ +.titlebar-center::before, .titlebar-center::after { + content: ""; flex: 1 1 0; min-width: 6px; height: 11px; + background: repeating-linear-gradient(to bottom, var(--ink) 0 1px, transparent 1px 2px); +} +.tl-logo { font-weight: bold; letter-spacing: 1px; padding: 0 5px; + background: var(--ink); color: var(--paper); } +.title-back { text-decoration: none; color: var(--ink); font-size: 16px; line-height: 1; + padding: 0 5px; } +.title-back:hover { background: var(--ink); color: var(--paper); } +.title-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + max-width: 40vw; padding: 0 4px; background: var(--paper); } +.titlebar-status { font-size: 10px; color: var(--mute); } +.titlebar-status.err { color: #e66; } + +/* bar buttons + popover menus (share / user) */ +.menu-wrap { position: relative; } +.bar-btn { font-family: var(--chicago); font-size: 11px; background: var(--paper); + color: var(--ink); border: 1px solid var(--ink); border-radius: 0; + padding: 1px 7px; cursor: pointer; } +.bar-btn:hover { background: var(--ink); color: var(--paper); } +.menu-backdrop { position: fixed; inset: 0; z-index: 30; background: transparent; } +.menu-pop { position: absolute; top: calc(100% + 3px); right: 0; z-index: 31; + min-width: 190px; background: var(--paper); border: 1px solid var(--ink); + box-shadow: 2px 2px 0 var(--ink); padding: 4px; } +.menu-pop button { display: block; width: 100%; text-align: left; background: var(--paper); + color: var(--ink); border: none; padding: 5px 7px; cursor: pointer; + font-family: var(--chicago); font-size: 11px; white-space: nowrap; } +.menu-pop button:hover { background: var(--ink); color: var(--paper); } /* --- annotation / script pane tabs ------------------------------------- */ .pane-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column; } diff --git a/tl/src/tl/views.cljs b/tl/src/tl/views.cljs index 1fabafa..6e990d3 100644 --- a/tl/src/tl/views.cljs +++ b/tl/src/tl/views.cljs @@ -957,33 +957,85 @@ [script-pane] (if draft? [annotation-form] [commentary]))]])) +;; --- top bar: HyperCard-style window title bar --------------------------- + +(defn- abs-url + "Absolute, shareable URL for a same-origin fragment route." + [rel] + (str (.. js/window -location -origin) (.. js/window -location -pathname) rel)) + +(defn- copy-text! [text] + (when-let [cb (.-clipboard js/navigator)] + (.writeText cb text))) + +(defn share-menu [] + (let [open? (r/atom false) + copied (r/atom nil)] + (fn [] + (let [proj @(rf/subscribe [::subs/project]) + stack @(rf/subscribe [::subs/stack]) + playhead @(rf/subscribe [::subs/playhead]) + id (:id proj) + grab (fn [rel which] + (copy-text! (abs-url rel)) + (reset! copied which) + (js/setTimeout #(reset! copied nil) 1600))] + [:div.menu-wrap + [:button.bar-btn {:on-click #(swap! open? not)} "Share ▾"] + (when @open? + [:<> + [:div.menu-backdrop {:on-click #(reset! open? false)}] + [:div.menu-pop + [:button {:on-click #(grab (routes/project-url id stack playhead) :moment)} + (if (= @copied :moment) "✓ Link copied" "Copy link to this moment")] + [:button {:on-click #(grab (routes/project-url id [:root] nil) :project)} + (if (= @copied :project) "✓ Link copied" "Copy link to the whole project")]]])])))) + +(defn user-menu [] + (let [open? (r/atom false)] + (fn [] + (let [user @(rf/subscribe [::subs/username])] + [:div.menu-wrap + [:button.bar-btn {:on-click #(swap! open? not)} (str (or user "account") " ▾")] + (when @open? + [:<> + [:div.menu-backdrop {:on-click #(reset! open? false)}] + [:div.menu-pop + [:button {:on-click #(do (reset! open? false) + (rf/dispatch [::events/logout]))} "Log out"]]])])))) + +(defn top-bar [] + (let [proj @(rf/subscribe [::subs/project]) + authed? @(rf/subscribe [::subs/authed?]) + thumb-status @(rf/subscribe [::subs/thumbnail-status])] + [:div.titlebar + [:div.titlebar-left + [:span.tl-logo "TL"] + (when (#{"pending" "running"} thumb-status) + [:span.titlebar-status "thumbnails…"]) + (when (= "failed" thumb-status) + [:span.titlebar-status.err "thumbnails failed"])] + [:div.titlebar-center + [:a.title-back {:href (routes/href :projects) :title "Back to projects"} "‹"] + [:span.title-name (:name proj)]] + [:div.titlebar-right + [share-menu] + (when authed? [user-menu])]])) + (defn editor [] (let [status @(rf/subscribe [::subs/status]) - proj @(rf/subscribe [::subs/project]) - authed? @(rf/subscribe [::subs/authed?]) - thumb-status @(rf/subscribe [::subs/thumbnail-status]) load-err @(rf/subscribe [::subs/load-error]) ;; "picker mode": a draft is open (pick clips/frame for marks); + linking ;; (pick clips/frame/annotations for a link). Drives the pulsing glow. authoring? (some? @(rf/subscribe [::subs/draft-group])) linking? (some? @(rf/subscribe [::subs/linking]))] [:div.app {:class (str (when authoring? "picking ") (when linking? "linking"))} + [top-bar] [:div.top [:div.video-pane [video-monitor] [frame-readout]] [:div.annot-pane [annot-pane]]] [:div.divider-h {:on-pointer-down drag-top!}] [:div.timeline-pane - [:div.project-bar - [:a.home-link {:href (routes/href :projects)} "← projects"] - [:span.project-name (:name proj)] - (when (#{"pending" "running"} thumb-status) - [:span.muted "thumbnails processing"]) - (when (= "failed" thumb-status) - [:span.err "thumbnail generation failed"]) - (when authed? - [:a.logout-link {:href "#" :on-click #(do (.preventDefault %) - (rf/dispatch [::events/logout]))} - "log out"])] [breadcrumbs] [toolbar] [:div.timeline-scroll