mirror of
https://github.com/penpot/penpot.git
synced 2026-08-12 15:58:38 +00:00
The sidebar measures panel numeric inputs (X, Y, width, height, rotation) emitted one full apply-modifiers commit per DOM event with no throttle: every arrow key-repeat, wheel tick and scrub pointermove became update-positions / update-dimensions / increase-rotation. A sustained gesture starved the React renderer and crashed the workspace with error #185 (Maximum update depth exceeded). Coalesce those bursts at the data layer (potok), following the update-position-data debounce pattern in texts.cljs: - update-positions is now burst-coalesced in place (its only caller is the measures panel); new update-dimensions-coalesced and increase-rotation-coalesced variants are used by the measures panel, while the immediate events keep serving plugins, variants and token application (including the delta? rotation path). - The first event of a burst commits immediately (leading edge, so single edits stay synchronous); further ticks commit at most once per 50 ms (throttle); a trailing debounced flush guarantees the exact final value lands. All payloads are absolute values, so keeping the latest queued value per shape/attribute is lossless. - Pending payloads are drained atomically and stale shape ids (deleted mid-burst) are skipped. The drain stream lives until the workspace is finalized, so bursts reuse a single subscription. - Fewer commits per burst also means fewer undo entries; scrub drags still produce a single entry via the input's outer transaction. Tests: new frontend-tests.logic.sidebar-transform-coalescing-test (8 tests, legacy SVG and WASM renderer branches) guards the invariant that a 20-event burst commits the exact final value in a handful of commits. The previously unregistered update-position-test is wired into the runner with WASM mock fixtures (it fails in full-suite context without them due to a pre-existing global mock-state issue). AI-assisted-by: kimi-k3
43 lines
1.7 KiB
Clojure
43 lines
1.7 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
|
|
|
(ns frontend-tests.logic.update-position-test
|
|
(:require
|
|
[app.common.geom.rect :as grc]
|
|
[app.common.test-helpers.compositions :as ctho]
|
|
[app.common.test-helpers.files :as cthf]
|
|
[app.common.test-helpers.shapes :as cths]
|
|
[app.main.data.workspace :as dw]
|
|
[cljs.test :as t :include-macros true]
|
|
[frontend-tests.helpers.state :as ths]
|
|
[frontend-tests.helpers.wasm :as thw]))
|
|
|
|
(t/use-fixtures :each
|
|
{:before (fn [] (thw/setup-wasm-mocks!))
|
|
:after (fn [] (thw/teardown-wasm-mocks!))})
|
|
|
|
(t/deftest test-update-positions-multiple-ids
|
|
(t/async
|
|
done
|
|
(let [file (-> (cthf/sample-file :file1)
|
|
(ctho/add-rect :rect1 :x 10 :y 20 :width 10 :height 10)
|
|
(ctho/add-rect :rect2 :x 30 :y 40 :width 10 :height 10))
|
|
store (ths/setup-store file)
|
|
rect1 (cths/get-shape file :rect1)
|
|
rect2 (cths/get-shape file :rect2)
|
|
ids [(:id rect1) (:id rect2)]
|
|
events [(dw/update-positions ids {:x 123.45})]]
|
|
(ths/run-store
|
|
store done events
|
|
(fn [new-state]
|
|
(let [file' (ths/get-file-from-state new-state)
|
|
rect1' (cths/get-shape file' :rect1)
|
|
rect2' (cths/get-shape file' :rect2)
|
|
x1 (-> rect1' :points grc/points->rect :x)
|
|
x2 (-> rect2' :points grc/points->rect :x)]
|
|
(t/is (= 123.45 x1))
|
|
(t/is (= 123.45 x2))))))))
|