From fb22c1547c7bcf0937e737295c3db9c7faf0c94a Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 3 Sep 2026 13:43:36 +0200 Subject: [PATCH] :bug: Skip component sync for derived WASM text layout commits (#11490) Post-font-load selrect fixes and position-data regeneration write sync-attrs on texts inside mains. That made watch-component-changes treat them as edits and run touch/sync per component, freezing large files. Mark those commits with skip-component-sync? (same idea as translation?) so only real user edits propagate. --- .../src/app/common/files/changes_builder.cljc | 6 ++ frontend/src/app/main/data/changes.cljs | 8 +- frontend/src/app/main/data/workspace.cljs | 8 +- .../app/main/data/workspace/libraries.cljs | 3 + .../src/app/main/data/workspace/shapes.cljs | 11 +- .../app/main/data/workspace/wasm_text.cljs | 102 +++++++++--------- 6 files changed, 80 insertions(+), 58 deletions(-) diff --git a/common/src/app/common/files/changes_builder.cljc b/common/src/app/common/files/changes_builder.cljc index e910978df9..a0c733fecb 100644 --- a/common/src/app/common/files/changes_builder.cljc +++ b/common/src/app/common/files/changes_builder.cljc @@ -74,6 +74,12 @@ translation? (assoc :translation? true))) +(defn set-skip-component-sync? + [changes skip-component-sync?] + (cond-> changes + skip-component-sync? + (assoc :skip-component-sync? true))) + (defn with-page [changes page] (vary-meta changes assoc diff --git a/frontend/src/app/main/data/changes.cljs b/frontend/src/app/main/data/changes.cljs index 1ecb0f67b9..d45fb82b7a 100644 --- a/frontend/src/app/main/data/changes.cljs +++ b/frontend/src/app/main/data/changes.cljs @@ -160,7 +160,7 @@ "Create a commit event instance" [{:keys [commit-id redo-changes undo-changes origin save-undo? features file-id file-revn file-vern undo-group tags stack-undo? source ignore-wasm? - selected-before translation?]}] + selected-before translation? skip-component-sync?]}] (assert (cpc/check-changes redo-changes) "expect valid vector of changes for redo-changes") @@ -188,7 +188,8 @@ :stack-undo? stack-undo? :ignore-wasm? ignore-wasm? :selected-before selected-before - :translation? translation?}] + :translation? translation? + :skip-component-sync? skip-component-sync?}] (ptk/reify ::commit cljs.core/IDeref @@ -227,7 +228,7 @@ undo-group, they will be undone or redone in a single step " [{:keys [redo-changes undo-changes save-undo? undo-group tags stack-undo? file-id - translation?] + translation? skip-component-sync?] :or {save-undo? true stack-undo? false undo-group (uuid/next) @@ -261,4 +262,5 @@ (assoc :redo-changes rchg) (assoc :selected-before selected) (assoc :translation? translation?) + (assoc :skip-component-sync? skip-component-sync?) (commit))))))))) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index d9afb0cc62..245df6008f 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -244,7 +244,8 @@ {:redo-changes changes :undo-changes [] :save-undo? false :origin it - :tags #{:position-data}})) + :tags #{:position-data} + :skip-component-sync? true})) (rx/empty))))))) (defn- workspace-initialized @@ -511,7 +512,7 @@ (rx/filter (ptk/type? :app.render-wasm.api/stale-text-selrects)) (rx/map deref) (rx/map (fn [{:keys [ids]}] - (dwwt/resize-wasm-text-all ids)))) + (dwwt/resize-wasm-text-all ids {:skip-component-sync? true})))) (let [local-commits-s (->> stream @@ -565,7 +566,8 @@ (dch/commit-changes {:redo-changes changes :undo-changes [] :save-undo? false - :tags #{:position-data}}))))) + :tags #{:position-data} + :skip-component-sync? true}))))) (rx/take-until stoper-s))) (->> stream diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index daf0876088..60baa7e15d 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -1446,6 +1446,9 @@ (rx/filter #(= :local (:source %))) ;; Translation commits never propagate component changes. (rx/filter (complement :translation?)) + ;; Derived / corrective commits (font-load selrect fix, + ;; position-data regen) are not user component edits. + (rx/filter (complement :skip-component-sync?)) ;; Keep waits pending while component changes are checked. (rx/map start-sync-barrier) (rx/observe-on :async)) diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 86ae4eff8c..2454b537a1 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -110,7 +110,7 @@ ([ids update-fn {:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id ignore-touched undo-group with-objects? changed-sub-attr - translation?] + translation? skip-component-sync?] :or {reg-objects? false save-undo? true stack-undo? false @@ -152,7 +152,8 @@ :ignore-touched ignore-touched :with-objects? with-objects?}) (cond-> reg-objects? (pcb/resize-parents ids)) - (pcb/set-translation? translation?))))] + (pcb/set-translation? translation?) + (pcb/set-skip-component-sync? skip-component-sync?))))] ;; Check buffered text candidates when the buffer is committed. (if (or (empty? text-ids) (not (wrfs/text-reflow-candidate? state props))) @@ -187,7 +188,8 @@ ([ids update-fn {:as props :keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id - ignore-touched undo-group with-objects? changed-sub-attr translation?] + ignore-touched undo-group with-objects? changed-sub-attr translation? + skip-component-sync?] :or {reg-objects? false save-undo? true stack-undo? false @@ -223,7 +225,8 @@ :translation? translation?}) (cond-> undo-group (pcb/set-undo-group undo-group)) - (pcb/set-translation? translation?)) + (pcb/set-translation? translation?) + (pcb/set-skip-component-sync? skip-component-sync?)) changed-objects (pcb/lookup-objects changes) diff --git a/frontend/src/app/main/data/workspace/wasm_text.cljs b/frontend/src/app/main/data/workspace/wasm_text.cljs index edbb7803e0..04495ad0f2 100644 --- a/frontend/src/app/main/data/workspace/wasm_text.cljs +++ b/frontend/src/app/main/data/workspace/wasm_text.cljs @@ -94,48 +94,55 @@ (rx/empty))] (wrf/with-pending :text-resize [id] resize-stream))))) +(defn- merge-resize-debounce-opts + [prev {:keys [undo-group undo-id skip-component-sync?]}] + (cond-> (or prev {}) + (some? undo-group) (assoc :undo-group undo-group) + (some? undo-id) (assoc :undo-id undo-id) + skip-component-sync? (assoc :skip-component-sync? true))) + (defn resize-wasm-text-debounce-commit - ([] - (resize-wasm-text-debounce-commit nil nil)) - ([undo-group undo-id] - (ptk/reify ::resize-wasm-text-debounce-commit - ptk/WatchEvent - (watch [_ state _] - (let [ids (get state ::resize-wasm-text-debounce-ids) - objects (dsh/lookup-page-objects state) + [] + (ptk/reify ::resize-wasm-text-debounce-commit + ptk/WatchEvent + (watch [_ state _] + (let [ids (get state ::resize-wasm-text-debounce-ids) + {:keys [undo-group undo-id skip-component-sync?]} (get state ::resize-wasm-text-debounce-opts) + objects (dsh/lookup-page-objects state) - modifiers - (reduce - (fn [modifiers id] - (let [shape (get objects id)] - (cond-> modifiers - (and (some? shape) - (cfh/text-shape? shape) - (not= :fixed (:grow-type shape))) - (merge (resize-wasm-text-modifiers shape))))) - {} - ids) + modifiers + (reduce + (fn [modifiers id] + (let [shape (get objects id)] + (cond-> modifiers + (and (some? shape) + (cfh/text-shape? shape) + (not= :fixed (:grow-type shape))) + (merge (resize-wasm-text-modifiers shape))))) + {} + ids) - ;; When undo-id is present, extend the current undo transaction instead of - ;; creating a new one, and commit it after the resize (single undo action). - extend-tx? (some? undo-id) - apply-opts (cond-> {} - (some? undo-group) (assoc :undo-group undo-group) - extend-tx? (assoc :undo-transation? false))] - (cond - (not (empty? modifiers)) - (if extend-tx? - (rx/concat - (rx/of (dwm/apply-wasm-modifiers modifiers apply-opts)) - (rx/of (dwu/commit-undo-transaction undo-id))) - (rx/of (dwm/apply-wasm-modifiers modifiers apply-opts))) + ;; When undo-id is present, extend the current undo transaction instead of + ;; creating a new one, and commit it after the resize (single undo action). + extend-tx? (some? undo-id) + apply-opts (cond-> {} + (some? undo-group) (assoc :undo-group undo-group) + extend-tx? (assoc :undo-transation? false) + skip-component-sync? (assoc :skip-component-sync? true))] + (cond + (not (empty? modifiers)) + (if extend-tx? + (rx/concat + (rx/of (dwm/apply-wasm-modifiers modifiers apply-opts)) + (rx/of (dwu/commit-undo-transaction undo-id))) + (rx/of (dwm/apply-wasm-modifiers modifiers apply-opts))) - extend-tx? - ;; No resize needed (e.g. :fixed grow-type) but we must commit the add - (rx/of (dwu/commit-undo-transaction undo-id)) + extend-tx? + ;; No resize needed (e.g. :fixed grow-type) but we must commit the add + (rx/of (dwu/commit-undo-transaction undo-id)) - :else - (rx/empty))))))) + :else + (rx/empty)))))) ;; This event will debounce the resize events so, if there are many, they ;; are processed at the same time and not one-by-one. This will improve @@ -144,7 +151,7 @@ (defn resize-wasm-text-debounce-inner ([id] (resize-wasm-text-debounce-inner id nil)) - ([id {:keys [undo-group undo-id]}] + ([id opts] (let [cur-event (js/Symbol) reflow-task (wrf/task :text-resize [id])] (ptk/reify ::resize-wasm-text-debounce-inner @@ -153,6 +160,8 @@ (-> state (update ::resize-wasm-text-debounce-ids (fnil conj []) id) (update ::resize-wasm-text-reflow-tasks (fnil conj []) reflow-task) + (cond-> (seq opts) + (update ::resize-wasm-text-debounce-opts merge-resize-debounce-opts opts)) (cond-> (nil? (::resize-wasm-text-debounce-event state)) (assoc ::resize-wasm-text-debounce-event cur-event)))) @@ -167,14 +176,9 @@ (rx/filter (ptk/type? ::resize-wasm-text-debounce-inner)) (rx/debounce debounce-resize-text-time) (rx/take 1) - (rx/map (fn [evt] - (resize-wasm-text-debounce-commit - (some-> evt meta :undo-group) - (some-> evt meta :undo-id)))) + (rx/map (fn [_] (resize-wasm-text-debounce-commit))) (rx/take-until stopper)) - (rx/of (with-meta - (resize-wasm-text-debounce-inner id) - {:undo-group undo-group :undo-id undo-id}))) + (rx/of (resize-wasm-text-debounce-inner id opts))) ;; Cleanup, reached both after the commit and when the stopper ;; cancels the debounce, so the batch always drains and stays ;; pending until the resize is applied. All exact tasks in the @@ -184,13 +188,14 @@ (dissoc state ::resize-wasm-text-debounce-ids ::resize-wasm-text-reflow-tasks + ::resize-wasm-text-debounce-opts ::resize-wasm-text-debounce-event))))) (rx/empty))))))) (defn resize-wasm-text-debounce ([id] (resize-wasm-text-debounce id nil)) - ([id {:keys [undo-group undo-id] :as opts}] + ([id {:keys [undo-group undo-id skip-component-sync?] :as opts}] (ptk/reify ::resize-wasm-text-debounce ptk/WatchEvent (watch [_ state _] @@ -208,10 +213,11 @@ resize-wasm-stream (if fonts-ready? - (let [pass-opts (when (or (some? undo-group) (some? undo-id)) + (let [pass-opts (when (or (some? undo-group) (some? undo-id) skip-component-sync?) (cond-> {} (some? undo-group) (assoc :undo-group undo-group) - (some? undo-id) (assoc :undo-id undo-id)))] + (some? undo-id) (assoc :undo-id undo-id) + skip-component-sync? (assoc :skip-component-sync? true)))] (rx/of (resize-wasm-text-debounce-inner id pass-opts))) ;; Fonts not loaded; retry after 20 msecs