Browse Source

Merge branch 'master' into enhance/ux-of-file-sync

charlie 3 years ago
parent
commit
d78a18e438
36 changed files with 751 additions and 431 deletions
  1. 2 2
      android/app/build.gradle
  2. 37 45
      deps/graph-parser/src/logseq/graph_parser/block.cljs
  3. 4 2
      deps/graph-parser/test/logseq/graph_parser/nbb_test_runner.cljs
  4. 43 0
      deps/graph-parser/test/logseq/graph_parser_test.cljs
  5. 4 4
      ios/App/App.xcodeproj/project.pbxproj
  6. 1 1
      resources/package.json
  7. 31 20
      src/electron/electron/fs_watcher.cljs
  8. 10 7
      src/electron/electron/handler.cljs
  9. 22 21
      src/main/frontend/components/block.cljs
  10. 1 2
      src/main/frontend/components/block.css
  11. 1 1
      src/main/frontend/components/right_sidebar.cljs
  12. 0 7
      src/main/frontend/db/model.cljs
  13. 6 1
      src/main/frontend/db/query_react.cljs
  14. 29 9
      src/main/frontend/dicts.cljc
  15. 12 8
      src/main/frontend/extensions/pdf/assets.cljs
  16. 15 7
      src/main/frontend/format/block.cljs
  17. 2 0
      src/main/frontend/fs/node.cljs
  18. 30 5
      src/main/frontend/fs/watcher_handler.cljs
  19. 3 1
      src/main/frontend/handler.cljs
  20. 92 80
      src/main/frontend/handler/block.cljs
  21. 76 45
      src/main/frontend/handler/editor.cljs
  22. 4 0
      src/main/frontend/handler/notification.cljs
  23. 11 9
      src/main/frontend/handler/page.cljs
  24. 2 1
      src/main/frontend/mobile/footer.cljs
  25. 16 26
      src/main/frontend/mobile/mobile_bar.cljs
  26. 1 1
      src/main/frontend/modules/instrumentation/sentry.cljs
  27. 109 77
      src/main/frontend/modules/outliner/core.cljs
  28. 3 1
      src/main/frontend/modules/outliner/datascript.cljc
  29. 9 10
      src/main/frontend/modules/outliner/pipeline.cljs
  30. 7 0
      src/main/frontend/modules/outliner/tree.cljs
  31. 4 0
      src/main/frontend/modules/shortcut/config.cljs
  32. 56 3
      src/main/frontend/modules/shortcut/dicts.cljc
  33. 1 0
      src/main/frontend/publishing/html.cljs
  34. 2 1
      src/main/frontend/state.cljs
  35. 1 1
      src/main/frontend/version.cljs
  36. 104 33
      src/test/frontend/modules/outliner/core_test.cljs

+ 2 - 2
android/app/build.gradle

