Browse Source

Merge branch 'feat/db' into feat/capacitor-new

charlie 6 months ago
parent
commit
664321469d
92 changed files with 871 additions and 724 deletions
  1. 1 0
      .clj-kondo/config.edn
  2. 2 2
      .github/workflows/build-android.yml
  3. 2 2
      .github/workflows/clj-e2e.yml
  4. 0 70
      .github/workflows/deploy-db-pages.yml
  5. 1 1
      clj-e2e/deps.edn
  6. 20 5
      clj-e2e/dev/user.clj
  7. 9 1
      clj-e2e/src/logseq/e2e/assert.clj
  8. 29 1
      clj-e2e/src/logseq/e2e/block.clj
  9. 30 0
      clj-e2e/src/logseq/e2e/locator.clj
  10. 11 25
      clj-e2e/src/logseq/e2e/util.clj
  11. 10 2
      clj-e2e/test/logseq/e2e/commands_test.clj
  12. 1 1
      clj-e2e/test/logseq/e2e/custom_report.clj
  13. 1 0
      clj-e2e/test/logseq/e2e/fixtures.clj
  14. 8 8
      clj-e2e/test/logseq/e2e/outliner_test.clj
  15. 45 0
      clj-e2e/test/logseq/e2e/plugins_test.clj
  16. 85 0
      clj-e2e/test/logseq/e2e/reference_test.clj
  17. 5 4
      deps/db/src/logseq/db/common/entity_plus.cljc
  18. 1 8
      deps/db/src/logseq/db/common/property_util.cljs
  19. 4 2
      deps/db/src/logseq/db/frontend/content.cljs
  20. 7 0
      deps/db/src/logseq/db/frontend/property.cljs
  21. 1 1
      deps/graph-parser/src/logseq/graph_parser.cljs
  22. 3 0
      deps/outliner/src/logseq/outliner/property.cljs
  23. 17 6
      deps/outliner/src/logseq/outliner/validate.cljs
  24. 1 1
      deps/shui/src/logseq/shui/table/core.cljc
  25. 3 0
      scripts/src/logseq/tasks/dev/db_and_file_graphs.clj
  26. 2 2
      src/main/electron/listener.cljs
  27. 2 1
      src/main/frontend/commands.cljs
  28. 94 78
      src/main/frontend/components/block.cljs
  29. 20 17
      src/main/frontend/components/container.cljs
  30. 8 0
      src/main/frontend/components/container.css
  31. 3 8
      src/main/frontend/components/content.cljs
  32. 2 1
      src/main/frontend/components/file.cljs
  33. 4 4
      src/main/frontend/components/file_based/hierarchy.cljs
  34. 2 2
      src/main/frontend/components/file_sync.cljs
  35. 2 4
      src/main/frontend/components/page.cljs
  36. 2 3
      src/main/frontend/components/property.cljs
  37. 1 1
      src/main/frontend/components/property/value.cljs
  38. 4 2
      src/main/frontend/components/query/builder.cljs
  39. 25 6
      src/main/frontend/components/views.cljs
  40. 0 1
      src/main/frontend/components/whiteboard.cljs
  41. 6 19
      src/main/frontend/db.cljs
  42. 2 1
      src/main/frontend/db/async.cljs
  43. 222 0
      src/main/frontend/db/file_based/model.cljs
  44. 14 285
      src/main/frontend/db/model.cljs
  45. 2 2
      src/main/frontend/db/query_custom.cljs
  46. 2 2
      src/main/frontend/db/query_dsl.cljs
  47. 0 16
      src/main/frontend/db/react.cljs
  48. 2 2
      src/main/frontend/extensions/pdf/assets.cljs
  49. 2 2
      src/main/frontend/extensions/zotero/handler.cljs
  50. 2 2
      src/main/frontend/fs/diff_merge.cljs
  51. 3 2
      src/main/frontend/fs/watcher_handler.cljs
  52. 5 7
      src/main/frontend/handler/dnd.cljs
  53. 24 20
      src/main/frontend/handler/editor.cljs
  54. 1 3
      src/main/frontend/handler/events/ui.cljs
  55. 2 2
      src/main/frontend/handler/file_based/editor.cljs
  56. 2 1
      src/main/frontend/handler/file_based/file.cljs
  57. 3 4
      src/main/frontend/handler/file_based/page.cljs
  58. 2 1
      src/main/frontend/handler/file_based/page_property.cljs
  59. 4 3
      src/main/frontend/handler/file_based/repo.cljs
  60. 2 1
      src/main/frontend/handler/file_based/reset_file.cljs
  61. 6 5
      src/main/frontend/handler/page.cljs
  62. 8 1
      src/main/frontend/handler/paste.cljs
  63. 2 1
      src/main/frontend/handler/repo.cljs
  64. 2 2
      src/main/frontend/mobile/action_bar.cljs
  65. 4 4
      src/main/frontend/mobile/intent.cljs
  66. 13 0
      src/main/frontend/ref.cljs
  67. 9 9
      src/main/frontend/template.cljs
  68. 1 1
      src/main/frontend/ui.cljs
  69. 13 5
      src/main/frontend/worker/db/migrate.cljs
  70. 2 2
      src/main/frontend/worker/handler/page/db_based/page.cljs
  71. 3 2
      src/main/logseq/api.cljs
  72. 0 1
      src/resources/dicts/ca.edn
  73. 0 1
      src/resources/dicts/cs.edn
  74. 0 1
      src/resources/dicts/de.edn
  75. 0 1
      src/resources/dicts/en.edn
  76. 0 1
      src/resources/dicts/es.edn
  77. 0 1
      src/resources/dicts/fr.edn
  78. 0 1
      src/resources/dicts/id.edn
  79. 0 1
      src/resources/dicts/it.edn
  80. 0 1
      src/resources/dicts/ja.edn
  81. 0 1
      src/resources/dicts/nb-no.edn
  82. 0 1
      src/resources/dicts/pl.edn
  83. 0 1
      src/resources/dicts/pt-br.edn
  84. 0 1
      src/resources/dicts/ru.edn
  85. 0 1
      src/resources/dicts/sk.edn
  86. 0 1
      src/resources/dicts/tr.edn
  87. 0 1
      src/resources/dicts/zh-cn.edn
  88. 0 1
      src/resources/dicts/zh-hant.edn
  89. 2 2
      src/test/frontend/components/file_based/query_table_test.cljs
  90. 33 0
      src/test/frontend/db/file_based/model_test.cljs
  91. 0 26
      src/test/frontend/db/model_test.cljs
  92. 3 2
      src/test/frontend/db/query_dsl_test.cljs

+ 1 - 0
.clj-kondo/config.edn

@@ -85,6 +85,7 @@
              frontend.config config
              frontend.date date
              frontend.db db
+             frontend.db.file-based.model file-model
              frontend.db-mixins db-mixins
              frontend.db.query-custom query-custom
              frontend.db.query-dsl query-dsl

+ 2 - 2
.github/workflows/build-android.yml

@@ -139,7 +139,7 @@ jobs:
         run: npx cap sync android
 
       - name: Setup Android SDK
-        uses: android-actions/setup-android@v2
+        uses: android-actions/setup-android@v3.2.2
 
       - name: Build Android
         run: |
@@ -152,7 +152,7 @@ jobs:
       - name: Sign Android APK
         run: |
           echo ${{ secrets.ANDROID_KEYSTORE }} | base64 -d > keystore.jks
-          /usr/local/lib/android/sdk/build-tools/33.0.0/apksigner sign \
+          $ANDROID_SDK_ROOT/build-tools/34.0.0/apksigner sign \
             --ks keystore.jks --ks-pass "pass:${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" \
             --in app/build/outputs/apk/release/app-release-unsigned.apk \
             --out app-signed.apk

+ 2 - 2
.github/workflows/clj-e2e.yml

@@ -2,7 +2,7 @@ name: Clojure E2E
 
 on:
   push:
-    branches: ["feat/db"]
+    branches: [master, "feat/db"]
     paths:
       - 'clj-e2e/**'
       - '.github/workflows/clj-e2e.yml'
