mirror of
https://github.com/penpot/penpot.git
synced 2026-08-28 07:38:42 +00:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
74ca40abd4
@ -64,7 +64,7 @@
|
|||||||
(defn- handle-single-export
|
(defn- handle-single-export
|
||||||
[{:keys [:request/auth-token] :as exchange} {:keys [export name skip-children is-wasm] :as params}]
|
[{:keys [:request/auth-token] :as exchange} {:keys [export name skip-children is-wasm] :as params}]
|
||||||
(let [resource (rsc/create (:type export) (or name (:name export)))
|
(let [resource (rsc/create (:type export) (or name (:name export)))
|
||||||
export (assoc export :skip-children skip-children :is-wasm is-wasm)]
|
export (assoc export :skip-children skip-children :is-wasm (boolean is-wasm))]
|
||||||
|
|
||||||
(->> (rd/render export
|
(->> (rd/render export
|
||||||
(fn [{:keys [path] :as object}]
|
(fn [{:keys [path] :as object}]
|
||||||
@ -112,7 +112,7 @@
|
|||||||
(rsc/add-to-zip zip path (str/replace filename sanitize-file-regex "_")))
|
(rsc/add-to-zip zip path (str/replace filename sanitize-file-regex "_")))
|
||||||
|
|
||||||
proc (->> exports
|
proc (->> exports
|
||||||
(map (fn [export] (rd/render (assoc export :is-wasm is-wasm) append)))
|
(map (fn [export] (rd/render (assoc export :is-wasm (boolean is-wasm)) append)))
|
||||||
(p/all)
|
(p/all)
|
||||||
(p/mcat (fn [_] (rsc/close-zip zip)))
|
(p/mcat (fn [_] (rsc/close-zip zip)))
|
||||||
(p/fmap (constantly resource))
|
(p/fmap (constantly resource))
|
||||||
|
|||||||
@ -23,6 +23,7 @@
|
|||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.util.perf :as perf]
|
[app.util.perf :as perf]
|
||||||
[app.util.storage :as storage]
|
[app.util.storage :as storage]
|
||||||
|
[app.util.timers :as timers]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[beicon.v2.operators :as rxo]
|
[beicon.v2.operators :as rxo]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
@ -216,7 +217,7 @@
|
|||||||
(rx/create
|
(rx/create
|
||||||
(fn [subs]
|
(fn [subs]
|
||||||
(let [start (perf/now)]
|
(let [start (perf/now)]
|
||||||
(js/requestAnimationFrame
|
(timers/raf
|
||||||
#(.postTask js/scheduler
|
#(.postTask js/scheduler
|
||||||
(fn []
|
(fn []
|
||||||
(let [time (- (perf/now) start)]
|
(let [time (- (perf/now) start)]
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.main.data.changes :as dch]
|
[app.main.data.changes :as dch]
|
||||||
[app.main.data.event :as ev]
|
[app.main.data.event :as ev]
|
||||||
|
[app.main.data.exports.wasm :as wasm.exports]
|
||||||
[app.main.data.helpers :as dsh]
|
[app.main.data.helpers :as dsh]
|
||||||
[app.main.data.notifications :as ntf]
|
[app.main.data.notifications :as ntf]
|
||||||
[app.main.data.persistence :as-alias dps]
|
[app.main.data.persistence :as-alias dps]
|
||||||
@ -1154,10 +1155,11 @@
|
|||||||
:enabled true
|
:enabled true
|
||||||
:name ""}
|
:name ""}
|
||||||
|
|
||||||
params {:exports [export]
|
;; Create a deferred promise immediately, before any async operations.
|
||||||
:profile-id (:profile-id state)
|
;; Registering the clipboard write NOW preserves the user-gesture security
|
||||||
:cmd :export-shapes
|
;; context; the actual blob is supplied asynchronously once the export finishes.
|
||||||
:wait true}]
|
deferred (p/deferred)
|
||||||
|
write-promise (clipboard/to-clipboard-promise "image/png" deferred)]
|
||||||
|
|
||||||
(rx/concat
|
(rx/concat
|
||||||
;; Ensure current state persisted before exporting.
|
;; Ensure current state persisted before exporting.
|
||||||
@ -1167,21 +1169,34 @@
|
|||||||
(rx/first)
|
(rx/first)
|
||||||
(rx/timeout 400 (rx/empty)))
|
(rx/timeout 400 (rx/empty)))
|
||||||
|
|
||||||
;; Exporting itself can time its time, better to notify that we are busy.
|
;; Exporting itself can take its time, better to notify that we are busy.
|
||||||
(rx/of (ntf/info (tr "workspace.clipboard.copying")))
|
(rx/of (ntf/info (tr "workspace.clipboard.copying")))
|
||||||
|
|
||||||
;; Call exporter to get image URI, then fetch and copy blob.
|
;; Call exporter to get image URI, then fetch blob and resolve the deferred.
|
||||||
(->> (rp/cmd! :export params)
|
(->> (if (and (features/active-feature? state "render-wasm/v1")
|
||||||
|
(contains? cf/flags :wasm-export))
|
||||||
|
(rx/of {:uri (wasm.exports/export-image-uri export)})
|
||||||
|
(rp/cmd! :export
|
||||||
|
{:exports [export]
|
||||||
|
:profile-id (:profile-id state)
|
||||||
|
:cmd :export-shapes
|
||||||
|
:wait true}))
|
||||||
|
|
||||||
(rx/mapcat (fn [{:keys [uri]}]
|
(rx/mapcat (fn [{:keys [uri]}]
|
||||||
(http/send! {:method :get
|
(http/send! {:method :get
|
||||||
:uri uri
|
:uri uri
|
||||||
:response-type :blob})))
|
:response-type :blob})))
|
||||||
(rx/map :body)
|
(rx/map :body)
|
||||||
(rx/tap (fn [blob]
|
(rx/mapcat (fn [blob]
|
||||||
(clipboard/to-clipboard-promise "image/png" (p/resolved blob))))
|
;; Resolve the deferred with the fetched blob; the browser
|
||||||
|
;; will now complete the clipboard write it started earlier.
|
||||||
|
(p/resolve! deferred blob)
|
||||||
|
(rx/from write-promise)))
|
||||||
(rx/map (fn [_]
|
(rx/map (fn [_]
|
||||||
(ntf/success (tr "workspace.clipboard.image-copied"))))
|
(ntf/success (tr "workspace.clipboard.image-copied"))))
|
||||||
(rx/catch (fn [e]
|
(rx/catch (fn [e]
|
||||||
(js/console.error "clipboard blocked:" e)
|
(js/console.error "clipboard error:" e)
|
||||||
(ntf/error (tr "workspace.clipboard.image-copy-failed"))
|
;; Reject the deferred in case the error occurred before the
|
||||||
(rx/empty)))))))))
|
;; blob was fetched, so the pending clipboard write is cancelled.
|
||||||
|
(p/reject! deferred e)
|
||||||
|
(rx/of (ntf/error (tr "workspace.clipboard.image-copy-failed")))))))))))
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.render-wasm.api :as wasm.api]
|
[app.render-wasm.api :as wasm.api]
|
||||||
|
[app.util.timers :as timers]
|
||||||
[app.util.webapi :as wapi]
|
[app.util.webapi :as wapi]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
@ -58,7 +59,7 @@
|
|||||||
(rx/create
|
(rx/create
|
||||||
(fn [subs]
|
(fn [subs]
|
||||||
(let [req-id
|
(let [req-id
|
||||||
(js/requestAnimationFrame
|
(timers/raf
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(try
|
(try
|
||||||
(let [objects (dsh/lookup-page-objects @st/state file-id page-id)]
|
(let [objects (dsh/lookup-page-objects @st/state file-id page-id)]
|
||||||
@ -83,7 +84,7 @@
|
|||||||
(rx/error! subs "Frame not found")))
|
(rx/error! subs "Frame not found")))
|
||||||
(catch :default err
|
(catch :default err
|
||||||
(rx/error! subs err)))))]
|
(rx/error! subs err)))))]
|
||||||
#(js/cancelAnimationFrame req-id)))))
|
#(timers/cancel-af! req-id)))))
|
||||||
|
|
||||||
(defn render-thumbnail
|
(defn render-thumbnail
|
||||||
"Renders a component thumbnail via WASM and updates the UI immediately.
|
"Renders a component thumbnail via WASM and updates the UI immediately.
|
||||||
|
|||||||
@ -322,7 +322,7 @@
|
|||||||
(let [trigger-el (mf/ref-val trigger-ref)
|
(let [trigger-el (mf/ref-val trigger-ref)
|
||||||
tooltip-el (mf/ref-val tooltip-ref)]
|
tooltip-el (mf/ref-val tooltip-ref)]
|
||||||
(when (and trigger-el tooltip-el)
|
(when (and trigger-el tooltip-el)
|
||||||
(js/requestAnimationFrame
|
(ts/raf
|
||||||
(fn []
|
(fn []
|
||||||
(let [origin-brect (dom/get-bounding-rect trigger-el)
|
(let [origin-brect (dom/get-bounding-rect trigger-el)
|
||||||
tooltip-brect (dom/get-bounding-rect tooltip-el)
|
tooltip-brect (dom/get-bounding-rect tooltip-el)
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
(ns app.main.ui.workspace.tokens.management.forms.controls.floating-dropdown
|
(ns app.main.ui.workspace.tokens.management.forms.controls.floating-dropdown
|
||||||
(:require
|
(:require
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
|
[app.util.timers :as timers]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn use-floating-dropdown [is-open input-wrapper-ref outer-wrapper-ref dropdown-ref]
|
(defn use-floating-dropdown [is-open input-wrapper-ref outer-wrapper-ref dropdown-ref]
|
||||||
@ -48,7 +49,7 @@
|
|||||||
(when is-open
|
(when is-open
|
||||||
(let [recalculate
|
(let [recalculate
|
||||||
(fn []
|
(fn []
|
||||||
(js/requestAnimationFrame
|
(timers/raf
|
||||||
(fn []
|
(fn []
|
||||||
(let [input-node (mf/ref-val input-wrapper-ref)]
|
(let [input-node (mf/ref-val input-wrapper-ref)]
|
||||||
(calculate-position input-node)))))
|
(calculate-position input-node)))))
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
[app.util.globals :as ug]
|
[app.util.globals :as ug]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
|
[app.util.timers :as timers]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[goog.events :as events]
|
[goog.events :as events]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
@ -96,7 +97,7 @@
|
|||||||
;; Store latest color synchronously so the click handler always reads
|
;; Store latest color synchronously so the click handler always reads
|
||||||
;; the correct pixel even before the rAF fires (fixes race condition)
|
;; the correct pixel even before the rAF fires (fixes race condition)
|
||||||
(mf/set-ref-val! last-picked-color color)
|
(mf/set-ref-val! last-picked-color color)
|
||||||
(js/requestAnimationFrame
|
(timers/raf
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dwc/pick-color color))))))))))
|
(st/emit! (dwc/pick-color color))))))))))
|
||||||
|
|
||||||
@ -304,7 +305,7 @@
|
|||||||
;; the correct pixel even before the rAF fires (fixes race condition)
|
;; the correct pixel even before the rAF fires (fixes race condition)
|
||||||
(mf/set-ref-val! last-picked-color color)
|
(mf/set-ref-val! last-picked-color color)
|
||||||
;; rAF throttles state updates to avoid an infinite React re-render loop
|
;; rAF throttles state updates to avoid an infinite React re-render loop
|
||||||
(js/requestAnimationFrame
|
(timers/raf
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dwc/pick-color color))))))))))
|
(st/emit! (dwc/pick-color color))))))))))
|
||||||
|
|
||||||
|
|||||||
@ -53,6 +53,7 @@
|
|||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[app.util.modules :as mod]
|
[app.util.modules :as mod]
|
||||||
[app.util.text.content :as tc]
|
[app.util.text.content :as tc]
|
||||||
|
[app.util.timers :as timers]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
@ -398,7 +399,7 @@
|
|||||||
(when-not @pending-render
|
(when-not @pending-render
|
||||||
(reset! pending-render true)
|
(reset! pending-render true)
|
||||||
(let [frame-id
|
(let [frame-id
|
||||||
(js/requestAnimationFrame
|
(timers/raf
|
||||||
(fn [ts]
|
(fn [ts]
|
||||||
(reset! pending-render false)
|
(reset! pending-render false)
|
||||||
(set! wasm/internal-frame-id nil)
|
(set! wasm/internal-frame-id nil)
|
||||||
@ -1708,7 +1709,7 @@
|
|||||||
[]
|
[]
|
||||||
(p/create
|
(p/create
|
||||||
(fn [resolve _reject]
|
(fn [resolve _reject]
|
||||||
(js/requestAnimationFrame (fn [] (resolve nil))))))
|
(timers/raf (fn [] (resolve nil))))))
|
||||||
|
|
||||||
(def ^:private default-context-options
|
(def ^:private default-context-options
|
||||||
#js {:antialias false
|
#js {:antialias false
|
||||||
@ -1878,7 +1879,7 @@
|
|||||||
|
|
||||||
;; Cancel any pending animation frame to prevent race conditions.
|
;; Cancel any pending animation frame to prevent race conditions.
|
||||||
(when wasm/internal-frame-id
|
(when wasm/internal-frame-id
|
||||||
(js/cancelAnimationFrame wasm/internal-frame-id))
|
(timers/cancel-af! wasm/internal-frame-id))
|
||||||
|
|
||||||
;; Reset render flags to prevent new renders from being scheduled.
|
;; Reset render flags to prevent new renders from being scheduled.
|
||||||
(reset! pending-render false)
|
(reset! pending-render false)
|
||||||
|
|||||||
@ -65,11 +65,20 @@
|
|||||||
#(.requestAnimationFrame js/globalThis %)
|
#(.requestAnimationFrame js/globalThis %)
|
||||||
#(js/setTimeout % 16)))
|
#(js/setTimeout % 16)))
|
||||||
|
|
||||||
|
(def ^:private cancel-animation-frame
|
||||||
|
(if (and (exists? js/globalThis)
|
||||||
|
(exists? (.-cancelAnimationFrame js/globalThis)))
|
||||||
|
#(.cancelAnimationFrame js/globalThis %)
|
||||||
|
#(js/clearTimeout %)))
|
||||||
|
|
||||||
(defn raf
|
(defn raf
|
||||||
[f]
|
[f]
|
||||||
(^function request-animation-frame f))
|
(^function request-animation-frame f))
|
||||||
|
|
||||||
|
(defn cancel-af!
|
||||||
|
[frame-id]
|
||||||
|
(^function cancel-animation-frame frame-id))
|
||||||
|
|
||||||
(defn idle-then-raf
|
(defn idle-then-raf
|
||||||
[f]
|
[f]
|
||||||
(schedule-on-idle #(^function raf f)))
|
(schedule-on-idle #(^function raf f)))
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user