From f389fcf46838cf6206a82d748859b1ec86b4cd03 Mon Sep 17 00:00:00 2001 From: Alonso Torres Date: Thu, 14 May 2026 12:01:30 +0200 Subject: [PATCH] :bug: Fix problem with copy-as-image action (#9586) --- exporter/src/app/handlers/export_shapes.cljs | 4 +- .../app/main/data/workspace/clipboard.cljs | 39 +++++++++++++------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/exporter/src/app/handlers/export_shapes.cljs b/exporter/src/app/handlers/export_shapes.cljs index 901a2f2bb2..1789ba9531 100644 --- a/exporter/src/app/handlers/export_shapes.cljs +++ b/exporter/src/app/handlers/export_shapes.cljs @@ -64,7 +64,7 @@ (defn- handle-single-export [{: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))) - 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 (fn [{:keys [path] :as object}] @@ -112,7 +112,7 @@ (rsc/add-to-zip zip path (str/replace filename sanitize-file-regex "_"))) 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/mcat (fn [_] (rsc/close-zip zip))) (p/fmap (constantly resource)) diff --git a/frontend/src/app/main/data/workspace/clipboard.cljs b/frontend/src/app/main/data/workspace/clipboard.cljs index bc8d88a746..f67b04b98e 100644 --- a/frontend/src/app/main/data/workspace/clipboard.cljs +++ b/frontend/src/app/main/data/workspace/clipboard.cljs @@ -34,6 +34,7 @@ [app.config :as cf] [app.main.data.changes :as dch] [app.main.data.event :as ev] + [app.main.data.exports.wasm :as wasm.exports] [app.main.data.helpers :as dsh] [app.main.data.notifications :as ntf] [app.main.data.persistence :as-alias dps] @@ -1131,10 +1132,11 @@ :enabled true :name ""} - params {:exports [export] - :profile-id (:profile-id state) - :cmd :export-shapes - :wait true}] + ;; Create a deferred promise immediately, before any async operations. + ;; Registering the clipboard write NOW preserves the user-gesture security + ;; context; the actual blob is supplied asynchronously once the export finishes. + deferred (p/deferred) + write-promise (clipboard/to-clipboard-promise "image/png" deferred)] (rx/concat ;; Ensure current state persisted before exporting. @@ -1144,21 +1146,34 @@ (rx/first) (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"))) - ;; Call exporter to get image URI, then fetch and copy blob. - (->> (rp/cmd! :export params) + ;; Call exporter to get image URI, then fetch blob and resolve the deferred. + (->> (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]}] (http/send! {:method :get :uri uri :response-type :blob}))) (rx/map :body) - (rx/tap (fn [blob] - (clipboard/to-clipboard-promise "image/png" (p/resolved blob)))) + (rx/mapcat (fn [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 [_] (ntf/success (tr "workspace.clipboard.image-copied")))) (rx/catch (fn [e] - (js/console.error "clipboard blocked:" e) - (ntf/error (tr "workspace.clipboard.image-copy-failed")) - (rx/empty))))))))) + (js/console.error "clipboard error:" e) + ;; Reject the deferred in case the error occurred before the + ;; blob was fetched, so the pending clipboard write is cancelled. + (p/reject! deferred e) + (rx/of (ntf/error (tr "workspace.clipboard.image-copy-failed")))))))))))