@@ -10,7 +10,7 @@ on:
       - deps/**
       - packages/**
   pull_request:
-    branches: ["feat/db"]
+    branches: [master, "feat/db"]
     paths:
       - 'clj-e2e/**'
       - '.github/workflows/clj-e2e.yml'

+ 0 - 70
.github/workflows/deploy-db-pages.yml

@@ -1,70 +0,0 @@
-name: Deploy DB Version to Cloud
-
-on:
-  push:
-    branches: ["feat/db"]
-
-env:
-  CLOJURE_VERSION: "1.11.1.1413"
-  NODE_VERSION: '20'
-  JAVA_VERSION: "11"
-
-jobs:
-  build-and-deploy:
-    runs-on: ubuntu-latest
-
-    steps:
-      - uses: actions/checkout@v3
-
-      - name: Setup Java JDK
-        uses: actions/setup-java@v3
-        with:
-          distribution: "zulu"
-          java-version: ${{ env.JAVA_VERSION }}
-
-      - name: Set up Node
-        uses: actions/setup-node@v3
-        with:
-          node-version: ${{ env.NODE_VERSION }}
-
-      - name: Setup clojure
-        uses: DeLaGuardo/[email protected]
-        with:
-          cli: ${{ env.CLOJURE_VERSION }}
-
-      - name: Fetch yarn deps
-        run: yarn install --frozen-lockfile
-
-      - name: Set Build Environment Variables
-        run: |
-          echo "ENABLE_FILE_SYNC_PRODUCTION=false" >> $GITHUB_ENV
-
-      - name: Build Released-Web
-        run: |
-          yarn gulp:build && clojure -M:cljs release app  --config-merge '{:compiler-options {:source-map-include-sources-content true :source-map-detail-level :symbols}}'
-          rsync -avz --exclude node_modules --exclude android --exclude ios ./static/ ./public/static/
-          ls -lR ./public && mkdir r2 && mv ./public/static/js/main.js.map ./r2/db-demo.main.js.map
-          sed -i 's/=main.js.map/=https:\/\/assets.logseq.io\/db-demo.main.js.map/g' ./public/static/js/main.js
-        env:
-          LOGSEQ_SENTRY_DSN: ${{ secrets.LOGSEQ_SENTRY_DSN }}
-          LOGSEQ_POSTHOG_TOKEN: ${{ secrets.LOGSEQ_POSTHOG_TOKEN }}
-
-      - name: Upload to R2
-        uses: ryand56/r2-upload-action@latest
-        with:
-          r2-account-id: 2553ea8236c11ea0f88de28fce1cbfee
-          r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
-          r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
-          r2-bucket: ${{ secrets.R2_ASSETS_BUCKET }}
-          source-dir: r2
-          destination-dir: ./
-
-      - name: Publish to Cloudflare Pages
-        uses: cloudflare/pages-action@1
-        with:
-          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
-          accountId: 2553ea8236c11ea0f88de28fce1cbfee
-          projectName: "logseq-db-demo"
-          directory: "public"
-          gitHubToken: ${{ secrets.GITHUB_TOKEN }}
-          branch: "main"

+ 1 - 1
clj-e2e/deps.edn

@@ -2,7 +2,7 @@
  :deps {org.clojure/clojure {:mvn/version "1.12.0"}
         ;; io.github.pfeodrippe/wally {:local/root "../../../wally"}
         io.github.pfeodrippe/wally {:git/url "https://github.com/logseq/wally"
-                                    :sha "80ea665c75704a8ca80cb22caeaec3ae6f39ffdb"}
+                                    :sha "d84a2e068ec2e56fc7028fc61e694ccd1ddea03b"}
         ;; io.github.zmedelis/bosquet {:mvn/version "2025.03.28"}
         org.clj-commons/claypoole          {:mvn/version "1.2.2"}
         clj-time/clj-time                  {:mvn/version "0.15.2"}}

+ 20 - 5
clj-e2e/dev/user.clj

@@ -9,6 +9,8 @@
             [logseq.e2e.keyboard :as k]
             [logseq.e2e.multi-tabs-test]
             [logseq.e2e.outliner-test]
+            [logseq.e2e.plugins-test]
+            [logseq.e2e.reference-test]
             [logseq.e2e.rtc-basic-test]
             [logseq.e2e.util :as util]
             [wally.main :as w]
@@ -18,7 +20,7 @@
 (reset! config/*port 3001)
 ;; show ui
 (reset! config/*headless false)
-(reset! config/*slow-mo 100)
+(reset! config/*slow-mo 50)
 
 (def *futures (atom {}))
 
@@ -47,12 +49,24 @@
   (->> (future (run-tests 'logseq.e2e.multi-tabs-test))
        (swap! *futures assoc :multi-tabs-test)))
 
+(defn run-reference-test
+  []
+  (->> (future (run-tests 'logseq.e2e.reference-test))
+       (swap! *futures assoc :reference-test)))
+
+(defn run-plugins-test
+  []
+  (->> (future (run-tests 'logseq.e2e.plugins-test))
+       (swap! *futures assoc :plugins-test)))
+
 (defn run-all-test
   []
   (run-tests 'logseq.e2e.commands-test
              'logseq.e2e.multi-tabs-test
              'logseq.e2e.outliner-test
-             'logseq.e2e.rtc-basic-test))
+             'logseq.e2e.rtc-basic-test
+             'logseq.e2e.plugins-test
+             'logseq.e2e.reference-test))
 
 (defn start
   []
@@ -85,9 +99,10 @@
 
   (do
     (reset! config/*headless true)
-    (reset! config/*slow-mo 50)
-    (dotimes [i 5]
-      (run-multi-tabs-test)))
+    (reset! config/*slow-mo 10)
+    (run-tests 'logseq.e2e.reference-test)
+    (dotimes [i 10]
+      (run-tests 'logseq.e2e.reference-test)))
 
   ;;
   )

+ 9 - 1
clj-e2e/src/logseq/e2e/assert.clj

@@ -14,12 +14,16 @@
   (-> (w/-query q) assert-that .isHidden)
   true)
 
+(defn assert-non-editor-mode
+  []
+  (assert-is-hidden (w/get-by-test-id "block editor")))
+
 (defn assert-in-normal-mode?
   "- not editing mode
   - no action bar
   - no search(cmdk) modal"
   []
-  (assert-is-hidden (w/get-by-test-id "block editor"))
+  (assert-non-editor-mode)
   (assert-is-hidden ".selection-action-bar")
   (assert-is-visible "#search-button")
   true)
@@ -39,3 +43,7 @@
         editor (w/-query klass)]
     (w/wait-for editor)
     editor))
+
+(defn assert-selected-block-text
+  [text]
+  (assert-is-visible (format ".ls-block.selected :text('%s')" text)))

+ 29 - 1
clj-e2e/src/logseq/e2e/block.clj

@@ -1,7 +1,9 @@
 (ns logseq.e2e.block
   (:require [clojure.string :as string]
+            [clojure.test :refer [is]]
             [logseq.e2e.assert :as assert]
             [logseq.e2e.keyboard :as k]
+            [logseq.e2e.locator :as loc]
             [logseq.e2e.util :as util]
             [wally.main :as w]))
 
@@ -13,7 +15,8 @@
 
 (defn save-block
   [text]
-  (w/fill util/editor-q text))
+  (w/fill util/editor-q text)
+  (assert/assert-is-visible (loc/filter util/editor-q :has-text text)))
 
 (defn new-block
   [title]
@@ -55,3 +58,28 @@
 (defn jump-to-block
   [block-text]
   (w/click (w/find-one-by-text ".ls-block .block-content" block-text)))
+
+(defn wait-editor-text
+  [text]
+  (w/wait-for (format ".editor-wrapper textarea:text('%s')" text)))
+
+(def copy #(k/press "ControlOrMeta+c" {:delay 100}))
+(def paste #(k/press "ControlOrMeta+v" {:delay 100}))
+
+(defn- indent-outdent
+  [indent?]
+  (let [editor (util/get-editor)
+        [x1 _] (util/bounding-xy editor)
+        _ (if indent? (k/tab) (k/shift+tab))
+        [x2 _] (util/bounding-xy editor)]
+    (if indent?
+      (is (< x1 x2))
+      (is (> x1 x2)))))
+
+(defn indent
+  []
+  (indent-outdent true))
+
+(defn outdent
+  []
+  (indent-outdent false))

+ 30 - 0
clj-e2e/src/logseq/e2e/locator.clj

@@ -0,0 +1,30 @@
+(ns logseq.e2e.locator
+  (:refer-clojure :exclude [or and filter])
+  (:require [wally.main :as w])
+  (:import (com.microsoft.playwright Locator$FilterOptions)))
+
+(defn or
+  "Return locator"
+  [q1 q2 & qs]
+  (let [locators (map w/-query (concat [q1 q2] qs))]
+    (reduce (fn [loc1 loc2] (.or loc1 loc2)) locators)))
+
+(defn and
+  "Return locator"
+  [q1 q2 & qs]
+  (let [locators (map w/-query (concat [q1 q2] qs))]
+    (reduce (fn [loc1 loc2] (.and loc1 loc2)) locators)))
+
+(defn- ->filter-options
+  [& {:keys [has has-text has-not has-not-text]}]
+  (cond-> (Locator$FilterOptions.)
+    has (.setHas (w/-query has))
+    has-not (.setHasNot (w/-query has-not))
+    has-text (.setHasText has-text)
+    has-not-text (.setHasNotText has-not-text)))
+
+(defn filter
+  "Return locator"
+  {:arglists '([q & {:keys [has has-text has-not has-not-text]}])}
+  [q & {:as opts}]
+  (.filter (w/-query q) (->filter-options opts)))

+ 11 - 25
clj-e2e/src/logseq/e2e/util.clj

@@ -3,6 +3,7 @@
   (:require [clojure.test :refer [is]]
             [logseq.e2e.assert :as assert]
             [logseq.e2e.keyboard :as k]
+            [logseq.e2e.locator :as loc]
             [wally.main :as w]
             [wally.repl :as repl])
   (:import (com.microsoft.playwright Locator$PressSequentiallyOptions
@@ -38,6 +39,10 @@
   []
   (let [editor (w/-query editor-q)]
     (when (w/visible? editor-q)
+      ;; ensure cursor exists
+      ;; Sometimes when the editor exists, there isn't a blinking cursor,
+      ;; causing subsequent operations (like pressing Enter) to fail.
+      (.focus editor)
       editor)))
 
 (defn get-edit-block-container
@@ -99,7 +104,8 @@
 
 (defn exit-edit
   []
-  (k/esc))
+  (k/esc)
+  (assert/assert-non-editor-mode))
 
 (defn get-text
   [locator]
@@ -117,24 +123,6 @@
   (let [box (.boundingBox locator)]
     [(.-x box) (.-y box)]))
 
-(defn indent-outdent
-  [indent?]
-  (let [editor (get-editor)
-        [x1 _] (bounding-xy editor)
-        _ (if indent? (k/tab) (k/shift+tab))
-        [x2 _] (bounding-xy editor)]
-    (if indent?
-      (is (< x1 x2))
-      (is (> x1 x2)))))
-
-(defn indent
-  []
-  (indent-outdent true))
-
-(defn outdent
-  []
-  (indent-outdent false))
-
 (defn repeat-keyboard
   [n shortcut]
   (dotimes [_i n]
@@ -191,14 +179,12 @@
   [tag]
   (press-seq " #" {:delay 20})
   (press-seq tag)
-  (w/click (format "a.menu-link:has-text(\"%s\")" tag))
+  (w/click (first (w/query (format "a.menu-link:has-text(\"%s\")" tag))))
   ;; wait tag added on ui
   (assert/assert-is-visible
-   (-> (w/-query ".ls-block")
-       (.filter (.setHas (Locator$FilterOptions.)
-                         (w/-query ".editor-wrapper textarea")))
-       (.filter (.setHas (Locator$FilterOptions.)
-                         (w/-query (format ".block-tag :text('%s')" tag)))))))
+   (-> ".ls-block"
+       (loc/filter :has ".editor-wrapper textarea")
+       (loc/filter :has (format ".block-tag :text('%s')" tag)))))
 
 (defn -query-last
   [q]

+ 10 - 2
clj-e2e/test/logseq/e2e/commands_test.clj

@@ -8,6 +8,7 @@
    [logseq.e2e.block :as b]
    [logseq.e2e.fixtures :as fixtures]
    [logseq.e2e.keyboard :as k]
+   [logseq.e2e.locator :as loc]
    [logseq.e2e.util :as util]
    [wally.main :as w]
    [wally.repl :as repl]))
@@ -128,6 +129,7 @@
           (util/input-command status)
           (is (= text (util/get-edit-content)))
           (util/exit-edit)
+          (k/esc)
           (w/wait-for (str ".ls-icon-" (get status->icon status status))))))))
 
 (deftest priority-test
@@ -188,6 +190,7 @@
   (testing "number list commands"
     (util/input-command "number list")
     (b/new-blocks ["a" "b" "c"])
+    (assert/assert-have-count "span.typed-list" 3)
     (is (= ["1." "2." "3."] (w/all-text-contents "span.typed-list")))
     ;; double `enter` convert the next block to bullet block
     (k/enter)
@@ -201,6 +204,7 @@
   (testing "number children commands"
     (b/new-blocks ["a" "a1" "a2" "a3" "b"])
     (k/arrow-up)
+    (w/wait-for "textarea:text('a3')")
     (util/repeat-keyboard 3 "Shift+ArrowUp")
     (k/tab)
     (b/jump-to-block "a")
@@ -245,15 +249,19 @@
     (util/set-tag "Template")
     (b/new-blocks ["block 1" "block 2" "block 3" "test"])
     (k/arrow-up)
+    (w/wait-for "textarea:text('block 3')")
     (util/repeat-keyboard 3 "Shift+ArrowUp")
     (k/tab)
     (b/jump-to-block "test")
     (util/input-command "template")
     (util/input "template 1")
+    (w/wait-for "a.menu-link.chosen:has-text('template 1')")
     (k/enter)
     (doseq [text ["block 1" "block 2" "block 3"]]
-      (assert/assert-have-count (.or (w/-query (format ".ls-block .block-title-wrap:text('%s')" text))
-                                     (w/-query (format ".ls-block textarea:text('%s')" text))) 2))))
+      (assert/assert-have-count
+       (loc/or (format ".ls-block .block-title-wrap:text('%s')" text)
+               (format ".ls-block textarea:text('%s')" text))
+       2))))
 
 (deftest embed-html-test
   (testing "embed html"

+ 1 - 1
clj-e2e/test/logseq/e2e/custom_report.clj

@@ -10,7 +10,7 @@
   record all playwright contexts in this dynamic var"
   nil)
 
-(defn- screenshot
+(defn screenshot
   [page test-name]
   (println :screenshot test-name)
   (.screenshot

+ 1 - 0
clj-e2e/test/logseq/e2e/fixtures.clj

@@ -12,6 +12,7 @@
     (w/make-page {:headless (or headless @config/*headless)
                   :persistent false
                   :slow-mo @config/*slow-mo})
+    (w/grant-permissions :clipboard-write :clipboard-read)
     (binding [custom-report/*pw-contexts* #{(.context (w/get-page))}]
       (w/navigate (str "http://localhost:" (or port @config/*port)))
       (f))))

+ 8 - 8
clj-e2e/test/logseq/e2e/outliner_test.clj

@@ -20,14 +20,14 @@
 (deftest indent-and-outdent-test
   (b/new-blocks ["b1" "b2"])
   (testing "simple indent and outdent"
-    (util/indent)
-    (util/outdent))
+    (b/indent)
+    (b/outdent))
 
   (testing "indent a block with its children"
     (b/new-block "b3")
-    (util/indent)
+    (b/indent)
     (k/arrow-up)
-    (util/indent)
+    (b/indent)
     (util/exit-edit)
     (let [[x1 x2 x3] (map (comp first util/bounding-xy #(w/find-one-by-text "span" %)) ["b1" "b2" "b3"])]
       (is (< x1 x2 x3))))
@@ -35,9 +35,9 @@
   (testing "unindent a block with its children"
     (b/open-last-block)
     (b/new-blocks ["b4" "b5"])
-    (util/indent)
+    (b/indent)
     (k/arrow-up)
-    (util/outdent)
+    (b/outdent)
     (util/exit-edit)
     (let [[x2 x3 x4 x5] (map (comp first util/bounding-xy #(w/find-one-by-text "span" %)) ["b2" "b3" "b4" "b5"])]
       (is (and (= x2 x4) (= x3 x5) (< x2 x3))))))
@@ -66,9 +66,9 @@
 (deftest delete-test-with-children
   (testing "Delete block with its children"
     (b/new-blocks ["b1" "b2" "b3" "b4"])
-    (util/indent)
+    (b/indent)
     (k/arrow-up)
-    (util/indent)
+    (b/indent)
     (k/arrow-up)
     (b/delete-blocks)
     (is (= "b1" (util/get-edit-content)))

+ 45 - 0
clj-e2e/test/logseq/e2e/plugins_test.clj

@@ -0,0 +1,45 @@
+(ns logseq.e2e.plugins-test
+  (:require
+   [clojure.string :as string]
+   [clojure.test :refer [deftest testing is use-fixtures]]
+   [logseq.e2e.fixtures :as fixtures]
+   [wally.main :as w]
+   [wally.repl :as repl]))
+
+(use-fixtures :once fixtures/open-page)
+(use-fixtures :each fixtures/new-logseq-page)
+
+(defn- to-snake-case
+  "Converts a string to snake_case. Handles camelCase, PascalCase, spaces, hyphens, and existing underscores.
+   Examples:
+     'HelloWorld' -> 'hello_world'
+     'Hello World' -> 'hello_world'
+     'hello-world' -> 'hello_world'
+     'Hello__World' -> 'hello_world'"
+  [s]
+  (when (string? s)
+    (-> s
+      ;; Normalize input: replace hyphens/spaces with underscores, collapse multiple underscores
+      (clojure.string/replace #"[-\s]+" "_")
+      ;; Split on uppercase letters (except at start) and join with underscore
+      (clojure.string/replace #"(?<!^)([A-Z])" "_$1")
+      ;; Remove redundant underscores and trim
+      (clojure.string/replace #"_+" "_")
+      (clojure.string/trim)
+      ;; Convert to lowercase
+      (clojure.string/lower-case))))
+
+(defn- ls-api-call!
+  [tag & args]
+  (let [tag (name tag)
+        ns' (string/split tag #"\.")
+        ns? (and (seq ns') (= (count ns') 2))
+        ns1 (string/lower-case (if ns? (str "sdk." (first ns')) "api"))
+        name1 (if ns? (to-snake-case (last ns')) tag)]
+    (w/eval-js
+      (format "args => { const o=logseq.%1$s; return o['%2$s']?.apply(null, args || []); }" ns1 name1)
+      (vec args))))
+
+(deftest apis-related-test
+  (testing "block related apis"
+    (ls-api-call! :ui.showMsg "hello world" "error")))

+ 85 - 0
clj-e2e/test/logseq/e2e/reference_test.clj

@@ -0,0 +1,85 @@
+(ns logseq.e2e.reference-test
+  (:require
+   [clojure.test :refer [deftest testing is use-fixtures]]
+   [logseq.e2e.assert :as assert]
+   [logseq.e2e.block :as b]
+   [logseq.e2e.fixtures :as fixtures]
+   [logseq.e2e.keyboard :as k]
+   [logseq.e2e.util :as util]
+   [wally.repl :as repl]))
+
+(use-fixtures :once fixtures/open-page)
+
+(use-fixtures :each fixtures/new-logseq-page)
+
+;; block references
+(deftest self-reference
+  (testing "self reference"
+    (b/new-block "b2")
+    (b/copy)
+    (b/paste)
+    (util/exit-edit)
+    (assert/assert-selected-block-text "b2")))
+
+(deftest self-tag-block-reference
+  (testing "self reference"
+    (b/new-block "b2")
+    (util/set-tag "task")
+    (b/copy)
+    (b/paste)
+    (util/exit-edit)
+    (assert/assert-selected-block-text "b2")))
+
+(deftest mutual-reference
+  (testing "mutual reference"
+    (b/new-blocks ["b1" "b2"])
+    (util/set-tag "task")
+    (b/copy)
+    (k/arrow-up)
+    (b/wait-editor-text "b1")
+    (b/paste)
+    (b/copy)
+    (k/arrow-down)
+    (b/wait-editor-text "b2")
+    (b/paste)
+    (util/exit-edit)
+    (b/assert-blocks-visible ["b1b2" "b2b1"])))
+
+(deftest parent-reference
+  (testing "parent reference"
+    (b/new-blocks ["b1" "b2"])
+    (util/set-tag "task")
+    (b/indent)
+    (b/copy)
+    (k/arrow-up)
+    (b/wait-editor-text "b1")
+    (b/paste)
+    (b/copy)
+    (k/arrow-down)
+    (b/wait-editor-text "b2")
+    (b/paste)
+    (util/exit-edit)
+    (b/assert-blocks-visible ["b1b2" "b2b1"])))
+
+(deftest cycle-reference
+  (testing "cycle reference"
+    (b/new-blocks ["b1" "b2" "b3"])
+    (util/set-tag "task")
+    (b/jump-to-block "b1")
+    (assert/assert-editor-mode)
+    (b/copy)
+    (k/arrow-down)
+    (b/wait-editor-text "b2")
+    (b/paste)
+    (b/copy)
+    (k/arrow-down)
+    (b/wait-editor-text "b3")
+    (b/paste)
+    (b/copy)
+    (b/jump-to-block "b1")
+    (assert/assert-editor-mode)
+    (b/paste)
+    (util/exit-edit)
+    (b/assert-blocks-visible ["b1b3b2" "b2b1b3" "b3b2b1"])))
+
+;; TODO: page references

+ 5 - 4
deps/db/src/logseq/db/common/entity_plus.cljc

@@ -11,6 +11,7 @@
             [datascript.core :as d]
             [datascript.impl.entity :as entity :refer [Entity]]
             [logseq.common.util.date-time :as date-time-util]
+            [logseq.db.frontend.content :as db-content]
             [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.frontend.property :as db-property]))
 
@@ -95,10 +96,11 @@
          (when-not (and search? (keyword-identical? k :block/title))
            (get (.-kv e) k))
          (let [result (lookup-entity e k default-value)
+               ;; Replace title for pages only, otherwise it'll recursively
+               ;; replace block id refs if there're cycle references of blocks
                refs (:block/refs e)
                result' (if (and (string? result) refs)
-                         ;; FIXME: Correct namespace dependencies instead of resolve workaround
-                         ((resolve 'logseq.db.frontend.content/id-ref->title-ref) result refs search?)
+                         (db-content/id-ref->title-ref result refs search?)
                          result)]
            (or result' default-value)))))))
 
@@ -189,8 +191,7 @@
   (let [v @(.-cache this)
         v' (if (:block/title v)
              (assoc v :block/title
-                    ((resolve 'logseq.db.frontend.content/id-ref->title-ref)
-                     (:block/title v) (:block/refs this) (:block.temp/search? this)))
+                    (db-content/id-ref->title-ref (:block/title v) (:block/refs this) (:block.temp/search? this)))
              v)]
     (concat (seq v')
             (seq (.-kv this)))))

+ 1 - 8
deps/db/src/logseq/db/common/property_util.cljs

@@ -3,7 +3,6 @@
   (:require [datascript.core :as d]
             [logseq.db.common.entity-plus :as entity-plus]
             [logseq.db.frontend.property :as db-property]
-            [logseq.db.frontend.property.type :as db-property-type]
             [logseq.db.sqlite.util :as sqlite-util]))
 
 (defn- get-file-pid-by-ident
@@ -27,18 +26,12 @@
     db-ident
     (get-file-pid-by-ident db-ident)))
 
-(defn built-in-has-ref-value?
-  "Given a built-in's db-ident, determine if its property value is a ref"
-  [db-ident]
-  (contains? db-property-type/value-ref-property-types
-             (get-in db-property/built-in-properties [db-ident :schema :type])))
-
 (defn lookup
   "Get the property value by a built-in property's db-ident from coll. For file and db graphs"
   [repo block db-ident]
   (if (sqlite-util/db-based-graph? repo)
     (let [val (get block db-ident)]
-      (if (built-in-has-ref-value? db-ident) (db-property/property-value-content val) val))
+      (if (db-property/built-in-has-ref-value? db-ident) (db-property/property-value-content val) val))
     (get (:block/properties block) (get-pid repo db-ident))))
 
 (defn get-block-property-value

+ 4 - 2
deps/db/src/logseq/db/frontend/content.cljs

@@ -4,6 +4,7 @@
             [datascript.core :as d]
             [logseq.common.util :as common-util]
             [logseq.common.util.page-ref :as page-ref]
+            [logseq.db.common.entity-util :as common-entity-util]
             [logseq.db.frontend.entity-util :as entity-util]))
 
 ;; [[uuid]]
@@ -40,8 +41,9 @@
 
 (defn id-ref->title-ref
   "Convert id ref backs to page name refs using refs."
-  [content* refs search?]
-  (let [content (str content*)]
+  [content* refs* search?]
+  (let [refs (filter common-entity-util/page? refs*)
+        content (str content*)]
     (if (re-find id-ref-pattern content)
       (reduce
        (fn [content ref]

+ 7 - 0
deps/db/src/logseq/db/frontend/property.cljs

@@ -6,6 +6,7 @@
             [flatland.ordered.map :refer [ordered-map]]
             [logseq.common.defkeywords :refer [defkeywords]]
             [logseq.common.uuid :as common-uuid]
+            [logseq.db.frontend.property.type :as db-property-type]
             [logseq.db.frontend.db-ident :as db-ident]))
 
 ;; Main property vars
@@ -770,3 +771,9 @@
 (defn get-property-schema
   [property-m]
   (select-keys property-m schema-properties))
+
+(defn built-in-has-ref-value?
+  "Given a built-in's db-ident, determine if its property value is a ref"
+  [db-ident]
+  (contains? db-property-type/value-ref-property-types
+             (get-in built-in-properties [db-ident :schema :type])))

+ 1 - 1
deps/graph-parser/src/logseq/graph_parser.cljs

@@ -19,7 +19,7 @@
           blocks))
 
 (defn- get-file-page
-  "Copy of db/get-file-page. Too basic to couple to main app"
+  "Copy of file-model/get-file-page. Too basic to couple to main app"
   [db file-path]
   (ffirst
    (d/q

+ 3 - 0
deps/outliner/src/logseq/outliner/property.cljs

@@ -437,6 +437,9 @@
   (when-let [property (d/entity @conn property-id)]
     (let [block (d/entity @conn block-eid)]
       (when (and block (not= property-id (:db/ident block)) (db-property/many? property))
+        (when (= property-id :block/tags)
+          (outliner-validate/validate-tags-property-deletion @conn [block-eid] property-value))
+
         (let [current-val (get block property-id)
               fv (first current-val)]
           (if (and (= 1 (count current-val)) (or (= property-value fv) (= property-value (:db/id fv))))

+ 17 - 6
deps/outliner/src/logseq/outliner/validate.cljs

@@ -183,30 +183,41 @@
                        :property-value v})))))
 
 (defn- disallow-node-cant-tag-with-private-tags
-  [db block-eids v]
+  [db block-eids v & {:keys [delete?]}]
   (when (and (ldb/private-tags (:db/ident (d/entity db v)))
              ;; Allow assets to be tagged
              (not (and
                    (every? (fn [id] (ldb/asset? (d/entity db id))) block-eids)
                    (= :logseq.class/Asset (:db/ident (d/entity db v))))))
-    (throw (ex-info (str "Can't set tag with built-in #" (:block/title (d/entity db v)))
+    (throw (ex-info (str (if delete? "Can't remove tag" "Can't set tag")
+                         " with built-in #" (:block/title (d/entity db v)))
                     {:type :notification
-                     :payload {:message (str "Can't set tag with built-in #" (:block/title (d/entity db v)))
+                     :payload {:message (str (if delete? "Can't remove tag" "Can't set tag")
+                                             " with built-in #" (:block/title (d/entity db v)))
                                :type :error}
                      :property-id :block/tags
                      :property-value v}))))
 
 (defn- disallow-tagging-a-built-in-entity
-  [db block-eids]
+  [db block-eids & {:keys [delete?]}]
   (when-let [built-in-ent (some #(when (:logseq.property/built-in? %) %)
                                 (map #(d/entity db %) block-eids))]
-    (throw (ex-info (str "Can't add tag on built-in " (pr-str (:block/title built-in-ent)))
+    (throw (ex-info (str (if delete? "Can't remove tag" "Can't add tag")
+                         " on built-in " (pr-str (:block/title built-in-ent)))
                     {:type :notification
-                     :payload {:message (str "Can't add tag on built-in " (pr-str (:block/title built-in-ent)))
+                     :payload {:message (str (if delete? "Can't remove tag" "Can't add tag")
+                                             " on built-in " (pr-str (:block/title built-in-ent)))
                                :type :error}}))))
 
 (defn validate-tags-property
+  "Validates adding a property value to :block/tags for given blocks"
   [db block-eids v]
   (disallow-tagging-a-built-in-entity db block-eids)
   (disallow-node-cant-tag-with-private-tags db block-eids v)
   (disallow-node-cant-tag-with-built-in-non-tags db block-eids v))
+
+(defn validate-tags-property-deletion
+  "Validates deleting a property value from :block/tags for given blocks"
+  [db block-eids v]
+  (disallow-tagging-a-built-in-entity db block-eids {:delete? true})
+  (disallow-node-cant-tag-with-private-tags db block-eids v {:delete? true}))

+ 1 - 1
deps/shui/src/logseq/shui/table/core.cljc

@@ -126,7 +126,7 @@
                               (select-all? row-selection filtered-rows))
            :selected-some? (select-some? row-selection filtered-rows)
            :row-selected? (fn [row] (row-selected? row row-selection))
-           :row-toggle-selected! (fn [row value] (row-toggle-selected! row value set-row-selection! row-selection))
+           :row-toggle-selected! (fn [row-selection row value] (row-toggle-selected! row value set-row-selection! row-selection))
            :toggle-selected-all! (fn [table value]
                                    (toggle-selected-all! table value set-row-selection!))
            :column-set-sorting! (fn [sorting column asc?] (column-set-sorting! column set-sorting! sorting asc?)))))

+ 3 - 0
scripts/src/logseq/tasks/dev/db_and_file_graphs.clj

@@ -100,6 +100,9 @@
 (defn- validate-multi-graph-fns-not-in-file-or-db
   []
   (let [multi-graph-fns ["/db-based-graph\\?" "\\(db-based-graph\\?"
+                         ;; from frontend.handler.property.util
+                         "/get-pid"
+                         "logseq.db.common.property-util"
                          ;; Use file-entity-util and entity-util when in a single graph context
                          "ldb/whiteboard\\?" "ldb/journal\\?" "ldb/page\\?"]
         res (grep-many multi-graph-fns (into file-graph-paths db-graph-paths))]

+ 2 - 2
src/main/electron/listener.cljs

@@ -7,7 +7,7 @@
             [electron.ipc :as ipc]
             [frontend.db :as db]
             [frontend.db.async :as db-async]
-            [frontend.db.model :as db-model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.fs.sync :as sync]
             [frontend.fs.watcher-handler :as watcher-handler]
             [frontend.handler.file-sync :as file-sync-handler]
@@ -95,7 +95,7 @@
                            (notification/show! (str "Open link failed. Block-id `" block-id "` doesn't exist in the graph.") :error false)))
 
                        file
-                       (if-let [db-page-name (db-model/get-file-page file false)]
+                       (if-let [db-page-name (file-model/get-file-page file false)]
                          (route-handler/redirect-to-page! db-page-name)
                          (notification/show! (str "Open link failed. File `" file "` doesn't exist in the graph.") :error false))))))
 

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

@@ -13,6 +13,7 @@
             [frontend.handler.notification :as notification]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.property.file :as property-file]
+            [frontend.ref :as ref]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.util :as util]
@@ -434,7 +435,7 @@
          ["Draw" (fn []
                    (let [file (draw/file-name)
                          path (str common-config/default-draw-directory "/" file)
-                         text (page-ref/->page-ref path)]
+                         text (ref/->page-ref path)]
                      (p/let [_ (draw/create-draw-with-default-content path)]
                        (println "draw file created, " path))
                      text)) "Draw a graph with Excalidraw"])

+ 94 - 78
src/main/frontend/components/block.cljs

@@ -28,6 +28,7 @@
             [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
             [frontend.db.async :as db-async]
+            [frontend.db.file-based.model :as file-model]
             [frontend.db.model :as model]
             [frontend.extensions.highlight :as highlight]
             [frontend.extensions.latex :as latex]
@@ -45,8 +46,8 @@
             [frontend.handler.db-based.property :as db-property-handler]
             [frontend.handler.dnd :as dnd]
             [frontend.handler.editor :as editor-handler]
-            [frontend.handler.file-based.editor :as file-editor-handler]
             [frontend.handler.export.common :as export-common-handler]
+            [frontend.handler.file-based.editor :as file-editor-handler]
             [frontend.handler.file-based.property.util :as property-util]
             [frontend.handler.file-sync :as file-sync]
             [frontend.handler.notification :as notification]
@@ -61,6 +62,7 @@
             [frontend.mobile.util :as mobile-util]
             [frontend.modules.outliner.tree :as tree]
             [frontend.modules.shortcut.utils :as shortcut-utils]
+            [frontend.ref :as ref]
             [frontend.security :as security]
             [frontend.state :as state]
             [frontend.template :as template]
@@ -904,10 +906,8 @@
 
 (rum/defc invalid-node-ref
   [id]
-  (let [db-based? (config/db-based-graph? (state/get-current-repo))
-        ->ref (if db-based? page-ref/->page-ref block-ref/->block-ref)]
-    [:span.warning.mr-1 {:title "Node ref invalid"}
-     (->ref (str id))]))
+  [:span.warning.mr-1 {:title "Node ref invalid"}
+   (ref/->block-ref (str id))])
 
 (defn inline-text
   ([format v]
@@ -943,10 +943,11 @@
    - `:preview?`: Is this component under preview mode? (If true, `page-preview-trigger` won't be registered to this `page-cp`)"
   [state {:keys [label children preview? disable-preview? show-non-exists-page? table-view? tag? _skip-async-load?] :as config} page]
   (when-let [entity' (rum/react (:*entity state))]
-    (let [entity (or (db/sub-block (:db/id entity')) entity')]
+    (let [entity (or (db/sub-block (:db/id entity')) entity')
+          config (assoc config :block entity)]
       (cond
         entity
-        (if (or (ldb/page? entity) (:block/tags entity))
+        (if (ldb/page? entity)
           (let [page-name (some-> (:block/title entity) util/page-name-sanity-lc)
                 whiteboard-page? (model/whiteboard-page? entity)
                 inner (page-inner (assoc config :whiteboard-page? whiteboard-page?) entity children label)
@@ -956,11 +957,7 @@
                      (not (false? preview?))
                      (not disable-preview?)
                      (not modal?))
-              (if (ldb/page? entity)
-                (page-preview-trigger (assoc config :children inner) entity)
-                (block-reference-preview inner {:repo (state/get-current-repo)
-                                                :config config
-                                                :id (:block/uuid entity)}))
+              (page-preview-trigger (assoc config :children inner) entity)
               inner))
           (block-reference config (:block/uuid entity)
                            (if (string? label)
@@ -1091,43 +1088,45 @@
                          :label (mldoc/plain->text label)
                          :contents-page? contents-page?
                          :show-icon? true?)
-          asset? (some? (:logseq.property.asset/type block))]
-      (cond
-        (and asset? (img-audio-video? block))
-        (asset-cp config block)
-
-        (or (ldb/page? block) (:block/tags block))
-        [:span.page-reference
-         {:data-ref (str uuid-or-title)}
-         (when (and (or show-brackets? nested-link?)
-                    (not html-export?)
-                    (not contents-page?))
-           [:span.text-gray-500.bracket page-ref/left-brackets])
-         (when (and (config/db-based-graph?) (ldb/class-instance? (db/entity :logseq.class/Task) block))
-           [:div.inline-block
-            {:style {:margin-right 1
-                     :margin-top -2
-                     :vertical-align "middle"}
-             :on-pointer-down (fn [e]
-                                (util/stop e))}
-            (block-positioned-properties config block :block-left)])
-         (page-cp config' (if (uuid? uuid-or-title)
-                            {:block/uuid uuid-or-title}
-                            {:block/name uuid-or-title}))
-         (when (and (or show-brackets? nested-link?)
-                    (not html-export?)
-                    (not contents-page?))
-           [:span.text-gray-500.bracket page-ref/right-brackets])]
-
-        (and (string? uuid-or-title) (string/ends-with? uuid-or-title ".excalidraw"))
-        [:div.draw {:on-click (fn [e]
-                                (.stopPropagation e))}
-         (excalidraw uuid-or-title (:block/uuid config))]
+          asset? (some? (:logseq.property.asset/type block))
+          page? (ldb/page? block)
+          brackets? (and (or show-brackets? nested-link?)
+                         (not html-export?)
+                         (not contents-page?)
+                         page?)]
+      (when-not (= (:db/id block) (:db/id (:block config)))
+        (cond
+          (and asset? (img-audio-video? block))
+          (asset-cp config block)
+
+          (or page? (:block/tags block))
+          [:span.page-reference
+           {:data-ref (str uuid-or-title)}
+           (when brackets?
+             [:span.text-gray-500.bracket page-ref/left-brackets])
+           (when (and (config/db-based-graph?) (ldb/class-instance? (db/entity :logseq.class/Task) block))
+             [:div.inline-block
+              {:style {:margin-right 1
+                       :margin-top -2
+                       :vertical-align "middle"}
+               :on-pointer-down (fn [e]
+                                  (util/stop e))}
+              (block-positioned-properties config block :block-left)])
+           (page-cp config' (if (uuid? uuid-or-title)
+                              {:block/uuid uuid-or-title}
+                              {:block/name uuid-or-title}))
+           (when brackets?
+             [:span.text-gray-500.bracket page-ref/right-brackets])]
+
+          (and (string? uuid-or-title) (string/ends-with? uuid-or-title ".excalidraw"))
+          [:div.draw {:on-click (fn [e]
+                                  (.stopPropagation e))}
+           (excalidraw uuid-or-title (:block/uuid config))]
 
-        :else
-        (page-cp config' (if (uuid? uuid-or-title)
-                           {:block/uuid uuid-or-title}
-                           {:block/name uuid-or-title}))))))
+          :else
+          (page-cp config' (if (uuid? uuid-or-title)
+                             {:block/uuid uuid-or-title}
+                             {:block/name uuid-or-title})))))))
 
 (defn- latex-environment-content
   [name option content]
@@ -1259,16 +1258,19 @@
                          :render render})))
 
 (rum/defc block-reference-aux < rum/reactive db-mixins/query
-  [config block-id label]
-  (let [block (db/entity [:block/uuid block-id])
-        db-id (:db/id block)
+  [config block label]
+  (let [db-id (:db/id block)
+        block-id (:block/uuid block)
         block (when db-id (db/sub-block db-id))
         block-type (keyword (pu/lookup block :logseq.property/ls-type))
         hl-type (pu/lookup block :logseq.property.pdf/hl-type)
         repo (state/get-current-repo)
-        stop-inner-events? (= block-type :whiteboard-shape)]
+        stop-inner-events? (= block-type :whiteboard-shape)
+        config' (assoc config
+                       :block-ref? true
+                       :stop-events? stop-inner-events?)]
     (if (and block (:block/title block))
-      (let [content-cp (block-content (assoc config :block-ref? true :stop-events? stop-inner-events?)
+      (let [content-cp (block-content config'
                                       block nil (:block/uuid block)
                                       (:slide? config)
                                       nil)
@@ -1334,7 +1336,9 @@
 (rum/defc block-reference
   [config id label]
   (let [block-id (and id (if (uuid? id) id (parse-uuid id)))
-        [block set-block!] (hooks/use-state (db/entity [:block/uuid block-id]))]
+        [block set-block!] (hooks/use-state (db/entity [:block/uuid block-id]))
+        self-reference? (when (set? (:ref-set config))
+                          (contains? (:ref-set config) block-id))]
     (hooks/use-effect!
      (fn []
        (p/let [block (db-async/<get-block (state/get-current-repo)
@@ -1343,10 +1347,14 @@
                                            :skip-refresh? true})]
          (set-block! block)))
      [])
-    (if (and block-id (= (:block/uuid (:block config)) block-id))
-      [:span.warning.text-sm "Self reference"]
+    (when-not self-reference?
       (if block
-        (block-reference-aux config block-id label)
+        (let [config' (update config :ref-set (fn [s]
+                                                (let [bid (:block/uuid (:block config))]
+                                                  (if (nil? s)
+                                                    #{bid}
+                                                    (conj s bid block-id)))))]
+          (block-reference-aux config' block label))
         (invalid-node-ref block-id)))))
 
 (defn- render-macro
@@ -1532,7 +1540,7 @@
           (image-link config url page nil metadata full_text)
           (let [label* (if (seq (mldoc/plain->text label)) label nil)]
             (if (and (string? page) (string/blank? page))
-              [:span (page-ref/->page-ref page)]
+              [:span (ref/->page-ref page)]
               (page-reference (:html-export? config) page config label*)))))
 
       ["Embed_data" src]
@@ -1565,7 +1573,7 @@
                   config (assoc config :redirect-page-name redirect-page-name)
                   label-text (get-label-text label)
                   page (if (string/blank? label-text)
-                         {:block/name (db/get-file-page (string/replace href "file:" "") false)}
+                         {:block/name (file-model/get-file-page (string/replace href "file:" "") false)}
                          (get-page label))
                   show-brackets? (state/show-brackets?)]
               (if (and page
@@ -1856,7 +1864,7 @@
         (let [namespace (first arguments)]
           (when-not (string/blank? namespace)
             (let [namespace (string/lower-case (page-ref/get-page-name! namespace))
-                  children (model/get-namespace-hierarchy (state/get-current-repo) namespace)]
+                  children (file-model/get-namespace-hierarchy (state/get-current-repo) namespace)]
               (namespace-hierarchy config namespace children)))))
 
       (= name "youtube")
@@ -2712,10 +2720,11 @@
                                 :on-click #(state/sidebar-add-block! (state/get-current-repo) (:db/id tag) :page)}
                                "Open in sidebar"
                                (shui/dropdown-menu-shortcut (shortcut-utils/decorate-binding "shift+click")))
-                              (shui/dropdown-menu-item
-                               {:key "Remove tag"
-                                :on-click #(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag))}
-                               "Remove tag")])
+                              (when-not (ldb/private-tags (:db/ident tag))
+                                (shui/dropdown-menu-item
+                                 {:key "Remove tag"
+                                  :on-click #(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag))}
+                                 "Remove tag"))])
                            popup-opts))}
       (if (and @*hover? (not (ldb/private-tags (:db/ident tag))))
         [:a.inline-flex.text-muted-foreground.mr-1
@@ -2874,18 +2883,18 @@
         content (if db-based?
                   (:block/raw-title block)
                   (property-util/remove-built-in-properties format (:block/raw-title block)))
+        content (if (string? content) (string/trim content) "")
+        block-ref? (:block-ref? config)
         block (merge block (block/parse-title-and-body uuid format pre-block? content))
         ast-body (:block.temp/ast-body block)
         ast-title (:block.temp/ast-title block)
         block (assoc block :block/title content)
         plugin-slotted? (and config/lsp-enabled? (state/slot-hook-exist? uuid))
-        block-ref? (:block-ref? config)
         stop-events? (:stop-events? config)
         block-ref-with-title? (and block-ref? (not (state/show-full-blocks?)) (seq ast-title))
         block-type (or
                     (pu/lookup block :logseq.property/ls-type)
                     :default)
-        content (if (string? content) (string/trim content) "")
         mouse-down-key (if (util/ios?)
                          :on-click
                          :on-pointer-down) ; TODO: it seems that Safari doesn't work well with on-pointer-down
@@ -3355,19 +3364,24 @@
             (prn ::unhandled-drop-data-transfer-type transfer-types))))))
   (block-drag-end event *move-to'))
 
+(defonce *block-last-mouse-event (atom nil))
+
 (defn- block-mouse-over
-  [e block *control-show? block-id doc-mode?]
-  (when-not (or @*dragging? (= (:block/uuid block) (:block/uuid (state/get-edit-block))))
-    (.preventDefault e)
-    (reset! *control-show? true)
-    (when-let [parent (gdom/getElement block-id)]
-      (let [node (.querySelector parent ".bullet-container")]
-        (when doc-mode?
-          (dom/remove-class! node "hide-inner-bullet"))))
-    (when (non-dragging? e)
-      (when-let [container (gdom/getElement "app-container-wrapper")]
-        (dom/add-class! container "blocks-selection-mode"))
-      (editor-handler/highlight-selection-area! block-id {:append? true}))))
+  [^js e block *control-show? block-id doc-mode?]
+  (let [mouse-moving? (not= (some-> @*block-last-mouse-event (.-clientY)) (.-clientY e))]
+    (when (and mouse-moving?
+               (not @*dragging?)
+               (not= (:block/uuid block) (:block/uuid (state/get-edit-block))))
+      (.preventDefault e)
+      (reset! *control-show? true)
+      (when-let [parent (gdom/getElement block-id)]
+        (let [node (.querySelector parent ".bullet-container")]
+          (when doc-mode?
+            (dom/remove-class! node "hide-inner-bullet"))))
+      (when (non-dragging? e)
+        (when-let [container (gdom/getElement "app-container-wrapper")]
+          (dom/add-class! container "blocks-selection-mode"))
+        (editor-handler/highlight-selection-area! block-id {:append? true})))))
 
 (defn- block-mouse-leave
   [*control-show? block-id doc-mode?]
@@ -3608,6 +3622,8 @@
                             (block-handler/on-touch-cancel *show-left-menu? *show-right-menu?))
          :on-mouse-enter (fn [e]
                            (block-mouse-over e block *control-show? block-id doc-mode?))
+         :on-mouse-move (fn [e]
+                          (reset! *block-last-mouse-event e))
          :on-mouse-leave (fn [_e]
                            (block-mouse-leave *control-show? block-id doc-mode?))}
 

+ 20 - 17
src/main/frontend/components/container.cljs

@@ -248,8 +248,9 @@
              (shui/tabler-icon "filter-edit" {:size 14})]}
      [:div.sidebar-navigations.flex.flex-col.mt-1
        ;; required custom home page
-      (let [page (:page default-home)]
-        (if (and page (not (state/enable-journals? (state/get-current-repo))))
+      (let [page (:page default-home)
+            enable-journals? (state/enable-journals? (state/get-current-repo))]
+        (if (and page (not enable-journals?))
           (sidebar-item
            {:class "home-nav"
             :title page
@@ -260,17 +261,18 @@
             :icon "home"
             :shortcut :go/home})
 
-          (sidebar-item
-           {:class "journals-nav"
-            :active (and (not srs-open?)
-                         (or (= route-name :all-journals) (= route-name :home)))
-            :title (t :left-side-bar/journals)
-            :on-click-handler (fn [e]
-                                (if (gobj/get e "shiftKey")
-                                  (route-handler/sidebar-journals!)
-                                  (route-handler/go-to-journals!)))
-            :icon "calendar"
-            :shortcut :go/journals})))
+          (when enable-journals?
+           (sidebar-item
+            {:class "journals-nav"
+             :active (and (not srs-open?)
+                          (or (= route-name :all-journals) (= route-name :home)))
+             :title (t :left-side-bar/journals)
+             :on-click-handler (fn [e]
+                                 (if (gobj/get e "shiftKey")
+                                   (route-handler/sidebar-journals!)
+                                   (route-handler/go-to-journals!)))
+             :icon "calendar"
+             :shortcut :go/journals}))))
 
       (for [nav checked-navs]
         (cond
@@ -725,10 +727,11 @@
               (:page default-home))
          (route-handler/redirect-to-page! (:page default-home))
 
-         (let [latest-journals (db/get-latest-journals (state/get-current-repo) 1)]
-           (and config/publishing?
-                (not default-home)
-                (empty? latest-journals)))
+         (or (not (state/enable-journals? current-repo))
+             (let [latest-journals (db/get-latest-journals (state/get-current-repo) 1)]
+               (and config/publishing?
+                    (not default-home)
+                    (empty? latest-journals))))
          (route-handler/redirect! {:to :all-pages})
 
          loading-files?

+ 8 - 0
src/main/frontend/components/container.css

@@ -243,6 +243,14 @@
           }
         }
       }
+
+      .icon-cp-container {
+        @apply pr-1;
+
+        > .ui__icon {
+          @apply mr-0;
+        }
+      }
     }
 
     .hd .more {

+ 3 - 8
src/main/frontend/components/content.cljs

@@ -19,6 +19,7 @@
             [frontend.handler.property :as property-handler]
             [frontend.handler.property.util :as pu]
             [frontend.modules.shortcut.core :as shortcut]
+            [frontend.ref :as ref]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
@@ -26,8 +27,6 @@
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [logseq.common.util :as common-util]
-            [logseq.common.util.block-ref :as block-ref]
-            [logseq.common.util.page-ref :as page-ref]
             [logseq.db :as ldb]
             [logseq.shui.ui :as shui]
             [promesa.core :as p]
@@ -232,18 +231,14 @@
          (shui/dropdown-menu-item
           {:key      "Copy block ref"
            :on-click (fn [_e]
-                       (editor-handler/copy-block-ref! block-id
-                                                       (if db? page-ref/->page-ref block-ref/->block-ref)))}
+                       (editor-handler/copy-block-ref! block-id ref/->block-ref))}
           (t :content/copy-block-ref))
 
          (when-not db?
            (shui/dropdown-menu-item
             {:key      "Copy block embed"
              :on-click (fn [_e]
-                         (editor-handler/copy-block-ref! block-id
-                                                         (if db?
-                                                           block-ref/->block-ref
-                                                           #(util/format "{{embed ((%s))}}" %))))}
+                         (editor-handler/copy-block-ref! block-id #(util/format "{{embed ((%s))}}" %)))}
             (t :content/copy-block-emebed)))
 
          ;; TODO Logseq protocol mobile support

+ 2 - 1
src/main/frontend/components/file.cljs

@@ -9,6 +9,7 @@
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.db.async :as db-async]
+            [frontend.db.file-based.model :as file-model]
             [frontend.fs :as fs]
             [frontend.state :as state]
             [frontend.ui :as ui]
@@ -103,7 +104,7 @@
   (let [repo-dir (config/get-repo-dir (state/get-current-repo))
         rel-path (when (string/starts-with? path repo-dir)
                    (path/trim-dir-prefix repo-dir path))
-        title (db/get-file-page (or path rel-path))
+        title (file-model/get-file-page (or path rel-path))
         in-db? (when-not (path/absolute? path)
                  (boolean (db/get-file (or path rel-path))))
         file-path (cond

+ 4 - 4
src/main/frontend/components/file_based/hierarchy.cljs

@@ -2,7 +2,7 @@
   (:require [clojure.string :as string]
             [frontend.components.block :as block]
             [frontend.db :as db]
-            [frontend.db.model :as db-model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
@@ -23,15 +23,15 @@
       (when-let [page (or (first (filter text/namespace-page? all-page-names))
                           (when (:block/_namespace (db/entity [:block/name (util/page-name-sanity-lc page)]))
                             page))]
-        (let [namespace-pages (db/get-namespace-pages repo page)
-              parent-routes (db-model/get-page-namespace-routes repo page)
+        (let [namespace-pages (file-model/get-namespace-pages repo page)
+              parent-routes (file-model/get-page-namespace-routes repo page)
               pages (->> (concat namespace-pages parent-routes)
                          (distinct)
                          (sort-by :block/name)
                          (map (fn [page]
                                 (or (:block/title page) (:block/name page))))
                          (map #(string/split % "/")))
-              page-namespace (db-model/get-page-namespace repo page)
+              page-namespace (file-model/get-page-namespace repo page)
               page-namespace (util/get-page-title page-namespace)]
           (cond
             (seq pages)

+ 2 - 2
src/main/frontend/components/file_sync.cljs

@@ -9,8 +9,8 @@
             [frontend.components.onboarding.quick-tour :as quick-tour]
             [frontend.components.page :as page]
             [frontend.config :as config]
-            [frontend.db :as db]
             [frontend.db.model :as db-model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.fs :as fs]
             [frontend.fs.sync :as fs-sync]
             [frontend.handler.file-based.nfs :as nfs-handler]
@@ -478,7 +478,7 @@
                (map-indexed (fn [i f] (:time f)
                               (when-let [path (:path f)]
                                 (let [full-path   (util/node-path.join (config/get-repo-dir current-repo) path)
-                                      page-name   (db/get-file-page full-path)]
+                                      page-name   (file-model/get-file-page full-path)]
                                   {:title [:div.files-history.cursor-pointer
                                            {:key      i :class (when (= i 0) "is-first")
                                             :on-click (fn []

+ 2 - 4
src/main/frontend/components/page.cljs

@@ -1176,7 +1176,7 @@
                      :show-journals? show-journals-in-page-graph})))
 
 (defn batch-delete-dialog
-  [pages orphaned-pages? refresh-fn]
+  [pages refresh-fn]
   (fn [{:keys [close]}]
     [:div
      [:div.sm:flex.items-center
@@ -1185,9 +1185,7 @@
         (ui/icon "alert-triangle")]]
       [:div.mt-3.text-center.sm:mt-0.sm:ml-4.sm:text-left
        [:h3#modal-headline.text-lg.leading-6.font-medium
-        (if orphaned-pages?
-          (t :remove-orphaned-pages)
-          (t :page/delete-confirmation))]]]
+        (t :page/delete-confirmation)]]]
 
      [:ol.p-2.pt-4
       (for [page pages]

+ 2 - 3
src/main/frontend/components/property.cljs

@@ -16,7 +16,6 @@
             [frontend.handler.db-based.property :as db-property-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.property :as property-handler]
-            [frontend.handler.property.util :as pu]
             [frontend.handler.route :as route-handler]
             [frontend.mixins :as mixins]
             [frontend.modules.shortcut.core :as shortcut]
@@ -308,7 +307,7 @@
                                 (db-property-handler/set-block-property! (:db/id property)
                                                                          :logseq.property/icon icon)
                                 (db-property-handler/remove-block-property! (:db/id property)
-                                                                            (pu/get-pid :logseq.property/icon)))
+                                                                            :logseq.property/icon))
                               (shui/popup-hide! id))
                             :icon-value icon
                             :del-btn? (boolean icon)}))]
@@ -374,7 +373,7 @@
                         (and page? (not (contains? types :page)))
                         (conj :page)
                         (empty? types)
-                        #{:block}))
+                        (conj :block)))
         exclude-properties (fn [m]
                              (let [view-context (get m :logseq.property/view-context :all)]
                                (or (contains? #{:logseq.property/query} (:db/ident m))

+ 1 - 1
src/main/frontend/components/property/value.cljs

@@ -117,7 +117,7 @@
          (clear-overlay!)
          (let [^js container (or (some-> js/document.activeElement (.closest ".page"))
                                  (gdom/getElement "main-content-container"))
-               icon (get block (pu/get-pid :logseq.property/icon))]
+               icon (get block :logseq.property/icon)]
            (util/schedule
             (fn []
               (when-let [^js target (some-> (.querySelector container (str "#ls-block-" (str (:block/uuid block))))

+ 4 - 2
src/main/frontend/components/query/builder.cljs

@@ -7,11 +7,13 @@
             [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
             [frontend.db.async :as db-async]
+            [frontend.db.file-based.model :as file-model]
             [frontend.db.model :as db-model]
             [frontend.db.query-dsl :as query-dsl]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.query.builder :as query-builder]
             [frontend.mixins :as mixins]
+            [frontend.ref :as ref]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
@@ -321,7 +323,7 @@
     [:div
      (case @*mode
        "namespace"
-       (let [items (sort (map :block/title (db-model/get-all-namespace-parents repo)))]
+       (let [items (sort (map :block/title (file-model/get-all-namespace-parents repo)))]
          (select items
                  (fn [{:keys [value]}]
                    (append-tree! *tree opts loc [:namespace value]))))
@@ -465,7 +467,7 @@
       (str "Search: " clause)
 
       (= (keyword f) :page-ref)
-      (page-ref/->page-ref (second clause))
+      (ref/->page-ref (second clause))
 
       (contains? #{:tags :page-tags} (keyword f))
       (cond

+ 25 - 6
src/main/frontend/components/views.cljs

@@ -80,10 +80,13 @@
    "ID"])
 
 (rum/defc row-checkbox < rum/static
-  [{:keys [row-selected? row-toggle-selected!]} row _column]
-  (let [id (str (:db/id row) "-" "checkbox")
+  [{:keys [row-selected? row-toggle-selected! data state data-fns]} row _column]
+  (let [idx (.indexOf data (:db/id row))
+        id (str (:db/id row) "-" "checkbox")
         [show? set-show!] (rum/use-state false)
-        checked? (row-selected? row)]
+        checked? (row-selected? row)
+        {:keys [last-selected-idx row-selection]} state
+        {:keys [set-last-selected-idx! set-row-selection!]} data-fns]
     [:label.h-8.w-8.flex.items-center.justify-center.cursor-pointer
      {:html-for (str (:db/id row) "-" "checkbox")
       :on-mouse-over #(set-show! true)
@@ -91,11 +94,24 @@
      (shui/checkbox
       {:id id
        :checked checked?
+       :on-click (fn [e]
+                   (when (and (.-shiftKey e) last-selected-idx)
+                     ;; add selection
+                     (util/stop e)
+                     (when (not= last-selected-idx idx)
+                       (let [new-ids (keep (fn [idx] (util/nth-safe data idx)) (range (min last-selected-idx idx) (inc (max last-selected-idx idx))))]
+                         (when (seq new-ids)
+                           (let [row-selection' (update row-selection :selected-ids set/union (set new-ids))]
+                             (set-row-selection! row-selection')))))))
        :on-checked-change (fn [v]
                             (p/do!
                              (when v (db-async/<get-block (state/get-current-repo) (:db/id row) {:skip-refresh? true
                                                                                                  :children? false}))
-                             (row-toggle-selected! row v)))
+                             (if v
+                               (set-last-selected-idx! idx)
+                               (when (= (:db/id row) last-selected-idx)
+                                 (set-last-selected-idx! nil)))
+                             (row-toggle-selected! row-selection row v)))
        :aria-label "Select row"
        :class (str "flex transition-opacity "
                    (if (or show? checked?) "opacity-100" "opacity-0"))})]))
@@ -1845,6 +1861,7 @@
                                           :set-sized-columns! set-sized-columns!
                                           :set-ordered-columns! set-ordered-columns!})
         [row-selection set-row-selection!] (rum/use-state {})
+        [last-selected-idx set-last-selected-idx!] (rum/use-state nil)
         columns (sort-columns columns ordered-columns)
         select? (first (filter (fn [item] (= (:id item) :select)) columns))
         id? (first (filter (fn [item] (= (:id item) :id)) columns))
@@ -1871,7 +1888,8 @@
                            :ordered-columns ordered-columns
                            :pinned-columns pinned
                            :unpinned-columns unpinned
-                           :group-by-property group-by-property}
+                           :group-by-property group-by-property
+                           :last-selected-idx last-selected-idx}
                    :data-fns {:set-data! set-data!
                               :set-filters! set-filters!
                               :set-sorting! set-sorting!
@@ -1879,7 +1897,8 @@
                               :set-ordered-columns! set-ordered-columns!
                               :set-sized-columns! set-sized-columns!
                               :set-row-selection! set-row-selection!
-                              :add-new-object! add-new-object!}}
+                              :add-new-object! add-new-object!
+                              :set-last-selected-idx! set-last-selected-idx!}}
         table (shui/table-option table-map)
         *view-ref (rum/use-ref nil)
         display-type (if (config/db-based-graph?)

+ 0 - 1
src/main/frontend/components/whiteboard.cljs

@@ -168,7 +168,6 @@
                 (map (fn [id]
                        (some (fn [w] (when (= (:db/id w) id) w)) whiteboards))
                      checked-page-ids)
-                false
                 (fn []
                   (set-checked-page-ids #{})
                   (route-handler/redirect-to-whiteboard-dashboard!)))))}))]

+ 6 - 19
src/main/frontend/db.cljs

@@ -3,7 +3,6 @@
   (:require [frontend.config :as config]
             [frontend.db.conn :as conn]
             [frontend.db.model]
-            [frontend.db.react :as react]
             [frontend.db.utils]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
@@ -25,27 +24,15 @@
   entity pull pull-many]
 
  [frontend.db.model
-  delete-blocks get-pre-block
-  delete-files delete-pages-by-files
-  get-block-and-children get-block-by-uuid get-block-children sort-by-order
-  get-block-parent get-block-parents parents-collapsed? get-block-referenced-blocks
+  delete-files get-block-and-children get-block-by-uuid get-block-children sort-by-order
+  get-block-parent get-block-parents parents-collapsed?
   get-block-immediate-children get-block-page
-  get-custom-css
-  get-file get-file-page get-file-page-id file-exists?
-  get-files-blocks get-files-full get-journals-length
+  get-file file-exists?  get-files-full
   get-latest-journals get-page get-case-page get-page-alias-names
-  get-page-blocks-count get-page-blocks-no-cache get-page-file get-page-format
-  get-referenced-blocks get-page-referenced-blocks-full
-  journal-page? page? page-alias-set sub-block sub-entity
+  get-page-blocks-no-cache get-page-format
+  journal-page? page? sub-block
   page-empty? page-exists? get-alias-source-page
-  has-children? whiteboard-page?
-  get-namespace-pages get-all-namespace-relation]
-
- [frontend.db.react
-  get-current-page
-  remove-query-component! add-q! add-query-component! clear-query-state!
-  q
-  query-state component->query-key set-new-result!])
+  has-children? whiteboard-page?])
 
 (defn start-db-conn!
   ([repo]

+ 2 - 1
src/main/frontend/db/async.cljs

@@ -10,6 +10,7 @@
             [frontend.db.async.util :as db-async-util]
             [frontend.db.file-based.async :as file-async]
             [frontend.db.model :as db-model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.db.react :as react]
             [frontend.db.utils :as db-utils]
             [frontend.handler.file-based.property.util :as property-util]
@@ -251,7 +252,7 @@
                                  [(>= ?d ?day)])]
                       date
                       future-day
-                      db-model/file-graph-block-attrs))]
+                      file-model/file-graph-block-attrs))]
           (->> result
                db-model/sort-by-order-recursive
                db-utils/group-by-page))))))

+ 222 - 0
src/main/frontend/db/file_based/model.cljs

@@ -0,0 +1,222 @@
+(ns frontend.db.file-based.model
+  "Core db functions for file graphs"
+  (:require [clojure.string :as string]
+            [datascript.core :as d]
+            [frontend.common.file-based.db :as common-file-db]
+            [frontend.db.utils :as db-utils]
+            [frontend.db.conn :as conn]
+            [frontend.state :as state]
+            [frontend.util :as util]
+            [logseq.db :as ldb]
+            [logseq.graph-parser.db :as gp-db]))
+
+(def file-graph-block-attrs
+  "In file graphs, use it to replace '*' for datalog queries"
+  '[:db/id
+    :block/uuid
+    :block/parent
+    :block/order
+    :block/collapsed?
+    :block/format
+    :block/refs
+    :block/_refs
+    :block/path-refs
+    :block/tags
+    :block/link
+    :block/title
+    :block/marker
+    :block/priority
+    :block/properties
+    :block/properties-order
+    :block/properties-text-values
+    :block/pre-block?
+    :block/scheduled
+    :block/deadline
+    :block/repeated?
+    :block/created-at
+    :block/updated-at
+    ;; TODO: remove this in later releases
+    :block/heading-level
+    :block/file
+    {:block/page [:db/id :block/name :block/title :block/uuid :block/journal-day :block/type]}
+    {:block/_parent ...}])
+
+;; File-based only
+;; Diverged of get-sorted-page-block-ids
+(defn get-sorted-page-block-ids-and-levels
+  "page-name: the page name, original name
+   return: a list with elements in:
+       :id    - a list of block ids, sorted by :block/order
+       :level - the level of the block, 1 for root, 2 for children of root, etc."
+  [page-name]
+  {:pre [(string? page-name)]}
+  (let [root (ldb/get-page (conn/get-db) page-name)]
+    (loop [result []
+           children (ldb/sort-by-order (:block/_parent root))
+           ;; BFS log of walking depth
+           levels (repeat (count children) 1)]
+      (if (seq children)
+        (let [child (first children)
+              cur-level (first levels)
+              next-children (ldb/sort-by-order (:block/_parent child))]
+          (recur (conj result {:id (:db/id child) :level cur-level})
+                 (concat
+                  next-children
+                  (rest children))
+                 (concat
+                  (repeat (count next-children) (inc cur-level))
+                  (rest levels))))
+        result))))
+
+(defn get-page-file
+  ([page-name]
+   (get-page-file (state/get-current-repo) page-name))
+  ([repo page-name]
+   (when-let [db (conn/get-db repo)]
+     (gp-db/get-page-file db page-name))))
+
+(defn get-block-file-path
+  [block]
+  (when-let [page-id (:db/id (:block/page block))]
+    (:file/path (:block/file (db-utils/entity page-id)))))
+
+(defn get-file-page-id
+  [file-path]
+  (when-let [repo (state/get-current-repo)]
+    (when-let [db (conn/get-db repo)]
+      (some->
+       (d/q
+        '[:find ?page
+          :in $ ?path
+          :where
+          [?file :file/path ?path]
+          [?page :block/name]
+          [?page :block/file ?file]]
+        db file-path)
+       db-utils/seq-flatten
+       first))))
+
+(defn- get-files-blocks
+  [repo-url paths]
+  (let [paths (set paths)
+        pred (fn [_db e]
+               (contains? paths e))]
+    (-> (d/q '[:find ?block
+               :in $ ?pred
+               :where
+               [?file :file/path ?path]
+               [(?pred $ ?path)]
+               [?p :block/file ?file]
+               [?block :block/page ?p]]
+             (conn/get-db repo-url) pred)
+        db-utils/seq-flatten)))
+
+(defn delete-blocks
+  [repo-url files _delete-page?]
+  (when (seq files)
+    (let [blocks (->> (get-files-blocks repo-url files)
+                      (remove nil?))]
+      (mapv (fn [eid] [:db.fn/retractEntity eid]) blocks))))
+
+(defn get-file-page
+  ([file-path]
+   (get-file-page file-path true))
+  ([file-path title?]
+   (when-let [repo (state/get-current-repo)]
+     (when-let [db (conn/get-db repo)]
+       (some->
+        (d/q
+         (if title?
+           '[:find ?page-name
+             :in $ ?path
+             :where
+             [?file :file/path ?path]
+             [?page :block/file ?file]
+             [?page :block/title ?page-name]]
+           '[:find ?page-name
+             :in $ ?path
+             :where
+             [?file :file/path ?path]
+             [?page :block/file ?file]
+             [?page :block/name ?page-name]])
+         db file-path)
+        db-utils/seq-flatten
+        first)))))
+
+;; file-based only so it's safe to use :block/name lookup refs here
+(defn delete-pages-by-files
+  [files]
+  (let [pages (->> (mapv get-file-page files)
+                   (remove nil?))]
+    (when (seq pages)
+      (mapv (fn [page] [:db.fn/retractEntity [:block/name page]]) (map util/page-name-sanity-lc pages)))))
+
+;; TODO: check whether this works when adding pdf back on Web
+(defn get-pre-block
+  [repo page-id]
+  (-> (d/q '[:find (pull ?b [*])
+             :in $ ?page
+             :where
+             [?b :block/page ?page]
+             [?b :block/pre-block? true]]
+           (conn/get-db repo)
+           page-id)
+      ffirst))
+
+(defn- get-all-namespace-relation
+  [repo]
+  (gp-db/get-all-namespace-relation (conn/get-db repo)))
+
+(defn get-all-namespace-parents
+  [repo]
+  (let [db (conn/get-db repo)]
+    (->> (get-all-namespace-relation repo)
+         (map (fn [[_ ?parent]]
+                (db-utils/entity db ?parent))))))
+
+(defn get-namespace-pages
+  "Accepts both sanitized and unsanitized namespaces"
+  [repo namespace]
+  (common-file-db/get-namespace-pages (conn/get-db repo) namespace))
+
+(defn- tree [flat-col root]
+  (let [sort-fn #(sort-by :block/name %)
+        children (group-by :block/namespace flat-col)
+        namespace-children (fn namespace-children [parent-id]
+                             (map (fn [m]
+                                    (assoc m :namespace/children
+                                           (sort-fn (namespace-children {:db/id (:db/id m)}))))
+                                  (sort-fn (get children parent-id))))]
+    (namespace-children root)))
+
+(defn get-namespace-hierarchy
+  "Unsanitized namespaces"
+  [repo namespace]
+  (let [children (get-namespace-pages repo namespace)
+        namespace-id (:db/id (db-utils/entity [:block/name (util/page-name-sanity-lc namespace)]))
+        root {:db/id namespace-id}
+        col (conj children root)]
+    (tree col root)))
+
+(defn get-page-namespace
+  [repo page]
+  (:block/namespace (db-utils/entity repo [:block/name (util/page-name-sanity-lc page)])))
+
+(defn get-page-namespace-routes
+  [repo page]
+  (assert (string? page))
+  (when-let [db (conn/get-db repo)]
+    (when-not (string/blank? page)
+      (let [page (util/page-name-sanity-lc (string/trim page))
+            page-exist? (db-utils/entity repo [:block/name page])
+            ids (if page-exist?
+                  '()
+                  (->> (d/datoms db :aevt :block/name)
+                       (filter (fn [datom]
+                                 (string/ends-with? (:v datom) (str "/" page))))
+                       (map :e)))]
+        (when (seq ids)
+          (db-utils/pull-many repo
+                              '[:db/id :block/name :block/title
+                                {:block/file [:db/id :file/path]}]
+                              ids))))))

+ 14 - 285
src/main/frontend/db/model.cljs

@@ -1,64 +1,27 @@
 (ns frontend.db.model
   "Core db functions."
-  ;; TODO: Remove this config once how repos are passed to this ns are standardized
-  {:clj-kondo/config {:linters {:unused-binding {:level :off}}}}
   (:require [clojure.set :as set]
             [clojure.string :as string]
             [clojure.walk :as walk]
             [datascript.core :as d]
-            [frontend.common.file-based.db :as common-file-db]
             [frontend.common.graph-view :as graph-view]
             [frontend.config :as config]
             [frontend.date :as date]
             [frontend.db.conn :as conn]
+            [frontend.db.file-based.model :as file-model]
             [frontend.db.react :as react]
             [frontend.db.utils :as db-utils]
             [frontend.state :as state]
             [frontend.util :as util :refer [react]]
             [logseq.common.util :as common-util]
-            [logseq.common.util.date-time :as date-time-util]
             [logseq.db :as ldb]
             [logseq.db.frontend.class :as db-class]
             [logseq.db.frontend.content :as db-content]
-            [logseq.db.frontend.rules :as rules]
-            [logseq.graph-parser.db :as gp-db]
-            [logseq.shui.hooks :as hooks]))
+            [logseq.db.frontend.rules :as rules]))
 
 ;; TODO: extract to specific models and move data transform logic to the
 ;; corresponding handlers.
 
-(def file-graph-block-attrs
-  "In file graphs, use it to replace '*' for datalog queries"
-  '[:db/id
-    :block/uuid
-    :block/parent
-    :block/order
-    :block/collapsed?
-    :block/format
-    :block/refs
-    :block/_refs
-    :block/path-refs
-    :block/tags
-    :block/link
-    :block/title
-    :block/marker
-    :block/priority
-    :block/properties
-    :block/properties-order
-    :block/properties-text-values
-    :block/pre-block?
-    :block/scheduled
-    :block/deadline
-    :block/repeated?
-    :block/created-at
-    :block/updated-at
-    ;; TODO: remove this in later releases
-    :block/heading-level
-    :block/file
-    :logseq.property/parent
-    {:block/page [:db/id :block/name :block/title :block/uuid :block/journal-day :block/type]}
-    {:block/_parent ...}])
-
 (def hidden-page? ldb/hidden?)
 
 (defn get-alias-source-page
@@ -67,21 +30,6 @@
   (when-let [db (conn/get-db repo)]
     (ldb/get-alias-source-page db alias-id)))
 
-(defn get-files-blocks
-  [repo-url paths]
-  (let [paths (set paths)
-        pred (fn [_db e]
-               (contains? paths e))]
-    (-> (d/q '[:find ?block
-               :in $ ?pred
-               :where
-               [?file :file/path ?path]
-               [(?pred $ ?path)]
-               [?p :block/file ?file]
-               [?block :block/page ?p]]
-             (conn/get-db repo-url) pred)
-        db-utils/seq-flatten)))
-
 (defn file-exists?
   [repo path]
   (when (and repo path)
@@ -197,8 +145,7 @@ independent of format as format specific heading characters are stripped"
 
 (defn get-page-alias-names
   [repo page-id]
-  (let [page (db-utils/entity page-id)
-        alias-ids (->> (page-alias-set repo page-id)
+  (let [alias-ids (->> (page-alias-set repo page-id)
                        (remove #{page-id}))]
     (when (seq alias-ids)
       (map (fn [id] (:block/title (db-utils/entity id))) alias-ids))))
@@ -234,21 +181,6 @@ independent of format as format specific heading characters are stripped"
               (when-let [id (:db/id e)]
                 (db-utils/entity id))))))))
 
-(defn sub-entity
-  "Used for react function components"
-  [entity* watch-id]
-  (let [id (:db/id entity*)
-        *ref (sub-block id {:ref? true})
-        [entity set-entity!] (hooks/use-state @*ref)]
-    (add-watch *ref watch-id (fn [_ _ _ new-value]
-                               (set-entity! new-value)))
-    (hooks/use-effect!
-     (fn []
-       #(remove-watch *ref watch-id))
-     [])
-
-    [entity set-entity!]))
-
 (defn sort-by-order-recursive
   [form]
   (walk/postwalk (fn [f]
@@ -261,33 +193,6 @@ independent of format as format specific heading characters are stripped"
                      f))
                  form))
 
-;; File-based only
-;; Diverged of get-sorted-page-block-ids
-(defn get-sorted-page-block-ids-and-levels
-  "page-name: the page name, original name
-   return: a list with elements in:
-       :id    - a list of block ids, sorted by :block/order
-       :level - the level of the block, 1 for root, 2 for children of root, etc."
-  [page-name]
-  {:pre [(string? page-name)]}
-  (let [root (ldb/get-page (conn/get-db) page-name)]
-    (loop [result []
-           children (sort-by-order (:block/_parent root))
-           ;; BFS log of walking depth
-           levels (repeat (count children) 1)]
-      (if (seq children)
-        (let [child (first children)
-              cur-level (first levels)
-              next-children (sort-by-order (:block/_parent child))]
-          (recur (conj result {:id (:db/id child) :level cur-level})
-                 (concat
-                  next-children
-                  (rest children))
-                 (concat
-                  (repeat (count next-children) (inc cur-level))
-                  (rest levels))))
-        result))))
-
 (defn has-children?
   ([block-id]
    (has-children? (conn/get-db) block-id))
@@ -421,59 +326,6 @@ independent of format as format specific heading characters are stripped"
   (let [db (conn/get-db repo)]
     (ldb/get-block-and-children db block-uuid opts)))
 
-(defn get-file-page
-  ([file-path]
-   (get-file-page file-path true))
-  ([file-path title?]
-   (when-let [repo (state/get-current-repo)]
-     (when-let [db (conn/get-db repo)]
-       (some->
-        (d/q
-         (if title?
-           '[:find ?page-name
-             :in $ ?path
-             :where
-             [?file :file/path ?path]
-             [?page :block/file ?file]
-             [?page :block/title ?page-name]]
-           '[:find ?page-name
-             :in $ ?path
-             :where
-             [?file :file/path ?path]
-             [?page :block/file ?file]
-             [?page :block/name ?page-name]])
-         db file-path)
-        db-utils/seq-flatten
-        first)))))
-
-(defn get-page-file
-  ([page-name]
-   (get-page-file (state/get-current-repo) page-name))
-  ([repo page-name]
-   (when-let [db (conn/get-db repo)]
-     (gp-db/get-page-file db page-name))))
-
-(defn get-block-file-path
-  [block]
-  (when-let [page-id (:db/id (:block/page block))]
-    (:file/path (:block/file (db-utils/entity page-id)))))
-
-(defn get-file-page-id
-  [file-path]
-  (when-let [repo (state/get-current-repo)]
-    (when-let [db (conn/get-db repo)]
-      (some->
-       (d/q
-        '[:find ?page
-          :in $ ?path
-          :where
-          [?file :file/path ?path]
-          [?page :block/name]
-          [?page :block/file ?file]]
-        db file-path)
-       db-utils/seq-flatten
-       first))))
-
 (defn get-page
   [page-id-name-or-uuid]
   (when page-id-name-or-uuid
@@ -522,27 +374,6 @@ independent of format as format specific heading characters are stripped"
                (:block/name page-entity)
                page-name)))))))
 
-(defn get-journals-length
-  []
-  (let [today (date-time-util/date->int (js/Date.))]
-    (if (config/db-based-graph?)
-      (d/q '[:find (count ?page) .
-             :in $ ?today
-             :where
-             [?page :block/tags :logseq.class/Journal]
-             [?page :block/journal-day ?journal-day]
-             [(<= ?journal-day ?today)]]
-           (conn/get-db (state/get-current-repo))
-           today)
-      (d/q '[:find (count ?page) .
-             :in $ ?today
-             :where
-             [?page :block/type "journal"]
-             [?page :block/journal-day ?journal-day]
-             [(<= ?journal-day ?today)]]
-           (conn/get-db (state/get-current-repo))
-           today))))
-
 (defn get-latest-journals
   ([n]
    (get-latest-journals (state/get-current-repo) n))
@@ -558,10 +389,8 @@ independent of format as format specific heading characters are stripped"
 
 (defn get-page-referenced-blocks-full
   ([page-id]
-   (get-page-referenced-blocks-full (state/get-current-repo) page-id nil))
-  ([page-id options]
-   (get-page-referenced-blocks-full (state/get-current-repo) page-id options))
-  ([repo page-id options]
+   (get-page-referenced-blocks-full (state/get-current-repo) page-id))
+  ([repo page-id]
    (when (and repo page-id)
      (when-let [db (conn/get-db repo)]
        (let [pages (page-alias-set repo page-id)
@@ -574,7 +403,7 @@ independent of format as format specific heading characters are stripped"
              [?block :block/path-refs ?ref-page]]
            db
            pages
-           (butlast file-graph-block-attrs))
+           (butlast file-model/file-graph-block-attrs))
           (remove (fn [block] (= page-id (:db/id (:block/page block)))))
           db-utils/group-by-page
           (map (fn [[k blocks]]
@@ -585,10 +414,8 @@ independent of format as format specific heading characters are stripped"
 
 (defn get-referenced-blocks
   ([eid]
-   (get-referenced-blocks (state/get-current-repo) eid nil))
-  ([eid options]
-   (get-referenced-blocks (state/get-current-repo) eid options))
-  ([repo eid options]
+   (get-referenced-blocks (state/get-current-repo) eid))
+  ([repo eid]
    (when repo
      (when (conn/get-db repo)
        (let [entity (db-utils/entity eid)
@@ -606,14 +433,12 @@ independent of format as format specific heading characters are stripped"
               (util/distinct-by :db/id)))))))
 
 (defn get-block-referenced-blocks
-  ([block-id]
-   (get-block-referenced-blocks block-id {}))
-  ([block-id options]
-   (when-let [repo (state/get-current-repo)]
-     (when (conn/get-db repo)
-       (->> (get-referenced-blocks repo block-id options)
-            (sort-by-order-recursive)
-            db-utils/group-by-page)))))
+  [block-id]
+  (when-let [repo (state/get-current-repo)]
+    (when (conn/get-db repo)
+      (->> (get-referenced-blocks repo block-id)
+           (sort-by-order-recursive)
+           db-utils/group-by-page))))
 
 (defn journal-page?
   "sanitized page-name only"
@@ -635,37 +460,10 @@ independent of format as format specific heading characters are stripped"
            [?refed-b   :block/uuid ?refed-uuid]
            [?referee-b :block/refs ?refed-b]] db)))
 
-(defn delete-blocks
-  [repo-url files _delete-page?]
-  (when (seq files)
-    (let [blocks (->> (get-files-blocks repo-url files)
-                      (remove nil?))]
-      (mapv (fn [eid] [:db.fn/retractEntity eid]) blocks))))
-
 (defn delete-files
   [files]
   (mapv (fn [path] [:db.fn/retractEntity [:file/path path]]) files))
 
-;; file-based only so it's safe to use :block/name lookup refs here
-(defn delete-pages-by-files
-  [files]
-  (let [pages (->> (mapv get-file-page files)
-                   (remove nil?))]
-    (when (seq pages)
-      (mapv (fn [page] [:db.fn/retractEntity [:block/name page]]) (map util/page-name-sanity-lc pages)))))
-
-;; TODO: check whether this works when adding pdf back on Web
-(defn get-pre-block
-  [repo page-id]
-  (-> (d/q '[:find (pull ?b [*])
-             :in $ ?page
-             :where
-             [?b :block/page ?page]
-             [?b :block/pre-block? true]]
-           (conn/get-db repo)
-           page-id)
-      ffirst))
-
 (defn whiteboard-page?
   "Given a page entity, page object or page name, check if it is a whiteboard page"
   [page]
@@ -674,17 +472,6 @@ independent of format as format specific heading characters are stripped"
                page)]
     (ldb/whiteboard? page)))
 
-(comment
-  (defn get-orphaned-pages
-    [opts]
-    (let [db (conn/get-db)]
-      (ldb/get-orphaned-pages db
-                              (merge opts
-                                     {:built-in-pages-names
-                                      (if (config/db-based-graph? (state/get-current-repo))
-                                        sqlite-create-graph/built-in-pages-names
-                                        gp-db/built-in-pages-names)})))))
-
 ;; FIXME: use `Untitled` instead of UUID for db based graphs
 (defn untitled-page?
   [page-name]
@@ -771,64 +558,6 @@ independent of format as format specific heading characters are stripped"
        (:block/_tags class))
      (remove ldb/hidden?))))
 
-(defn get-all-namespace-relation
-  [repo]
-  (gp-db/get-all-namespace-relation (conn/get-db repo)))
-
-(defn get-all-namespace-parents
-  [repo]
-  (let [db (conn/get-db repo)]
-    (->> (get-all-namespace-relation repo)
-         (map (fn [[_ ?parent]]
-                (db-utils/entity db ?parent))))))
-
-(defn get-namespace-pages
-  "Accepts both sanitized and unsanitized namespaces"
-  [repo namespace]
-  (common-file-db/get-namespace-pages (conn/get-db repo) namespace))
-
-(defn- tree [flat-col root]
-  (let [sort-fn #(sort-by :block/name %)
-        children (group-by :block/namespace flat-col)
-        namespace-children (fn namespace-children [parent-id]
-                             (map (fn [m]
-                                    (assoc m :namespace/children
-                                           (sort-fn (namespace-children {:db/id (:db/id m)}))))
-                                  (sort-fn (get children parent-id))))]
-    (namespace-children root)))
-
-(defn get-namespace-hierarchy
-  "Unsanitized namespaces"
-  [repo namespace]
-  (let [children (get-namespace-pages repo namespace)
-        namespace-id (:db/id (db-utils/entity [:block/name (util/page-name-sanity-lc namespace)]))
-        root {:db/id namespace-id}
-        col (conj children root)]
-    (tree col root)))
-
-(defn get-page-namespace
-  [repo page]
-  (:block/namespace (db-utils/entity repo [:block/name (util/page-name-sanity-lc page)])))
-
-(defn get-page-namespace-routes
-  [repo page]
-  (assert (string? page))
-  (when-let [db (conn/get-db repo)]
-    (when-not (string/blank? page)
-      (let [page (util/page-name-sanity-lc (string/trim page))
-            page-exist? (db-utils/entity repo [:block/name page])
-            ids (if page-exist?
-                  '()
-                  (->> (d/datoms db :aevt :block/name)
-                       (filter (fn [datom]
-                                 (string/ends-with? (:v datom) (str "/" page))))
-                       (map :e)))]
-        (when (seq ids)
-          (db-utils/pull-many repo
-                              '[:db/id :block/name :block/title
-                                {:block/file [:db/id :file/path]}]
-                              ids))))))
-
 (comment
   ;; For debugging
   (defn get-all-blocks

+ 2 - 2
src/main/frontend/db/query_custom.cljs

@@ -2,7 +2,7 @@
   "Handles executing custom queries a.k.a. advanced queries"
   (:require [clojure.walk :as walk]
             [frontend.config :as config]
-            [frontend.db.model :as model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.db.query-dsl :as query-dsl]
             [frontend.db.query-react :as query-react]
             [frontend.state :as state]
@@ -13,7 +13,7 @@
 ;; FIXME: what if users want to query other attributes than block-attrs?
 (defn- replace-star-with-block-attrs!
   [l]
-  (let [block-attrs (butlast model/file-graph-block-attrs)]
+  (let [block-attrs (butlast file-model/file-graph-block-attrs)]
     (walk/postwalk
      (fn [f]
        (if (and (list? f)

+ 2 - 2
src/main/frontend/db/query_dsl.cljs

@@ -8,7 +8,7 @@
             [clojure.walk :as walk]
             [frontend.config :as config]
             [frontend.date :as date]
-            [frontend.db.model :as model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.db.query-react :as query-react]
             [frontend.db.utils :as db-utils]
             [frontend.state :as state]
@@ -707,7 +707,7 @@ Some bindings in this fn:
 
 (defn query-wrapper
   [where {:keys [blocks? block-attrs]}]
-  (let [block-attrs (or block-attrs (butlast model/file-graph-block-attrs))
+  (let [block-attrs (or block-attrs (butlast file-model/file-graph-block-attrs))
         q (if blocks?                   ; FIXME: it doesn't need to be either blocks or pages
             `[:find (~'pull ~'?b ~block-attrs)
               :in ~'$ ~'%

+ 0 - 16
src/main/frontend/db/react.cljs

@@ -6,14 +6,11 @@
   "
   (:require [clojure.core.async :as async]
             [datascript.core :as d]
-            [frontend.date :as date]
             [frontend.db.async.util :as db-async-util]
             [frontend.db.conn :as conn]
             [frontend.db.utils :as db-utils]
             [frontend.state :as state]
             [frontend.util :as util]
-            [logseq.common.util :as common-util]
-            [logseq.db :as ldb]
             [promesa.core :as p]))
 
 ;; Query atom of map of Key ([repo q inputs]) -> atom
@@ -151,19 +148,6 @@
                 (set! (.-state result-atom) result')
                 result-atom))))))))
 
-(defn get-current-page
-  []
-  (let [match (:route-match @state/state)
-        route-name (get-in match [:data :name])
-        page (case route-name
-               :page
-               (get-in match [:path-params :name])
-               (date/journal-name))]
-    (when page
-      (if (common-util/uuid-string? page)
-        (db-utils/entity [:block/uuid (uuid page)])
-        (ldb/get-page (conn/get-db) page)))))
-
 (defn- execute-query!
   [graph db k {:keys [query inputs transform-fn query-fn inputs-fn result built-in-query?]
                :or {transform-fn identity}}]

+ 2 - 2
src/main/frontend/extensions/pdf/assets.cljs

@@ -19,12 +19,12 @@
             [frontend.handler.property :as property-handler]
             [frontend.handler.property.util :as pu]
             [frontend.handler.route :as route-handler]
+            [frontend.ref :as ref]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [logseq.common.config :as common-config]
             [logseq.common.path :as path]
-            [logseq.common.util.block-ref :as block-ref]
             [logseq.publishing.db :as publish-db]
             [medley.core :as medley]
             [promesa.core :as p]
@@ -324,7 +324,7 @@
   (p/let [ref-block (ensure-ref-block! (state/get-current-pdf) highlight nil)]
     (when ref-block
       (util/copy-to-clipboard!
-       (block-ref/->block-ref (:block/uuid ref-block))
+       (ref/->block-ref (:block/uuid ref-block))
        :owner-window (pdf-windows/resolve-own-window viewer)))))
 
 (defn file-based-open-block-ref!

+ 2 - 2
src/main/frontend/extensions/zotero/handler.cljs

@@ -9,8 +9,8 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
+            [frontend.ref :as ref]
             [frontend.state :as state]
-            [logseq.common.util.page-ref :as page-ref]
             [promesa.core :as p]))
 
 ;; TODO: test
@@ -48,7 +48,7 @@
 (defn handle-command-zotero
   [id page-name]
   (state/clear-editor-action!)
-  (editor-handler/insert-command! id (page-ref/->page-ref page-name) nil {}))
+  (editor-handler/insert-command! id (ref/->page-ref page-name) nil {}))
 
 (defn- create-abstract-note!
   [page-name abstract-note]

+ 2 - 2
src/main/frontend/fs/diff_merge.cljs

@@ -2,7 +2,7 @@
   "Implementation of text (file) based content diff & merge for conflict resolution"
   (:require ["@logseq/diff-merge" :refer [attach_uuids Differ Merger]]
             [cljs-bean.core :as bean]
-            [frontend.db.model :as db-model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.db.utils :as db-utils]
             [logseq.graph-parser.block :as gp-block]
             [logseq.graph-parser.mldoc :as gp-mldoc]
@@ -29,7 +29,7 @@
    page-name: string"
   [page-name]
   {:pre (string? page-name)}
-  (let [walked (db-model/get-sorted-page-block-ids-and-levels page-name)
+  (let [walked (file-model/get-sorted-page-block-ids-and-levels page-name)
         blocks (db-utils/pull-many [:block/uuid :block/title :block/level] (map :id walked))
         levels (map :level walked)
         blocks (map (fn [block level]

+ 3 - 2
src/main/frontend/fs/watcher_handler.cljs

@@ -6,6 +6,7 @@
             [frontend.db :as db]
             [frontend.db.async :as db-async]
             [frontend.db.model :as model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.fs :as fs]
             [frontend.handler.file-based.file :as file-handler]
             [frontend.handler.file-based.property :as file-property-handler]
@@ -101,7 +102,7 @@
                      exists-in-db?)
                 (p/let [dir-exists? (fs/file-exists? dir "")]
                   (when dir-exists?
-                    (when-let [page-name (db/get-file-page path)]
+                    (when-let [page-name (file-model/get-file-page path)]
                       (println "Delete page: " page-name ", file path: " path ".")
                       (page-handler/<delete! page-name #()))))
 
@@ -177,7 +178,7 @@
         (p/do!
          (when (seq deleted-files)
            (p/all (map (fn [path]
-                         (when-let [page-name (db/get-file-page path)]
+                         (when-let [page-name (file-model/get-file-page path)]
                            (println "Delete page: " page-name ", file path: " path ".")
                            (page-handler/<delete! page-name #())))
                        deleted-files)))

+ 5 - 7
src/main/frontend/handler/dnd.cljs

@@ -1,15 +1,13 @@
 (ns frontend.handler.dnd
   "Provides fns for drag and drop"
-  (:require [frontend.config :as config]
-            [frontend.db :as db]
+  (:require [frontend.db :as db]
             [frontend.handler.block :as block-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.property :as property-handler]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
+            [frontend.ref :as ref]
             [frontend.state :as state]
-            [logseq.common.util.block-ref :as block-ref]
-            [logseq.common.util.page-ref :as page-ref]
             [logseq.db :as ldb]))
 
 (defn move-blocks
@@ -27,15 +25,15 @@
     (cond
       ;; alt pressed, make a block-ref
       (and alt-key? (= (count blocks) 1))
-      (let [->ref (if (config/db-based-graph?) page-ref/->page-ref block-ref/->block-ref)]
+      (do
         (property-handler/file-persist-block-id! (state/get-current-repo) (:block/uuid first-block))
         (editor-handler/api-insert-new-block!
-         (->ref (:block/uuid first-block))
+         (ref/->block-ref (:block/uuid first-block))
          {:block-uuid (:block/uuid target-block)
           :sibling? (not nested?)
           :before? top?}))
 
-      ;; format mismatch
+;; format mismatch
       (and current-format target-format (not= current-format target-format))
       (state/pub-event! [:notification/show
                          {:content [:div "Those two pages have different formats."]

+ 24 - 20
src/main/frontend/handler/editor.cljs

@@ -8,6 +8,7 @@
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.db.async :as db-async]
+            [frontend.db.file-based.model :as file-model]
             [frontend.db.model :as db-model]
             [frontend.db.utils :as db-utils]
             [frontend.diff :as diff]
@@ -19,6 +20,7 @@
             [frontend.handler.assets :as assets-handler]
             [frontend.handler.block :as block-handler]
             [frontend.handler.common :as common-handler]
+            [frontend.handler.common.editor :as editor-common-handler]
             [frontend.handler.db-based.editor :as db-editor-handler]
             [frontend.handler.export.html :as export-html]
             [frontend.handler.export.text :as export-text]
@@ -33,6 +35,7 @@
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.tree :as tree]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
+            [frontend.ref :as ref]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.template :as template]
@@ -66,8 +69,7 @@
             [logseq.outliner.property :as outliner-property]
             [logseq.shui.popup.core :as shui-popup]
             [promesa.core :as p]
-            [rum.core :as rum]
-            [frontend.handler.common.editor :as editor-common-handler]))
+            [rum.core :as rum]))
 
 ;; FIXME: should support multiple images concurrently uploading
 
@@ -1006,12 +1008,12 @@
           copy-str (some->> adjusted-blocks
                             (map (fn [{:keys [id level]}]
                                    (if (config/db-based-graph? (state/get-current-repo))
-                                     (str (string/join (repeat (dec level) "\t")) "- " (page-ref/->page-ref id))
+                                     (str (string/join (repeat (dec level) "\t")) "- " (ref/->page-ref id))
                                      (condp = (get block :block/format :markdown)
                                        :org
-                                       (str (string/join (repeat level "*")) " " (block-ref/->block-ref id))
+                                       (str (string/join (repeat level "*")) " " (ref/->block-ref id))
                                        :markdown
-                                       (str (string/join (repeat (dec level) "\t")) "- " (block-ref/->block-ref id))))))
+                                       (str (string/join (repeat (dec level) "\t")) "- " (ref/->block-ref id))))))
                             (string/join "\n\n"))]
       (set-blocks-id! (map :id blocks))
       (util/copy-to-clipboard! copy-str))))
@@ -1024,7 +1026,7 @@
                    (remove nil?))
           ids-str (if (config/db-based-graph? (state/get-current-repo))
                     (some->> ids
-                             (map (fn [id] (block-ref/->block-ref id)))
+                             (map (fn [id] (ref/->block-ref id)))
                              (string/join "\n\n"))
                     (some->> ids
                              (map (fn [id] (util/format "{{embed ((%s))}}" id)))
@@ -1389,7 +1391,7 @@
                                     {:block-id block-id})))
         text (:block/title block)
         content (if asset-block
-                  (string/replace text (page-ref/->page-ref (:block/uuid asset-block)) "")
+                  (string/replace text (ref/->page-ref (:block/uuid asset-block)) "")
                   (string/replace text full-text ""))]
     (save-block! repo block content)
     (when (and local? delete-local?)
@@ -1397,7 +1399,7 @@
         (delete-block-aux! asset-block)
         (when-let [href (if (util/electron?) href
                             (second (re-find #"\((.+)\)$" full-text)))]
-          (let [block-file-rpath (db-model/get-block-file-path block)
+          (let [block-file-rpath (file-model/get-block-file-path block)
                 asset-fpath (if (string/starts-with? href "assets://")
                               (path/url-to-path href)
                               (config/get-repo-fpath
@@ -1496,7 +1498,7 @@
            (let [entity (first entities)]
              (insert-command!
               id
-              (page-ref/->page-ref (:block/uuid entity))
+              (ref/->page-ref (:block/uuid entity))
               format
               {:last-pattern (if drop-or-paste? "" commands/command-trigger)
                :restore?     true
@@ -1922,7 +1924,7 @@
 
       ;; block reference
       (insert-command! id
-                       (block-ref/->block-ref uuid-string)
+                       (ref/->block-ref uuid-string)
                        format
                        {:last-pattern (str block-ref/left-parens (if selected-text "" q))
                         :end-pattern block-ref/right-parens
@@ -2387,7 +2389,7 @@
               {:keys [selection-start selection-end selection]} selection]
           (if selection
             (do (delete-and-update input selection-start selection-end)
-                (insert (page-ref/->page-ref selection)))
+                (insert (ref/->page-ref selection)))
             (if-let [embed-ref (thingatpt/embed-macro-at-point input)]
               (let [{:keys [raw-content start end]} embed-ref]
                 (delete-and-update input start end)
@@ -3193,15 +3195,15 @@
           (if db?
             (p/do!
              (save-current-block!)
-             (util/copy-to-clipboard! (page-ref/->page-ref block-id)
+             (util/copy-to-clipboard! (ref/->page-ref block-id)
                                       {:graph (state/get-current-repo)
                                        :blocks [{:block/uuid (:block/uuid current-block)}]
                                        :embed-block? true}))
             (copy-block-ref! block-id #(str "{{embed ((" % "))}}")))
           (copy-block-ref! block-id
                            (if db?
-                             page-ref/->page-ref
-                             block-ref/->block-ref)))))))
+                             ref/->page-ref
+                             ref/->block-ref)))))))
 
 (defn copy-current-block-embed []
   (copy-current-block-ref "embed"))
@@ -3847,14 +3849,16 @@
 (defn copy-current-ref
   [block-id]
   (when block-id
-    (util/copy-to-clipboard! (block-ref/->block-ref block-id))))
+    (util/copy-to-clipboard! (ref/->block-ref block-id))))
 
 (defn delete-current-ref!
   [block ref-id]
   (when (and block ref-id)
-    (let [match (re-pattern (str "\\s?"
-                                 (string/replace (block-ref/->block-ref ref-id) #"([\(\)])" "\\$1")))
-          content (string/replace-first (:block/title block) match "")]
+    (let [content (if (config/db-based-graph?)
+                    (string/replace (:block/title block) (ref/->page-ref ref-id) "")
+                    (let [match (re-pattern (str "\\s?"
+                                                 (string/replace (ref/->block-ref ref-id) #"([\(\)])" "\\$1")))]
+                      (string/replace (:block/title block) match "")))]
       (save-block! (state/get-current-repo)
                    (:block/uuid block)
                    content))))
@@ -3863,7 +3867,7 @@
   [block ref-id]
   (when (and block ref-id)
     (let [repo (state/get-current-repo)
-          match (block-ref/->block-ref ref-id)
+          match (ref/->block-ref ref-id)
           ref-block (db/entity [:block/uuid ref-id])
           block-ref-content (->> (or (:block/title ref-block) "")
                                  (property-file/remove-built-in-properties-when-file-based repo
@@ -3878,7 +3882,7 @@
 (defn replace-ref-with-embed!
   [block ref-id]
   (when (and block ref-id)
-    (let [match (block-ref/->block-ref ref-id)
+    (let [match (ref/->block-ref ref-id)
           content (string/replace-first (:block/title block) match
                                         (util/format "{{embed ((%s))}}"
                                                      (str ref-id)))]

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

@@ -91,9 +91,7 @@
 
 (defmethod events/handle :page/show-delete-dialog [[_ selected-rows ok-handler]]
   (shui/dialog-open!
-   (component-page/batch-delete-dialog
-    selected-rows false
-    ok-handler)))
+   (component-page/batch-delete-dialog selected-rows ok-handler)))
 
 (defn ask-permission
   [repo]

+ 2 - 2
src/main/frontend/handler/file_based/editor.cljs

@@ -5,7 +5,7 @@
             [frontend.config :as config]
             [frontend.date :as date]
             [frontend.db :as db]
-            [frontend.db.model :as db-model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.db.query-dsl :as query-dsl]
             [frontend.format.block :as block]
             [frontend.format.mldoc :as mldoc]
@@ -354,7 +354,7 @@
 
    Requires editing state"
   [file-path]
-  (if-let [current-file-rpath (or (db-model/get-block-file-path (state/get-edit-block))
+  (if-let [current-file-rpath (or (file-model/get-block-file-path (state/get-edit-block))
                                   ;; fix dummy file path of page
                                   (when (config/get-pages-directory)
                                     (path/path-join (config/get-pages-directory) "_.md"))

+ 2 - 1
src/main/frontend/handler/file_based/file.cljs

@@ -3,6 +3,7 @@
   (:refer-clojure :exclude [load-file])
   (:require [frontend.config :as config]
             [frontend.db :as db]
+            [frontend.db.file-based.model :as file-model]
             [frontend.fs :as fs]
             [frontend.fs.nfs :as nfs]
             [frontend.fs.capacitor-fs :as capacitor-fs]
@@ -163,7 +164,7 @@
             result (if reset?
                      (do
                        (when-not skip-db-transact?
-                         (when-let [page-id (db/get-file-page-id path)]
+                         (when-let [page-id (file-model/get-file-page-id path)]
                            (db/transact! repo
                                          [[:db/retract page-id :block/alias]
                                           [:db/retract page-id :block/tags]]

+ 3 - 4
src/main/frontend/handler/file_based/page.cljs

@@ -2,8 +2,7 @@
   (:require [clojure.string :as string]
             [frontend.config :as config]
             [frontend.date :as date]
-            [frontend.db :as db]
-            [frontend.db.model :as model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.handler.common.page :as page-common-handler]
             [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
@@ -29,11 +28,11 @@
 
 (defn get-page-ref-text
   [page]
-  (let [edit-block-file-path (model/get-block-file-path (state/get-edit-block))
+  (let [edit-block-file-path (file-model/get-block-file-path (state/get-edit-block))
         page-name (string/lower-case page)]
     (if (and edit-block-file-path
              (state/org-mode-file-link? (state/get-current-repo)))
-      (if-let [ref-file-path (:file/path (db/get-page-file page-name))]
+      (if-let [ref-file-path (:file/path (file-model/get-page-file page-name))]
         (util/format "[[file:%s][%s]]"
                      (util/get-relative-path edit-block-file-path ref-file-path)
                      page)

+ 2 - 1
src/main/frontend/handler/file_based/page_property.cljs

@@ -2,6 +2,7 @@
   "Page property fns for file graphs"
   (:require [clojure.string :as string]
             [frontend.db :as db]
+            [frontend.db.file-based.model :as file-model]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.state :as state]
@@ -53,7 +54,7 @@
   [page key value]
   (let [repo (state/get-current-repo)
         key (keyword key)
-        pre-block (db/get-pre-block repo (:db/id page))
+        pre-block (file-model/get-pre-block repo (:db/id page))
         format (state/get-preferred-format)
         page-id {:db/id (:db/id page)}
         org? (= format :org)

+ 4 - 3
src/main/frontend/handler/file_based/repo.cljs

@@ -2,6 +2,7 @@
   "Repo fns for creating, loading and parsing file graphs"
   (:require [frontend.config :as config]
             [frontend.db :as db]
+            [frontend.db.file-based.model :as file-model]
             [frontend.fs :as fs]
             [frontend.handler.file-based.file :as file-handler]
             [frontend.handler.repo-config :as repo-config-handler]
@@ -313,14 +314,14 @@
               add-files (filter-diffs "add")
               delete-files (when (seq remove-files)
                              (db/delete-files remove-files))
-              delete-blocks (db/delete-blocks repo-url remove-files true)
+              delete-blocks (file-model/delete-blocks repo-url remove-files true)
               delete-blocks (->>
                              (concat
                               delete-blocks
-                              (db/delete-blocks repo-url modify-files false))
+                              (file-model/delete-blocks repo-url modify-files false))
                              (remove nil?))
               delete-pages (if (seq remove-files)
-                             (db/delete-pages-by-files remove-files)
+                             (file-model/delete-pages-by-files remove-files)
                              [])
               add-or-modify-files (some->>
                                    (concat modify-files add-files)

+ 2 - 1
src/main/frontend/handler/file_based/reset_file.cljs

@@ -3,6 +3,7 @@
   (:require [frontend.config :as config]
             [frontend.state :as state]
             [frontend.db :as db]
+            [frontend.db.file-based.model :as file-model]
             [logseq.graph-parser :as graph-parser]
             [logseq.common.util :as common-util]
             [frontend.fs.diff-merge :as diff-merge]
@@ -17,7 +18,7 @@
   "Conflict of files towards same page"
   [repo-url page file]
   (when-let [page-name (:block/name page)]
-    (let [current-file (:file/path (db/get-page-file repo-url page-name))]
+    (let [current-file (:file/path (file-model/get-page-file repo-url page-name))]
       (when (not= file current-file)
         current-file))))
 

+ 6 - 5
src/main/frontend/handler/page.cljs

@@ -28,6 +28,7 @@
             [frontend.mobile.util :as mobile-util]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
+            [frontend.ref :as ref]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.cursor :as cursor]
@@ -145,7 +146,7 @@
 (defn get-page-ref-text
   [page]
   (if (config/db-based-graph?)
-    (page-ref/->page-ref page)
+    (ref/->page-ref page)
     (file-page-handler/get-page-ref-text page)))
 
 (defn init-commands!
@@ -196,7 +197,7 @@
   (if (state/org-mode-file-link? (state/get-current-repo))
     (let [page-ref-text (get-page-ref-text q)
           value (gobj/get input "value")
-          old-page-ref (page-ref/->page-ref q)
+          old-page-ref (ref/->page-ref q)
           new-value (string/replace value
                                     old-page-ref
                                     page-ref-text)]
@@ -232,7 +233,7 @@
                              (text/get-namespace-last-part chosen)
                              chosen)
           wrapped-tag (if (and (util/safe-re-find #"\s+" chosen-last-part) (not wrapped?))
-                        (page-ref/->page-ref chosen-last-part)
+                        (ref/->page-ref chosen-last-part)
                         chosen-last-part)
           q (if (editor-handler/get-selected-text) "" q)
           last-pattern (if wrapped?
@@ -271,7 +272,7 @@
                                               [page (db/get-page page)])))
                                         [chosen' chosen-result])
             ref-text (if (and (de/entity? chosen-result) (not (ldb/page? chosen-result)))
-                       (page-ref/->page-ref (:block/uuid chosen-result))
+                       (ref/->page-ref (:block/uuid chosen-result))
                        (get-page-ref-text chosen'))
             result (when db-based?
                      (when-not (de/entity? chosen-result)
@@ -281,7 +282,7 @@
                                   :split-namespace? true})))
             ref-text' (if result
                         (let [title (:block/title result)]
-                          (page-ref/->page-ref title))
+                          (ref/->page-ref title))
                         ref-text)]
       (p/do!
        (editor-handler/insert-command! id

+ 8 - 1
src/main/frontend/handler/paste.cljs

@@ -236,9 +236,16 @@
    (fn [error]
      (js/console.error error))))
 
+(defn- editing-display-type-block?
+  []
+  (boolean
+   (when-let [editing-block (some-> (state/get-edit-block) :db/id db/entity)]
+     (:logseq.property.node/display-type editing-block))))
+
 (defn- paste-text-or-blocks-aux
   [input e text html]
-  (if (or (thingatpt/markdown-src-at-point input)
+  (if (or (editing-display-type-block?)
+          (thingatpt/markdown-src-at-point input)
           (thingatpt/org-admonition&src-at-point input))
     (when-not (mobile-util/native-ios?)
       (util/stop e)

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

@@ -9,6 +9,7 @@
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.db.persist :as db-persist]
+            [frontend.db.react :as react]
             [frontend.db.restore :as db-restore]
             [frontend.handler.common.config-edn :as config-edn-common-handler]
             [frontend.handler.global-config :as global-config-handler]
@@ -86,7 +87,7 @@
     (when url
       (search/reset-indice! url)
       (db/remove-conn! url)
-      (db/clear-query-state!)
+      (react/clear-query-state!)
       (-> (p/do! (db-persist/delete-graph! url))
           (p/catch (fn [error]
                      (prn "Delete repo failed, error: " error)))))))

+ 2 - 2
src/main/frontend/mobile/action_bar.cljs

@@ -7,13 +7,13 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.mixins :as mixins]
             [frontend.mobile.util :as mobile-util]
+            [frontend.ref :as ref]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util.url :as url-util]
             [goog.dom :as gdom]
             [goog.object :as gobj]
-            [logseq.common.util.block-ref :as block-ref]
             [rum.core :as rum]))
 
 (defn- action-command
@@ -64,7 +64,7 @@
         (action-command "cut" "Cut" #(editor-handler/cut-selection-blocks true))
         (action-command "trash" "Delete" #(editor-handler/delete-block-aux! block))
         (action-command "registered" "Copy ref"
-                        (fn [_event] (editor-handler/copy-block-ref! uuid block-ref/->block-ref)))
+                        (fn [_event] (editor-handler/copy-block-ref! uuid ref/->block-ref)))
         (action-command "link" "Copy url"
                         (fn [_event] (let [current-repo (state/get-current-repo)
                                            tap-f (fn [block-id]

+ 4 - 4
src/main/frontend/mobile/intent.cljs

@@ -1,7 +1,7 @@
 (ns frontend.mobile.intent
-  (:require ["@capacitor/filesystem" :refer [Filesystem]]
+  (:require ["@capacitor/action-sheet" :refer [ActionSheet]]
+            ["@capacitor/filesystem" :refer [Filesystem]]
             ["@capacitor/share" :refer [^js Share]]
-            ["@capacitor/action-sheet" :refer [ActionSheet]]
             ["path" :as node-path]
             ["send-intent" :refer [^js SendIntent]]
             [clojure.pprint :as pprint]
@@ -14,6 +14,7 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.notification :as notification]
             [frontend.mobile.util :as mobile-util]
+            [frontend.ref :as ref]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.fs :as fs-util]
@@ -21,7 +22,6 @@
             [lambdaisland.glogi :as log]
             [logseq.common.config :as common-config]
             [logseq.common.util :as common-util]
-            [logseq.common.util.page-ref :as page-ref]
             [promesa.core :as p]))
 
 (defn open-or-share-file
@@ -121,7 +121,7 @@
              (.copy Filesystem (clj->js {:from url :to path}))
              (fn [error]
                (log/error :copy-file-error {:error error})))
-          url (page-ref/->page-ref title)
+          url (ref/->page-ref title)
           template (get-in (state/get-config)
                            [:quick-capture-templates :text]
                            "**{time}** [[quick capture]]: {url}")]

+ 13 - 0
src/main/frontend/ref.cljs

@@ -0,0 +1,13 @@
+(ns frontend.ref
+  "Reference-related fns"
+  (:require [frontend.config :as config]
+            [logseq.common.util.block-ref :as block-ref]
+            [logseq.common.util.page-ref :as page-ref]))
+
+(defn ->block-ref
+  [id]
+  (if (config/db-based-graph?)
+    (page-ref/->page-ref id)
+    (block-ref/->block-ref id)))
+
+(def ->page-ref page-ref/->page-ref)

+ 9 - 9
src/main/frontend/template.cljs

@@ -2,17 +2,17 @@
   "Provides template related functionality"
   (:require [clojure.string :as string]
             [frontend.date :as date]
-            [frontend.state :as state]
+            [frontend.db.conn :as conn]
             [frontend.db.utils :as db-utils]
-            [logseq.common.util.page-ref :as page-ref]
-            [logseq.db :as ldb]
-            [frontend.db.conn :as conn]))
+            [frontend.ref :as ref]
+            [frontend.state :as state]
+            [logseq.db :as ldb]))
 
 (defn- variable-rules
   []
-  {"today" (page-ref/->page-ref (date/today))
-   "yesterday" (page-ref/->page-ref (date/yesterday))
-   "tomorrow" (page-ref/->page-ref (date/tomorrow))
+  {"today" (ref/->page-ref (date/today))
+   "yesterday" (ref/->page-ref (date/yesterday))
+   "tomorrow" (ref/->page-ref (date/tomorrow))
    "time" (date/get-current-time)
    "current page" (when-let [current-page (or
                                            (state/get-current-page)
@@ -22,7 +22,7 @@
                                  (db-utils/entity [:block/uuid block-uuid])
                                  (ldb/get-page (conn/get-db) current-page))
                           current-page' (:block/title page)]
-                      (when current-page' (page-ref/->page-ref current-page'))))})
+                      (when current-page' (ref/->page-ref current-page'))))})
 
 (def template-re #"<%([^%].*?)%>")
 
@@ -41,5 +41,5 @@
                         :else
                         (if-let [nld (date/nld-parse match)]
                           (let [date (doto (goog.date.DateTime.) (.setTime (.getTime nld)))]
-                            (page-ref/->page-ref (date/journal-name date)))
+                            (ref/->page-ref (date/journal-name date)))
                           match))))))

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

@@ -715,7 +715,7 @@
     [:a.text-xs.opacity-50.hover:opacity-80
      {:href "https://github.com/logseq/logseq/issues/new?labels=from:in-app&template=bug_report.yaml"
       :target "_blank"} "report issue"]]
-   (when content [:pre.m-0.text-sm content])])
+   (when content [:pre.m-0.text-sm (str content)])])
 
 (def component-error
   "Well styled error message for higher level components. Currently same as

+ 13 - 5
src/main/frontend/worker/db/migrate.cljs

@@ -126,10 +126,13 @@
                      props-to-rename)
         titles-tx (->> (d/datoms db :avet :block/title)
                        (keep (fn [d]
-                               (when-let [props (seq (filter (fn [[old _new]] (string/includes? (:v d) (str old))) props-to-rename))]
-                                 (let [title' (reduce (fn [title [old new]]
-                                                        (string/replace title (str old) (str new))) (:v d) props)]
-                                   [:db/add (:e d) :block/title title'])))))
+                               (let [title (:v d)]
+                                 (if (string? title)
+                                   (when-let [props (seq (filter (fn [[old _new]] (string/includes? (:v d) (str old))) props-to-rename))]
+                                     (let [title' (reduce (fn [title [old new]]
+                                                            (string/replace title (str old) (str new))) title props)]
+                                       [:db/add (:e d) :block/title title']))
+                                   [:db/retract (:e d) :block/title])))))
         sorting-tx (->> (d/datoms db :avet :logseq.property.table/sorting)
                         (keep (fn [d]
                                 (when (coll? (:v d))
@@ -1082,7 +1085,7 @@
                (fn [d]
                  (let [entity (d/entity @conn (:e d))]
                    [(when-not (:block/title entity)
-                      [:db/add (:e d) :block/title (:v d)])
+                      [:db/add (:e d) :block/title (str (:v d))])
                     (when-not (:block/uuid entity)
                       [:db/add (:e d) :block/uuid (d/squuid)])]))
                (d/datoms @conn :avet :block/name))
@@ -1304,6 +1307,11 @@
       (let [data (deprecate-logseq-user-ns conn nil)]
         (when (seq data)
           (d/transact! conn data {:fix-db? true})))
+      (let [data1 (rename-repeated-properties conn nil)
+            data2 (rename-task-properties conn nil)
+            data (concat data1 data2)]
+        (when (seq data)
+          (d/transact! conn data {:fix-db? true})))
       (when (seq invalid-entity-ids)
         (fix-invalid-data! conn invalid-entity-ids))
 

+ 2 - 2
src/main/frontend/worker/handler/page/db_based/page.cljs

@@ -8,10 +8,10 @@
             [logseq.db :as ldb]
             [logseq.db.common.entity-plus :as entity-plus]
             [logseq.db.common.order :as db-order]
-            [logseq.db.common.property-util :as db-property-util]
             [logseq.db.frontend.class :as db-class]
             [logseq.db.frontend.entity-util :as entity-util]
             [logseq.db.frontend.malli-schema :as db-malli-schema]
+            [logseq.db.frontend.property :as db-property]
             [logseq.db.frontend.property.build :as db-property-build]
             [logseq.db.sqlite.util :as sqlite-util]
             [logseq.graph-parser.block :as gp-block]
@@ -49,7 +49,7 @@
                 (keep (fn [[k v]]
                         ;; TODO: Pass in property type in order to support property
                         ;; types other than :default
-                        (when (db-property-util/built-in-has-ref-value? k)
+                        (when (db-property/built-in-has-ref-value? k)
                           [k v])))
                 (into {})))]
       (cond-> (if class?

+ 3 - 2
src/main/logseq/api.cljs

@@ -10,6 +10,7 @@
             [frontend.db.async :as db-async]
             [frontend.db.conn :as conn]
             [frontend.db.model :as db-model]
+            [frontend.db.file-based.model :as file-model]
             [frontend.db.query-custom :as query-custom]
             [frontend.db.query-dsl :as query-dsl]
             [frontend.db.query-react :as query-react]
@@ -1046,13 +1047,13 @@
 (defn ^:export get_pages_from_namespace
   [ns]
   (when-let [repo (and ns (state/get-current-repo))]
-    (when-let [pages (db-model/get-namespace-pages repo ns)]
+    (when-let [pages (file-model/get-namespace-pages repo ns)]
       (bean/->js (sdk-utils/normalize-keyword-for-json pages)))))
 
 (defn ^:export get_pages_tree_from_namespace
   [ns]
   (when-let [repo (and ns (state/get-current-repo))]
-    (when-let [pages (db-model/get-namespace-hierarchy repo ns)]
+    (when-let [pages (file-model/get-namespace-hierarchy repo ns)]
       (bean/->js (sdk-utils/normalize-keyword-for-json pages)))))
 
 (defn- first-child-of-block

+ 0 - 1
src/resources/dicts/ca.edn

@@ -50,7 +50,6 @@
  :relaunch-confirm-to-work                          "Ha de reiniciar la aplicació per fer que funcioni. Desitja reiniciar-la ara?"
  :remove-background                                 "Eliminar el fons"
  :remove-heading                                    "Eliminar encapçalament"
- :remove-orphaned-pages                             "Eliminar pàgines orfes?"
  :save                                              "Desar"
  :settings                                          "Opcions"
  :settings-of-plugins                               "Opcions d'Extensions"

+ 0 - 1
src/resources/dicts/cs.edn

@@ -432,7 +432,6 @@
  :all-pages                                        "Všechny stránky"
  :all-whiteboards                                  "Všechny tabule"
  :all-files                                        "Všechny soubory"
- :remove-orphaned-pages                            "Odstranit stránky bez rodičovské stránky?"
  :all-journals                                     "Všechny denníky"
  :settings                                         "Nastavení"
  :settings-of-plugins                              "Zásuvné moduly"

+ 0 - 1
src/resources/dicts/de.edn

@@ -48,7 +48,6 @@
  :relaunch-confirm-to-work "Sie sollten die App neu starten, damit sie funktioniert. Möchten Sie sie jetzt neu starten?"
  :remove-background "Hintergrund entfernen"
  :remove-heading "Überschrift entfernen"
- :remove-orphaned-pages "Nie referenzierte Seiten löschen?"
  :save "Speichern"
  :settings "Einstellungen"
  :settings-of-plugins "Plugins"

+ 0 - 1
src/resources/dicts/en.edn

@@ -464,7 +464,6 @@
  :all-pages/failed-to-delete-pages "These pages had their content deleted but were unable to be deleted: {1}. See javascript console for more details."
  :all-whiteboards "All whiteboards"
  :all-files "All files"
- :remove-orphaned-pages "Remove orphaned pages?"
  :all-journals "All journals"
  :settings "Settings"
  :settings-of-plugins "Plugins"

+ 0 - 1
src/resources/dicts/es.edn

@@ -50,7 +50,6 @@
  :relaunch-confirm-to-work                          "Debe relanzar la aplicación para hacer que funcione. ¿Desea reiniciarla ahora?"
  :remove-background                                 "Eliminar el fondo"
  :remove-heading                                    "Eliminar encabezado"
- :remove-orphaned-pages                             "¿Eliminar páginas huérfanas?"
  :save                                              "Guardar"
  :settings                                          "Opciones"
  :settings-of-plugins                               "Opciones de Extensiones"

+ 0 - 1
src/resources/dicts/fr.edn

@@ -373,7 +373,6 @@
     :export-transparent-background "Arrière-plan transparent"
     :home "Accueil"
     :new-page "Nouvelle page :"
-    :remove-orphaned-pages "Supprimer les pages orphelines ?"
     :toggle-theme "Changer le thème"
     :whiteboards "Tableaux blancs"
     :bug-report/clipboard-inspector-title "Inspecteur du presse-papier"

+ 0 - 1
src/resources/dicts/id.edn

@@ -429,7 +429,6 @@
  :all-pages "Semua halaman"
  :all-whiteboards "Semua papan tulis"
  :all-files "Semua berkas"
- :remove-orphaned-pages "Hapus halaman yang tidak terhubung?"
  :all-journals "Semua jurnal"
  :settings "Pengaturan"
  :settings-of-plugins "Pengaya"

+ 0 - 1
src/resources/dicts/it.edn

@@ -298,7 +298,6 @@
  :loading "Caricamento..."
  :new-page "Nuova pagina:"
  :remove-heading "Rimuovi titolo"
- :remove-orphaned-pages "Rimuovere le pagine orfane?"
  :toggle-theme "Inverti tema"
  :untitled "Senza titolo"
  :whiteboards "Lavagne"

+ 0 - 1
src/resources/dicts/ja.edn

@@ -449,7 +449,6 @@
  :all-pages "全ページ"
  :all-whiteboards "全てのホワイトボード"
  :all-files "全ファイル"
- :remove-orphaned-pages "孤立したページを削除しますか?"
  :all-journals "全日誌"
  :settings "設定"
  :settings-of-plugins "プラグイン設定"

+ 0 - 1
src/resources/dicts/nb-no.edn

@@ -548,7 +548,6 @@
 
  :loading "Laster..."
  :new-page "Ny side:"
- :remove-orphaned-pages "Fjern foreldreløse sider?"
  :bug-report/inspector-page-btn-back "Gå tilbake"
  :bug-report/inspector-page-btn-copy "Kopier resultatet"
  :bug-report/inspector-page-btn-create-issue "Opprett et problem"

+ 0 - 1
src/resources/dicts/pl.edn

@@ -314,7 +314,6 @@
  :loading                                         "Wczytywanie..."
  :new-page                                        "Nowa strona:"
  :remove-heading                                  "Usuń nagłówek"
- :remove-orphaned-pages                           "Usunąć nie linkowane strony?"
  :toggle-theme                                    "Przełącz motyw"
  :untitled                                        "Bez tytułu"
  :whiteboards                                     "Tablice"

+ 0 - 1
src/resources/dicts/pt-br.edn

@@ -433,7 +433,6 @@
  :all-pages "Todas as páginas"
  :all-whiteboards "Todos os whiteboards"
  :all-files "Todos os arquivos"
- :remove-orphaned-pages "Remover páginas órfãs?"
  :all-journals "Todos os diários"
  :settings "Configurações"
  :plugins "Plugins"

+ 0 - 1
src/resources/dicts/ru.edn

@@ -310,7 +310,6 @@
  :all-pages                                            "Все страницы"
  :all-whiteboards                                      "Все интерактивные доски"
  :all-files                                            "Все файлы"
- :remove-orphaned-pages                                "Удалить страницы без родителя?"
  :all-journals                                         "Все журналы"
  :settings                                             "Настройки"
  :settings-of-plugins                                  "Расширения"

+ 0 - 1
src/resources/dicts/sk.edn

@@ -432,7 +432,6 @@
  :all-pages                                        "Všetky stránky"
  :all-whiteboards                                  "Všetky tabule"
  :all-files                                        "Všetky súbory"
- :remove-orphaned-pages                            "Odstrániť stránky bez rodičovskej stránky?"
  :all-journals                                     "Všetky denníky"
  :settings                                         "Nastavenia"
  :settings-of-plugins                              "Doplnky"

+ 0 - 1
src/resources/dicts/tr.edn

@@ -445,7 +445,6 @@
  :all-pages "Tüm sayfalar"
  :all-whiteboards "Tüm beyaz tahtalar"
  :all-files "Tüm dosyalar"
- :remove-orphaned-pages "Yalnız bırakılmış sayfalar kaldırılsın mı?"
  :all-journals "Bütün günlükler"
  :settings "Ayarlar"
  :settings-of-plugins "Eklenti ayarları"

+ 0 - 1
src/resources/dicts/zh-cn.edn

@@ -222,7 +222,6 @@
  :all-graphs "所有图谱"
  :all-pages "所有页面"
  :all-files "所有文件"
- :remove-orphaned-pages "删除孤立页面?"
  :settings "设置"
  :settings-of-plugins "插件设置"
  :plugins "插件"

+ 0 - 1
src/resources/dicts/zh-hant.edn

@@ -183,7 +183,6 @@
  :all-pages "所有分頁"
  :all-whiteboards "所有白板"
  :all-files "所有資料"
- :remove-orphaned-pages "移除孤立頁面?"
  :all-journals "所有日記"
  :settings "設定"
  :settings-of-plugins "外掛設定"

+ 2 - 2
src/test/frontend/components/file_based/query_table_test.cljs

@@ -4,7 +4,7 @@
             [frontend.state :as state]
             [frontend.components.file-based.query-table :as query-table]
             [frontend.db.query-dsl :as query-dsl]
-            [frontend.db :as db]
+            [frontend.db.react :as react]
             [frontend.util :as util]))
 
 (use-fixtures :each {:before test-helper/start-test-db!
@@ -12,7 +12,7 @@
 
 (defn- dsl-query
   [s]
-  (db/clear-query-state!)
+  (react/clear-query-state!)
   (when-let [result (query-dsl/query test-helper/test-db s)]
     (map first (deref result))))
 

+ 33 - 0
src/test/frontend/db/file_based/model_test.cljs

@@ -0,0 +1,33 @@
+(ns frontend.db.file-based.model-test
+  (:require [cljs.test :refer [use-fixtures deftest is]]
+            [frontend.db.file-based.model :as file-model]
+            [frontend.test.helper :as test-helper :refer [load-test-files]]))
+
+(use-fixtures :each {:before test-helper/start-test-db!
+                     :after test-helper/destroy-test-db!})
+
+(deftest get-namespace-pages
+  (load-test-files [{:file/path "pages/a.b.c.md"
+                     :file/content "foo"}
+                    {:file/path "pages/b.c.md"
+                     :file/content "bar"}
+                    {:file/path "pages/b.d.md"
+                     :file/content "baz"}])
+
+  (is (= ["a/b" "a/b/c"]
+         (map :block/name (file-model/get-namespace-pages test-helper/test-db "a"))))
+
+  (is (= ["b/c" "b/d"]
+         (map :block/name (file-model/get-namespace-pages test-helper/test-db "b")))))
+
+(deftest get-page-namespace-routes
+  (load-test-files [{:file/path "pages/a.b.c.md"
+                     :file/content "foo"}
+                    {:file/path "pages/b.c.md"
+                     :file/content "bar"}
+                    {:file/path "pages/b.d.md"
+                     :file/content "baz"}])
+
+  (is (= '()
+         (map :block/name (file-model/get-page-namespace-routes test-helper/test-db "b/c")))
+      "Empty if page exists"))

+ 0 - 26
src/test/frontend/db/model_test.cljs

@@ -9,32 +9,6 @@
 (use-fixtures :each {:before test-helper/start-test-db!
                      :after test-helper/destroy-test-db!})
 
-(deftest get-namespace-pages
-  (load-test-files [{:file/path "pages/a.b.c.md"
-                     :file/content "foo"}
-                    {:file/path "pages/b.c.md"
-                     :file/content "bar"}
-                    {:file/path "pages/b.d.md"
-                     :file/content "baz"}])
-
-  (is (= ["a/b" "a/b/c"]
-         (map :block/name (model/get-namespace-pages test-helper/test-db "a"))))
-
-  (is (= ["b/c" "b/d"]
-         (map :block/name (model/get-namespace-pages test-helper/test-db "b")))))
-
-(deftest get-page-namespace-routes
-  (load-test-files [{:file/path "pages/a.b.c.md"
-                     :file/content "foo"}
-                    {:file/path "pages/b.c.md"
-                     :file/content "bar"}
-                    {:file/path "pages/b.d.md"
-                     :file/content "baz"}])
-
-  (is (= '()
-         (map :block/name (model/get-page-namespace-routes test-helper/test-db "b/c")))
-      "Empty if page exists"))
-
 (deftest test-page-alias-with-multiple-alias
   (load-test-files [{:file/path "aa.md"
                      :file/content "alias:: ab, ac"}

+ 3 - 2
src/test/frontend/db/query_dsl_test.cljs

@@ -3,6 +3,7 @@
             [clojure.string :as string]
             [frontend.db :as db]
             [frontend.db.query-dsl :as query-dsl]
+            [frontend.db.react :as react]
             [frontend.test.helper :as test-helper :include-macros true :refer [load-test-files load-test-files-for-db-graph]]
             [frontend.util :as util]
             [logseq.common.util.page-ref :as page-ref]))
@@ -72,13 +73,13 @@
 
 (defn- dsl-query
   [s]
-  (db/clear-query-state!)
+  (react/clear-query-state!)
   (when-let [result (dsl-query* test-helper/test-db (->smart-query s))]
     (map first (deref result))))
 
 (defn- custom-query
   [query]
-  (db/clear-query-state!)
+  (react/clear-query-state!)
   (when-let [result (with-redefs [query-dsl/db-block-attrs db-block-attrs]
                       (query-dsl/custom-query test-helper/test-db query {}))]
     (map first (deref result))))