diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc
index 270df14d5..1966fc03f 100644
--- a/common/src/app/common/geom/shapes/modifiers.cljc
+++ b/common/src/app/common/geom/shapes/modifiers.cljc
@@ -317,33 +317,47 @@
       modif-tree)))
 
 (defn set-objects-modifiers
-  [modif-tree objects ignore-constraints snap-pixel?]
+  ([modif-tree objects ignore-constraints snap-pixel?]
+   (set-objects-modifiers nil modif-tree objects ignore-constraints snap-pixel?))
 
-  (let [objects (apply-structure-modifiers objects modif-tree)
+  ([old-modif-tree modif-tree objects ignore-constraints snap-pixel?]
+   (let [objects (-> objects
+                     (cond-> (some? old-modif-tree)
+                       (apply-structure-modifiers old-modif-tree))
+                     (apply-structure-modifiers modif-tree))
 
-        bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points]))
-        shapes-tree (resolve-tree-sequence (-> modif-tree keys set) objects)
+         bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points]))
+         bounds (cond-> bounds
+                  (some? old-modif-tree)
+                  (transform-bounds objects old-modif-tree))
 
-        ;; Calculate the input transformation and constraints
-        modif-tree (reduce #(propagate-modifiers-constraints objects bounds ignore-constraints %1 %2) modif-tree shapes-tree)
-        bounds (transform-bounds bounds objects modif-tree shapes-tree)
+         shapes-tree (resolve-tree-sequence (-> modif-tree keys set) objects)
 
-        [modif-tree-layout sizing-auto-layouts]
-        (reduce #(propagate-modifiers-layout objects bounds ignore-constraints %1 %2) [{} #{}] shapes-tree)
+         ;; Calculate the input transformation and constraints
+         modif-tree (reduce #(propagate-modifiers-constraints objects bounds ignore-constraints %1 %2) modif-tree shapes-tree)
+         bounds (transform-bounds bounds objects modif-tree shapes-tree)
 
-        modif-tree (merge-modif-tree modif-tree modif-tree-layout)
+         [modif-tree-layout sizing-auto-layouts]
+         (reduce #(propagate-modifiers-layout objects bounds ignore-constraints %1 %2) [{} #{}] shapes-tree)
 
-        ;; Calculate hug layouts positions
-        bounds (transform-bounds bounds objects modif-tree-layout shapes-tree)
+         modif-tree (merge-modif-tree modif-tree modif-tree-layout)
 
-        modif-tree
-        (-> modif-tree
-            (sizing-auto-modifiers sizing-auto-layouts objects bounds ignore-constraints))
+         ;; Calculate hug layouts positions
+         bounds (transform-bounds bounds objects modif-tree-layout shapes-tree)
 
-        modif-tree
-        (cond-> modif-tree
-          snap-pixel? (gpp/adjust-pixel-precision objects))]
+         modif-tree
+         (-> modif-tree
+             (sizing-auto-modifiers sizing-auto-layouts objects bounds ignore-constraints))
 
-    ;;#?(:cljs
-    ;;   (.log js/console ">result" (modif->js modif-tree objects)))
-    modif-tree))
+         modif-tree
+         (if old-modif-tree
+           (merge-modif-tree old-modif-tree modif-tree)
+           modif-tree)
+
+         modif-tree
+         (cond-> modif-tree
+           snap-pixel? (gpp/adjust-pixel-precision objects))]
+
+     ;;#?(:cljs
+     ;;   (.log js/console ">result" (modif->js modif-tree objects)))
+     modif-tree)))
diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc
index 4383d92bb..3d9852680 100644
--- a/common/src/app/common/geom/shapes/transforms.cljc
+++ b/common/src/app/common/geom/shapes/transforms.cljc
@@ -111,10 +111,10 @@
        (cond-> (some? transform)
          (gmt/multiply transform))
 
-       (cond-> (and flip-x (not no-flip))
+       (cond-> (and flip-x no-flip)
          (gmt/scale (gpt/point -1 1)))
 
-       (cond-> (and flip-y (not no-flip))
+       (cond-> (and flip-y no-flip)
          (gmt/scale (gpt/point 1 -1)))
 
        (gmt/translate (gpt/negate shape-center)))))
