diff --git a/package-lock.json b/package-lock.json index d8e7e043..a2e6bef3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,13 +21,13 @@ }, "devDependencies": { "@mdn/browser-compat-data": "5.4.1", - "@playwright/test": "^1.39.0", + "@playwright/test": "1.39.0", "@radix-ui/react-context-menu": "2.1.5", "@radix-ui/react-dropdown-menu": "2.0.6", "@radix-ui/react-menubar": "1.0.4", "@radix-ui/react-popover": "1.0.7", "@radix-ui/react-select": "1.2.2", - "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-switch": "1.0.3", "@radix-ui/react-tooltip": "1.0.7", "@re-path/react-color": "2.19.4", "@sentry/react": "7.66.0", diff --git a/resources/public/icons/go-to-end.svg b/resources/public/icons/go-to-end.svg new file mode 100644 index 00000000..386b8d00 --- /dev/null +++ b/resources/public/icons/go-to-end.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/public/icons/go-to-start.svg b/resources/public/icons/go-to-start.svg new file mode 100644 index 00000000..9dc73e38 --- /dev/null +++ b/resources/public/icons/go-to-start.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/renderer/db.cljs b/src/renderer/db.cljs index 2dab5505..550ea4aa 100644 --- a/src/renderer/db.cljs +++ b/src/renderer/db.cljs @@ -52,7 +52,6 @@ :visible? true} :properties {:size 300 :visible? true} - :history? false :timeline {:visible? true} :xml {:visible? false} :repl-history {:visible? false}} @@ -61,6 +60,7 @@ :fullscreen? false :header? true} :timeline {:time 0 + :replay? true :grid-snap? false :guide-snap? true :paused? false}}) diff --git a/src/renderer/reepl/views.cljs b/src/renderer/reepl/views.cljs index 0b318522..7ae8e3c7 100644 --- a/src/renderer/reepl/views.cljs +++ b/src/renderer/reepl/views.cljs @@ -18,7 +18,7 @@ (replumb/print-doc doc))))) (defn main-view [] - [:div.flex.flex-col {:style {:overflow "visible"}} + [:div.flex.flex-col.overflow-visible [reepl/repl :execute #(replumb/run-repl (if (= @(rf/subscribe [:repl-mode]) :cljs) %1 (str "(js/eval \"" %1 "\")")) {:warning-as-error true} %2) :complete-word replumb/process-apropos diff --git a/src/renderer/timeline/events.cljs b/src/renderer/timeline/events.cljs index 34f9af1d..0e78254a 100644 --- a/src/renderer/timeline/events.cljs +++ b/src/renderer/timeline/events.cljs @@ -49,3 +49,8 @@ :timeline/set-guide-snap (fn [db [_ state]] (assoc-in db [:timeline :guide-snap?] state))) + + (rf/reg-event-db + :timeline/toggle-replay + (fn [db _] + (update-in db [:timeline :replay?] not))) diff --git a/src/renderer/timeline/styles.css b/src/renderer/timeline/styles.css index 0323b589..b96d6e5c 100644 --- a/src/renderer/timeline/styles.css +++ b/src/renderer/timeline/styles.css @@ -1,7 +1,7 @@ .timeline-editor { - @apply level-1 w-full !important; - height: 200px !important; + @apply level-1 w-full min-h-0 !important; font-family: inherit !important; + color: var(--font-color-muted) !important; } .timeline-editor-action { @@ -20,7 +20,15 @@ fill: var(--accent) !important; } +.timeline-editor-time-unit { + border-color: var(--border-color) !important; +} + +.timeline-editor-time-unit-scale { + color: var(--font-color-muted) !important; +} + .timeline-editor-edit-row { background-image: linear-gradient(var(--level-1), var(--level-1)), - linear-gradient(90deg, rgba(255, 255, 255, 0.08) 1px, transparent 0) !important; + linear-gradient(90deg, var(--border-color) 1px, transparent 0) !important; } diff --git a/src/renderer/timeline/subs.cljs b/src/renderer/timeline/subs.cljs index 2126603c..7e161cf3 100644 --- a/src/renderer/timeline/subs.cljs +++ b/src/renderer/timeline/subs.cljs @@ -37,6 +37,12 @@ (mapv animation->timeline-row) clj->js))) +(rf/reg-sub + :timeline/end + :<- [:animations] + (fn [animations] + (reduce #(max (js/parseInt (-> %2 :attrs :end)) %1) 0 animations))) + (defn animation->effect [{:keys [attrs] :as el}] {:id (effect-id el) @@ -55,10 +61,15 @@ (-> n str js/parseInt str (.padStart 2 "0"))) (rf/reg-sub - :timeline/time-formatted + :timeline/time (fn [db _] - (let [time (-> db :timeline :time) - min (-> time (/ 60) pad-2) + (-> db :timeline :time))) + +(rf/reg-sub + :timeline/time-formatted + :<- [:timeline/time] + (fn [time] + (let [min (-> time (/ 60) pad-2) sec (-> time (rem 60) pad-2) ms (-> time (rem 1) (* 100) pad-2 (str/replace "0." ""))] (str min ":" sec ":" ms)))) @@ -77,3 +88,8 @@ :timeline/guide-snap? (fn [db _] (-> db :timeline :guide-snap?))) + +(rf/reg-sub + :timeline/replay? + (fn [db _] + (-> db :timeline :replay?))) diff --git a/src/renderer/timeline/views.cljs b/src/renderer/timeline/views.cljs index 6ae55635..9419a7db 100644 --- a/src/renderer/timeline/views.cljs +++ b/src/renderer/timeline/views.cljs @@ -7,18 +7,11 @@ [reagent.core :as ra] [renderer.components :as comp])) -(defn toolbar - [editor-ref] - (let [time @(rf/subscribe [:timeline/time-formatted]) - paused? @(rf/subscribe [:timeline/paused?]) - grid-snap? @(rf/subscribe [:timeline/grid-snap?]) +(defn snap-controls + [] + (let [grid-snap? @(rf/subscribe [:timeline/grid-snap?]) guide-snap? @(rf/subscribe [:timeline/guide-snap?])] - [:div.toolbar.level-1 - (if paused? - [comp/icon-button "play" {:on-click #(.play (.-current editor-ref))}] - [comp/icon-button "pause" {:on-click #(.pause (.-current editor-ref))}]) - [:span.p-2.font-mono time] - [:span.v-divider] + [:<> [:span.inline-flex.items-center [:label {:for "grid-snap" @@ -36,7 +29,7 @@ {:for "guide-snap" :style {:background "transparent" :height "auto"}} - "Line snap"] + "Guide snap"] [:> Switch/Root {:class "switch-root" :id "guide-snap" @@ -44,6 +37,41 @@ :on-checked-change #(rf/dispatch [:timeline/set-guide-snap %])} [:> Switch/Thumb {:class "switch-thumb"}]]]])) +(defn toolbar + [editor-ref] + (let [time @(rf/subscribe [:timeline/time]) + time-formatted @(rf/subscribe [:timeline/time-formatted]) + paused? @(rf/subscribe [:timeline/paused?]) + replay? @(rf/subscribe [:timeline/replay?]) + end @(rf/subscribe [:timeline/end]) + timeline? @(rf/subscribe [:panel/visible? :timeline])] + [:div.toolbar.level-1.mb-px + [:div.flex-1.flex + [comp/icon-button "go-to-start" {:on-click #(.setTime (.-current editor-ref) 0) + :disabled (zero? time)}] + (if paused? + [comp/icon-button "play" {:on-click #(.play (.-current editor-ref) #js {:autoEnd true})}] + [comp/icon-button "pause" {:on-click #(.pause (.-current editor-ref))}]) + [comp/icon-button "go-to-end" {:on-click #(.setTime (.-current editor-ref) end) + :disabled (>= time end)}] + [comp/radio-icon-button + {:title "Replay" + :active? replay? + :icon "refresh" + :action #(rf/dispatch [:timeline/toggle-replay])}] + [:span.p-2.font-mono time-formatted] + (when timeline? + [:<> + [:span.v-divider] + [snap-controls]])] + [comp/toggle-icon-button + {:active? (not timeline?) + :active-icon "chevron-up" + :active-text "Show timeline" + :inactive-icon "times" + :inactive-text "Hide timeline" + :action #(rf/dispatch [:panel/toggle :timeline])}]])) + (defn root [] (let [ref (react/createRef)] @@ -56,6 +84,9 @@ [[e f] [["play" #(rf/dispatch-sync [:timeline/play])] ;; Prevent navigation ["paused" #(rf/dispatch-sync [:timeline/pause])] + ["ended" #(when @(rf/subscribe [:timeline/replay?]) + (.setTime (.-current ref) 0) + (.play (.-current ref) #js {:autoEnd true}))] ["afterSetTime" #(rf/dispatch-sync [:timeline/set-time (.-time %)])] ["setTimeByTick" #(rf/dispatch-sync [:timeline/set-time (.-time %)])]]] (.on (.-listener (.-current ref)) e f))) @@ -68,10 +99,12 @@ (let [data @(rf/subscribe [:timeline/rows]) effects @(rf/subscribe [:timeline/effects]) grid-snap? @(rf/subscribe [:timeline/grid-snap?]) - guide-snap? @(rf/subscribe [:timeline/guide-snap?])] + guide-snap? @(rf/subscribe [:timeline/guide-snap?]) + timeline? @(rf/subscribe [:panel/visible? :timeline])] [:div [toolbar ref] - [:> Timeline {:editor-data data + [:> Timeline {:style {:height (if timeline? "200px" 0)} + :editor-data data :effects effects :ref ref :grid-snap grid-snap? diff --git a/src/renderer/tools/animate_motion.cljs b/src/renderer/tools/animate_motion.cljs index 99e99378..9865a7ea 100644 --- a/src/renderer/tools/animate_motion.cljs +++ b/src/renderer/tools/animate_motion.cljs @@ -9,4 +9,6 @@ [] {:description "The SVG element let define how an element moves along a motion path." - :attrs []}) + :attrs [:keyPoints + :path + :rotate]}) diff --git a/src/renderer/tools/animate_transform.cljs b/src/renderer/tools/animate_transform.cljs index f7018129..9b239b0c 100644 --- a/src/renderer/tools/animate_transform.cljs +++ b/src/renderer/tools/animate_transform.cljs @@ -10,4 +10,7 @@ {:description "The animateTransform element animates a transformation attribute on its target element, thereby allowing animations to control translation, scaling, rotation, and/or skewing." - :attrs []}) + :attrs [:type + :from + :to + :by]})