feat: HyperCard-style top title bar (logo, project name, share, user menu)
Replace the mid-page .project-bar with a proper top nav bar pinned above the video/timeline, styled like a classic Mac window title bar (centered title flanked by racing stripes). - left: TL wordmark + thumbnail-status indicator - center: ‹ back-to-projects + project name, between the title stripes - right: Share popover and a user dropdown with Log out - Share offers two clearly named links, copied to clipboard: "Copy link to this moment" (current context + playhead) and "Copy link to the whole project" (root) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
28aafc047f
commit
96da4a6bd8
2 changed files with 105 additions and 20 deletions
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue