浏览代码

Fix pencil not working if drawing quickly (replace tldraw's undo/redo history with logseq's) (#7786)

* fix: pencil not working if drawing quickly

* feat: mod+c mod+g to toggle grid

Co-authored-by: Konstantinos Kaloutas <[email protected]>
Tienson Qin 2 年之前
父节点
当前提交
6a5b0c8199

+ 4 - 1
src/main/frontend/db.cljs

@@ -105,7 +105,10 @@
   (let [job (js/setTimeout
   (let [job (js/setTimeout
              (fn []
              (fn []
                (if (and (state/input-idle? repo)
                (if (and (state/input-idle? repo)
-                        (state/db-idle? repo))
+                        (state/db-idle? repo)
+                        ;; It's ok to not persist here since new changes
+                        ;; will be notified when restarting the app.
+                        (not (state/whiteboard-route?)))
                  (persist! repo)
                  (persist! repo)
                  ;; (state/set-db-persisted! repo true)
                  ;; (state/set-db-persisted! repo true)
 
 

+ 16 - 8
src/main/frontend/db/model.cljs

@@ -1743,11 +1743,19 @@ independent of format as format specific heading characters are stripped"
 
 
 (defn get-all-whiteboards
 (defn get-all-whiteboards
   [repo]
   [repo]
-  (->> (d/q
-        '[:find [(pull ?page [:block/name
-                              :block/created-at
-                              :block/updated-at]) ...]
-          :where
-          [?page :block/name]
-          [?page :block/type "whiteboard"]]
-        (conn/get-db repo))))
+  (d/q
+    '[:find [(pull ?page [:block/name
+                          :block/created-at
+                          :block/updated-at]) ...]
+      :where
+      [?page :block/name]
+      [?page :block/type "whiteboard"]]
+    (conn/get-db repo)))
+
+(defn get-whiteboard-id-nonces
+  [repo page-name]
+  (->> (get-page-blocks-no-cache repo page-name {:keys [:block/uuid :block/properties]})
+       (filter #(:logseq.tldraw.shape (:block/properties %)))
+       (map (fn [{:block/keys [uuid properties]}]
+              {:id (str uuid)
+               :nonce (get-in properties [:logseq.tldraw.shape :nonce])}))))

+ 20 - 12
src/main/frontend/extensions/tldraw.cljs

@@ -7,6 +7,7 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
+            [frontend.handler.history :as history]
             [frontend.rum :as r]
             [frontend.rum :as r]
             [frontend.search :as search]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.state :as state]
@@ -80,6 +81,8 @@
                        :BacklinksCount references-count
                        :BacklinksCount references-count
                        :BlockReference block-reference})
                        :BlockReference block-reference})
 
 
+(def undo (fn [] (history/undo! nil)))
+(def redo (fn [] (history/redo! nil)))
 (defn get-tldraw-handlers [current-whiteboard-name]
 (defn get-tldraw-handlers [current-whiteboard-name]
   {:search search-handler
   {:search search-handler
    :queryBlockByUUID (fn [block-uuid]
    :queryBlockByUUID (fn [block-uuid]
@@ -117,16 +120,20 @@
   (let [populate-onboarding?  (whiteboard-handler/should-populate-onboarding-whiteboard? page-name)
   (let [populate-onboarding?  (whiteboard-handler/should-populate-onboarding-whiteboard? page-name)
         data (whiteboard-handler/page-name->tldr! page-name)
         data (whiteboard-handler/page-name->tldr! page-name)
         [loaded-app set-loaded-app] (rum/use-state nil)
         [loaded-app set-loaded-app] (rum/use-state nil)
-        on-mount (fn [tln]
-                   (when-let [^js api (gobj/get tln "api")]
-                     (p/then (when populate-onboarding?
-                               (whiteboard-handler/populate-onboarding-whiteboard api))
-                             #(do (state/focus-whiteboard-shape tln block-id)
-                                  (set-loaded-app tln)))))]
-    (rum/use-effect! (fn [] (when (and loaded-app block-id)
-                              (state/focus-whiteboard-shape loaded-app block-id)) #())
+        on-mount (fn [^js tln]
+                   (when tln
+                     (set! (.-appUndo tln) undo)
+                     (set! (.-appRedo tln) redo)
+                     (when-let [^js api (gobj/get tln "api")]
+                      (p/then (when populate-onboarding?
+                                (whiteboard-handler/populate-onboarding-whiteboard api))
+                              #(do (state/focus-whiteboard-shape tln block-id)
+                                   (set-loaded-app tln))))))]
+    (rum/use-effect! (fn []
+                       (when (and loaded-app block-id)
+                         (state/focus-whiteboard-shape loaded-app block-id))
+                       #())
                      [block-id loaded-app])
                      [block-id loaded-app])
-
     (when data
     (when data
       [:div.draw.tldraw.whiteboard.relative.w-full.h-full
       [:div.draw.tldraw.whiteboard.relative.w-full.h-full
        {:style {:overscroll-behavior "none"}
        {:style {:overscroll-behavior "none"}
@@ -144,7 +151,8 @@
        (tldraw {:renderers tldraw-renderers
        (tldraw {:renderers tldraw-renderers
                 :handlers (get-tldraw-handlers page-name)
                 :handlers (get-tldraw-handlers page-name)
                 :onMount on-mount
                 :onMount on-mount
-                :onPersist (fn [app]
-                             (let [document (gobj/get app "serialized")]
-                               (whiteboard-handler/transact-tldr! page-name document)))
+                :onPersist (fn [app _info]
+                             (state/set-state! [:whiteboard/last-persisted-at (state/get-current-repo)] (util/time-ms))
+                             (util/profile "tldraw persist"
+                                           (whiteboard-handler/transact-tldr-delta! page-name app)))
                 :model data})])))
                 :model data})])))

+ 7 - 0
src/main/frontend/handler/events.cljs

@@ -50,6 +50,7 @@
             [frontend.handler.shell :as shell-handler]
             [frontend.handler.shell :as shell-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.user :as user-handler]
             [frontend.handler.user :as user-handler]
+            [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.web.nfs :as nfs-handler]
             [frontend.handler.web.nfs :as nfs-handler]
             [frontend.mobile.core :as mobile]
             [frontend.mobile.core :as mobile]
             [frontend.mobile.graph-picker :as graph-picker]
             [frontend.mobile.graph-picker :as graph-picker]
@@ -921,6 +922,12 @@
   (when (and command (not (string/blank? content)))
   (when (and command (not (string/blank? content)))
     (shell-handler/run-cli-command-wrapper! command content)))
     (shell-handler/run-cli-command-wrapper! command content)))
 
 
+(defmethod handle :whiteboard/undo [[_ e]]
+  (whiteboard-handler/undo! e))
+
+(defmethod handle :whiteboard/redo [[_ e]]
+  (whiteboard-handler/redo! e))
+
 (defmethod handle :editor/quick-capture [[_ args]]
 (defmethod handle :editor/quick-capture [[_ args]]
   (quick-capture/quick-capture args))
   (quick-capture/quick-capture args))
 
 

+ 2 - 1
src/main/frontend/handler/page.cljs

@@ -828,7 +828,8 @@
   []
   []
   (when-let [repo (state/get-current-repo)]
   (when-let [repo (state/get-current-repo)]
     (when (and (state/enable-journals? repo)
     (when (and (state/enable-journals? repo)
-               (not (state/loading-files? repo)))
+               (not (state/loading-files? repo))
+               (not (state/whiteboard-route?)))
       (state/set-today! (date/today))
       (state/set-today! (date/today))
       (when (or (config/local-db? repo)
       (when (or (config/local-db? repo)
                 (and (= "local" repo) (not (mobile-util/native-platform?))))
                 (and (= "local" repo) (not (mobile-util/native-platform?))))

+ 236 - 85
src/main/frontend/handler/whiteboard.cljs

@@ -2,79 +2,37 @@
   "Whiteboard related handlers"
   "Whiteboard related handlers"
   (:require [datascript.core :as d]
   (:require [datascript.core :as d]
             [dommy.core :as dom]
             [dommy.core :as dom]
+            [frontend.db :as db]
             [frontend.db.model :as model]
             [frontend.db.model :as model]
             [frontend.db.utils :as db-utils]
             [frontend.db.utils :as db-utils]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
+            [frontend.modules.editor.undo-redo :as history]
             [frontend.modules.outliner.core :as outliner]
             [frontend.modules.outliner.core :as outliner]
             [frontend.modules.outliner.file :as outliner-file]
             [frontend.modules.outliner.file :as outliner-file]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util :as util]
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.whiteboard :as gp-whiteboard]
             [logseq.graph-parser.whiteboard :as gp-whiteboard]
-            [promesa.core :as p]))
+            [promesa.core :as p]
+            [goog.object :as gobj]
+            [clojure.set :as set]
+            [clojure.string :as string]
+            [cljs-bean.core :as bean]))
 
 
-(defn shape->block [shape page-name idx]
+(defn js->clj-keywordize
+  [obj]
+  (js->clj obj :keywordize-keys true))
+
+(defn shape->block [shape page-name]
   (let [properties {:ls-type :whiteboard-shape
   (let [properties {:ls-type :whiteboard-shape
-                    :logseq.tldraw.shape (assoc shape :index idx)}
+                    :logseq.tldraw.shape shape}
         block {:block/page {:block/name (util/page-name-sanity-lc page-name)}
         block {:block/page {:block/name (util/page-name-sanity-lc page-name)}
                :block/parent {:block/name page-name}
                :block/parent {:block/name page-name}
                :block/properties properties}
                :block/properties properties}
         additional-props (gp-whiteboard/with-whiteboard-block-props block page-name)]
         additional-props (gp-whiteboard/with-whiteboard-block-props block page-name)]
     (merge block additional-props)))
     (merge block additional-props)))
 
 
-(defn- tldr-page->blocks-tx [page-name tldr-data]
-  (let [page-name (util/page-name-sanity-lc page-name)
-        page-entity (model/get-page page-name)
-        page-block (merge {:block/name page-name
-                           :block/type "whiteboard"
-                           :block/properties {:ls-type :whiteboard-page
-                                              :logseq.tldraw.page (dissoc tldr-data :shapes)}}
-                          (when page-entity (select-keys page-entity [:block/created-at])))
-        ;; todo: use get-paginated-blocks instead?
-        existing-blocks (model/get-page-blocks-no-cache (state/get-current-repo)
-                                                        page-name
-                                                        {:pull-keys '[:db/id
-                                                                      :block/uuid
-                                                                      :block/properties [:ls-type]
-                                                                      :block/created-at
-                                                                      :block/updated-at
-                                                                      {:block/parent [:block/uuid]}]})
-        id->block (zipmap (map :block/uuid existing-blocks) existing-blocks)
-        shapes (:shapes tldr-data)
-        ;; we should maintain the order of the shapes in the page
-        ;; bring back/forward is depending on this ordering
-        blocks (map-indexed
-                (fn [idx shape]
-                  (let [block (shape->block shape page-name idx)]
-                    (merge block
-                           (select-keys (id->block (:block/uuid block))
-                                        [:block/created-at :block/updated-at])))) shapes)
-        block-ids (->> shapes
-                       (map (fn [shape] (when (= (:blockType shape) "B")
-                                          (uuid (:pageId shape)))))
-                       (concat (map :block/uuid blocks))
-                       (remove nil?)
-                       (set))
-        ;; delete blocks when all of the following are false
-        ;; - the block is not in the new blocks list
-        ;; - the block's parent is not in the new block list
-        ;; - the block is not a shape block
-        delete-blocks (filterv (fn [block]
-                                 (not
-                                  (or (block-ids (:block/uuid block))
-                                      (block-ids (:block/uuid (:block/parent block)))
-                                      (not (gp-whiteboard/shape-block? block)))))
-                               existing-blocks)
-        ;; always recalcuate refs for now. 
-        ;; todo: optimize in frontend.modules.outliner.pipeline/compute-block-path-refs?
-        refs-tx (mapcat (fn [m] [[:db/retract (:db/id m) :block/path-refs]
-                                 [:db/retract (:db/id m) :block/refs]]) existing-blocks)
-        delete-blocks-tx (mapv (fn [s] [:db/retractEntity (:db/id s)]) delete-blocks)
-        page-and-blocks (->> (cons page-block blocks)
-                             (map outliner/block-with-timestamps))]
-    (concat refs-tx page-and-blocks delete-blocks-tx)))
-
 (defn- get-whiteboard-clj [page-name]
 (defn- get-whiteboard-clj [page-name]
   (when (model/page-exists? page-name)
   (when (model/page-exists? page-name)
     (let [page-block (model/get-page page-name)
     (let [page-block (model/get-page page-name)
@@ -82,12 +40,20 @@
           blocks (model/get-page-blocks-no-cache page-name)]
           blocks (model/get-page-blocks-no-cache page-name)]
       [page-block blocks])))
       [page-block blocks])))
 
 
+(defn- build-shapes
+  [page-block blocks]
+  (let [shapes-index (get-in page-block [:block/properties :logseq.tldraw.page :shapes-index])
+        shape-id->index (zipmap shapes-index (range 0 (count shapes-index)))]
+    (->> blocks
+         (map (fn [block]
+                (assoc block :index (get shape-id->index (str (:block/uuid block)) 0))))
+         (sort-by :index)
+         (filter gp-whiteboard/shape-block?)
+         (map gp-whiteboard/block->shape))))
+
 (defn- whiteboard-clj->tldr [page-block blocks]
 (defn- whiteboard-clj->tldr [page-block blocks]
   (let [id (str (:block/uuid page-block))
   (let [id (str (:block/uuid page-block))
-        shapes (->> blocks
-                    (filter gp-whiteboard/shape-block?)
-                    (map gp-whiteboard/block->shape)
-                    (sort-by :index))
+        shapes (build-shapes page-block blocks)
         tldr-page (gp-whiteboard/page-block->tldr-page page-block)
         tldr-page (gp-whiteboard/page-block->tldr-page page-block)
         assets (:assets tldr-page)
         assets (:assets tldr-page)
         tldr-page (dissoc tldr-page :assets)]
         tldr-page (dissoc tldr-page :assets)]
@@ -99,23 +65,128 @@
                               :name (:block/name page-block)
                               :name (:block/name page-block)
                               :shapes shapes})]})))
                               :shapes shapes})]})))
 
 