@@ -126,8 +126,8 @@
   ([{:keys [transform flip-x flip-y] :as shape} {:keys [no-flip] :as params}]
    (if (and (some? shape)
             (or (some? transform)
-                (and (not no-flip) flip-x)
-                (and (not no-flip) flip-y)))
+                (and no-flip flip-x)
+                (and no-flip flip-y)))
      (dm/str (transform-matrix shape params))
      "")))
 
diff --git a/frontend/deps.edn b/frontend/deps.edn
index 606fa73c9..c158bea0e 100644
--- a/frontend/deps.edn
+++ b/frontend/deps.edn
@@ -9,7 +9,7 @@
 
   funcool/beicon {:mvn/version "2021.07.05-1"}
   funcool/okulary {:mvn/version "2022.04.11-16"}
-  funcool/potok {:mvn/version "2022.04.28-67"}
+  funcool/potok {:mvn/version "2022.12.16-71"}
   funcool/tubax {:mvn/version "2021.05.20-0"}
 
   funcool/rumext
diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs
index 808798457..ec2688421 100644
--- a/frontend/src/app/main/data/workspace/modifiers.cljs
+++ b/frontend/src/app/main/data/workspace/modifiers.cljs
@@ -190,26 +190,27 @@
                            [(get-in objects [k :name]) v]))
                     modif-tree)))
 
+(defn apply-text-modifier
+  [shape {:keys [width height]}]
+  (cond-> shape
+    (some? width)
+    (assoc :width width)
+
+    (some? height)
+    (assoc :height height)
+
+    (or (some? width) (some? height))
+    (cts/setup-rect-selrect)))
+
 (defn apply-text-modifiers
   [objects text-modifiers]
-  (letfn [(apply-text-modifier
-            [shape {:keys [width height]}]
-            (cond-> shape
-              (some? width)
-              (assoc :width width)
-
-              (some? height)
-              (assoc :height height)
-
-              (or (some? width) (some? height))
-              (cts/setup-rect-selrect)))]
-    (loop [modifiers (seq text-modifiers)
-           result objects]
-      (if (empty? modifiers)
-        result
-        (let [[id text-modifier] (first modifiers)]
-          (recur (rest modifiers)
-                 (update objects id apply-text-modifier text-modifier)))))))
+  (loop [modifiers (seq text-modifiers)
+         result objects]
+    (if (empty? modifiers)
+      result
+      (let [[id text-modifier] (first modifiers)]
+        (recur (rest modifiers)
+               (update objects id apply-text-modifier text-modifier))))))
 
 #_(defn apply-path-modifiers
   [objects path-modifiers]
@@ -242,6 +243,33 @@
        ;;(apply-path-modifiers $ (get-in state [:workspace-local :edit-path]))
        (gsh/set-objects-modifiers modif-tree $ ignore-constraints snap-pixel?)))))
 
+(defn- calculate-update-modifiers
+  [old-modif-tree state ignore-constraints ignore-snap-pixel modif-tree]
+  (let [objects
+        (wsh/lookup-page-objects state)
+
+        snap-pixel?
+        (and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))
+
+        objects
+        (-> objects
+            (apply-text-modifiers (get state :workspace-text-modifier)))]
+
+    (gsh/set-objects-modifiers  old-modif-tree modif-tree objects ignore-constraints snap-pixel?)))
+
+(defn update-modifiers
+  ([modif-tree]
+   (update-modifiers modif-tree false))
+
+  ([modif-tree ignore-constraints]
+   (update-modifiers modif-tree ignore-constraints false))
+
+  ([modif-tree ignore-constraints ignore-snap-pixel]
+   (ptk/reify ::update-modifiers
+     ptk/UpdateEvent
+     (update [_ state]
+       (update state :workspace-modifiers calculate-update-modifiers state ignore-constraints ignore-snap-pixel modif-tree)))))
+
 (defn set-modifiers
   ([modif-tree]
    (set-modifiers modif-tree false))
diff --git a/frontend/src/app/main/data/workspace/persistence.cljs b/frontend/src/app/main/data/workspace/persistence.cljs
index 2d8d1d8e9..c6f7d4479 100644
--- a/frontend/src/app/main/data/workspace/persistence.cljs
+++ b/frontend/src/app/main/data/workspace/persistence.cljs
@@ -30,6 +30,7 @@
 (declare persist-changes)
 (declare persist-synchronous-changes)
 (declare shapes-changes-persisted)
+(declare shapes-changes-persisted-finished)
 (declare update-persistence-status)
 
 ;; --- Persistence
@@ -42,6 +43,7 @@
       (log/debug :hint "initialize persistence")
       (let [stoper   (rx/filter (ptk/type? ::initialize-persistence) stream)
             commits  (l/atom [])
+            saving?  (l/atom false)
 
             local-file?
             #(as-> (:file-id %) event-file-id
@@ -61,13 +63,15 @@
 
             on-saving
             (fn []
+              (reset! saving? true)
               (st/emit! (update-persistence-status {:status :saving})))
 
             on-saved
             (fn []
               ;; Disable reload stoper
               (swap! st/ongoing-tasks disj :workspace-change)
-              (st/emit! (update-persistence-status {:status :saved})))]
+              (st/emit! (update-persistence-status {:status :saved}))
+              (reset! saving? false))]
 
         (rx/merge
          (->> stream
@@ -88,12 +92,15 @@
 
          (->> (rx/from-atom commits)
               (rx/filter (complement empty?))
-              (rx/sample-when (rx/merge
-                               (rx/interval 5000)
-                               (rx/filter #(= ::force-persist %) stream)
-                               (->> (rx/from-atom commits)
-                                    (rx/filter (complement empty?))
-                                    (rx/debounce 2000))))
+              (rx/sample-when
+               (->> (rx/merge
+                     (rx/interval 5000)
+                     (rx/filter #(= ::force-persist %) stream)
+                     (->> (rx/from-atom commits)
+                          (rx/filter (complement empty?))
+                          (rx/debounce 2000)))
+                    ;; Not sample while saving so there are no race conditions
+                    (rx/filter #(not @saving?))))
               (rx/tap #(reset! commits []))
               (rx/tap on-saving)
               (rx/mapcat (fn [changes]
@@ -101,9 +108,11 @@
                            ;; next persistence before this one is
                            ;; finished.
                            (rx/merge
-                            (rx/of (persist-changes file-id changes))
+                            (->> (rx/of (persist-changes file-id changes commits))
+                                 (rx/observe-on :async))
                             (->> stream
-                                 (rx/filter (ptk/type? ::changes-persisted))
+                                 ;; We wait for every change to be persisted
+                                 (rx/filter (ptk/type? ::shapes-changes-persisted-finished))
                                  (rx/take 1)
                                  (rx/tap on-saved)
                                  (rx/ignore)))))
@@ -123,7 +132,7 @@
                              (log/debug :hint "finalize persistence: synchronous save loop")))))))))
 
 (defn persist-changes
-  [file-id changes]
+  [file-id changes pending-commits]
   (log/debug :hint "persist changes" :changes (count changes))
   (us/verify ::us/uuid file-id)
   (ptk/reify ::persist-changes
@@ -150,20 +159,29 @@
                             (log/debug :hint "changes persisted" :lagged (count lagged))
                             (let [frame-updates
                                   (-> (group-by :page-id changes)
-                                      (update-vals #(into #{} (mapcat :frames) %)))]
+                                      (update-vals #(into #{} (mapcat :frames) %)))
 
-                              (rx/merge
-                               (->> (rx/from frame-updates)
-                                    (rx/mapcat (fn [[page-id frames]]
-                                                 (->> frames (map #(vector page-id %)))))
-                                    (rx/map (fn [[page-id frame-id]] (dwt/update-thumbnail (:id file) page-id frame-id))))
-                               (->> (rx/from lagged)
-                                    (rx/merge-map (fn [{:keys [changes] :as entry}]
-                                                    (rx/merge
-                                                     (rx/from
-                                                      (for [[page-id changes] (group-by :page-id changes)]
-                                                        (dch/update-indices page-id changes)))
-                                                     (rx/of (shapes-changes-persisted file-id entry))))))))))
+                                  commits
+                                  (->> @pending-commits
+                                       (map #(assoc % :revn (:revn file))))]
+
+                              (rx/concat
+                               (rx/merge
+                                (->> (rx/from frame-updates)
+                                     (rx/mapcat (fn [[page-id frames]]
+                                                  (->> frames (map #(vector page-id %)))))
+                                     (rx/map (fn [[page-id frame-id]] (dwt/update-thumbnail (:id file) page-id frame-id))))
+
+                                (->> (rx/from (concat lagged commits))
+                                     (rx/merge-map
+                                      (fn [{:keys [changes] :as entry}]
+                                        (rx/merge
+                                         (rx/from
+                                          (for [[page-id changes] (group-by :page-id changes)]
+                                            (dch/update-indices page-id changes)))
+                                         (rx/of (shapes-changes-persisted file-id entry)))))))
+
+                               (rx/of (shapes-changes-persisted-finished))))))
                (rx/catch (fn [cause]
                            (rx/concat
                             (if (= :authentication (:type cause))
@@ -171,6 +189,11 @@
                               (rx/of (rt/assign-exception cause)))
                             (rx/throw cause))))))))))
 
+;; Event to be thrown after the changes have been persisted
+(defn shapes-changes-persisted-finished
+  []
+  (ptk/reify ::shapes-changes-persisted-finished))
+
 (defn persist-synchronous-changes
   [{:keys [file-id changes]}]
   (us/verify ::us/uuid file-id)
diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs
index 7659f8b35..1d4c0098f 100644
--- a/frontend/src/app/main/data/workspace/texts.cljs
+++ b/frontend/src/app/main/data/workspace/texts.cljs
@@ -408,8 +408,7 @@
                       (not (mth/close? (:height props) current-height))))
 
            (let [modif-tree (dwm/create-modif-tree [id] (ctm/reflow-modifiers))]
-             (->> (rx/of (dwm/set-modifiers modif-tree))
-                  (rx/observe-on :async)))
+             (rx/of (dwm/update-modifiers modif-tree)))
            (rx/empty)))))))
 
 (defn clean-text-modifier
diff --git a/frontend/src/app/main/store.cljs b/frontend/src/app/main/store.cljs
index 431ff6d3a..0c66507fc 100644
--- a/frontend/src/app/main/store.cljs
+++ b/frontend/src/app/main/store.cljs
@@ -23,8 +23,22 @@
   [type data]
   (ptk/data-event type data))
 
