diff --git a/.serena/memories/frontend/workspace-state-persistence-subtleties.md b/.serena/memories/frontend/workspace-state-persistence-subtleties.md index 201c16c624..373f28aa26 100644 --- a/.serena/memories/frontend/workspace-state-persistence-subtleties.md +++ b/.serena/memories/frontend/workspace-state-persistence-subtleties.md @@ -19,11 +19,16 @@ - `commit-changes` refuses to create commits unless `:permissions :can-edit` is true. It captures file revn/vern, selected-before, features, tags, undo group, and translation flag into a `::commit` event. A refusal answers with an empty stream, never nil, so callers subscribe uniformly instead of branching on nil. - Applying a remote commit first rolls back pending local commits, applies the remote changes, then replays pending local redo changes. Index updates are emitted for undo, remote redo, and replayed redo paths. -- Local commits are independently consumed by undo, persistence, WASM model updates, thumbnail/library watchers, and text position-data recalculation. +- Persistence, WASM model updates, thumbnails and text position-data watchers + consume local commits independently. Undo records applied transitions first. - `dch/changes-applied?` follows local application and carries the commit - with its prior file data. The component watcher syncs each transition - before the next user commit, keeping undo groups adjacent. Its barrier - remains open through the event turn. Foreign file cascades stay in common. + with its prior file data. The workspace records the undo entry, then emits + `dch/changes-recorded?` synchronously with that same transition. Component + sync starts from this handoff, after history records the original edit. + Syncing from `changes-applied?` can record derived commits before the user + commit and reverse the undo/redo order inside a transaction (F137). + The watcher syncs before the next user commit; its barrier remains open + through the event turn. Foreign file cascades stay in common. - Undo changes carry `:app.main.data.workspace.undo/file-id`, separate from change payload fields named `:file-id` (e.g. `:set-tokens-source`). Undo and redo replay consecutive runs per destination in order; old entries diff --git a/frontend/src/app/main/data/changes.cljs b/frontend/src/app/main/data/changes.cljs index 5e9f35be91..b0db0c8856 100644 --- a/frontend/src/app/main/data/changes.cljs +++ b/frontend/src/app/main/data/changes.cljs @@ -38,6 +38,15 @@ (def changes-applied? (ptk/type? ::changes-applied)) +(def changes-recorded? + (ptk/type? ::changes-recorded)) + +(defn changes-recorded + "The commit has applied and its undo entry has been recorded. Derived + edits can now follow it without reversing transaction history." + [transition] + (ptk/data-event ::changes-recorded transition)) + (defn- fix-page-id "For events that modifies the page, page-id does not comes as a property so we assign it from the `id` property." diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index ea18725eb7..3f4ba69dc7 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -494,18 +494,22 @@ (rx/take-until stopper-s))) (->> stream - (rx/filter dch/commit?) + (rx/filter dch/changes-applied?) (rx/map deref) (rx/mapcat - (fn [{:keys [file-id save-undo? undo-changes redo-changes undo-group tags stack-undo? selected-before]}] - (if (and save-undo? (seq undo-changes)) - (let [entry {:undo-changes (mapv #(assoc % ::dwu/file-id file-id) undo-changes) - :redo-changes (mapv #(assoc % ::dwu/file-id file-id) redo-changes) - :undo-group undo-group - :tags tags - :selected-before selected-before}] - (rx/of (dwu/append-undo entry stack-undo?))) - (rx/empty)))))) + (fn [{:keys [commit] :as transition}] + (let [{:keys [file-id save-undo? undo-changes redo-changes undo-group + tags stack-undo? selected-before]} commit] + (rx/concat + (if (and save-undo? (seq undo-changes)) + (let [entry {:undo-changes (mapv #(assoc % ::dwu/file-id file-id) undo-changes) + :redo-changes (mapv #(assoc % ::dwu/file-id file-id) redo-changes) + :undo-group undo-group + :tags tags + :selected-before selected-before}] + (rx/of (dwu/append-undo entry stack-undo?))) + (rx/empty)) + (rx/of (dch/changes-recorded transition)))))))) (rx/take-until stopper-s))))))) (defn initialize-workspace diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 0b973ccaa3..28c37118b6 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -1420,7 +1420,7 @@ local-commits-s (->> stream - (rx/filter dch/changes-applied?) + (rx/filter dch/changes-recorded?) (rx/map deref) (rx/filter #(= :local (get-in % [:commit :source]))) ;; Keep waits pending while component changes are checked. diff --git a/frontend/test/frontend_tests/composable_tests/generated_test.cljs b/frontend/test/frontend_tests/composable_tests/generated_test.cljs index d21402a152..a64bca9ee6 100644 --- a/frontend/test/frontend_tests/composable_tests/generated_test.cljs +++ b/frontend/test/frontend_tests/composable_tests/generated_test.cljs @@ -86,3 +86,6 @@ (t/deftest ^:async nested-text-rotation-shrunk-regression (await (replay {:world [2 0 {:text true}] :ops [[:nest 0] [:detach 0] [:rotate 0]]}))) + +(t/deftest ^:async nesting-restores-default-constraints-on-undo + (await (replay {:world [2 0] :ops [[:nest 0]]})))