🐛 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.
This commit is contained in:
Alejandro Alonso 2026-09-03 13:43:36 +02:00 committed by GitHub
parent 7b26949c76
commit fb22c1547c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 80 additions and 58 deletions

View File

@ -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

View File

@ -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)))))))))

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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