+;;(def debug-exclude-events
+;;  #{:app.main.data.workspace.notifications/handle-pointer-update
+;;    :app.main.data.workspace.notifications/handle-pointer-send
+;;    :app.main.data.workspace.persistence/update-persistence-status
+;;    :app.main.data.workspace.changes/update-indices
+;;    :app.main.data.websocket/send-message
+;;    :app.main.data.workspace.selection/change-hover-state})
+;; (def ^:dynamic *debug-events* false)
+
 (defonce state
   (ptk/store {:resolve ptk/resolve
+              ;;:on-event (fn [e]
+              ;;            (when (and *debug-events*
+              ;;                       (ptk/event? e)
+              ;;                       (not (debug-exclude-events (ptk/type e))))
+              ;;              (.log js/console (str "[stream]: " (ptk/repr-event e)) )))
               :on-error (fn [e] (@on-error e))}))
 
 (defonce stream
diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs
index 6086efb9d..64edf8503 100644
--- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs
+++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs
@@ -8,8 +8,6 @@
   (:require
    [app.common.data :as d]
    [app.common.data.macros :as dm]
-   [app.common.geom.matrix :as gmt]
-   [app.common.geom.point :as gpt]
    [app.common.geom.shapes :as gsh]
    [app.config :as cf]
    [app.main.ui.context :as muc]
@@ -32,23 +30,6 @@
         (d/update-when :position-data #(mapv update-color %))
         (assoc :stroke-color "#FFFFFF" :stroke-opacity 1))))
 