-(defn transact-tldr! [page-name tldr]
-  (let [{:keys [pages assets]} (js->clj tldr :keywordize-keys true)
-        page (first pages)
-        tx (tldr-page->blocks-tx page-name (assoc page :assets assets))]
-    (db-utils/transact! tx)))
-
-(defn get-default-tldr
-  [page-id]
-  {:currentPageId page-id,
-   :selectedIds [],
-   :pages [{:id page-id
-            :name page-id
-            :ls-type :whiteboard-page
-            :shapes []
-            :bindings {}
-            :nonce 1}]
-   :assets []})
+(defn build-page-block
+  [page-name tldraw-page assets shapes-index]
+  (let [page-entity (model/get-page page-name)
+        get-k #(gobj/get tldraw-page %)]
+    {:block/name page-name
+     :block/type "whiteboard"
+     :block/properties {:ls-type :whiteboard-page
+                        :logseq.tldraw.page {:id (get-k "id")
+                                             :name (get-k "name")
+                                             :bindings (js->clj-keywordize (get-k "bindings"))
+                                             :nonce (get-k "nonce")
+                                             :assets (js->clj-keywordize assets)
+                                             :shapes-index shapes-index}}
+     :block/updated-at (util/time-ms)
+     :block/created-at (or (:block/created-at page-entity)
+                           (util/time-ms))}))
+
+(defn- compute-tx
+  [^js app ^js tl-page new-id-nonces db-id-nonces page-name]
+  (let [assets (js->clj-keywordize (.getCleanUpAssets app))
+        new-shapes (.-shapes tl-page)
+        shapes-index (map #(gobj/get % "id") new-shapes)
+        upsert-shapes (->> (set/difference new-id-nonces db-id-nonces)
+                           (map (fn [{:keys [id]}]
+                                  (-> (.-serialized ^js (.getShapeById tl-page id))
+                                      js->clj-keywordize)))
+                           (set))
+        old-ids (set (map :id db-id-nonces))
+        new-ids (set (map :id new-id-nonces))
+        created-ids (->> (set/difference new-ids old-ids)
+                         (remove string/blank?)
+                         (set))
+        created-shapes (set (filter #(created-ids (:id %)) upsert-shapes))
+        deleted-ids (->> (set/difference old-ids new-ids)
+                         (remove string/blank?))
+        repo (state/get-current-repo)
+        deleted-shapes (when (seq deleted-ids)
+                         (->> (db/pull-many repo '[*] (mapv (fn [id] [:block/uuid (uuid id)]) deleted-ids))
+                              (map (fn [b]
+                                     (get-in b [:block/properties :logseq.tldraw.shape])))))
+        deleted-shapes-tx (mapv (fn [id] [:db/retractEntity [:block/uuid (uuid id)]]) deleted-ids)
+        with-timestamps (fn [block]
+                          (if (contains? created-ids (str (:block/uuid block)))
+                            (assoc block :block/updated-at (util/time-ms))
+                            (outliner/block-with-timestamps block)))
+        changed-shapes (set/difference upsert-shapes created-shapes)
+        prev-changed-blocks (when (seq changed-shapes)
+                              (db/pull-many repo '[*] (mapv (fn [shape]
+                                                              [:block/uuid (uuid (:id shape))]) changed-shapes)))]
+    {:page-block (build-page-block page-name tl-page assets shapes-index)
+     :upserted-blocks (->> upsert-shapes
+                           (map #(shape->block % page-name))
+                           (map with-timestamps))
+     :delete-blocks deleted-shapes-tx
+     :metadata {:whiteboard/transact? true
+                :data {:page-name page-name
+                       :deleted-shapes deleted-shapes
+                       :new-shapes created-shapes
+                       :changed-shapes changed-shapes
+                       :prev-changed-blocks prev-changed-blocks}}}))
+
+(defonce *last-shapes-nonce (atom {}))
+(defn transact-tldr-delta! [page-name ^js app]
+  (let [tl-page ^js (second (first (.-pages app)))
+        shapes (.-shapes ^js tl-page)
+        new-id-nonces (set (map (fn [shape]
+                                  {:id (.-id shape)
+                                   :nonce (.-nonce shape)}) shapes))
+        repo (state/get-current-repo)
+        db-id-nonces (or
+                      (get-in @*last-shapes-nonce [repo page-name])
+                      (set (->> (model/get-whiteboard-id-nonces repo page-name)
+                                (map #(update % :id str)))))
+        {:keys [page-block upserted-blocks delete-blocks metadata]}
+        (compute-tx app tl-page new-id-nonces db-id-nonces page-name)
+        tx-data (concat delete-blocks [page-block] upserted-blocks)
+        new-shapes (get-in metadata [:data :new-shapes])
+        metadata' (cond
+                    ;; group
+                    (some #(= "group" (:type %)) new-shapes)
+                    (assoc metadata :whiteboard/op :group)
+
+                    ;; ungroup
+                    (some #(= "group" (:type %)) (get-in metadata [:data :deleted-shapes]))
+                    (assoc metadata :whiteboard/op :un-group)
+
+                    ;; arrow
+                    (some #(and (= "line" (:type %))
+                                (= "arrow "(:end (:decorations %)))) new-shapes)
+
+                    (assoc metadata :whiteboard/op :new-arrow)
+                    :else
+                    metadata)
+        metadata' (if (seq (concat upserted-blocks delete-blocks))
+                    metadata'
+                    (assoc metadata :undo? true))]
+    (swap! *last-shapes-nonce assoc-in [repo page-name] new-id-nonces)
+    (if (contains? #{:new-arrow} (:whiteboard/op metadata'))
+      (state/set-state! :whiteboard/pending-tx-data
+                        {:tx-data tx-data
+                         :metadata metadata'})
+      (let [pending-tx-data (:whiteboard/pending-tx-data @state/state)
+            tx-data' (concat (:tx-data pending-tx-data) tx-data)
+            metadata'' (merge metadata' (:metadata pending-tx-data))]
+        (state/set-state! :whiteboard/pending-tx-data {})
+        (db-utils/transact! repo tx-data' metadata'')))))
+
+(defn get-default-new-whiteboard-tx
+  [page-name id]
+  [#:block{:name page-name,
+           :type "whiteboard",
+           :properties
+           {:ls-type :whiteboard-page,
+            :logseq.tldraw.page
+            {:id id,
+             :name page-name,
+             :ls-type :whiteboard-page,
+             :bindings {},
+             :nonce 1,
+             :assets []}},
+           :updated-at (util/time-ms),
+           :created-at (util/time-ms)}])
 
 
 (defn get-whiteboard-entity [page-name]
 (defn get-whiteboard-entity [page-name]
   (db-utils/entity [:block/name (util/page-name-sanity-lc page-name)]))
   (db-utils/entity [:block/name (util/page-name-sanity-lc page-name)]))
@@ -125,17 +196,15 @@
    (create-new-whiteboard-page! nil))
    (create-new-whiteboard-page! nil))
   ([name]
   ([name]
    (let [uuid (or (and name (parse-uuid name)) (d/squuid))
    (let [uuid (or (and name (parse-uuid name)) (d/squuid))
-         name (or name (str uuid))
-         tldr (get-default-tldr (str uuid))]
-     (transact-tldr! name (get-default-tldr (str uuid)))
+         name (or name (str uuid))]
+     (db/transact! (get-default-new-whiteboard-tx name (str uuid)))
      (let [entity (get-whiteboard-entity name)
      (let [entity (get-whiteboard-entity name)
            tx (assoc (select-keys entity [:db/id])
            tx (assoc (select-keys entity [:db/id])
                      :block/uuid uuid)]
                      :block/uuid uuid)]
        (db-utils/transact! [tx])
        (db-utils/transact! [tx])
        (let [page-entity (get-whiteboard-entity name)]
        (let [page-entity (get-whiteboard-entity name)]
          (when (and page-entity (nil? (:block/file page-entity)))
          (when (and page-entity (nil? (:block/file page-entity)))
-           (outliner-file/sync-to-file page-entity))))
-     tldr)))
+           (outliner-file/sync-to-file page-entity)))))))
 
 
 (defn create-new-whiteboard-and-redirect!
 (defn create-new-whiteboard-and-redirect!
   ([]
   ([]
@@ -237,10 +306,7 @@
   ([{:keys [pages blocks]} api]
   ([{:keys [pages blocks]} api]
    (let [page-block (first pages)
    (let [page-block (first pages)
          ;; FIXME: should also clone normal blocks
          ;; FIXME: should also clone normal blocks
-         shapes (->> blocks
-                     (filter gp-whiteboard/shape-block?)
-                     (map gp-whiteboard/block->shape)
-                     (sort-by :index))
+         shapes (build-shapes page-block blocks)
          tldr-page (gp-whiteboard/page-block->tldr-page page-block)
          tldr-page (gp-whiteboard/page-block->tldr-page page-block)
          assets (:assets tldr-page)
          assets (:assets tldr-page)
          bindings (:bindings tldr-page)]
          bindings (:bindings tldr-page)]
@@ -265,3 +331,88 @@
           (state/set-onboarding-whiteboard! true))
           (state/set-onboarding-whiteboard! true))
         (p/catch
         (p/catch
          (fn [e] (js/console.warn "Failed to populate onboarding whiteboard" e))))))
          (fn [e] (js/console.warn "Failed to populate onboarding whiteboard" e))))))
+
+(defn- delete-shapes!
+  [^js api shapes]
+  (apply (.-deleteShapes api) (map :id shapes)))
+
+(defn- create-shapes!
+  [^js api shapes]
+  (apply (.-createShapes api) (bean/->js shapes)))
+
+(defn- update-shapes!
+  [^js api shapes]
+  (apply (.-updateShapes api) (bean/->js shapes)))
+
+(defn- select-shapes
+  [^js api ids]
+  (apply (.-selectShapes api) ids))
+
+(defn update-bindings!
+  [^js tl-page page-name]
+  (when-let [page (db/entity [:block/name page-name])]
+    (let [bindings (get-in page [:block/properties :logseq.tldraw.page :bindings])]
+      (when (seq bindings)
+        (.updateBindings tl-page (bean/->js bindings))))))
+
+(defn undo!
+  [{:keys [tx-meta]}]
+  (history/pause-listener!)
+  (try
+    (when-let [app (state/active-tldraw-app)]
+      (let [{:keys [deleted-shapes new-shapes changed-shapes prev-changed-blocks]} (:data tx-meta)
+            whiteboard-op (:whiteboard/op tx-meta)
+            ^js api (.-api app)]
+        (when api
+          (case whiteboard-op
+            :group
+            (do
+              (select-shapes api (map :id new-shapes))
+              (.unGroup api))
+            :un-group
+            (do
+              (select-shapes api (mapcat :children deleted-shapes))
+              (.doGroup api))
+            (do
+              (when (seq deleted-shapes)
+                (create-shapes! api deleted-shapes))
+              (when (seq new-shapes)
+                (delete-shapes! api new-shapes))
+              (when (seq changed-shapes)
+                (let [prev-shapes (map (fn [b] (get-in b [:block/properties :logseq.tldraw.shape]))
+                                    prev-changed-blocks)]
+                  (update-shapes! api prev-shapes))))))))
+    (catch :default e
+      (js/console.error e)))
+  (history/resume-listener!))
+
+(defn redo!
+  [{:keys [tx-meta]}]
+  (history/pause-listener!)
+  (try
+    (when-let [app (state/active-tldraw-app)]
+      (let [{:keys [page-name deleted-shapes new-shapes changed-shapes]} (:data tx-meta)
+            whiteboard-op (:whiteboard/op tx-meta)
+            ^js api (.-api app)
+            tl-page ^js (second (first (.-pages app)))]
+        (when api
+          (update-bindings! tl-page page-name)
+          (case whiteboard-op
+            :group
+            (do
+              (select-shapes api (mapcat :children new-shapes))
+              (.doGroup api))
+            :un-group
+            (do
+              (select-shapes api (map :id deleted-shapes))
+              (.unGroup api))
+            (do
+              (when (seq deleted-shapes)
+                (delete-shapes! api deleted-shapes))
+              (when (seq new-shapes)
+                (create-shapes! api new-shapes))
+              (when (seq changed-shapes)
+                (update-shapes! api changed-shapes)))))))
+    (catch :default e
+      (js/console.error e)))
+  (history/resume-listener!))

+ 29 - 6
src/main/frontend/modules/editor/undo_redo.cljs

@@ -2,11 +2,13 @@
   (:require [datascript.core :as d]
   (:require [datascript.core :as d]
             [frontend.db.conn :as conn]
             [frontend.db.conn :as conn]
             [frontend.modules.datascript-report.core :as db-report]
             [frontend.modules.datascript-report.core :as db-report]
-            [frontend.state :as state]))
+            [frontend.state :as state]
+            [clojure.set :as set]))
 
 
 ;;;; APIs
 ;;;; APIs
 
 
 (def ^:private undo-redo-states (atom {}))
 (def ^:private undo-redo-states (atom {}))
+(def *pause-listener (atom false))
 
 
 (defn- get-state
 (defn- get-state
   []
   []
@@ -99,7 +101,7 @@
   []
   []
   (let [[e prev-e] (pop-undo)]
   (let [[e prev-e] (pop-undo)]
     (when e
     (when e
-      (let [{:keys [txs]} e
+      (let [{:keys [txs tx-meta]} e
             new-txs (get-txs false txs)
             new-txs (get-txs false txs)
             editor-cursor (if (= (get-in e [:editor-cursor :last-edit-block :block/uuid])
             editor-cursor (if (= (get-in e [:editor-cursor :last-edit-block :block/uuid])
                                  (get-in prev-e [:editor-cursor :last-edit-block :block/uuid])) ; same block
                                  (get-in prev-e [:editor-cursor :last-edit-block :block/uuid])) ; same block
@@ -107,23 +109,43 @@
                             (:editor-cursor e))]
                             (:editor-cursor e))]
         (push-redo e)
         (push-redo e)
         (transact! new-txs (merge {:undo? true}
         (transact! new-txs (merge {:undo? true}
+                                  tx-meta
                                   (select-keys e [:pagination-blocks-range])))
                                   (select-keys e [:pagination-blocks-range])))
+        (when (:whiteboard/transact? tx-meta)
+          (state/pub-event! [:whiteboard/undo e]))
         (assoc e
         (assoc e
                :txs-op new-txs
                :txs-op new-txs
                :editor-cursor editor-cursor)))))
                :editor-cursor editor-cursor)))))
 
 
 (defn redo
 (defn redo
   []
   []
-  (when-let [{:keys [txs]:as e} (pop-redo)]
+  (when-let [{:keys [txs tx-meta] :as e} (pop-redo)]
     (let [new-txs (get-txs true txs)]
     (let [new-txs (get-txs true txs)]
       (push-undo e)
       (push-undo e)
       (transact! new-txs (merge {:redo? true}
       (transact! new-txs (merge {:redo? true}
+                                tx-meta
                                 (select-keys e [:pagination-blocks-range])))
                                 (select-keys e [:pagination-blocks-range])))
+      (when (:whiteboard/transact? tx-meta)
+        (state/pub-event! [:whiteboard/redo e]))
       (assoc e :txs-op new-txs))))
       (assoc e :txs-op new-txs))))
 
 
-(defn listen-outliner-operation
+(defn pause-listener!
+  []
+  (reset! *pause-listener true))
+
+(defn resume-listener!
+  []
+  (reset! *pause-listener false))
+
+(defn listen-db-changes!
   [{:keys [tx-data tx-meta] :as tx-report}]
   [{:keys [tx-data tx-meta] :as tx-report}]
-  (when-not (empty? tx-data)
+  (when (and (seq tx-data)
+             (not (or (:undo? tx-meta)
+                      (:redo? tx-meta)))
+             (not @*pause-listener)
+             (not (set/subset?
+                   (set (map :a tx-data))
+                   #{:block/created-at :block/updated-at})))
     (reset-redo)
     (reset-redo)
     (if (:compute-new-refs? tx-meta)
     (if (:compute-new-refs? tx-meta)
       (let [[removed-e _prev-e] (pop-undo)
       (let [[removed-e _prev-e] (pop-undo)
@@ -132,6 +154,7 @@
       (let [updated-blocks (db-report/get-blocks tx-report)
       (let [updated-blocks (db-report/get-blocks tx-report)
             entity {:blocks updated-blocks
             entity {:blocks updated-blocks
                     :txs tx-data
                     :txs tx-data
+                    :tx-meta tx-meta
                     :editor-cursor (:editor-cursor tx-meta)
                     :editor-cursor (:editor-cursor tx-meta)
                     :pagination-blocks-range (get-in [:ui/pagination-blocks-range (get-in tx-report [:db-after :max-tx])] @state/state)}]
                     :pagination-blocks-range (get-in [:ui/pagination-blocks-range (get-in tx-report [:db-after :max-tx])] @state/state)}]
-       (push-undo entity)))))
+        (push-undo entity)))))

+ 3 - 2
src/main/frontend/modules/outliner/datascript.cljc

@@ -27,8 +27,9 @@
      (when-not config/test?
      (when-not config/test?
        (pipelines/invoke-hooks tx-report)
        (pipelines/invoke-hooks tx-report)
 
 
-       (when (:outliner/transact? tx-meta)
-         (undo-redo/listen-outliner-operation tx-report))
+       (when (or (:outliner/transact? tx-meta)
+                 (:whiteboard/transact? tx-meta))
+         (undo-redo/listen-db-changes! tx-report))
 
 
        (search/sync-search-indice! repo tx-report))))
        (search/sync-search-indice! repo tx-report))))
 
 

+ 1 - 1
src/main/frontend/modules/outliner/file.cljs

@@ -55,7 +55,7 @@
       (if (or (and (> blocks-count 500)
       (if (or (and (> blocks-count 500)
                    (not (state/input-idle? repo {:diff 3000}))) ;; long page
                    (not (state/input-idle? repo {:diff 3000}))) ;; long page
               ;; when this whiteboard page is just being updated
               ;; when this whiteboard page is just being updated
-              (and whiteboard? (not (state/whiteboard-page-idle? repo page-block))))
+              (and whiteboard? (not (state/whiteboard-idle? repo))))
         (async/put! (state/get-file-write-chan) [repo page-db-id outliner-op])
         (async/put! (state/get-file-write-chan) [repo page-db-id outliner-op])
         (let [pull-keys (if whiteboard? whiteboard-blocks-pull-keys-with-persisted-ids '[*])
         (let [pull-keys (if whiteboard? whiteboard-blocks-pull-keys-with-persisted-ids '[*])
               blocks (model/get-page-blocks-no-cache repo (:block/name page-block) {:pull-keys pull-keys})
               blocks (model/get-page-blocks-no-cache repo (:block/name page-block) {:pull-keys pull-keys})

+ 3 - 3
src/main/frontend/modules/shortcut/config.cljs

@@ -239,7 +239,7 @@
    :editor/undo                    {:binding "mod+z"
    :editor/undo                    {:binding "mod+z"
                                     :fn      history/undo!}
                                     :fn      history/undo!}
 
 
-   :editor/redo                    {:binding ["shift+mod+z" "mod+y"]
+   :editor/redo                    {:binding ["mod+shift+z" "mod+y"]
                                     :fn      history/redo!}
                                     :fn      history/redo!}
 
 
    :editor/insert-link             {:binding "mod+l"
    :editor/insert-link             {:binding "mod+l"
@@ -573,8 +573,6 @@
                           :editor/copy
                           :editor/copy
                           :editor/copy-text
                           :editor/copy-text
                           :editor/cut
                           :editor/cut
-                          :editor/undo
-                          :editor/redo
                           :command/toggle-favorite])
                           :command/toggle-favorite])
      (with-meta {:before m/enable-when-not-component-editing!}))
      (with-meta {:before m/enable-when-not-component-editing!}))
 
 
@@ -584,6 +582,8 @@
                           :editor/select-all-blocks
                           :editor/select-all-blocks
                           :editor/zoom-in
                           :editor/zoom-in
                           :editor/zoom-out
                           :editor/zoom-out
+                          :editor/undo
+                          :editor/redo
                           :ui/toggle-brackets
                           :ui/toggle-brackets
                           :go/search-in-page
                           :go/search-in-page
                           :go/search
                           :go/search

+ 14 - 20
src/main/frontend/state.cljs

@@ -278,7 +278,9 @@
      :graph/importing-state                 {}
      :graph/importing-state                 {}
 
 
      :whiteboard/onboarding-whiteboard?     (or (storage/get :ls-onboarding-whiteboard?) false)
      :whiteboard/onboarding-whiteboard?     (or (storage/get :ls-onboarding-whiteboard?) false)
-     :whiteboard/onboarding-tour?           (or (storage/get :whiteboard-onboarding-tour?) false)})))
+     :whiteboard/onboarding-tour?           (or (storage/get :whiteboard-onboarding-tour?) false)
+     :whiteboard/last-persisted-at          {}
+     :whiteboard/pending-tx-data            {}})))
 
 
 ;; Block ast state
 ;; Block ast state
 ;; ===============
 ;; ===============
@@ -729,9 +731,13 @@ Similar to re-frame subscriptions"
     (get-in (get-route-match)
     (get-in (get-route-match)
             [:path-params :name])))
             [:path-params :name])))
 
 
+(defn whiteboard-route?
+  []
+  (= :whiteboard (get-current-route)))
+
 (defn get-current-whiteboard
 (defn get-current-whiteboard
   []
   []
-  (when (= :whiteboard (get-current-route))
+  (when (whiteboard-route?)
     (get-in (get-route-match)
     (get-in (get-route-match)
             [:path-params :name])))
             [:path-params :name])))
 
 
@@ -1630,25 +1636,13 @@ Similar to re-frame subscriptions"
      ;; Is this a good idea to put whiteboard check here?
      ;; Is this a good idea to put whiteboard check here?
      (not (get-edit-input-id)))))
      (not (get-edit-input-id)))))
 
 
