From 1d8299a9199d696f20d8f6dd77b903877c119cce Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 10 Apr 2026 11:51:40 +0200 Subject: [PATCH] :bug: Fix problem with component thumbnails --- .../app/main/data/workspace/libraries.cljs | 13 ++- .../main/data/workspace/thumbnails_wasm.cljs | 93 ++++++++++++++----- frontend/src/app/render_wasm/helpers.cljc | 1 + 3 files changed, 79 insertions(+), 28 deletions(-) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index ef7bfd2f93..ca4362ef5b 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -411,9 +411,16 @@ (when id-ref (reset! id-ref component-id)) (when-not (empty? (:redo-changes changes)) - (rx/of (dch/commit-changes changes) - (dws/select-shapes (d/ordered-set (:id root))) - (ptk/data-event :layout/update {:ids parents})))))))))) + (rx/concat + (rx/of (dch/commit-changes changes) + (dws/select-shapes (d/ordered-set (:id root))) + (ptk/data-event :layout/update {:ids parents})) + + ;; When activated the wasm rendering we need to recreate its thumbnail on creation + (if (features/active-feature? state "render-wasm/v1") + (rx/of (dwt.wasm/render-thumbnail file-id page-id (:id root)) + (dwt.wasm/persist-thumbnail file-id page-id (:id root))) + (rx/empty))))))))))) (defn add-component "Add a new component to current file library, from the currently selected shapes. diff --git a/frontend/src/app/main/data/workspace/thumbnails_wasm.cljs b/frontend/src/app/main/data/workspace/thumbnails_wasm.cljs index 44459e9df8..c64e9a2729 100644 --- a/frontend/src/app/main/data/workspace/thumbnails_wasm.cljs +++ b/frontend/src/app/main/data/workspace/thumbnails_wasm.cljs @@ -15,11 +15,15 @@ - persist-thumbnail: pushes current data-uri to the server (debounced)" (:require [app.common.data.macros :as dm] + [app.common.files.helpers :as cfh] [app.common.logging :as l] + [app.common.math :as mth] [app.common.thumbnails :as thc] [app.common.time :as ct] + [app.main.data.helpers :as dsh] [app.main.data.workspace.thumbnails :as dwt] [app.main.repo :as rp] + [app.main.store :as st] [app.render-wasm.api :as wasm.api] [app.util.webapi :as wapi] [beicon.v2.core :as rx] @@ -41,27 +45,38 @@ (fn [e] (reject e))) (.readAsDataURL reader blob))))) +;; This constant stores the target thumbnail minimum max-size so +;; the images doesn't lose quality when rendered +(def target-size 200) + (defn- render-component-pixels "Renders a component frame using the workspace WASM context. Returns an observable that emits a data-uri string. Deferred by one animation frame so that process-shape-changes! has time to sync all child shapes to WASM memory first." - [frame-id] + [file-id page-id frame-id] (rx/create (fn [subs] (js/requestAnimationFrame (fn [_] (try - (let [png-bytes (wasm.api/render-shape-pixels frame-id 1)] + (let [objects (dsh/lookup-page-objects @st/state file-id page-id) + frame (get objects frame-id) + {:keys [width height]} (:selrect frame) + max-size (mth/max width height) + scale (mth/max 1 (/ target-size max-size)) + png-bytes (wasm.api/render-shape-pixels frame-id scale)] (if (or (nil? png-bytes) (zero? (.-length png-bytes))) - (do (js/console.error "[thumbnails] render-shape-pixels returned empty for" (str frame-id)) - (rx/end! subs)) - (.then (png-bytes->data-uri png-bytes) - (fn [data-uri] - (rx/push! subs data-uri) - (rx/end! subs)) - (fn [err] - (rx/error! subs err))))) + (do + (js/console.error "[thumbnails] render-shape-pixels returned empty for" (str frame-id)) + (rx/end! subs)) + (.then + (png-bytes->data-uri png-bytes) + (fn [data-uri] + (rx/push! subs data-uri) + (rx/end! subs)) + (fn [err] + (rx/error! subs err))))) (catch :default err (rx/error! subs err))))) nil))) @@ -71,29 +86,57 @@ Does NOT persist to the server — persistence is handled separately by `persist-thumbnail` on a debounced schedule." [file-id page-id frame-id] + (let [object-id (thc/fmt-object-id file-id page-id frame-id "component")] (ptk/reify ::render-thumbnail cljs.core/IDeref (-deref [_] object-id) ptk/WatchEvent - (watch [_ _ stream] - (let [tp (ct/tpoint-ms)] - (->> (render-component-pixels frame-id) - (rx/map - (fn [data-uri] - (l/dbg :hint "component thumbnail rendered (wasm)" - :elapsed (dm/str (tp) "ms")) - (dwt/assoc-thumbnail object-id data-uri))) + (watch [_ state stream] + ;; When the component is removed it can arrived a render + ;; request with frame-id=null + (when (some? frame-id) + (letfn [(load-objects-stream + [] + (rx/create + (fn [subs] + (let [objects (dsh/lookup-page-objects state file-id page-id) - (rx/catch (fn [err] - (js/console.error "[thumbnails] error rendering component thumbnail" err) - (rx/empty))) + ;; retrieves a subtree with only the id and its children + ;; to be loaded before rendering the thumbnail + subtree + (into {} + (map #(vector (:id %) %)) + (cfh/get-children-with-self objects frame-id))] + (try + (wasm.api/set-objects subtree #(rx/push! subs %)) + (catch :default err + (rx/error! subs err))))))) - (rx/take-until - (->> stream - (rx/filter (ptk/type? ::dwt/clear-thumbnail)) - (rx/filter #(= (deref %) object-id)))))))))) + (do-render-thumbnail + [] + (let [tp (ct/tpoint-ms)] + (->> (render-component-pixels file-id page-id frame-id) + (rx/map + (fn [data-uri] + (l/dbg :hint "component thumbnail rendered (wasm)" + :elapsed (dm/str (tp) "ms")) + (dwt/assoc-thumbnail object-id data-uri))) + + (rx/catch (fn [err] + (js/console.error "[thumbnails] error rendering component thumbnail" err) + (rx/empty))) + + (rx/take-until + (->> stream + (rx/filter (ptk/type? ::dwt/clear-thumbnail)) + (rx/filter #(= (deref %) object-id)))))))] + + (if (not= page-id (:current-page-id state)) + (->> (load-objects-stream) + (rx/mapcat do-render-thumbnail)) + (do-render-thumbnail)))))))) (defn persist-thumbnail "Persists the current component thumbnail data-uri to the server. diff --git a/frontend/src/app/render_wasm/helpers.cljc b/frontend/src/app/render_wasm/helpers.cljc index 0ce16bd955..debf07eab9 100644 --- a/frontend/src/app/render_wasm/helpers.cljc +++ b/frontend/src/app/render_wasm/helpers.cljc @@ -24,6 +24,7 @@ cause-sym (gensym "cause")] `(let [~fn-sym (cljs.core/unchecked-get ~module ~name)] (try + ;; (prn ~name ~@params) (~fn-sym ~@params) (catch :default ~cause-sym (let [read-code-fn# (cljs.core/unchecked-get ~module "_read_error_code")