-(defn position-data-transform
-  [shape {:keys [x y width height]}]
-  (let [rect (gsh/make-rect x (- y height) width height)
-        center (gsh/center-rect rect)]
-    (when (or (:flip-x shape) (:flip-y shape))
-      (-> (gmt/matrix)
-          (gmt/translate center)
-
-          (cond-> (:flip-x shape)
-            (gmt/scale (gpt/point -1 1))
-
-            (:flip-y shape)
-            (gmt/scale (gpt/point 1 -1)))
-
-          (gmt/translate (gpt/negate center))
-          (dm/str)))))
-
 (mf/defc text-shape
   {::mf/wrap-props false
    ::mf/wrap [mf/memo]}
@@ -60,7 +41,7 @@
 
         {:keys [x y width height position-data]} shape
 
-        transform (gsh/transform-str shape {:no-flip true})
+        transform (gsh/transform-str shape)
 
         ;; These position attributes are not really necessary but they are convenient for for the export
         group-props (-> #js {:transform transform
@@ -96,7 +77,6 @@
                              :y (- (:y data) (:height data))
                              :textLength (:width data)
                              :lengthAdjust "spacingAndGlyphs"
-                             :transform (position-data-transform shape data)
                              :alignmentBaseline alignment-bl
                              :dominantBaseline dominant-bl
                              :style (-> #js {:fontFamily (:font-family data)
diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs
index b51432b5b..39ae91b1c 100644
--- a/frontend/src/app/main/ui/workspace/shapes/text.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs
@@ -39,7 +39,7 @@
       [:& text/text-shape {:shape shape}]]
 
      (when (and (debug? :text-outline) (d/not-empty? (:position-data shape)))
-       [:g {:transform (gsh/transform-str shape {:no-flip true})}
+       [:g {:transform (gsh/transform-str shape)}
         (let [bounding-box (gsht/position-data-selrect shape)]
           [:rect {
                   :x (:x bounding-box)
diff --git a/frontend/src/app/main/ui/workspace/shapes/text/text_edition_outline.cljs b/frontend/src/app/main/ui/workspace/shapes/text/text_edition_outline.cljs
index aa4e29164..d7766d517 100644
--- a/frontend/src/app/main/ui/workspace/shapes/text/text_edition_outline.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/text/text_edition_outline.cljs
@@ -28,7 +28,7 @@
                 (some? text-modifier)
                 (dwt/apply-text-modifier text-modifier))
 
-        transform (gsh/transform-str shape {:no-flip true})
+        transform (gsh/transform-str shape)
         {:keys [x y width height]} shape]
 
     [:rect.main.viewport-selrect
diff --git a/frontend/src/app/main/ui/workspace/viewport/selection.cljs b/frontend/src/app/main/ui/workspace/viewport/selection.cljs
index f4f829fc7..57fc23d8a 100644
--- a/frontend/src/app/main/ui/workspace/viewport/selection.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/selection.cljs
@@ -272,7 +272,7 @@
         current-transform (mf/deref refs/current-transform)
 
         selrect (:selrect shape)
-        transform (gsh/transform-str shape {:no-flip true})]
+        transform (gsh/transform-str shape)]
 
     (when (not (#{:move :rotate} current-transform))
       [:g.controls {:pointer-events (if disable-handlers "none" "visible")}
@@ -297,7 +297,7 @@
         workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
 
         selrect (:selrect shape)
-        transform (gsh/transform-matrix shape {:no-flip true})
+        transform (gsh/transform-matrix shape)
 
         rotation (-> (gpt/point 1 0)
                      (gpt/transform (:transform shape))
@@ -309,7 +309,22 @@
       [:g.controls {:pointer-events (if disable-handlers "none" "visible")}
        ;; Handlers
        (for [{:keys [type position props]} (handlers-for-selection selrect shape zoom)]
-         (let [common-props {:key (dm/str (name type) "-" (name position))
+         (let [rotation
+               (cond
+                 (and (#{:top-left :bottom-right} position)
+                      (or (and (:flip-x shape) (not (:flip-y shape)))
+                          (and (:flip-y shape) (not (:flip-x shape)))))
+                 (- rotation 90)
+
+                 (and (#{:top-right :bottom-left} position)
+                      (or (and (:flip-x shape) (not (:flip-y shape)))
+                          (and (:flip-y shape) (not (:flip-x shape)))))
+                 (+ rotation 90)
+
+                 :else
+                 rotation)
+
+               common-props {:key (dm/str (name type) "-" (name position))
                              :zoom zoom
                              :position position
                              :on-rotate on-rotate