-(defn whiteboard-page-idle?
-  "Check if whiteboard page is idle.
-   - when current tool is select and idle
-     - and whiteboard page is updated longer than 1000 seconds
-   - when current tool is other tool and idle
-     - and whiteboard page is updated longer than 3000 seconds"
-  [repo whiteboard-page & {:keys [select-idle-ms tool-idle-ms]
-                           :or {select-idle-ms 1000
-                                tool-idle-ms 3000}}]
+(defn whiteboard-idle?
+  "Check if whiteboard is idle."
+  [repo]
   (when repo
   (when repo
-    (if-let [tldraw-app (active-tldraw-app)]
-      (let [last-time (:block/updated-at whiteboard-page)
-            now (util/time-ms)
-            elapsed (- now last-time)
-            select-idle (.. tldraw-app (isIn "select.idle"))
-            tool-idle (.. tldraw-app -selectedTool (isIn "idle"))]
-        (or (and select-idle (>= elapsed select-idle-ms))
-            (and (not select-idle) tool-idle (>= elapsed tool-idle-ms))))
-      true)))
+    (>= (- (util/time-ms) (or (get-in @state [:whiteboard/last-persisted-at repo])
+                              (- (util/time-ms) 10000)))
+        3000)))
 
 
 (defn set-nfs-refreshing!
 (defn set-nfs-refreshing!
   [value]
   [value]

+ 3 - 2
src/main/frontend/util.cljc

@@ -1260,14 +1260,15 @@
 #?(:cljs
 #?(:cljs
    (defn- get-dom-top
    (defn- get-dom-top
      [node]
      [node]
-     (gobj/get (.getBoundingClientRect node) "top")))
+     (when node
+       (gobj/get (.getBoundingClientRect node) "top"))))
 
 
 #?(:cljs
 #?(:cljs
    (defn sort-by-height
    (defn sort-by-height
      [elements]
      [elements]
      (sort (fn [x y]
      (sort (fn [x y]
              (< (get-dom-top x) (get-dom-top y)))
              (< (get-dom-top x) (get-dom-top y)))
-           elements)))
+           (remove nil? elements))))
 
 
 #?(:cljs
 #?(:cljs
    (defn calc-delta-rect-offset
    (defn calc-delta-rect-offset

+ 3 - 4
tldraw/apps/tldraw-logseq/src/app.tsx

@@ -1,6 +1,6 @@
 /* eslint-disable @typescript-eslint/no-non-null-assertion */
 /* eslint-disable @typescript-eslint/no-non-null-assertion */
 /* eslint-disable @typescript-eslint/no-explicit-any */
 /* eslint-disable @typescript-eslint/no-explicit-any */
-import { deepEqual, TLDocumentModel } from '@tldraw/core'
+import { TLDocumentModel } from '@tldraw/core'
 import {
 import {
   AppCanvas,
   AppCanvas,
   AppProvider,
   AppProvider,
@@ -68,6 +68,7 @@ const BacklinksCount: LogseqContextValue['renderers']['BacklinksCount'] = props
 const AppImpl = () => {
 const AppImpl = () => {
   const ref = React.useRef<HTMLDivElement>(null)
   const ref = React.useRef<HTMLDivElement>(null)
   const app = useApp()
   const app = useApp()
+
   const components = React.useMemo(
   const components = React.useMemo(
     () => ({
     () => ({
       ContextBar,
       ContextBar,
@@ -98,9 +99,7 @@ const AppInner = ({
 
 
   const onPersistOnDiff: TLReactCallbacks<Shape>['onPersist'] = React.useCallback(
   const onPersistOnDiff: TLReactCallbacks<Shape>['onPersist'] = React.useCallback(
     (app, info) => {
     (app, info) => {
-      if (!deepEqual(app.serialized, model)) {
-        onPersist?.(app, info)
-      }
+       onPersist?.(app, info)
     },
     },
     [model]
     [model]
   )
   )

+ 5 - 0
tldraw/packages/core/src/lib/TLApi/TLApi.ts

@@ -201,6 +201,11 @@ export class TLApi<S extends TLShape = TLShape, K extends TLEventMap = TLEventMa
     return this
     return this
   }
   }
 
 
+  persist = () => {
+    this.app.persist()
+    return this
+  }
+
   createNewLineBinding = (source: S | string, target: S | string) => {
   createNewLineBinding = (source: S | string, target: S | string) => {
     return this.app.createNewLineBinding(source, target)
     return this.app.createNewLineBinding(source, target)
   }
   }

+ 9 - 11
tldraw/packages/core/src/lib/TLApp/TLApp.ts

@@ -120,18 +120,10 @@ export class TLApp<
         keys: 'mod+=',
         keys: 'mod+=',
         fn: () => this.api.zoomIn(),
         fn: () => this.api.zoomIn(),
       },
       },
-      {
-        keys: 'mod+z',
-        fn: () => this.undo(),
-      },
       {
       {
         keys: 'mod+x',
         keys: 'mod+x',
         fn: () => this.cut(),
         fn: () => this.cut(),
       },
       },
-      {
-        keys: 'mod+shift+z',
-        fn: () => this.redo(),
-      },
       {
       {
         keys: '[',
         keys: '[',
         fn: () => this.sendBackward(),
         fn: () => this.sendBackward(),
@@ -195,6 +187,12 @@ export class TLApp<
           this.api.unGroup()
           this.api.unGroup()
         },
         },
       },
       },
+      {
+        keys: 'shift+g',
+        fn: () => {
+          this.api.toggleGrid()
+        },
+      }
     ]
     ]
     // eslint-disable-next-line @typescript-eslint/ban-ts-comment
     // eslint-disable-next-line @typescript-eslint/ban-ts-comment
     // @ts-ignore
     // @ts-ignore
@@ -279,9 +277,9 @@ export class TLApp<
   @computed get serialized(): TLDocumentModel<S> {
   @computed get serialized(): TLDocumentModel<S> {
     return {
     return {
       // currentPageId: this.currentPageId,
       // currentPageId: this.currentPageId,
-      selectedIds: Array.from(this.selectedIds.values()),
-      pages: Array.from(this.pages.values()).map(page => page.serialized),
-      assets: this.getCleanUpAssets(),
+      // selectedIds: Array.from(this.selectedIds.values()),
+      // pages: Array.from(this.pages.values()).map(page => page.serialized),
+      // assets: this.getCleanUpAssets(),
     }
     }
   }
   }
 
 

+ 5 - 54
tldraw/packages/core/src/lib/TLHistory.ts

@@ -6,12 +6,6 @@ import type { TLGroupShape } from './shapes/TLGroupShape'
 import type { TLApp, TLDocumentModel } from './TLApp'
 import type { TLApp, TLDocumentModel } from './TLApp'
 import { TLPage } from './TLPage'
 import { TLPage } from './TLPage'
 
 
-const shouldPersist = (a: TLDocumentModel, b: TLDocumentModel) => {
-  const page0 = omit(a.pages[0], 'nonce')
-  const page1 = omit(b.pages[0], 'nonce')
-  return !deepEqual(page0, page1)
-}
-
 export class TLHistory<S extends TLShape = TLShape, K extends TLEventMap = TLEventMap> {
 export class TLHistory<S extends TLShape = TLShape, K extends TLEventMap = TLEventMap> {
   constructor(app: TLApp<S, K>) {
   constructor(app: TLApp<S, K>) {
     this.app = app
     this.app = app
@@ -20,23 +14,12 @@ export class TLHistory<S extends TLShape = TLShape, K extends TLEventMap = TLEve
 
 
   app: TLApp<S, K>
   app: TLApp<S, K>
   @observable stack: TLDocumentModel[] = []
   @observable stack: TLDocumentModel[] = []
-  @observable pointer = 0
   isPaused = true
   isPaused = true
 
 
   get creating() {
   get creating() {
     return this.app.selectedTool.currentState.id === 'creating'
     return this.app.selectedTool.currentState.id === 'creating'
   }
   }
 
 
-  @computed
-  get canUndo() {
-    return this.pointer > 0
-  }
-
-  @computed
-  get canRedo() {
-    return this.pointer < this.stack.length - 1
-  }
-
   pause = () => {
   pause = () => {
     if (this.isPaused) return
     if (this.isPaused) return
     this.isPaused = true
     this.isPaused = true
@@ -47,58 +30,26 @@ export class TLHistory<S extends TLShape = TLShape, K extends TLEventMap = TLEve
     this.isPaused = false
     this.isPaused = false
   }
   }
 
 
-  @action reset = () => {
-    this.stack = [this.app.serialized]
-    this.pointer = 0
-    this.resume()
-
-    this.app.notify('persist', null)
-  }
-
   @action persist = (replace = false) => {
   @action persist = (replace = false) => {
     if (this.isPaused || this.creating) return
     if (this.isPaused || this.creating) return
-
-    const { serialized } = this.app
-
-    // Do not persist if the serialized state is the same as the last one
-    if (this.stack.length > 0 && !shouldPersist(this.stack[this.pointer], serialized)) {
-      return
-    }
-
-    if (replace) {
-      this.stack[this.pointer] = serialized
-    } else {
-      if (this.pointer < this.stack.length) {
-        this.stack = this.stack.slice(0, this.pointer + 1)
-      }
-      this.stack.push(serialized)
-      this.pointer = this.stack.length - 1
-    }
-
     this.app.pages.forEach(page => page.bump()) // Is it ok here?
     this.app.pages.forEach(page => page.bump()) // Is it ok here?
     this.app.notify('persist', null)
     this.app.notify('persist', null)
   }
   }
 
 
-  @action setPointer = (pointer: number) => {
-    this.pointer = pointer
-    const snapshot = this.stack[this.pointer]
-    this.deserialize(snapshot)
-    this.app.notify('persist', null)
-  }
-
   @action undo = () => {
   @action undo = () => {
     if (this.isPaused) return
     if (this.isPaused) return
     if (this.app.selectedTool.currentState.id !== 'idle') return
     if (this.app.selectedTool.currentState.id !== 'idle') return
-    if (this.canUndo) {
-      this.setPointer(this.pointer - 1)
+
+    if (this.app.appUndo) {
+      this.app.appUndo()
     }
     }
   }
   }
 
 
   @action redo = () => {
   @action redo = () => {
     if (this.isPaused) return
     if (this.isPaused) return
     if (this.app.selectedTool.currentState.id !== 'idle') return
     if (this.app.selectedTool.currentState.id !== 'idle') return
-    if (this.canRedo) {
-      this.setPointer(this.pointer + 1)
+    if (this.app.appRedo) {
+      this.app.appRedo()
     }
     }
   }
   }
 
 

+ 1 - 1
tldraw/packages/core/src/lib/TLPage/TLPage.ts

@@ -92,7 +92,7 @@ export class TLPage<S extends TLShape = TLShape, E extends TLEventMap = TLEventM
   @observable nonce = 0
   @observable nonce = 0
 
 
   @action bump = () => {
   @action bump = () => {
-    this.nonce++
+    // this.nonce++
   }
   }
 
 
   @action update(props: Partial<TLPageProps<S>>) {
   @action update(props: Partial<TLPageProps<S>>) {

+ 9 - 1
tldraw/packages/core/src/lib/tools/TLDrawTool/states/CreatingState.tsx

@@ -5,6 +5,7 @@ import type { TLShape, TLDrawShape } from '../../../shapes'
 import type { TLApp } from '../../../TLApp'
 import type { TLApp } from '../../../TLApp'
 import { TLToolState } from '../../../TLToolState'
 import { TLToolState } from '../../../TLToolState'
 import type { TLDrawTool } from '../TLDrawTool'
 import type { TLDrawTool } from '../TLDrawTool'
+import { debounce } from '../../../../utils'
 
 
 export class CreatingState<
 export class CreatingState<
   S extends TLShape,
   S extends TLShape,
@@ -18,6 +19,8 @@ export class CreatingState<
   private shape = {} as T
   private shape = {} as T
   private points: number[][] = [[0, 0, 0.5]]
   private points: number[][] = [[0, 0, 0.5]]
 
 
+  private persistDebounced = debounce(this.app.persist, 200)
+
   // Add a new point and offset the shape, if necessary
   // Add a new point and offset the shape, if necessary
   private addNextPoint(point: number[]) {
   private addNextPoint(point: number[]) {
     const { shape } = this
     const { shape } = this
@@ -96,7 +99,12 @@ export class CreatingState<
     })
     })
     this.tool.previousShape = this.shape
     this.tool.previousShape = this.shape
     this.tool.transition('idle')
     this.tool.transition('idle')
-    this.app.persist()
+    let tool = this.app.selectedTool.id
+    if (tool === 'pencil' || tool === 'highlighter') {
+      this.persistDebounced()
+    } else {
+      this.app.persist()
+    }
   }
   }
 
 
   onKeyDown: TLStateEvents<S>['onKeyDown'] = (info, e) => {
   onKeyDown: TLStateEvents<S>['onKeyDown'] = (info, e) => {

+ 4 - 1
tldraw/packages/react/src/hooks/useCanvasEvents.ts

@@ -77,7 +77,10 @@ export function useCanvasEvents() {
       onDragOver,
       onDragOver,
       // fix touch callout in iOS
       // fix touch callout in iOS
       onTouchEnd: (e: TouchEvent) => {
       onTouchEnd: (e: TouchEvent) => {
-        e.preventDefault()
+        let tool = app.selectedTool.id
+        if (tool === 'pencil' || tool === 'highlighter') {
+          e.preventDefault()
+        }
       }
       }
     }
     }
   }, [callbacks])
   }, [callbacks])

+ 0 - 1
tldraw/packages/react/src/hooks/useSetup.ts

@@ -26,7 +26,6 @@ export function useSetup<
   React.useLayoutEffect(() => {
   React.useLayoutEffect(() => {
     const unsubs: (() => void)[] = []
     const unsubs: (() => void)[] = []
     if (!app) return
     if (!app) return
-    app.history.reset()
     if (typeof window !== undefined) {
     if (typeof window !== undefined) {
       window['tlapps'] = window['tlapps'] || {}
       window['tlapps'] = window['tlapps'] || {}
       window['tlapps'][app.uuid] = app
       window['tlapps'][app.uuid] = app