@@ -6,8 +6,8 @@ android {
         applicationId "com.logseq.app"
         applicationId "com.logseq.app"
         minSdkVersion rootProject.ext.minSdkVersion
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
-        versionCode 25
-        versionName "0.7.1"
+        versionCode 26
+        versionName "0.7.2"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         aaptOptions {
         aaptOptions {
              // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
              // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.

+ 37 - 45
deps/graph-parser/src/logseq/graph_parser/block.cljc → deps/graph-parser/src/logseq/graph_parser/block.cljs

@@ -1,7 +1,4 @@
 (ns logseq.graph-parser.block
 (ns logseq.graph-parser.block
-  ;; Disable clj linters since we don't support clj
-  #?(:clj {:clj-kondo/config {:linters {:unresolved-namespace {:level :off}
-                                        :unresolved-symbol {:level :off}}}})
   "Block related code needed for graph-parser"
   "Block related code needed for graph-parser"
   (:require [clojure.string :as string]
   (:require [clojure.string :as string]
             [clojure.walk :as walk]
             [clojure.walk :as walk]
@@ -12,9 +9,7 @@
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.mldoc :as gp-mldoc]
             [logseq.graph-parser.mldoc :as gp-mldoc]
-            [logseq.graph-parser.date-time-util :as date-time-util]
-            #?(:org.babashka/nbb [logseq.graph-parser.log :as log]
-               :default [lambdaisland.glogi :as log])))
+            [logseq.graph-parser.date-time-util :as date-time-util]))
 
 
 (defn heading-block?
 (defn heading-block?
   [block]
   [block]
@@ -546,45 +541,42 @@
      :date-formatter and :db"
      :date-formatter and :db"
   [blocks content with-id? format {:keys [user-config] :as options}]
   [blocks content with-id? format {:keys [user-config] :as options}]
   {:pre [(seq blocks) (string? content) (boolean? with-id?) (contains? #{:markdown :org} format)]}
   {:pre [(seq blocks) (string? content) (boolean? with-id?) (contains? #{:markdown :org} format)]}
-  (try
-    (let [encoded-content (utf8/encode content)
-          [blocks body pre-block-properties]
-          (loop [headings []
-                 blocks (reverse blocks)
-                 timestamps {}
-                 properties {}
-                 body []]
-            (if (seq blocks)
-              (let [[block pos-meta] (first blocks)
-                    ;; fix start_pos
-                    pos-meta (assoc pos-meta :end_pos
-                                    (if (seq headings)
-                                      (get-in (last headings) [:meta :start_pos])
-                                      nil))]
-                (cond
-                  (paragraph-timestamp-block? block)
-                  (let [timestamps (extract-timestamps block)
-                        timestamps' (merge timestamps timestamps)]
-                    (recur headings (rest blocks) timestamps' properties body))
-
-                  (gp-property/properties-ast? block)
-                  (let [properties (extract-properties format (second block) user-config)]
-                    (recur headings (rest blocks) timestamps properties body))
-
-                  (heading-block? block)
-                  (let [block (construct-block block properties timestamps body encoded-content format pos-meta with-id? options)]
-                    (recur (conj headings block) (rest blocks) {} {} []))
-
-                  :else
-                  (recur headings (rest blocks) timestamps properties (conj body block))))
-              [(-> (reverse headings)
-                   sanity-blocks-data)
-               body
-               properties]))
-          result (with-pre-block-if-exists blocks body pre-block-properties encoded-content options)]
-      (map #(dissoc % :block/meta) result))
-    (catch :default e
-      (log/error :extract-blocks-failure e))))
+  (let [encoded-content (utf8/encode content)
+        [blocks body pre-block-properties]
+        (loop [headings []
+               blocks (reverse blocks)
+               timestamps {}
+               properties {}
+               body []]
+          (if (seq blocks)
+            (let [[block pos-meta] (first blocks)
+                  ;; fix start_pos
+                  pos-meta (assoc pos-meta :end_pos
+                                  (if (seq headings)
+                                    (get-in (last headings) [:meta :start_pos])
+                                    nil))]
+              (cond
+                (paragraph-timestamp-block? block)
+                (let [timestamps (extract-timestamps block)
+                      timestamps' (merge timestamps timestamps)]
+                  (recur headings (rest blocks) timestamps' properties body))
+
+                (gp-property/properties-ast? block)
+                (let [properties (extract-properties format (second block) user-config)]
+                  (recur headings (rest blocks) timestamps properties body))
+
+                (heading-block? block)
+                (let [block (construct-block block properties timestamps body encoded-content format pos-meta with-id? options)]
+                  (recur (conj headings block) (rest blocks) {} {} []))
+
+                :else
+                (recur headings (rest blocks) timestamps properties (conj body block))))
+            [(-> (reverse headings)
+                 sanity-blocks-data)
+             body
+             properties]))
+        result (with-pre-block-if-exists blocks body pre-block-properties encoded-content options)]
+    (map #(dissoc % :block/meta) result)))
 
 
 (defn with-parent-and-left
 (defn with-parent-and-left
   [page-id blocks]
   [page-id blocks]

+ 4 - 2
deps/graph-parser/test/logseq/graph_parser/nbb_test_runner.cljs

@@ -6,7 +6,8 @@
             [logseq.graph-parser.block-test]
             [logseq.graph-parser.block-test]
             [logseq.graph-parser.property-test]
             [logseq.graph-parser.property-test]
             [logseq.graph-parser.extract-test]
             [logseq.graph-parser.extract-test]
-            [logseq.graph-parser.cli-test]))
+            [logseq.graph-parser.cli-test]
+            [logseq.graph-parser-test]))
 
 
 (defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
 (defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
   (when-not (cljs.test/successful? m)
   (when-not (cljs.test/successful? m)
@@ -19,4 +20,5 @@
                'logseq.graph-parser.property-test
                'logseq.graph-parser.property-test
                'logseq.graph-parser.block-test
                'logseq.graph-parser.block-test
                'logseq.graph-parser.extract-test
                'logseq.graph-parser.extract-test
-               'logseq.graph-parser.cli-test))
+               'logseq.graph-parser.cli-test
+               'logseq.graph-parser-test))

+ 43 - 0
deps/graph-parser/test/logseq/graph_parser_test.cljs

@@ -0,0 +1,43 @@
+(ns logseq.graph-parser-test
+  (:require [cljs.test :refer [deftest testing is]]
+            [logseq.graph-parser :as graph-parser]
+            [logseq.graph-parser.db :as gp-db]
+            [logseq.graph-parser.block :as gp-block]
+            [datascript.core :as d]))
+
+(deftest parse-file
+  (testing "id properties"
+    (let [conn (gp-db/start-conn)]
+      (graph-parser/parse-file conn "foo.md" "- id:: 628953c1-8d75-49fe-a648-f4c612109098" {})
+      (is (= [{:id "628953c1-8d75-49fe-a648-f4c612109098"}]
+             (->> (d/q '[:find (pull ?b [*])
+                         :in $
+                         :where [?b :block/content] [(missing? $ ?b :block/name)]]
+                       @conn)
+                  (map first)
+                  (map :block/properties)))
+          "id as text has correct :block/properties"))
+
+    (let [conn (gp-db/start-conn)]
+      (graph-parser/parse-file conn "foo.md" "- id:: [[628953c1-8d75-49fe-a648-f4c612109098]]" {})
+      (is (= [{:id #{"628953c1-8d75-49fe-a648-f4c612109098"}}]
+             (->> (d/q '[:find (pull ?b [*])
+                         :in $
+                         :where [?b :block/content] [(missing? $ ?b :block/name)]]
+                       @conn)
+                  (map first)
+                  (map :block/properties)))
+          "id as linked ref has correct :block/properties")))
+
+  (testing "unexpected failure during block extraction"
+    (let [conn (gp-db/start-conn)
+          deleted-page (atom nil)]
+      (with-redefs [gp-block/with-pre-block-if-exists (fn stub-failure [& _args]
+                                              (throw (js/Error "Testing unexpected failure")))]
+        (try
+          (graph-parser/parse-file conn "foo.md" "- id:: 628953c1-8d75-49fe-a648-f4c612109098"
+                                  {:delete-blocks-fn (fn [page _file]
+                                                       (reset! deleted-page page))})
+          (catch :default _)))
+      (is (= nil @deleted-page)
+          "Page should not be deleted when there is unexpected failure"))))

+ 4 - 4
ios/App/App.xcodeproj/project.pbxproj

@@ -554,7 +554,7 @@
 				INFOPLIST_FILE = App/Info.plist;
 				INFOPLIST_FILE = App/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				MARKETING_VERSION = 0.7.1;
+				MARKETING_VERSION = 0.7.2;
 				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
 				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -580,7 +580,7 @@
 				INFOPLIST_FILE = App/Info.plist;
 				INFOPLIST_FILE = App/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
-				MARKETING_VERSION = 0.7.1;
+				MARKETING_VERSION = 0.7.2;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
@@ -605,7 +605,7 @@
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
-				MARKETING_VERSION = 0.7.1;
+				MARKETING_VERSION = 0.7.2;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
@@ -632,7 +632,7 @@
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
-				MARKETING_VERSION = 0.7.1;
+				MARKETING_VERSION = 0.7.2;
 				MTL_FAST_MATH = YES;
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
 				PRODUCT_BUNDLE_IDENTIFIER = com.logseq.logseq.ShareViewController;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				PRODUCT_NAME = "$(TARGET_NAME)";

+ 1 - 1
resources/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "Logseq",
   "name": "Logseq",
-  "version": "0.7.1",
+  "version": "0.7.2",
   "main": "electron.js",
   "main": "electron.js",
   "author": "Logseq",
   "author": "Logseq",
   "license": "AGPL-3.0",
   "license": "AGPL-3.0",

+ 31 - 20
src/electron/electron/fs_watcher.cljs

@@ -29,10 +29,13 @@
 
 
 (defn- publish-file-event!
 (defn- publish-file-event!
   [dir path event]
   [dir path event]
-  (let [content (when (and (not= event "unlink")
+  (let [dir-path? (= dir path)
+        content (when (and (not= event "unlink")
+                           (not dir-path?)
                            (utils/should-read-content? path))
                            (utils/should-read-content? path))
                   (utils/read-file path))
                   (utils/read-file path))
-        stat (when (not= event "unlink")
+        stat (when (and (not= event "unlink")
+                        (not dir-path?))
                (fs/statSync path))]
                (fs/statSync path))]
     (send-file-watcher! dir event {:dir (utils/fix-win-path! dir)
     (send-file-watcher! dir event {:dir (utils/fix-win-path! dir)
                                    :path (utils/fix-win-path! path)
                                    :path (utils/fix-win-path! path)
@@ -44,31 +47,39 @@
   [_win dir]
   [_win dir]
   (when (and (fs/existsSync dir)
   (when (and (fs/existsSync dir)
              (not (get @*file-watcher dir)))
              (not (get @*file-watcher dir)))
-    (let [watcher (.watch watcher dir
-                          (clj->js
-                           {:ignored (fn [path]
-                                       (utils/ignored-path? dir path))
-                            :ignoreInitial false
-                            :ignorePermissionErrors true
-                            :interval polling-interval
-                            :binaryInterval polling-interval
-                            :persistent true
-                            :disableGlobbing true
-                            :usePolling false
-                            :awaitWriteFinish true}))
-          watcher-del-f #(.close watcher)]
-      (swap! *file-watcher assoc dir [watcher watcher-del-f])
+    (let [watcher-opts (clj->js
+                        {:ignored (fn [path]
+                                    (utils/ignored-path? dir path))
+                         :ignoreInitial false
+                         :ignorePermissionErrors true
+                         :interval polling-interval
+                         :binaryInterval polling-interval
+                         :persistent true
+                         :disableGlobbing true
+                         :usePolling false
+                         :awaitWriteFinish true})
+          dir-watcher (.watch watcher dir watcher-opts)
+          watcher-del-f #(.close dir-watcher)]
+      (swap! *file-watcher assoc dir [dir-watcher watcher-del-f])
       ;; TODO: batch sender
       ;; TODO: batch sender
-      (.on watcher "add"
+      (.on dir-watcher "unlinkDir"
+           (fn [path]
+             (when (= dir path)
+               (publish-file-event! dir dir "unlinkDir"))))
+      (.on dir-watcher "addDir"
+           (fn [path]
+             (when (= dir path)
+               (publish-file-event! dir dir "addDir"))))
+      (.on dir-watcher "add"
            (fn [path]
            (fn [path]
              (publish-file-event! dir path "add")))
              (publish-file-event! dir path "add")))
-      (.on watcher "change"
+      (.on dir-watcher "change"
            (fn [path]
            (fn [path]
              (publish-file-event! dir path "change")))
              (publish-file-event! dir path "change")))
-      (.on watcher "unlink"
+      (.on dir-watcher "unlink"
            (fn [path]
            (fn [path]
              (publish-file-event! dir path "unlink")))
              (publish-file-event! dir path "unlink")))
-      (.on watcher "error"
+      (.on dir-watcher "error"
            (fn [path]
            (fn [path]
              (println "Watch error happened: "
              (println "Watch error happened: "
                       {:path path})))
                       {:path path})))

+ 10 - 7
src/electron/electron/handler.cljs

@@ -53,13 +53,16 @@
 (defmethod handle :unlink [_window [_ repo path]]
 (defmethod handle :unlink [_window [_ repo path]]
   (if (plugin/dotdir-file? path)
   (if (plugin/dotdir-file? path)
     (fs/unlinkSync path)
     (fs/unlinkSync path)
-    (let [file-name   (-> (string/replace path (str repo "/") "")
-                          (string/replace "/" "_")
-                          (string/replace "\\" "_"))
-          recycle-dir (str repo "/logseq/.recycle")
-          _           (fs-extra/ensureDirSync recycle-dir)
-          new-path    (str recycle-dir "/" file-name)]
-      (fs/renameSync path new-path))))
+    (try
+      (let [file-name   (-> (string/replace path (str repo "/") "")
+                           (string/replace "/" "_")
+                           (string/replace "\\" "_"))
+           recycle-dir (str repo "/logseq/.recycle")
+           _           (fs-extra/ensureDirSync recycle-dir)
+           new-path    (str recycle-dir "/" file-name)]
+        (fs/renameSync path new-path))
+      (catch :default _e
+        nil))))
 
 
 (defonce Diff (google-diff.))
 (defonce Diff (google-diff.))
 (defn string-some-deleted?
 (defn string-some-deleted?

+ 22 - 21
src/main/frontend/components/block.cljs

@@ -1617,7 +1617,9 @@
                      (if collapsed?
                      (if collapsed?
                        (editor-handler/expand-block! uuid)
                        (editor-handler/expand-block! uuid)
                        (editor-handler/collapse-block! uuid))))}
                        (editor-handler/collapse-block! uuid))))}
-      [:span {:class (if control-show? "control-show cursor-pointer" "control-hide")}
+      [:span {:class (if (or collapsed?
+                             (and control-show?
+                                  (editor-handler/collapsable? uuid {:semantic? true}))) "control-show cursor-pointer" "control-hide")}
        (ui/rotating-arrow collapsed?)]]
        (ui/rotating-arrow collapsed?)]]
      (let [bullet [:a {:on-click (fn [event]
      (let [bullet [:a {:on-click (fn [event]
                                    (bullet-on-click event block uuid))}
                                    (bullet-on-click event block uuid))}
@@ -2154,12 +2156,14 @@
     [:div.indent (ui/icon "indent-increase" {:style {:fontSize 16}})]]])
     [:div.indent (ui/icon "indent-increase" {:style {:fontSize 16}})]]])
 
 
 (rum/defc block-right-menu < rum/reactive
 (rum/defc block-right-menu < rum/reactive
-  [_config {:block/keys [uuid] :as _block}]
+  [_config {:block/keys [uuid] :as _block} edit?]
   [:div.block-right-menu.flex.bg-base-2.rounded-md.ml-1
   [:div.block-right-menu.flex.bg-base-2.rounded-md.ml-1
    [:div.commands-button.w-0.flex.flew-col.rounded-md
    [:div.commands-button.w-0.flex.flew-col.rounded-md
-    {:id (str "block-right-menu-" uuid)}
-    [:div.more (ui/icon "dots-circle-horizontal" {:style {:fontSize 16}})]
-    [:div.outdent (ui/icon "indent-decrease" {:style {:fontSize 16}})]]])
+    {:id (str "block-right-menu-" uuid)
+     :style {:max-width (if edit? 40 80)}}
+    [:div.outdent (ui/icon "indent-decrease" {:style {:fontSize 16}})]
+    (when-not edit?
+      [:div.more (ui/icon "dots-circle-horizontal" {:style {:fontSize 16}})])]])
 
 
 (rum/defcs block-content-or-editor < rum/reactive
 (rum/defcs block-content-or-editor < rum/reactive
   (rum/local true :hide-block-refs?)
   (rum/local true :hide-block-refs?)
@@ -2361,13 +2365,10 @@
   (editor-handler/unhighlight-blocks!))
   (editor-handler/unhighlight-blocks!))
 
 
 (defn- block-mouse-over
 (defn- block-mouse-over
-  [uuid e *control-show? block-id doc-mode?]
+  [e *control-show? block-id doc-mode?]
   (when-not @*dragging?
   (when-not @*dragging?
     (util/stop e)
     (util/stop e)
-    (when (or
-           (model/block-collapsed? uuid)
-           (editor-handler/collapsable? uuid {:semantic? true}))
-      (reset! *control-show? true))
+    (reset! *control-show? true)
     (when-let [parent (gdom/getElement block-id)]
     (when-let [parent (gdom/getElement block-id)]
       (let [node (.querySelector parent ".bullet-container")]
       (let [node (.querySelector parent ".bullet-container")]
         (when doc-mode?
         (when doc-mode?
@@ -2491,7 +2492,7 @@
         :data-collapsed (and collapsed? has-child?)
         :data-collapsed (and collapsed? has-child?)
         :class (str uuid
         :class (str uuid
                     (when pre-block? " pre-block")
                     (when pre-block? " pre-block")
-                    (when (and card? (not review-cards?)) " shadow-xl")
+                    (when (and card? (not review-cards?)) " shadow-md")
                     (when (:ui/selected? block) " selected noselect"))
                     (when (:ui/selected? block) " selected noselect"))
         :blockid (str uuid)
         :blockid (str uuid)
         :haschild (str has-child?)}
         :haschild (str has-child?)}
@@ -2522,14 +2523,14 @@
 
 
      [:div.flex.flex-row.pr-2
      [:div.flex.flex-row.pr-2
       {:class (if (and heading? (seq (:block/title block))) "items-baseline" "")
       {:class (if (and heading? (seq (:block/title block))) "items-baseline" "")
-       :on-touch-start block-handler/on-touch-start
+       :on-touch-start (fn [event uuid] (block-handler/on-touch-start event uuid))
        :on-touch-move (fn [event]
        :on-touch-move (fn [event]
-                        (block-handler/on-touch-move event block uuid *show-left-menu? *show-right-menu?))
+                        (block-handler/on-touch-move event block uuid edit? *show-left-menu? *show-right-menu?))
        :on-touch-end (fn [event]
        :on-touch-end (fn [event]
                        (block-handler/on-touch-end event block uuid *show-left-menu? *show-right-menu?))
                        (block-handler/on-touch-end event block uuid *show-left-menu? *show-right-menu?))
        :on-touch-cancel block-handler/on-touch-cancel
        :on-touch-cancel block-handler/on-touch-cancel
        :on-mouse-over (fn [e]
        :on-mouse-over (fn [e]
-                        (block-mouse-over uuid e *control-show? block-id doc-mode?))
+                        (block-mouse-over e *control-show? block-id doc-mode?))
        :on-mouse-leave (fn [e]
        :on-mouse-leave (fn [e]
                          (block-mouse-leave e *control-show? block-id doc-mode?))}
                          (block-mouse-leave e *control-show? block-id doc-mode?))}
       (when (not slide?)
       (when (not slide?)
@@ -2539,7 +2540,7 @@
         (block-left-menu config block))
         (block-left-menu config block))
       (block-content-or-editor config block edit-input-id block-id heading-level edit?)
       (block-content-or-editor config block edit-input-id block-id heading-level edit?)
       (when @*show-right-menu?
       (when @*show-right-menu?
-        (block-right-menu config block))]
+        (block-right-menu config block edit?))]
 
 
      (block-children config children collapsed?)
      (block-children config children collapsed?)
 
 
@@ -2846,12 +2847,12 @@
            [:span.opacity-60.text-sm.ml-2.results-count
            [:span.opacity-60.text-sm.ml-2.results-count
             (str (count transformed-query-result) " results")]]
             (str (count transformed-query-result) " results")]]
            ;;insert an "edit" button in the query view
            ;;insert an "edit" button in the query view
-           [:a.opacity-70.hover:opacity-100.svg-small.inline 
-            {:on-mouse-down (fn [e]
-                              (util/stop e)
-                              (editor-handler/edit-block! current-block :max (:block/uuid current-block)))}
-            svg/edit]]
-          
+           (when-not built-in?
+            [:a.opacity-70.hover:opacity-100.svg-small.inline
+                      {:on-mouse-down (fn [e]
+                                        (util/stop e)
+                                        (editor-handler/edit-block! current-block :max (:block/uuid current-block)))}
+                      svg/edit])]
           (fn []
           (fn []
             [:div
             [:div
              (when (and current-block (not view-f) (nil? table-view?))
              (when (and current-block (not view-f) (nil? table-view?))

+ 1 - 2
src/main/frontend/components/block.css

@@ -191,7 +191,7 @@
     
     
     .commands-button {
     .commands-button {
         overflow: hidden;
         overflow: hidden;
-        max-width: 50px;
+        max-width: 40px;
         text-align: center;
         text-align: center;
         margin: auto 0;
         margin: auto 0;
 
 
@@ -207,7 +207,6 @@
 
 
     .commands-button {
     .commands-button {
         overflow: hidden;
         overflow: hidden;
-        max-width: 80px;
         text-align: center;
         text-align: center;
         margin: auto 0;
         margin: auto 0;
 
 

+ 1 - 1
src/main/frontend/components/right_sidebar.cljs

@@ -122,7 +122,7 @@
   (let [item (build-sidebar-item repo idx db-id block-type)]
   (let [item (build-sidebar-item repo idx db-id block-type)]
     (when item
     (when item
       (let [collapse? (state/sub [:ui/sidebar-collapsed-blocks db-id])]
       (let [collapse? (state/sub [:ui/sidebar-collapsed-blocks db-id])]
-        [:div.sidebar-item.content.color-level.px-4.shadow-lg
+        [:div.sidebar-item.content.color-level.px-4.shadow-md
          (let [[title component] item]
          (let [[title component] item]
            [:div.flex.flex-col
            [:div.flex.flex-col
             [:div.flex.flex-row.justify-between
             [:div.flex.flex-row.justify-between

+ 0 - 7
src/main/frontend/db/model.cljs

@@ -802,13 +802,6 @@
     (->> (tree-seq map? (fn [x] [(:block/parent x)]) block)
     (->> (tree-seq map? (fn [x] [(:block/parent x)]) block)
          (some util/collapsed?))))
          (some util/collapsed?))))
 
 
-(defn block-collapsed?
-  ([block-id]
-   (block-collapsed? (state/get-current-repo) block-id))
-  ([repo block-id]
-   (when-let [block (db-utils/entity repo [:block/uuid block-id])]
-     (util/collapsed? block))))
-
 (defn get-block-page
 (defn get-block-page
   [repo block-id]
   [repo block-id]
   (when-let [block (db-utils/entity repo [:block/uuid block-id])]
   (when-let [block (db-utils/entity repo [:block/uuid block-id])]

+ 6 - 1
src/main/frontend/db/query_react.cljs

@@ -114,6 +114,11 @@
                page-ref (string/lower-case page-ref)]
                page-ref (string/lower-case page-ref)]
            (list 'contains? sym (text/page-ref-un-brackets! page-ref)))
            (list 'contains? sym (text/page-ref-un-brackets! page-ref)))
 
 
+         (and (vector? f)
+              (= (first f) 'page-property)
+              (keyword? (util/nth-safe f 2)))
+         (update f 2 (fn [k] (keyword (string/replace (name k) "_" "-"))))
+
          :else
          :else
          f)) query)))
          f)) query)))
 
 
@@ -132,4 +137,4 @@
           k [:custom query']]
           k [:custom query']]
       (pprint "inputs (post-resolution):" resolved-inputs)
       (pprint "inputs (post-resolution):" resolved-inputs)
       (pprint "query-opts:" query-opts)
       (pprint "query-opts:" query-opts)
-      (apply react/q repo k query-opts query inputs))))
+      (apply react/q repo k query-opts query inputs))))

+ 29 - 9
src/main/frontend/dicts.cljc

@@ -1564,9 +1564,9 @@
         :file-sync/graph-deleted "El gráfico remoto actual se ha eliminado"}
         :file-sync/graph-deleted "El gráfico remoto actual se ha eliminado"}
 
 
    :nb-NO {:tutorial/text #?(:cljs (rc/inline "tutorial-no.md")
    :nb-NO {:tutorial/text #?(:cljs (rc/inline "tutorial-no.md")
-                                :default "tutorial-no.md")
+                             :default "tutorial-no.md")
            :tutorial/dummy-notes #?(:cljs (rc/inline "dummy-notes-no.md")
            :tutorial/dummy-notes #?(:cljs (rc/inline "dummy-notes-no.md")
-                                       :default "dummy-notes-no.md")
+                                    :default "dummy-notes-no.md")
            :on-boarding/demo-graph "Dette er en demo graf, endringer vil ikke bli lagret før du åpner en lokal mappe."
            :on-boarding/demo-graph "Dette er en demo graf, endringer vil ikke bli lagret før du åpner en lokal mappe."
            :on-boarding/add-graph "Legg til en graf"
            :on-boarding/add-graph "Legg til en graf"
            :on-boarding/open-local-dir "Åpne en lokal mappe"
            :on-boarding/open-local-dir "Åpne en lokal mappe"
@@ -1623,11 +1623,11 @@
            :right-side-bar/favorites "Favoritter"
            :right-side-bar/favorites "Favoritter"
            :right-side-bar/page-graph "Sidegraf"
            :right-side-bar/page-graph "Sidegraf"
            :right-side-bar/block-ref "Blokkreferanse"
            :right-side-bar/block-ref "Blokkreferanse"
-           :right-side-bar/graph-view "Graph view"
-           :right-side-bar/all-pages "All pages"
+           :right-side-bar/graph-view "Grafvisning"
+           :right-side-bar/all-pages "Alle sider"
            :right-side-bar/flashcards "Flashcards"
            :right-side-bar/flashcards "Flashcards"
-           :right-side-bar/new-page "New page"
-           :left-side-bar/journals "Journals"
+           :right-side-bar/new-page "Ny side"
+           :left-side-bar/journals "Dagbøker"
            :left-side-bar/new-page "Ny side"
            :left-side-bar/new-page "Ny side"
            :left-side-bar/nav-favorites "Favoritter"
            :left-side-bar/nav-favorites "Favoritter"
            :left-side-bar/nav-shortcuts "Snarveier"
            :left-side-bar/nav-shortcuts "Snarveier"
@@ -1864,9 +1864,29 @@
            :select.graph/empty-placeholder-description "Ingen grafer matcher. Vil du legge til en ny?"
            :select.graph/empty-placeholder-description "Ingen grafer matcher. Vil du legge til en ny?"
            :select.graph/add-graph "Ja, legg til en ny graf"
            :select.graph/add-graph "Ja, legg til en ny graf"
 
 
-           :file-sync/other-user-graph "Nåværende lokal graf er bundet til annen brukers fjernkontroll. Så kan ikke begynne å synkronisere."
-           :file-sync/graph-deleted "Nåværende fjernkontrollen er slettet"}
-
+           :file-sync/other-user-graph "Nåværende lokal graf er bundet til annen brukers fjerngraf. Kan ikke begynne å synkronisere."
+           :file-sync/graph-deleted "Nåværende fjerngraf er slettet"
+           :host "Vert"
+           :port "Port"
+           :re-index-discard-unsaved-changes-warning "Reindeksering vil forkaste nåværende graf, og deretter prosessere alle filene på nytt slik de er på disk akkurat nå. Du vil miste ulagrede endringer, og det kan ta litt tid. Forsette?"
+           :re-index-multiple-windows-warning "Du må lukke de andre vinduene før du kan reindeksere denne grafen"
+           :save "Lagrer..."
+           :settings-of-plugins "Innstillinger for utvidelser"
+           :sync-from-local-changes-detected "Oppfrisk oppdager og prosesserer filer på disk som er modifiserte og avviker fra sideinnholdet som vises i Logseq. Fortsett?"
+           :type "Type"
+           :graph/persist "Logeq synkroniserer intern status, vennligst vent i flere sekunder."
+           :graph/persist-error "Intern status synk feilet"
+           :graph/save "Lagrer..."
+           :graph/save-error "Lagring feilet"
+           :graph/save-success "Lagring vellykket"
+           :page/copy-page-url "Kopier side URL"
+           :page/file-sync-versions "Versjoner av siden"
+           :page/open-backup-directory "Åpne mappe med sidens sikkerhetskopier"
+           :plugin/not-installed "Ikke installert"
+           :settings-page/edit-export-css "Rediger export.css"
+           :settings-page/network-proxy "Nettverksproxy"
+           :settings-page/plugin-system "System for utvidelser"}
+   
    :pt-BR {:on-boarding/demo-graph "Esse é um gráfico de demonstração, mudanças não serão salvas enquanto uma pasta local não for aberta."
    :pt-BR {:on-boarding/demo-graph "Esse é um gráfico de demonstração, mudanças não serão salvas enquanto uma pasta local não for aberta."
            :on-boarding/add-graph "Adicionar gráfico"
            :on-boarding/add-graph "Adicionar gráfico"
            :on-boarding/open-local-dir "Abrir pasta local"
            :on-boarding/open-local-dir "Abrir pasta local"

+ 12 - 8
src/main/frontend/extensions/pdf/assets.cljs

@@ -7,6 +7,7 @@
             [frontend.fs :as fs]
             [frontend.fs :as fs]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.page :as page-handler]
             [frontend.handler.page :as page-handler]
+            [frontend.util.page-property :as page-property]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util :as util]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.config :as gp-config]
@@ -154,14 +155,14 @@
         page-name (str "hls__" page-name)
         page-name (str "hls__" page-name)
         page (db-model/get-page page-name)
         page (db-model/get-page page-name)
         url (:url pdf-current)
         url (:url pdf-current)
-        format (state/get-preferred-format)]
+        format (state/get-preferred-format)
+        repo-dir (config/get-repo-dir (state/get-current-repo))
+        asset-dir (util/node-path.join repo-dir gp-config/local-assets-dir)
+        url (if (string/includes? url asset-dir)
+              (str ".." (last (string/split url repo-dir)))
+              url)]
     (if-not page
     (if-not page
-      (let [repo-dir (config/get-repo-dir (state/get-current-repo))
-            asset-dir (util/node-path.join repo-dir gp-config/local-assets-dir)
-            url (if (string/includes? url asset-dir)
-                  (str ".." (last (string/split url repo-dir)))
-                  url)
-            label (:filename pdf-current)]
+      (let [label (:filename pdf-current)]
         (page-handler/create! page-name {:redirect?        false :create-first-block? false
         (page-handler/create! page-name {:redirect?        false :create-first-block? false
                                          :split-namespace? false
                                          :split-namespace? false
                                          :format           format
                                          :format           format
@@ -175,7 +176,10 @@
                                                                          url)
                                                                          url)
                                                             :file-path url}})
                                                             :file-path url}})
         (db-model/get-page page-name))
         (db-model/get-page page-name))
-      page)))
+
+      ;; try to update file path
+      (page-property/add-property! page-name :file-path url))
+    page))
 
 
 (defn create-ref-block!
 (defn create-ref-block!
   [{:keys [id content page]}]
   [{:keys [id content page]}]

+ 15 - 7
src/main/frontend/format/block.cljs

@@ -6,19 +6,27 @@
             [frontend.db :as db]
             [frontend.db :as db]
             [frontend.format :as format]
             [frontend.format :as format]
             [frontend.state :as state]
             [frontend.state :as state]
+            [frontend.handler.notification :as notification]
+            ["@sentry/react" :as Sentry]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.config :as gp-config]
             [logseq.graph-parser.property :as gp-property]
             [logseq.graph-parser.property :as gp-property]
             [logseq.graph-parser.mldoc :as gp-mldoc]))
             [logseq.graph-parser.mldoc :as gp-mldoc]))
 
 
 (defn extract-blocks
 (defn extract-blocks
-  "Wrapper around logseq.graph-parser.block/extract-blocks that adds in system state"
+  "Wrapper around logseq.graph-parser.block/extract-blocks that adds in system state
+and handles unexpected failure."
   [blocks content with-id? format]
   [blocks content with-id? format]
-  (gp-block/extract-blocks blocks content with-id? format
-                           {:user-config (state/get-config)
-                            :block-pattern (config/get-block-pattern format)
-                            :supported-formats (gp-config/supported-formats)
-                            :db (db/get-db (state/get-current-repo))
-                            :date-formatter (state/get-date-formatter)}))
+  (try
+    (gp-block/extract-blocks blocks content with-id? format
+                             {:user-config (state/get-config)
+                              :block-pattern (config/get-block-pattern format)
+                              :supported-formats (gp-config/supported-formats)
+                              :db (db/get-db (state/get-current-repo))
+                              :date-formatter (state/get-date-formatter)})
+    (catch :default e
+      (Sentry/captureException e)
+      (notification/show! "An unexpected error occurred during block extraction." :error)
+      [])))
 
 
 (defn page-name->map
 (defn page-name->map
   "Wrapper around logseq.graph-parser.block/page-name->map that adds in db"
   "Wrapper around logseq.graph-parser.block/page-name->map that adds in db"

+ 2 - 0
src/main/frontend/fs/node.cljs

@@ -69,6 +69,8 @@
         (->
         (->
          (p/let [result (ipc/ipc "writeFile" repo path content)
          (p/let [result (ipc/ipc "writeFile" repo path content)
                  mtime (gobj/get result "mtime")]
                  mtime (gobj/get result "mtime")]
+           (when-not contents-matched?
+             (ipc/ipc "backupDbFile" (config/get-local-dir repo) path disk-content content))
            (db/set-file-last-modified-at! repo path mtime)
            (db/set-file-last-modified-at! repo path mtime)
            (p/let [content (if (encrypt/encrypted-db? (state/get-current-repo))
            (p/let [content (if (encrypt/encrypted-db? (state/get-current-repo))
                              (encrypt/decrypt content)
                              (encrypt/decrypt content)

+ 30 - 5
src/main/frontend/fs/watcher_handler.cljs

@@ -8,13 +8,15 @@
             [frontend.handler.page :as page-handler]
             [frontend.handler.page :as page-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.ui :as ui-handler]
+            [frontend.handler.notification :as notification]
             [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.util :as gp-util]
             [frontend.util.text :as text-util]
             [frontend.util.text :as text-util]
             [lambdaisland.glogi :as log]
             [lambdaisland.glogi :as log]
             [electron.ipc :as ipc]
             [electron.ipc :as ipc]
             [promesa.core :as p]
             [promesa.core :as p]
             [frontend.state :as state]
             [frontend.state :as state]
-            [frontend.encrypt :as encrypt]))
+            [frontend.encrypt :as encrypt]
+            [frontend.fs :as fs]))
 
 
 ;; all IPC paths must be normalized! (via gp-util/path-normalize)
 ;; all IPC paths must be normalized! (via gp-util/path-normalize)
 
 
@@ -48,10 +50,31 @@
           pages-metadata-path (config/get-pages-metadata-path)
           pages-metadata-path (config/get-pages-metadata-path)
           {:keys [mtime]} stat
           {:keys [mtime]} stat
           db-content (or (db/get-file repo path) "")]
           db-content (or (db/get-file repo path) "")]
-      (when (and (or content (= type "unlink"))
+      (when (and (or content (contains? #{"unlink" "unlinkDir" "addDir"} type))
                  (not (encrypt/content-encrypted? content))
                  (not (encrypt/content-encrypted? content))
                  (not (:encryption/graph-parsing? @state/state)))
                  (not (:encryption/graph-parsing? @state/state)))
         (cond
         (cond
+          (and (= "unlinkDir" type) dir)
+          (do
+            (state/pub-event! [:notification/show
+                               {:content (str "The directory " dir " has been renamed or deleted, the editor will be disabled for this graph, you can unlink the graph.")
+                                :status :error
+                                :clear? false}])
+            (state/update-state! :file/unlinked-dirs (fn [dirs] (conj dirs dir))))
+
+          (= "addDir" type)
+          (when (contains? (:file/unlinked-dirs @state/state) dir)
+            (notification/clear-all!)
+            (state/pub-event! [:notification/show
+                               {:content (str "The directory " dir " has been back, you can edit your graph now.")
+                                :status :success
+                                :clear? true}])
+            (fs/watch-dir! dir)
+            (state/update-state! :file/unlinked-dirs (fn [dirs] (disj dirs dir))))
+
+          (contains? (:file/unlinked-dirs @state/state) dir)
+          nil
+
           (and (= "add" type)
           (and (= "add" type)
                (not= (string/trim content) (string/trim db-content))
                (not= (string/trim content) (string/trim db-content))
                (not= path pages-metadata-path))
                (not= path pages-metadata-path))
@@ -76,9 +99,11 @@
 
 
           (and (= "unlink" type)
           (and (= "unlink" type)
                (db/file-exists? repo path))
                (db/file-exists? repo path))
-          (when-let [page-name (db/get-file-page path)]
-            (println "Delete page: " page-name ", file path: " path ".")
-            (page-handler/delete! page-name #() :delete-file? false))
+          (p/let [dir-exists? (fs/file-exists? dir "")]
+            (when dir-exists?
+              (when-let [page-name (db/get-file-page path)]
+                (println "Delete page: " page-name ", file path: " path ".")
+                (page-handler/delete! page-name #() :unlink-file? true))))
 
 
           (and (contains? #{"add" "change" "unlink"} type)
           (and (contains? #{"add" "change" "unlink"} type)
                (string/ends-with? path "logseq/custom.css"))
                (string/ends-with? path "logseq/custom.css"))

+ 3 - 1
src/main/frontend/handler.cljs

@@ -53,7 +53,9 @@
   (let [f (fn []
   (let [f (fn []
             #_:clj-kondo/ignore
             #_:clj-kondo/ignore
             (let [repo (state/get-current-repo)]
             (let [repo (state/get-current-repo)]
-              (when-not (state/nfs-refreshing?)
+              (when (and (not (state/nfs-refreshing?))
+                         (not (contains? (:file/unlinked-dirs @state/state)
+                                         (config/get-repo-dir repo))))
                 ;; Don't create the journal file until user writes something
                 ;; Don't create the journal file until user writes something
                 (page-handler/create-today-journal!))))]
                 (page-handler/create-today-journal!))))]
     (f)
     (f)

+ 92 - 80
src/main/frontend/handler/block.cljs

@@ -1,6 +1,7 @@
 (ns frontend.handler.block
 (ns frontend.handler.block
   (:require
   (:require
    [clojure.set :as set]
    [clojure.set :as set]
+   [clojure.string :as string]
    [clojure.walk :as walk]
    [clojure.walk :as walk]
    [frontend.db :as db]
    [frontend.db :as db]
    [frontend.db.model :as db-model]
    [frontend.db.model :as db-model]
@@ -147,88 +148,99 @@
       (state/exit-editing-and-set-selected-blocks! blocks))))
       (state/exit-editing-and-set-selected-blocks! blocks))))
 
 
 (def *swipe (atom nil))
 (def *swipe (atom nil))
+(def *touch-start (atom nil))
 
 
 (defn on-touch-start
 (defn on-touch-start
-  [event]
-  (when-let [touches (.-targetTouches event)]
-    (when (= (.-length touches) 1)
-      (let [touch (aget touches 0)
-            x (.-clientX touch)
-            y (.-clientY touch)]
-        (reset! *swipe {:x0 x :y0 y :xi x :yi y :tx x :ty y :direction nil})))))
+  [event uuid]
+  (let [input (state/get-input)
+        input-id (state/get-edit-input-id)
+        selection-type (.-type (.getSelection js/document))]
+    (reset! *touch-start (js/Date.now))
+    (when-not (and input
+                   (string/ends-with? input-id (str uuid)))
+      (state/clear-edit!))
+    (when (not= selection-type "Range")
+      (when-let [touches (.-targetTouches event)]
+        (when (= (.-length touches) 1)
+          (let [touch (aget touches 0)
+                x (.-clientX touch)
+                y (.-clientY touch)]
+            (reset! *swipe {:x0 x :y0 y :xi x :yi y :tx x :ty y :direction nil})))))))
 
 
 (defn on-touch-move
 (defn on-touch-move
-  [event block uuid *show-left-menu? *show-right-menu?]
+  [event block uuid edit? *show-left-menu? *show-right-menu?]
   (when-let [touches (.-targetTouches event)]
   (when-let [touches (.-targetTouches event)]
-    (when (and (= (.-length touches) 1) @*swipe)
-      (let [{:keys [x0 xi direction]} @*swipe
-            touch (aget touches 0)
-            tx (.-clientX touch)
-            ty (.-clientY touch)
-            direction (if (nil? direction)
-                        (if (> tx x0)
-                          :right
-                          :left)
-                        direction)]
-        (swap! *swipe #(-> %
-                           (assoc :tx tx)
-                           (assoc :ty ty)
-                           (assoc :xi tx)
-                           (assoc :yi ty)
-                           (assoc :direction direction)))
-        (when (< (* (- xi x0) (- tx xi)) 0)
-          (swap! *swipe #(-> %
-                             (assoc :x0 tx)
-                             (assoc :y0 ty))))
-        (let [{:keys [x0 y0]} @*swipe
-              dx (- tx x0)
-              dy (- ty y0)]
-          (when (and (< (. js/Math abs dy) 20)
-                     (> (. js/Math abs dx) 10))
-            (let [left (gdom/getElement (str "block-left-menu-" uuid))
-                  right (gdom/getElement (str "block-right-menu-" uuid))]
+    (let [selection-type (.-type (.getSelection js/document))]
+      (when-not (= selection-type "Range")
+        (when (< (- (js/Date.now) @*touch-start) 600)
+          (when (and (= (.-length touches) 1) @*swipe)
+            (let [{:keys [x0 xi direction]} @*swipe
+                  touch (aget touches 0)
+                  tx (.-clientX touch)
+                  ty (.-clientY touch)
+                  direction (if (nil? direction)
+                              (if (> tx x0)
+                                :right
+                                :left)
+                              direction)]
+              (swap! *swipe #(-> %
+                                 (assoc :tx tx)
+                                 (assoc :ty ty)
+                                 (assoc :xi tx)
+                                 (assoc :yi ty)
+                                 (assoc :direction direction)))
+              (when (< (* (- xi x0) (- tx xi)) 0)
+                (swap! *swipe #(-> %
+                                   (assoc :x0 tx)
+                                   (assoc :y0 ty))))
+              (let [{:keys [x0 y0]} @*swipe
+                    dx (- tx x0)
+                    dy (- ty y0)]
+                (when (and (< (. js/Math abs dy) 20)
+                           (> (. js/Math abs dx) 10))
+                  (let [left (gdom/getElement (str "block-left-menu-" uuid))
+                        right (gdom/getElement (str "block-right-menu-" uuid))]
 
 
-              (cond
-                (= direction :right)
-                (do
-                  (reset! *show-left-menu? true)
-                  (when left
-                    (when (>= dx 0)
-                      (set! (.. left -style -width) (str dx "px")))
-                    (when (< dx 0)
-                      (set! (.. left -style -width) (str (max (+ 50 dx) 0) "px")))
+                    (cond
+                      (= direction :right)
+                      (do
+                        (reset! *show-left-menu? true)
+                        (when left
+                          (when (>= dx 0)
+                            (set! (.. left -style -width) (str dx "px")))
+                          (when (< dx 0)
+                            (set! (.. left -style -width) (str (max (+ 40 dx) 0) "px")))
 
 
-                    (let [indent (gdom/getFirstElementChild left)]
-                      (when (indentable? block)
-                        (if (>= (.-clientWidth left) 50)
-                          (set! (.. indent -style -opacity) "100%")
-                          (set! (.. indent -style -opacity) "30%"))))))
+                          (let [indent (gdom/getFirstElementChild left)]
+                            (when (indentable? block)
+                              (if (>= (.-clientWidth left) 40)
+                                (set! (.. indent -style -opacity) "100%")
+                                (set! (.. indent -style -opacity) "30%"))))))
 
 
-                (= direction :left)
-                (do
-                  (reset! *show-right-menu? true)
-                  (when right
-                    (when (<= dx 0)
-                      (set! (.. right -style -width) (str (- dx) "px")))
-                    (when (> dx 0)
-                      (set! (.. right -style -width) (str (max (- 80 dx) 0) "px")))
+                      (= direction :left)
+                      (do
+                        (reset! *show-right-menu? true)
+                        (when right
+                          (when (<= dx 0)
+                            (set! (.. right -style -width) (str (- dx) "px")))
+                          (when (> dx 0)
+                            (set! (.. right -style -width) (str (max (- 80 dx) 0) "px")))
 
 
-                    (let [outdent (gdom/getFirstElementChild right)
-                          more (gdom/getLastElementChild right)]
-                      (if (and (>= (.-clientWidth right) 40)
-                               (< (.-clientWidth right) 80))
-                        (set! (.. outdent -style -opacity) "100%")
-                        (set! (.. outdent -style -opacity) "30%"))
+                          (let [outdent (gdom/getFirstElementChild right)
+                                more (when-not edit?
+                                       (gdom/getLastElementChild right))]
+                            (when (and outdent (outdentable? block))
+                              (if (and (>= (.-clientWidth right) 40)
+                                       (< (.-clientWidth right) 80))
+                                (set! (.. outdent -style -opacity) "100%")
+                                (set! (.. outdent -style -opacity) "30%")))
 
 
-                      (when (outdentable? block)
-                        (if (>= (.-clientWidth right) 80)
-                          (set! (.. more -style -opacity) "100%")
-                          (set! (.. more -style -opacity) "30%") 
-                        ;; (set! (.. outdent -style -opacity) "100%")
-                          ;; (set! (.. outdent -style -opacity) "30%")
-                        )))))
-                :else
-                nil))))))))
+                            (when more
+                              (if (>= (.-clientWidth right) 80)
+                                (set! (.. more -style -opacity) "100%")
+                                (set! (.. more -style -opacity) "30%"))))))
+                      :else
+                      nil)))))))))))
 
 
 (defn on-touch-end
 (defn on-touch-end
   [_event block uuid *show-left-menu? *show-right-menu?]
   [_event block uuid *show-left-menu? *show-right-menu?]
@@ -240,25 +252,25 @@
       (try
       (try
         (when (> (. js/Math abs dx) 10)
         (when (> (. js/Math abs dx) 10)
           (cond
           (cond
-            (and left-menu (>= (.-clientWidth left-menu) 50))
+            (and left-menu (>= (.-clientWidth left-menu) 40))
             (when (indentable? block)
             (when (indentable? block)
               (haptics/with-haptics-impact
               (haptics/with-haptics-impact
                 (indent-outdent-block! block :right)
                 (indent-outdent-block! block :right)
                 :light))
                 :light))
 
 
-            (and right-menu (< 40 (.-clientWidth right-menu) 80))
+            (and right-menu (<= 40 (.-clientWidth right-menu) 79))
+            (when (outdentable? block)
+              (haptics/with-haptics-impact
+                (indent-outdent-block! block :left)
+                :light))
+
+            (and right-menu (>= (.-clientWidth right-menu) 80))
             (haptics/with-haptics-impact
             (haptics/with-haptics-impact
               (do (state/set-state! :mobile/show-action-bar? true)
               (do (state/set-state! :mobile/show-action-bar? true)
                   (state/set-state! :mobile/actioned-block block)
                   (state/set-state! :mobile/actioned-block block)
                   (select-block! uuid))
                   (select-block! uuid))
               :light)
               :light)
 
 
-            (and right-menu (>= (.-clientWidth right-menu) 80))
-            (when (outdentable? block)
-              (haptics/with-haptics-impact
-                (indent-outdent-block! block :left)
-                :light))
-
             :else
             :else
             nil))
             nil))
         (catch js/Error e
         (catch js/Error e

+ 76 - 45
src/main/frontend/handler/editor.cljs

@@ -970,11 +970,12 @@
   [repo block-ids]
   [repo block-ids]
   (let [blocks (db-utils/pull-many repo '[*] (mapv (fn [id] [:block/uuid id]) block-ids))
   (let [blocks (db-utils/pull-many repo '[*] (mapv (fn [id] [:block/uuid id]) block-ids))
         top-level-block-uuids (->> (outliner-core/get-top-level-blocks blocks)
         top-level-block-uuids (->> (outliner-core/get-top-level-blocks blocks)
-                                   (map :block/uuid))]
-    (export/export-blocks-as-markdown
-     repo top-level-block-uuids
-     (state/get-export-block-text-indent-style)
-     (into [] (state/get-export-block-text-remove-options)))))
+                                   (map :block/uuid))
+        content (export/export-blocks-as-markdown
+                 repo top-level-block-uuids
+                 (state/get-export-block-text-indent-style)
+                 (into [] (state/get-export-block-text-remove-options)))]
+    [top-level-block-uuids content]))
 
 
 (defn copy-selection-blocks
 (defn copy-selection-blocks
   [html?]
   [html?]
@@ -982,10 +983,10 @@
     (let [repo (state/get-current-repo)
     (let [repo (state/get-current-repo)
           ids (distinct (keep #(when-let [id (dom/attr % "blockid")]
           ids (distinct (keep #(when-let [id (dom/attr % "blockid")]
                                  (uuid id)) blocks))
                                  (uuid id)) blocks))
-          content (compose-copied-blocks-contents repo ids)
+          [top-level-block-uuids content] (compose-copied-blocks-contents repo ids)
           block (db/entity [:block/uuid (first ids)])]
           block (db/entity [:block/uuid (first ids)])]
       (when block
       (when block
-        (let [html (export/export-blocks-as-html repo ids)]
+        (let [html (export/export-blocks-as-html repo top-level-block-uuids)]
           (common-handler/copy-to-clipboard-without-id-property! (:block/format block) content (when html? html)))
           (common-handler/copy-to-clipboard-without-id-property! (:block/format block) content (when html? html)))
         (state/set-copied-blocks content ids)
         (state/set-copied-blocks content ids)
         (notification/show! "Copied!" :success)))))
         (notification/show! "Copied!" :success)))))
@@ -1059,15 +1060,19 @@
   (when-let [blocks (seq (get-selected-blocks))]
   (when-let [blocks (seq (get-selected-blocks))]
     ;; remove embeds, references and queries
     ;; remove embeds, references and queries
     (let [dom-blocks (remove (fn [block]
     (let [dom-blocks (remove (fn [block]
-                           (or (= "true" (dom/attr block "data-transclude"))
-                               (= "true" (dom/attr block "data-query")))) blocks)]
+                              (or (= "true" (dom/attr block "data-transclude"))
+                                  (= "true" (dom/attr block "data-query")))) blocks)]
       (when (seq dom-blocks)
       (when (seq dom-blocks)
         (let [repo (state/get-current-repo)
         (let [repo (state/get-current-repo)
               block-uuids (distinct (map #(uuid (dom/attr % "blockid")) dom-blocks))
               block-uuids (distinct (map #(uuid (dom/attr % "blockid")) dom-blocks))
               lookup-refs (map (fn [id] [:block/uuid id]) block-uuids)
               lookup-refs (map (fn [id] [:block/uuid id]) block-uuids)
-              blocks (db/pull-many repo '[*] lookup-refs)]
-          (state/set-copied-full-blocks nil blocks)
-          (delete-blocks! repo block-uuids blocks dom-blocks))))))
+              blocks (db/pull-many repo '[*] lookup-refs)
+              top-level-blocks (outliner-core/get-top-level-blocks blocks)
+              sorted-blocks (mapcat (fn [block]
+                                      (tree/get-sorted-block-and-children repo (:db/id block)))
+                                    top-level-blocks)]
+          (state/set-copied-full-blocks nil sorted-blocks)
+          (delete-blocks! repo (map :block/uuid sorted-blocks) sorted-blocks dom-blocks))))))
 
 
 (def url-regex
 (def url-regex
   "Didn't use link/plain-link as it is incorrectly detects words as urls."
   "Didn't use link/plain-link as it is incorrectly detects words as urls."
@@ -1194,9 +1199,10 @@
   (when-let [block (db/pull [:block/uuid block-id])]
   (when-let [block (db/pull [:block/uuid block-id])]
     (let [repo (state/get-current-repo)
     (let [repo (state/get-current-repo)
           ;; TODO: support org mode
           ;; TODO: support org mode
-          md-content (compose-copied-blocks-contents repo [block-id])
-          html (export/export-blocks-as-html repo [block-id])]
-      (state/set-copied-full-blocks md-content [block])
+          [_top-level-block-uuids md-content] (compose-copied-blocks-contents repo [block-id])
+          html (export/export-blocks-as-html repo [block-id])
+          sorted-blocks (tree/get-sorted-block-and-children repo (:db/id block))]
+      (state/set-copied-full-blocks md-content sorted-blocks)
       (common-handler/copy-to-clipboard-without-id-property! (:block/format block) md-content html)
       (common-handler/copy-to-clipboard-without-id-property! (:block/format block) md-content html)
       (delete-block-aux! block true))))
       (delete-block-aux! block true))))
 
 
@@ -1934,7 +1940,8 @@
               blocks' (map (fn [block]
               blocks' (map (fn [block]
                              (paste-block-cleanup block page exclude-properties format content-update-fn))
                              (paste-block-cleanup block page exclude-properties format content-update-fn))
                         blocks)
                         blocks)
-              result (outliner-core/insert-blocks! blocks' target-block {:sibling? sibling?})]
+              result (outliner-core/insert-blocks! blocks' target-block {:sibling? sibling?
+                                                                         :outliner-op :paste})]
           (edit-last-block-after-inserted! result))))))
           (edit-last-block-after-inserted! result))))))
 
 
 (defn- block-tree->blocks
 (defn- block-tree->blocks
@@ -2885,7 +2892,9 @@
     (util/format "{{twitter %s}}" url)
     (util/format "{{twitter %s}}" url)
 
 
     :else
     :else
-    (notification/show! (util/format "No macro is available for %s" url) :warning)))
+    (do
+      (notification/show! (util/format "No macro is available for %s" url) :warning)
+      nil)))
 
 
 (defn- paste-copied-blocks-or-text
 (defn- paste-copied-blocks-or-text
   [initial-text text e]
   [initial-text text e]
@@ -2893,7 +2902,6 @@
         copied-block-ids (:copy/block-ids copied-blocks)
         copied-block-ids (:copy/block-ids copied-blocks)
         copied-graph (:copy/graph copied-blocks)
         copied-graph (:copy/graph copied-blocks)
         input (state/get-input)]
         input (state/get-input)]
-    (util/stop e)
     (cond
     (cond
       ;; Internal blocks by either copy or cut blocks
       ;; Internal blocks by either copy or cut blocks
       (and
       (and
@@ -2904,20 +2912,26 @@
        ;; not copied from the external clipboard
        ;; not copied from the external clipboard
        (= (string/replace (string/trim initial-text) "\r" "")
        (= (string/replace (string/trim initial-text) "\r" "")
           (string/replace (string/trim (or (:copy/content copied-blocks) "")) "\r" "")))
           (string/replace (string/trim (or (:copy/content copied-blocks) "")) "\r" "")))
-      (let [blocks (or
-                    (:copy/full-blocks copied-blocks)
-                    (get-all-blocks-by-ids (state/get-current-repo) copied-block-ids))]
-        (when (seq blocks)
-          (state/set-copied-full-blocks! blocks)
-          (paste-blocks blocks {})))
+      (do
+        (util/stop e)
+        (let [blocks (or
+                      (:copy/full-blocks copied-blocks)
+                      (get-all-blocks-by-ids (state/get-current-repo) copied-block-ids))]
+          (when (seq blocks)
+            (state/set-copied-full-blocks! blocks)
+            (paste-blocks blocks {}))))
 
 
       (and (gp-util/url? text)
       (and (gp-util/url? text)
            (not (string/blank? (util/get-selected-text))))
            (not (string/blank? (util/get-selected-text))))
-      (html-link-format! text)
+      (do
+        (util/stop e)
+        (html-link-format! text))
 
 
       (and (text/block-ref? text)
       (and (text/block-ref? text)
            (wrapped-by? input "((" "))"))
            (wrapped-by? input "((" "))"))
-      (commands/simple-insert! (state/get-edit-input-id) (text/get-block-ref text) nil)
+      (do
+        (util/stop e)
+        (commands/simple-insert! (state/get-edit-input-id) (text/get-block-ref text) nil))
 
 
       :else
       :else
       ;; from external
       ;; from external
@@ -2927,31 +2941,39 @@
                 (nil? (util/safe-re-find #"(?m)^\s*\*+\s+" text))
                 (nil? (util/safe-re-find #"(?m)^\s*\*+\s+" text))
                 (nil? (util/safe-re-find #"(?:\r?\n){2,}" text))]
                 (nil? (util/safe-re-find #"(?:\r?\n){2,}" text))]
           [:markdown false _ _]
           [:markdown false _ _]
-          (paste-text-parseable format text)
+          (do
+            (util/stop e)
+            (paste-text-parseable format text))
 
 
           [:org _ false _]
           [:org _ false _]
-          (paste-text-parseable format text)
+          (do
+            (util/stop e)
+            (paste-text-parseable format text))
 
 
           [:markdown true _ false]
           [:markdown true _ false]
-          (paste-segmented-text format text)
+          (do
+            (util/stop e)
+            (paste-segmented-text format text))
 
 
           [:markdown true _ true]
           [:markdown true _ true]
-          (commands/simple-insert! (state/get-edit-input-id) text nil)
+          nil
 
 
           [:org _ true false]
           [:org _ true false]
-          (paste-segmented-text format text)
+          (do
+            (util/stop e)
+            (paste-segmented-text format text))
 
 
           [:org _ true true]
           [:org _ true true]
-          (commands/simple-insert! (state/get-edit-input-id) text nil))))))
+          nil)))))
 
 
 (defn paste-text-in-one-block-at-point
 (defn paste-text-in-one-block-at-point
   []
   []
   (utils/getClipText
   (utils/getClipText
    (fn [clipboard-data]
    (fn [clipboard-data]
      (when-let [_ (state/get-input)]
      (when-let [_ (state/get-input)]
-       (let [data (if (gp-util/url? clipboard-data)
-                        (wrap-macro-url clipboard-data)
-                        clipboard-data)]
+       (let [data (or (when (gp-util/url? clipboard-data)
+                        (wrap-macro-url clipboard-data))
+                      clipboard-data)]
          (insert data true))))
          (insert data true))))
    (fn [error]
    (fn [error]
      (js/console.error error))))
      (js/console.error error))))
@@ -2965,9 +2987,9 @@
           edit-block (state/get-edit-block)
           edit-block (state/get-edit-block)
           format (or (:block/format edit-block) :markdown)
           format (or (:block/format edit-block) :markdown)
           initial-text (.getData clipboard-data "text")
           initial-text (.getData clipboard-data "text")
-          text (if-not (string/blank? html)
-                 (html-parser/convert format html)
-                 initial-text)
+          text (or (when (string/blank? html)
+                     (html-parser/convert format html))
+                   initial-text)
           input (state/get-input)]
           input (state/get-input)]
       (if-not (string/blank? text)
       (if-not (string/blank? text)
         (if (or (thingatpt/markdown-src-at-point input)
         (if (or (thingatpt/markdown-src-at-point input)
@@ -3005,18 +3027,25 @@
 
 
 ;; credits to @pengx17
 ;; credits to @pengx17
 (defn- copy-current-block-ref
 (defn- copy-current-block-ref
-  []
+  [format]
   (when-let [current-block (state/get-edit-block)]
   (when-let [current-block (state/get-edit-block)]
     (when-let [block-id (:block/uuid current-block)]
     (when-let [block-id (:block/uuid current-block)]
-      (copy-block-ref! block-id #(str "((" % "))"))
+      (if (= format "embed")
+       (copy-block-ref! block-id #(str "{{embed ((" % "))}}"))
+       (copy-block-ref! block-id #(str "((" % "))")))
       (notification/show!
       (notification/show!
        [:div
        [:div
-        [:span.mb-1.5 "Block ref copied!"]
-        [:div [:code.whitespace-nowrap (str "((" block-id "))")]]]
+        [:span.mb-1.5 (str "Block " format " copied!")]
+        [:div [:code.whitespace.break-all (if (= format "embed")
+                                         (str "{{embed ((" block-id "))}}")
+                                         (str "((" block-id "))"))]]]
        :success true
        :success true
        ;; use uuid to make sure there is only one toast a time
        ;; use uuid to make sure there is only one toast a time
        (str "copied-block-ref:" block-id)))))
        (str "copied-block-ref:" block-id)))))
 
 
+(defn copy-current-block-embed []
+  (copy-current-block-ref "embed"))
+
 (defn shortcut-copy
 (defn shortcut-copy
   "shortcut copy action:
   "shortcut copy action:
   * when in selection mode, copy selected blocks
   * when in selection mode, copy selected blocks
@@ -3033,7 +3062,7 @@
             selected-start (util/get-selection-start input)
             selected-start (util/get-selection-start input)
             selected-end (util/get-selection-end input)]
             selected-end (util/get-selection-end input)]
         (if (= selected-start selected-end)
         (if (= selected-start selected-end)
-          (copy-current-block-ref)
+          (copy-current-block-ref "ref")
           (js/document.execCommand "copy")))
           (js/document.execCommand "copy")))
 
 
       :else
       :else
@@ -3110,9 +3139,9 @@
           ;; if the move is to cross block boundary, select the whole block
           ;; if the move is to cross block boundary, select the whole block
          (or (and (= direction :up) (cursor/textarea-cursor-rect-first-row? cursor-rect))
          (or (and (= direction :up) (cursor/textarea-cursor-rect-first-row? cursor-rect))
              (and (= direction :down) (cursor/textarea-cursor-rect-last-row? cursor-rect)))
              (and (= direction :down) (cursor/textarea-cursor-rect-last-row? cursor-rect)))
-          (select-block-up-down direction)
+         (select-block-up-down direction)
           ;; simulate text selection
           ;; simulate text selection
-          (cursor/select-up-down input direction anchor cursor-rect)))
+         (cursor/select-up-down input direction anchor cursor-rect)))
       (select-block-up-down direction))))
       (select-block-up-down direction))))
 
 
 (defn open-selected-block!
 (defn open-selected-block!
@@ -3185,6 +3214,8 @@
     (util/forward-kill-word input)
     (util/forward-kill-word input)
     (state/set-edit-content! (state/get-edit-input-id) (.-value input))))
     (state/set-edit-content! (state/get-edit-input-id) (.-value input))))
 
 
+
+  
 (defn block-with-title?
 (defn block-with-title?
   [format content semantic?]
   [format content semantic?]
   (and (string/includes? content "\n")
   (and (string/includes? content "\n")

+ 4 - 0
src/main/frontend/handler/notification.cljs

@@ -7,6 +7,10 @@
   (let [contents (state/get-notification-contents)]
   (let [contents (state/get-notification-contents)]
     (state/set-state! :notification/contents (dissoc contents uid))))
     (state/set-state! :notification/contents (dissoc contents uid))))
 
 
+(defn clear-all!
+  []
+  (state/set-state! :notification/contents nil))
+
 (defn show!
 (defn show!
   ([content status]
   ([content status]
    (show! content status true nil 1500))
    (show! content status true nil 1500))

+ 11 - 9
src/main/frontend/handler/page.cljs

@@ -160,19 +160,21 @@
      page-name)))
      page-name)))
 
 
 (defn delete-file!
 (defn delete-file!
-  [repo page-name]
+  [repo page-name unlink-file?]
   (let [file (db/get-page-file page-name)
   (let [file (db/get-page-file page-name)
         file-path (:file/path file)]
         file-path (:file/path file)]
     ;; delete file
     ;; delete file
     (when-not (string/blank? file-path)
     (when-not (string/blank? file-path)
       (db/transact! [[:db.fn/retractEntity [:file/path file-path]]])
       (db/transact! [[:db.fn/retractEntity [:file/path file-path]]])
-      (->
-       (p/let [_ (and (config/local-db? repo)
-                      (mobile-util/native-platform?)
-                      (fs/delete-file! repo file-path file-path {}))
-               _ (fs/unlink! repo (config/get-repo-path repo file-path) nil)])
-       (p/catch (fn [err]
-                  (js/console.error "error: " err)))))))
+      (when unlink-file?
+        (->
+         (p/let [_ (and (config/local-db? repo)
+                        (mobile-util/native-platform?)
+                        ;; TODO: @leizhe remove fs/delete-file! and use fs/unlink!
+                        (fs/delete-file! repo file-path file-path {}))
+                 _ (fs/unlink! repo (config/get-repo-path repo file-path) nil)])
+         (p/catch (fn [err]
+                    (js/console.error "error: " err))))))))
 
 
 (defn- compute-new-file-path
 (defn- compute-new-file-path
   [old-path new-name]
   [old-path new-name]
@@ -316,7 +318,7 @@
             page (db/entity [:block/name page-name])]
             page (db/entity [:block/name page-name])]
         (db/transact! tx-data)
         (db/transact! tx-data)
 
 
-        (when delete-file? (delete-file! repo page-name))
+        (delete-file! repo page-name delete-file?)
 
 
         ;; if other page alias this pagename,
         ;; if other page alias this pagename,
         ;; then just remove some attrs of this entity instead of retractEntity
         ;; then just remove some attrs of this entity instead of retractEntity

+ 2 - 1
src/main/frontend/mobile/footer.cljs

@@ -50,7 +50,8 @@
 
 
 (rum/defc footer < rum/reactive
 (rum/defc footer < rum/reactive
   []
   []
-  (when (and (state/sub :mobile/show-tabbar?)
+  (when (and (not (state/sub :editor/editing?))
+             (state/sub :mobile/show-tabbar?)
              (state/get-current-repo))
              (state/get-current-repo))
     [:div.cp__footer.w-full.bottom-0.justify-between
     [:div.cp__footer.w-full.bottom-0.justify-between
      (audio-record-cp)
      (audio-record-cp)

+ 16 - 26
src/main/frontend/mobile/mobile_bar.cljs

@@ -7,6 +7,7 @@
             [frontend.handler.history :as history]
             [frontend.handler.history :as history]
             [frontend.handler.page :as page-handler]
             [frontend.handler.page :as page-handler]
             [frontend.mobile.camera :as mobile-camera]
             [frontend.mobile.camera :as mobile-camera]
+            [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util :as util]
@@ -44,14 +45,6 @@
                       (state/set-state! :mobile/toolbar-update-observer (rand-int 1000000)))}
                       (state/set-state! :mobile/toolbar-update-observer (rand-int 1000000)))}
     (ui/icon icon {:style {:fontSize ui/icon-size}})]])
     (ui/icon icon {:style {:fontSize ui/icon-size}})]])
 
 
-(rum/defc indent-outdent [indent? icon]
-  [:div
-   [:button.bottom-action
-    {:on-mouse-down (fn [e]
-                      (util/stop e)
-                      (editor-handler/indent-outdent indent?))}
-    (ui/icon icon {:style {:fontSize ui/icon-size}})]])
-
 (rum/defc timestamp-submenu
 (rum/defc timestamp-submenu
   [parent-id]
   [parent-id]
   (let [callback (fn [event]
   (let [callback (fn [event]
@@ -115,27 +108,24 @@
 
 
 (rum/defc mobile-bar < rum/reactive
 (rum/defc mobile-bar < rum/reactive
   []
   []
-  (when (and (state/sub :mobile/toolbar-update-observer)
-             (state/sub :mobile/show-toolbar?))
+  (when (and (state/sub :editor/editing?)
+             (or (state/sub :mobile/show-toolbar?)
+                 (mobile-util/native-ipad?))
+             (state/sub :mobile/toolbar-update-observer))
     (when-let [config-toolbar-stats (:mobile/toolbar-stats (state/get-config))]
     (when-let [config-toolbar-stats (:mobile/toolbar-stats (state/get-config))]
-      (prn :config-toolbar-stats config-toolbar-stats)
       (reset! commands-stats config-toolbar-stats))
       (reset! commands-stats config-toolbar-stats))
     (let [parent-id (state/get-edit-input-id)
     (let [parent-id (state/get-edit-input-id)
           commands (commands parent-id)
           commands (commands parent-id)
           sorted-commands (sort-by (comp :counts second) > @commands-stats)]
           sorted-commands (sort-by (comp :counts second) > @commands-stats)]
-      (when (and (state/sub :mobile/show-toolbar?)
-                 (state/sub :editor/editing?))
-        [:div#mobile-editor-toolbar.bg-base-2
-         [:div.toolbar-commands
-          (indent-outdent false "indent-decrease")
-          (indent-outdent true "indent-increase")
-          (command (editor-handler/move-up-down true) "arrow-bar-to-up")
-          (command (editor-handler/move-up-down false) "arrow-bar-to-down")
-          (command #(if (state/sub :document/mode?)
-                      (editor-handler/insert-new-block! nil)
-                      (commands/simple-insert! parent-id "\n" {})) "arrow-back")
-          (for [command sorted-commands]
-            ((first command) commands))]
-         [:div.toolbar-hide-keyboard
-          (command #(state/clear-edit!) "keyboard-show")]]))))
+      [:div#mobile-editor-toolbar.bg-base-2
+       [:div.toolbar-commands
+        (command (editor-handler/move-up-down true) "arrow-bar-to-up")
+        (command (editor-handler/move-up-down false) "arrow-bar-to-down")
+        (command #(if (state/sub :document/mode?)
+                    (editor-handler/insert-new-block! nil)
+                    (commands/simple-insert! parent-id "\n" {})) "arrow-back")
+        (for [command sorted-commands]
+          ((first command) commands))]
+       [:div.toolbar-hide-keyboard
+        (command #(state/clear-edit!) "keyboard-show")]])))
 
 

+ 1 - 1
src/main/frontend/modules/instrumentation/sentry.cljs

@@ -45,4 +45,4 @@
 (defn init []
 (defn init []
   (when-not config/dev?
   (when-not config/dev?
     (let [config (clj->js config)]
     (let [config (clj->js config)]
-     (Sentry/init config))))
+      (Sentry/init config))))

+ 109 - 77
src/main/frontend/modules/outliner/core.cljs

@@ -370,6 +370,21 @@
 
 
 ;;; ### insert-blocks, delete-blocks, move-blocks
 ;;; ### insert-blocks, delete-blocks, move-blocks
 
 
+(defn- fix-top-level-blocks
+  "Blocks with :block/level"
+  [blocks]
+  (loop [blocks blocks
+         last-top-level-block nil
+         result []]
+    (if-let [block (first blocks)]
+      (if (= 1 (:block/level block))
+        (let [block' (assoc block
+                            :block/left {:db/id (:db/id last-top-level-block)}
+                            :block/parent (:block/parent last-top-level-block))]
+          (recur (rest blocks) block (conj result block')))
+        (recur (rest blocks) last-top-level-block (conj result block)))
+      result)))
+
 (defn- insert-blocks-aux
 (defn- insert-blocks-aux
   [blocks target-block {:keys [sibling? replace-empty-target? keep-uuid? move? outliner-op]}]
   [blocks target-block {:keys [sibling? replace-empty-target? keep-uuid? move? outliner-op]}]
   (let [block-uuids (map :block/uuid blocks)
   (let [block-uuids (map :block/uuid blocks)
@@ -454,6 +469,9 @@
                                      (> (count blocks) 1)
                                      (> (count blocks) 1)
                                      (not move?)))
                                      (not move?)))
         blocks' (blocks-with-level blocks)
         blocks' (blocks-with-level blocks)
+        blocks' (if (= outliner-op ::paste)
+                  (fix-top-level-blocks blocks')
+                  blocks')
         insert-opts {:sibling? sibling?
         insert-opts {:sibling? sibling?
                      :replace-empty-target? replace-empty-target?
                      :replace-empty-target? replace-empty-target?
                      :keep-uuid? keep-uuid?
                      :keep-uuid? keep-uuid?
@@ -479,7 +497,8 @@
             next (if sibling?
             next (if sibling?
                    (tree/-get-right target-node)
                    (tree/-get-right target-node)
                    (tree/-get-down target-node))
                    (tree/-get-down target-node))
-            next-tx (when (and next (not (contains? (set (map :db/id blocks)) (:db/id (:data next)))))
+            next-tx (when (and next
+                               (if move? (not (contains? (set (map :db/id blocks)) (:db/id (:data next)))) true))
                       (when-let [left (last (filter (fn [b] (= 1 (:block/level b))) tx))]
                       (when-let [left (last (filter (fn [b] (= 1 (:block/level b))) tx))]
                         [{:block/uuid (tree/-get-id next)
                         [{:block/uuid (tree/-get-id next)
                           :block/left (:db/id left)}]))
                           :block/left (:db/id left)}]))
@@ -598,42 +617,53 @@
           (swap! txs-state concat fix-non-consecutive-tx))))
           (swap! txs-state concat fix-non-consecutive-tx))))
     {:tx-data @txs-state}))
     {:tx-data @txs-state}))
 
 
+(defn- move-to-original-position?
+  [blocks target-block sibling?]
+  (let [non-consecutive-blocks (db-model/get-non-consecutive-blocks blocks)]
+    (and (empty? non-consecutive-blocks)
+         (= (:db/id (:block/left (first blocks))) (:db/id target-block))
+         (not= (= (:db/id (:block/parent (first blocks)))
+                  (:db/id target-block))
+               sibling?))))
+
 (defn move-blocks
 (defn move-blocks
   "Move `blocks` to `target-block` as siblings or children."
   "Move `blocks` to `target-block` as siblings or children."
   [blocks target-block {:keys [sibling? outliner-op]}]
   [blocks target-block {:keys [sibling? outliner-op]}]
   [:pre [(seq blocks)
   [:pre [(seq blocks)
          (s/valid? ::block-map-or-entity target-block)]]
          (s/valid? ::block-map-or-entity target-block)]]
-  (when (not (contains? (set (map :db/id blocks)) (:db/id target-block)))
-    (let [parents (->> (db/get-block-parents (state/get-current-repo) (:block/uuid target-block))
-                       (map :db/id)
-                       (set))
-          move-parents-to-child? (some parents (map :db/id blocks))]
-      (when-not move-parents-to-child?
-        (let [blocks (get-top-level-blocks blocks)
-              first-block (first blocks)
-              {:keys [tx-data]} (insert-blocks blocks target-block {:sibling? sibling?
-                                                                    :outliner-op (or outliner-op :move-blocks)})]
-          (when (seq tx-data)
-            (let [first-block-page (:db/id (:block/page first-block))
-                  target-page (or (:db/id (:block/page target-block))
-                                  (:db/id target-block))
-                  not-same-page? (not= first-block-page target-page)
-                  move-blocks-next-tx [(build-move-blocks-next-tx blocks)]
-                  children-page-tx (when not-same-page?
-                                     (let [children-ids (mapcat #(db/get-block-children-ids (state/get-current-repo) (:block/uuid %)) blocks)]
-                                       (map (fn [uuid] {:block/uuid uuid
-                                                        :block/page target-page}) children-ids)))
-                  fix-non-consecutive-tx (->> (fix-non-consecutive-blocks blocks target-block sibling?)
-                                              (remove (fn [b]
-                                                        (contains? (set (map :db/id move-blocks-next-tx)) (:db/id b)))))
-                  full-tx (util/concat-without-nil tx-data move-blocks-next-tx children-page-tx fix-non-consecutive-tx)
-                  tx-meta (cond-> {:move-blocks (mapv :db/id blocks)
-                                   :target (:db/id target-block)}
-                            not-same-page?
-                            (assoc :from-page first-block-page
-                                   :target-page target-page))]
-              {:tx-data full-tx
-               :tx-meta tx-meta})))))))
+  (let [original-position? (move-to-original-position? blocks target-block sibling?)]
+    (when (and (not (contains? (set (map :db/id blocks)) (:db/id target-block)))
+               (not original-position?))
+      (let [parents (->> (db/get-block-parents (state/get-current-repo) (:block/uuid target-block))
+                         (map :db/id)
+                         (set))
+            move-parents-to-child? (some parents (map :db/id blocks))]
+        (when-not move-parents-to-child?
+          (let [blocks (get-top-level-blocks blocks)
+                first-block (first blocks)
+                {:keys [tx-data]} (insert-blocks blocks target-block {:sibling? sibling?
+                                                                      :outliner-op (or outliner-op :move-blocks)})]
+            (when (seq tx-data)
+              (let [first-block-page (:db/id (:block/page first-block))
+                    target-page (or (:db/id (:block/page target-block))
+                                    (:db/id target-block))
+                    not-same-page? (not= first-block-page target-page)
+                    move-blocks-next-tx [(build-move-blocks-next-tx blocks)]
+                    children-page-tx (when not-same-page?
+                                       (let [children-ids (mapcat #(db/get-block-children-ids (state/get-current-repo) (:block/uuid %)) blocks)]
+                                         (map (fn [uuid] {:block/uuid uuid
+                                                          :block/page target-page}) children-ids)))
+                    fix-non-consecutive-tx (->> (fix-non-consecutive-blocks blocks target-block sibling?)
+                                                (remove (fn [b]
+                                                          (contains? (set (map :db/id move-blocks-next-tx)) (:db/id b)))))
+                    full-tx (util/concat-without-nil tx-data move-blocks-next-tx children-page-tx fix-non-consecutive-tx)
+                    tx-meta (cond-> {:move-blocks (mapv :db/id blocks)
+                                     :target (:db/id target-block)}
+                              not-same-page?
+                              (assoc :from-page first-block-page
+                                     :target-page target-page))]
+                {:tx-data full-tx
+                 :tx-meta tx-meta}))))))))
 
 
 (defn move-blocks-up-down
 (defn move-blocks-up-down
   "Move blocks up/down."
   "Move blocks up/down."
@@ -681,51 +711,53 @@
   "Indent or outdent `blocks`."
   "Indent or outdent `blocks`."
   [blocks indent?]
   [blocks indent?]
   {:pre [(seq blocks) (boolean? indent?)]}
   {:pre [(seq blocks) (boolean? indent?)]}
-  (let [first-block (db/entity (:db/id (first blocks)))
-        left (db/entity (:db/id (:block/left first-block)))
-        parent (:block/parent first-block)
-        db (db/get-db)
-        top-level-blocks (get-top-level-blocks blocks)
-        concat-tx-fn (fn [& results]
-                       {:tx-data (->> (map :tx-data results)
-                                      (apply util/concat-without-nil))
-                        :tx-meta (:tx-meta (first results))})
-        opts {:outliner-op :indent-outdent-blocks}]
-    (if indent?
-      (when (and left (not (page-first-child? first-block)))
-        (let [last-direct-child-id (db-model/get-block-last-direct-child db (:db/id left) false)
-              blocks' (drop-while (fn [b]
-                                    (= (:db/id (:block/parent b))
-                                       (:db/id left)))
-                                  top-level-blocks)]
-          (when (seq blocks')
-            (if last-direct-child-id
-              (let [last-direct-child (db/entity last-direct-child-id)
-                    result (move-blocks blocks' last-direct-child (merge opts {:sibling? true}))
-                    ;; expand `left` if it's collapsed
-                    collapsed-tx (when (:block/collapsed? left)
-                                   {:tx-data [{:db/id (:db/id left)
-                                               :block/collapsed? false}]})]
-                (concat-tx-fn result collapsed-tx))
-              (move-blocks blocks' left (merge opts {:sibling? false}))))))
-      (when (and parent (not (page-block? (db/entity (:db/id parent)))))
-        (let [blocks' (take-while (fn [b]
-                                    (not= (:db/id (:block/parent b))
-                                          (:db/id (:block/parent parent))))
-                                  top-level-blocks)
-              result (move-blocks blocks' parent (merge opts {:sibling? true}))]
-          (if (state/logical-outdenting?)
-            result
-            ;; direct outdenting (default behavior)
-            (let [last-top-block (db/pull (:db/id (last blocks')))
-                  right-siblings (->> (get-right-siblings (block last-top-block))
-                                      (map :data))]
-              (if (seq right-siblings)
-                (let [result2 (if-let [last-direct-child-id (db-model/get-block-last-direct-child db (:db/id last-top-block) false)]
-                                (move-blocks right-siblings (db/entity last-direct-child-id) (merge opts {:sibling? true}))
-                                (move-blocks right-siblings last-top-block (merge opts {:sibling? false})))]
-                  (concat-tx-fn result result2))
-                result))))))))
+  (let [non-consecutive-blocks (db-model/get-non-consecutive-blocks blocks)]
+    (when (empty? non-consecutive-blocks)
+      (let [first-block (db/entity (:db/id (first blocks)))
+            left (db/entity (:db/id (:block/left first-block)))
+            parent (:block/parent first-block)
+            db (db/get-db)
+            top-level-blocks (get-top-level-blocks blocks)
+            concat-tx-fn (fn [& results]
+                           {:tx-data (->> (map :tx-data results)
+                                          (apply util/concat-without-nil))
+                            :tx-meta (:tx-meta (first results))})
+            opts {:outliner-op :indent-outdent-blocks}]
+        (if indent?
+          (when (and left (not (page-first-child? first-block)))
+            (let [last-direct-child-id (db-model/get-block-last-direct-child db (:db/id left) false)
+                  blocks' (drop-while (fn [b]
+                                        (= (:db/id (:block/parent b))
+                                           (:db/id left)))
+                                      top-level-blocks)]
+              (when (seq blocks')
+                (if last-direct-child-id
+                  (let [last-direct-child (db/entity last-direct-child-id)
+                        result (move-blocks blocks' last-direct-child (merge opts {:sibling? true}))
+                        ;; expand `left` if it's collapsed
+                        collapsed-tx (when (:block/collapsed? left)
+                                       {:tx-data [{:db/id (:db/id left)
+                                                   :block/collapsed? false}]})]
+                    (concat-tx-fn result collapsed-tx))
+                  (move-blocks blocks' left (merge opts {:sibling? false}))))))
+          (when (and parent (not (page-block? (db/entity (:db/id parent)))))
+            (let [blocks' (take-while (fn [b]
+                                        (not= (:db/id (:block/parent b))
+                                              (:db/id (:block/parent parent))))
+                                      top-level-blocks)
+                  result (move-blocks blocks' parent (merge opts {:sibling? true}))]
+              (if (state/logical-outdenting?)
+                result
+                ;; direct outdenting (default behavior)
+                (let [last-top-block (db/pull (:db/id (last blocks')))
+                      right-siblings (->> (get-right-siblings (block last-top-block))
+                                          (map :data))]
+                  (if (seq right-siblings)
+                    (let [result2 (if-let [last-direct-child-id (db-model/get-block-last-direct-child db (:db/id last-top-block) false)]
+                                    (move-blocks right-siblings (db/entity last-direct-child-id) (merge opts {:sibling? true}))
+                                    (move-blocks right-siblings last-top-block (merge opts {:sibling? false})))]
+                      (concat-tx-fn result result2))
+                    result))))))))))
 
 
 ;;; ### write-operations have side-effects (do transactions) ;;;;;;;;;;;;;;;;
 ;;; ### write-operations have side-effects (do transactions) ;;;;;;;;;;;;;;;;
 
 

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

@@ -54,7 +54,9 @@
                                       :block/title :block/body :block/level :block/container :db/other-tx)
                                       :block/title :block/body :block/level :block/container :db/other-tx)
                               m)) txs)]
                               m)) txs)]
        (when (and (seq txs)
        (when (and (seq txs)
-                  (not (:skip-transact? opts)))
+                  (not (:skip-transact? opts))
+                  (not (contains? (:file/unlinked-dirs @state/state)
+                                  (config/get-repo-dir (state/get-current-repo)))))
          ;; (frontend.util/pprint txs)
          ;; (frontend.util/pprint txs)
          (try
          (try
            (let [repo (get opts :repo (state/get-current-repo))
            (let [repo (get opts :repo (state/get-current-repo))

+ 9 - 10
src/main/frontend/modules/outliner/pipeline.cljs

@@ -9,13 +9,12 @@
 
 
 (defn invoke-hooks
 (defn invoke-hooks
   [tx-report]
   [tx-report]
-  (let [{:keys [pages blocks]} (ds-report/get-blocks-and-pages tx-report)]
-    (when-not (:from-disk? (:tx-meta tx-report))
-      (doseq [p (seq pages)] (updated-page-hook tx-report p)))
-    (when (and state/lsp-enabled? (seq blocks))
-      (state/pub-event! [:plugin/hook-db-tx
-                         {:blocks  blocks
-                          :tx-data (:tx-data tx-report)
-                          :tx-meta (:tx-meta tx-report)}]))
-    ;; TODO: Add blocks to hooks
-    #_(doseq [b (seq blocks)])))
+  (when (and (not (:from-disk? (:tx-meta tx-report)))
+             (not (:new-graph? (:tx-meta tx-report))))
+    (let [{:keys [pages blocks]} (ds-report/get-blocks-and-pages tx-report)]
+      (doseq [p (seq pages)] (updated-page-hook tx-report p))
+      (when (and state/lsp-enabled? (seq blocks))
+        (state/pub-event! [:plugin/hook-db-tx
+                           {:blocks  blocks
+                            :tx-data (:tx-data tx-report)
+                            :tx-meta (:tx-meta tx-report)}])))))

+ 7 - 0
src/main/frontend/modules/outliner/tree.cljs

@@ -79,3 +79,10 @@
   [blocks-exclude-root root]
   [blocks-exclude-root root]
   (let [parent-groups (atom (group-by :block/parent blocks-exclude-root))]
   (let [parent-groups (atom (group-by :block/parent blocks-exclude-root))]
     (flatten (concat (sort-blocks-aux [root] parent-groups) (vals @parent-groups)))))
     (flatten (concat (sort-blocks-aux [root] parent-groups) (vals @parent-groups)))))
+
+(defn get-sorted-block-and-children
+  [repo db-id]
+  (when-let [root-block (db/pull db-id)]
+    (let [blocks (db/get-block-and-children repo (:block/uuid root-block))
+          blocks-exclude-root (remove (fn [b] (= (:db/id b) db-id)) blocks)]
+      (sort-blocks blocks-exclude-root root-block))))

+ 4 - 0
src/main/frontend/modules/shortcut/config.cljs

@@ -145,6 +145,8 @@
 
 
    :editor/replace-block-reference-at-point {:binding "mod+shift+r"
    :editor/replace-block-reference-at-point {:binding "mod+shift+r"
                                              :fn      editor-handler/replace-block-reference-with-content-at-point}
                                              :fn      editor-handler/replace-block-reference-with-content-at-point}
+   :editor/copy-embed {:binding "mod+e"
+                       :fn      editor-handler/copy-current-block-embed}
 
 
    :editor/paste-text-in-one-block-at-point {:binding "mod+shift+v"
    :editor/paste-text-in-one-block-at-point {:binding "mod+shift+v"
                                              :fn      editor-handler/paste-text-in-one-block-at-point}
                                              :fn      editor-handler/paste-text-in-one-block-at-point}
@@ -447,6 +449,7 @@
                           :editor/forward-kill-word
                           :editor/forward-kill-word
                           :editor/backward-kill-word
                           :editor/backward-kill-word
                           :editor/replace-block-reference-at-point
                           :editor/replace-block-reference-at-point
+                          :editor/copy-embed
                           :editor/paste-text-in-one-block-at-point
                           :editor/paste-text-in-one-block-at-point
                           :editor/insert-youtube-timestamp])
                           :editor/insert-youtube-timestamp])
      (with-meta {:before m/enable-when-editing-mode!}))
      (with-meta {:before m/enable-when-editing-mode!}))
@@ -609,6 +612,7 @@
     :editor/forward-kill-word
     :editor/forward-kill-word
     :editor/backward-kill-word
     :editor/backward-kill-word
     :editor/replace-block-reference-at-point
     :editor/replace-block-reference-at-point
+    :editor/copy-embed
     :editor/paste-text-in-one-block-at-point
     :editor/paste-text-in-one-block-at-point
     :editor/select-up
     :editor/select-up
     :editor/select-down]
     :editor/select-down]

+ 56 - 3
src/main/frontend/modules/shortcut/dicts.cljc

@@ -38,6 +38,7 @@
    :editor/strike-through        "Strikethrough"
    :editor/strike-through        "Strikethrough"
    :editor/clear-block           "Delete entire block content"
    :editor/clear-block           "Delete entire block content"
    :editor/kill-line-before      "Delete line before cursor position"
    :editor/kill-line-before      "Delete line before cursor position"
+   :editor/copy-embed            "Copy a block embed pointing to the current block"
    :editor/kill-line-after       "Delete line after cursor position"
    :editor/kill-line-after       "Delete line after cursor position"
    :editor/beginning-of-block    "Move cursor to the beginning of a block"
    :editor/beginning-of-block    "Move cursor to the beginning of a block"
    :editor/end-of-block          "Move cursor to the end of a block"
    :editor/end-of-block          "Move cursor to the end of a block"
@@ -488,7 +489,59 @@
              :command.editor/backward-kill-word       "Slett ett ord bakover"
              :command.editor/backward-kill-word       "Slett ett ord bakover"
              :command.editor/open-edit                "Rediger valgt blokk"
              :command.editor/open-edit                "Rediger valgt blokk"
              :command.editor/delete-selection         "Slett valgte blokker"
              :command.editor/delete-selection         "Slett valgte blokker"
-             :command.editor/toggle-open-blocks       "Veksle åpne blokker (slå sammen eller utvid alle blokker)"}
+             :command.editor/toggle-open-blocks       "Veksle åpne blokker (slå sammen eller utvid alle blokker)"
+             :command.auto-complete/complete "Autofullfør: Bruk valgt punkt"
+             :command.auto-complete/next "Autofullfør: Velg neste punkt"
+             :command.auto-complete/open-link "Autofullfør: Åpne valgt punkt i nettleser"
+             :command.auto-complete/prev "Autofullfør: Velg forrige punkt"
+             :command.auto-complete/shift-complete "Autofullfør: Åpne valgt punkt i sidestolpen"
+             :command.cards/forgotten "Kort: Glemte"
+             :command.cards/next-card "Kort: Neste kort"
+             :command.cards/recall "Kort: bruk litt tid på å huske"
+             :command.cards/remembered "Kort: husket"
+             :command.cards/toggle-answers "Kort: vis/skjul svar/clozes"
+             :command.command/run "Kjør git kommando"
+             :command.command/toggle-favorite "Legg til eller fjern fra favoritter"
+             :command.command-palette/toggle "Veksle kommandolinje"
+             :command.date-picker/complete "Datovelger: Bruk valgt dag"
+             :command.date-picker/next-day "Datovelger: Velg neste dag"
+             :command.date-picker/next-week "Datovelger: Velg neste uke"
+             :command.date-picker/prev-day "Datovelger: Velg forrige dag"
+             :command.date-picker/prev-week "Datovelger: Velg forrige uke"
+             :command.editor/copy-current-file "Kopier nåværende fil"
+             :command.editor/escape-editing "Avslutt redigering"
+             :command.editor/insert-youtube-timestamp "Sett inn YouTube tidsstempel"
+             :command.editor/open-file-in-default-app "Åpne fil i forhåndsvalgt app"
+             :command.editor/open-file-in-directory "Åpne fil i overordnet katalog"
+             :command.editor/paste-text-in-one-block-at-point "Lim inn tekst i blokk ved markør"
+             :command.editor/replace-block-reference-at-point "Erstatt blokkreferanse med dets innhold ved markør"
+             :command.editor/select-down "Velg innhold under"
+             :command.editor/select-up "Velg innhold over"
+             :command.editor/strike-through "Gjennomstreking"
+             :command.go/all-pages "Gå til alle sider"
+             :command.go/backward "Bakover"
+             :command.go/flashcards "Veksle flashcards"
+             :command.go/forward "Fremover"
+             :command.go/graph-view "Gå til graf visning"
+             :command.go/home "Gå hjem"
+             :command.go/keyboard-shortcuts "Gå til tastatursnarveier"
+             :command.go/next-journal "Gå til neste dagbok"
+             :command.go/prev-journal "Gå til forrige dagbok"
+             :command.go/tomorrow "Gå til i morgen"
+             :command.graph/add "Legg til graf"
+             :command.graph/open "Velg graf for å åpne"
+             :command.graph/remove "Fjern en graf"
+             :command.graph/save "Lagre nåværende graf til disk"
+             :command.misc/copy "mod+c"
+             :command.pdf/close "Lukk nåværende pdf leser"
+             :command.pdf/next-page "Neste side i nåværende pdf dok"
+             :command.pdf/previous-page "Forrige side i nåværende pdf dok"
+             :command.sidebar/clear "Fjern alt i høyre sidestolpe"
+             :command.sidebar/open-today-page "Åpne dagens side i høyre sidestolpe"
+             :command.ui/goto-plugins "Gå til dashbord for utvidelser"
+             :command.ui/open-new-window "Åpne et nytt vindu"
+             :command.ui/select-theme-color "Velg tilgjengelige temafarger"
+             :command.ui/toggle-cards "Veksle kort"}
 
 
    :pt-PT   {:shortcut.category/formatting            "Formatação"
    :pt-PT   {:shortcut.category/formatting            "Formatação"
              :shortcut.category/basics                "Básico"
              :shortcut.category/basics                "Básico"
@@ -900,9 +953,9 @@
              :shortcut.category/block-command-editing "Modifica comandi blocco"
              :shortcut.category/block-command-editing "Modifica comandi blocco"
              :shortcut.category/block-selection       "Selezione blocco (premi Esc per uscire dalla selezione)"
              :shortcut.category/block-selection       "Selezione blocco (premi Esc per uscire dalla selezione)"
              :shortcut.category/toggle                "Attiva/disattiva"
              :shortcut.category/toggle                "Attiva/disattiva"
-             :shortcut.category/others                "Altri"
+             :shortcut.category/others                "Altri"}
 
 
-   }
+   
    :tr      {:shortcut.category/basics "Temel bilgiler"
    :tr      {:shortcut.category/basics "Temel bilgiler"
              :shortcut.category/formatting "Biçimlendirme"
              :shortcut.category/formatting "Biçimlendirme"
              :shortcut.category/navigating "Gezinme"
              :shortcut.category/navigating "Gezinme"

+ 1 - 0
src/main/frontend/publishing/html.cljs

@@ -87,4 +87,5 @@
            [:script {:src "static/js/main.js"}]
            [:script {:src "static/js/main.js"}]
            [:script {:src "static/js/highlight.min.js"}]
            [:script {:src "static/js/highlight.min.js"}]
            [:script {:src "static/js/interact.min.js"}]
            [:script {:src "static/js/interact.min.js"}]
+           [:script {:src "static/js/katex.min.js"}]
            [:script {:src "static/js/code-editor.js"}]]))))
            [:script {:src "static/js/code-editor.js"}]]))))

+ 2 - 1
src/main/frontend/state.cljs

@@ -27,6 +27,7 @@
      :system/events                         (async/chan 100)
      :system/events                         (async/chan 100)
      :db/batch-txs                          (async/chan 100)
      :db/batch-txs                          (async/chan 100)
      :file/writes                           (async/chan 100)
      :file/writes                           (async/chan 100)
+     :file/unlinked-dirs                    #{}
      :reactive/custom-queries               (async/chan 100)
      :reactive/custom-queries               (async/chan 100)
      :notification/show?                    false
      :notification/show?                    false
      :notification/content                  nil
      :notification/content                  nil
@@ -1442,7 +1443,7 @@
 (defn set-copied-full-blocks
 (defn set-copied-full-blocks
   [content blocks]
   [content blocks]
   (set-state! :copy/blocks {:copy/graph (get-current-repo)
   (set-state! :copy/blocks {:copy/graph (get-current-repo)
-                            :copy/content content
+                            :copy/content (or content (get-in @state [:copy/blocks :copy/content]))
                             :copy/full-blocks blocks}))
                             :copy/full-blocks blocks}))
 
 
 (defn set-copied-full-blocks!
 (defn set-copied-full-blocks!

+ 1 - 1
src/main/frontend/version.cljs

@@ -1,3 +1,3 @@
 (ns frontend.version)
 (ns frontend.version)
 
 
-(defonce version "0.7.1")
+(defonce version "0.7.2")

+ 104 - 33
src/test/frontend/modules/outliner/core_test.cljs

@@ -10,7 +10,8 @@
             [clojure.walk :as walk]
             [clojure.walk :as walk]
             [logseq.graph-parser.block :as gp-block]
             [logseq.graph-parser.block :as gp-block]
             [datascript.core :as d]
             [datascript.core :as d]
-            [frontend.test.helper :as helper]))
+            [frontend.test.helper :as helper]
+            [clojure.set :as set]))
 
 
 (def test-db helper/test-db)
 (def test-db helper/test-db)
 
 
@@ -89,6 +90,10 @@
   []
   []
   (count (d/datoms (db/get-db test-db) :avet :block/uuid)))
   (count (d/datoms (db/get-db test-db) :avet :block/uuid)))
 
 
+(defn get-blocks-ids
+  []
+  (set (map :v (d/datoms (db/get-db test-db) :avet :block/uuid))))
+
 (defn get-children
 (defn get-children
   [id]
   [id]
   (->> (get-block id true)
   (->> (get-block id true)
@@ -174,6 +179,42 @@
       (outliner-core/indent-outdent-blocks! [(get-block 6) (get-block 9)] true))
       (outliner-core/indent-outdent-blocks! [(get-block 6) (get-block 9)] true))
     (is (= [4 5 6 9] (get-children 3)))))
     (is (= [4 5 6 9] (get-children 3)))))
 
 
+(deftest test-indent-blocks-regression-5604
+  (testing "
+  [22 [[2 [[3
+           [[4]
+            [5]
+            [6 [[7 [[8]]]]]
+            [9 [[10]
+                [11]]]]]]]
+      [12 [[13]                         ; outdents 13, 14, 15
+           [14]
+           [15]]]
+      [16 [[17]]]]]
+  "
+    (transact-tree! tree)
+    (outliner-tx/transact!
+      {:graph test-db}
+      (outliner-core/indent-outdent-blocks! [(get-block 13) (get-block 14) (get-block 15)] false))
+    (is (= [2 12 13 14 15 16] (get-children 22))))
+  (testing "
+  [22 [[2 [[3
+           [[4]
+            [5]
+            [6 [[7 [[8]]]]]
+            [9 [[10]
+                [11]]]]]]]
+      [12 [[13]                         ; outdents 13, 14
+           [14]
+           [15]]]
+      [16 [[17]]]]]
+  "
+    (transact-tree! tree)
+    (outliner-tx/transact!
+      {:graph test-db}
+      (outliner-core/indent-outdent-blocks! [(get-block 13) (get-block 14)] false))
+    (is (= [2 12 13 14 16] (get-children 22)))))
+
 (deftest test-outdent-blocks
 (deftest test-outdent-blocks
   (testing "
   (testing "
   [1 [[2 [[3]
   [1 [[2 [[3]
@@ -321,6 +362,21 @@
              :block/parent #:db{:id 6},
              :block/parent #:db{:id 6},
              :block/uuid 9}]))))
              :block/uuid 9}]))))
 
 
+(deftest test-get-sorted-block-and-children
+  (testing "get-sorted-block-and-children"
+    (transact-tree! tree)
+    (is (=
+         '(2 3 4 5 6 7 8 9 10 11)
+         (map :block/uuid (tree/get-sorted-block-and-children test-db (:db/id (get-block 2))))))
+
+    (is (=
+         '(22 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17)
+         (map :block/uuid (tree/get-sorted-block-and-children test-db (:db/id (get-block 22))))))
+
+    (is (=
+         '(16 17)
+         (map :block/uuid (tree/get-sorted-block-and-children test-db (:db/id (get-block 16))))))))
+
 ;;; Fuzzy tests
 ;;; Fuzzy tests
 
 
 (def init-id (atom 100))
 (def init-id (atom 100))
@@ -390,22 +446,28 @@
               (recur (conj result next) next)))
               (recur (conj result next) next)))
           result)))))
           result)))))
 
 
-#_(deftest ^:long random-inserts
+(deftest ^:long random-inserts
   (testing "Random inserts"
   (testing "Random inserts"
     (transact-random-tree!)
     (transact-random-tree!)
-    (let [c1 (get-blocks-count)
-          *random-count (atom 0)]
+    (let [c1 (get-blocks-ids)
+          *random-blocks (atom c1)]
       (dotimes [_i 100]
       (dotimes [_i 100]
+        ;; (prn "random insert: " i)
         (let [blocks (gen-blocks)]
         (let [blocks (gen-blocks)]
-          (swap! *random-count + (count blocks))
-          (insert-blocks! blocks (get-random-block))))
-      (let [total (get-blocks-count)]
-        (is (= total (+ c1 @*random-count)))))))
+          (swap! *random-blocks (fn [old]
+                                  (set/union old (set (map :block/uuid blocks)))))
+          (insert-blocks! blocks (get-random-block)))
+        (let [total (get-blocks-count)]
+          ;; (when (not= total (count @*random-blocks))
+          ;;   (defonce wrong-db (db/get-db test-db))
+          ;;   (defonce random-blocks @*random-blocks))
+          (is (= total (count @*random-blocks))))))))
 
 
-#_(deftest ^:long random-deletes
+(deftest ^:long random-deletes
   (testing "Random deletes"
   (testing "Random deletes"
     (transact-random-tree!)
     (transact-random-tree!)
     (dotimes [_i 100]
     (dotimes [_i 100]
+      ;; (prn "Random deletes: " i)
       (insert-blocks! (gen-blocks) (get-random-block))
       (insert-blocks! (gen-blocks) (get-random-block))
       (let [blocks (get-random-successive-blocks)]
       (let [blocks (get-random-successive-blocks)]
         (when (seq blocks)
         (when (seq blocks)
@@ -415,11 +477,13 @@
 (deftest ^:long random-moves
 (deftest ^:long random-moves
   (testing "Random moves"
   (testing "Random moves"
     (transact-random-tree!)
     (transact-random-tree!)
-    (let [c1 (get-blocks-count)
-          *random-count (atom 0)]
+    (let [c1 (get-blocks-ids)
+          *random-blocks (atom c1)]
       (dotimes [_i 100]
       (dotimes [_i 100]
+        ;; (prn "Random move: " i)
         (let [blocks (gen-blocks)]
         (let [blocks (gen-blocks)]
-          (swap! *random-count + (count blocks))
+          (swap! *random-blocks (fn [old]
+                                  (set/union old (set (map :block/uuid blocks)))))
           (insert-blocks! blocks (get-random-block)))
           (insert-blocks! blocks (get-random-block)))
         (let [blocks (get-random-successive-blocks)]
         (let [blocks (get-random-successive-blocks)]
           (when (seq blocks)
           (when (seq blocks)
@@ -427,59 +491,63 @@
               (outliner-tx/transact! {:graph test-db}
               (outliner-tx/transact! {:graph test-db}
                 (outliner-core/move-blocks! blocks target (gen/generate gen/boolean)))
                 (outliner-core/move-blocks! blocks target (gen/generate gen/boolean)))
               (let [total (get-blocks-count)]
               (let [total (get-blocks-count)]
-                (is (= total (+ c1 @*random-count)))))))))))
+                (is (= total (count @*random-blocks)))))))))))
 
 
-;; TODO: Enable when not failing as intermittently
-#_(deftest ^:long random-move-up-down
+(deftest ^:long random-move-up-down
   (testing "Random move up down"
   (testing "Random move up down"
     (transact-random-tree!)
     (transact-random-tree!)
-    (let [c1 (get-blocks-count)
-          *random-count (atom 0)]
+    (let [c1 (get-blocks-ids)
+          *random-blocks (atom c1)]
       (dotimes [_i 100]
       (dotimes [_i 100]
+        ;; (prn "Random move up/down: " i)
         (let [blocks (gen-blocks)]
         (let [blocks (gen-blocks)]
-          (swap! *random-count + (count blocks))
+          (swap! *random-blocks (fn [old]
+                                  (set/union old (set (map :block/uuid blocks)))))
           (insert-blocks! blocks (get-random-block)))
           (insert-blocks! blocks (get-random-block)))
         (let [blocks (get-random-successive-blocks)]
         (let [blocks (get-random-successive-blocks)]
           (when (seq blocks)
           (when (seq blocks)
             (outliner-tx/transact! {:graph test-db}
             (outliner-tx/transact! {:graph test-db}
               (outliner-core/move-blocks-up-down! blocks (gen/generate gen/boolean)))
               (outliner-core/move-blocks-up-down! blocks (gen/generate gen/boolean)))
             (let [total (get-blocks-count)]
             (let [total (get-blocks-count)]
-              (is (= total (+ c1 @*random-count))))))))))
+              (is (= total (count @*random-blocks))))))))))
 
 
-;; TODO: Enable when not failing as intermittently
-#_(deftest ^:long random-indent-outdent
+(deftest ^:long random-indent-outdent
   (testing "Random indent and outdent"
   (testing "Random indent and outdent"
     (transact-random-tree!)
     (transact-random-tree!)
-    (let [c1 (get-blocks-count)
-          *random-count (atom 0)]
+    (let [c1 (get-blocks-ids)
+          *random-blocks (atom c1)]
       (dotimes [_i 100]
       (dotimes [_i 100]
+        ;; (prn "Random move indent/outdent: " i)
         (let [blocks (gen-blocks)]
         (let [blocks (gen-blocks)]
-          (swap! *random-count + (count blocks))
+          (swap! *random-blocks (fn [old]
+                                  (set/union old (set (map :block/uuid blocks)))))
           (insert-blocks! blocks (get-random-block)))
           (insert-blocks! blocks (get-random-block)))
         (let [blocks (get-random-successive-blocks)]
         (let [blocks (get-random-successive-blocks)]
           (when (seq blocks)
           (when (seq blocks)
             (outliner-tx/transact! {:graph test-db}
             (outliner-tx/transact! {:graph test-db}
               (outliner-core/indent-outdent-blocks! blocks (gen/generate gen/boolean)))
               (outliner-core/indent-outdent-blocks! blocks (gen/generate gen/boolean)))
             (let [total (get-blocks-count)]
             (let [total (get-blocks-count)]
-              (is (= total (+ c1 @*random-count))))))))))
+              (is (= total (count @*random-blocks))))))))))
 
 
 (deftest ^:long random-mixed-ops
 (deftest ^:long random-mixed-ops
   (testing "Random mixed operations"
   (testing "Random mixed operations"
     (transact-random-tree!)
     (transact-random-tree!)
-    (let [c1 (get-blocks-count)
-          *random-count (atom 0)
+    (let [c1 (get-blocks-ids)
+          *random-blocks (atom c1)
           ops [
           ops [
                ;; insert
                ;; insert
                (fn []
                (fn []
                  (let [blocks (gen-blocks)]
                  (let [blocks (gen-blocks)]
-                   (swap! *random-count + (count blocks))
+                   (swap! *random-blocks (fn [old]
+                                           (set/union old (set (map :block/uuid blocks)))))
                    (insert-blocks! blocks (get-random-block))))
                    (insert-blocks! blocks (get-random-block))))
 
 
                ;; delete
                ;; delete
                (fn []
                (fn []
                  (let [blocks (get-random-successive-blocks)]
                  (let [blocks (get-random-successive-blocks)]
                    (when (seq blocks)
                    (when (seq blocks)
-                     (swap! *random-count - (count blocks))
+                     (swap! *random-blocks (fn [old]
+                                             (set/difference old (set (map :block/uuid blocks)))))
                      (outliner-tx/transact! {:graph test-db}
                      (outliner-tx/transact! {:graph test-db}
                        (outliner-core/delete-blocks! blocks {})))))
                        (outliner-core/delete-blocks! blocks {})))))
 
 
@@ -503,15 +571,15 @@
                    (when (seq blocks)
                    (when (seq blocks)
                      (outliner-tx/transact! {:graph test-db}
                      (outliner-tx/transact! {:graph test-db}
                        (outliner-core/indent-outdent-blocks! blocks (gen/generate gen/boolean))))))]]
                        (outliner-core/indent-outdent-blocks! blocks (gen/generate gen/boolean))))))]]
-      (dotimes [_i 500]
+      (dotimes [_i 100]
         ((rand-nth ops)))
         ((rand-nth ops)))
       (let [total (get-blocks-count)
       (let [total (get-blocks-count)
             page-id 1]
             page-id 1]
 
 
         ;; Invariants:
         ;; Invariants:
 
 
-        ;; 1. created blocks length >= existing blocks + deleted top-level blocks
-        (is (<= total (+ c1 @*random-count)))
+        ;; 1. total blocks <= inserted blocks - deleted block
+        (is (<= total (count @*random-blocks)))
 
 
         ;; 2. verify page's length + page itself = total blocks
         ;; 2. verify page's length + page itself = total blocks
         (is (= (inc (db-model/get-page-blocks-count test-db page-id))
         (is (= (inc (db-model/get-page-blocks-count test-db page-id))
@@ -524,5 +592,8 @@
 
 
 (comment
 (comment
   (dotimes [i 5]
   (dotimes [i 5]
-    (cljs.test/run-tests))
+    (do
+      (frontend.test.fixtures/reset-datascript test-db)
+      (cljs.test/run-tests))
+    )
   )
   )